mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	Compare commits
	
		
			149 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3afbbfe921 | ||
| 
						 | 
					bfce841b04 | ||
| 
						 | 
					139fc7cfee | ||
| 
						 | 
					596eebb2b6 | ||
| 
						 | 
					1d5d745851 | ||
| 
						 | 
					3dabfd4933 | ||
| 
						 | 
					6ee6731290 | ||
| 
						 | 
					602fe45936 | ||
| 
						 | 
					e2da9cd21f | ||
| 
						 | 
					c0b917b7eb | ||
| 
						 | 
					54ea58ddf0 | ||
| 
						 | 
					0158725387 | ||
| 
						 | 
					f3cacf1332 | ||
| 
						 | 
					a15dc93011 | ||
| 
						 | 
					66b31786d3 | ||
| 
						 | 
					931ddfec6d | ||
| 
						 | 
					7e0a5b17db | ||
| 
						 | 
					07688231c2 | ||
| 
						 | 
					21eaeb8418 | ||
| 
						 | 
					9a929ad17f | ||
| 
						 | 
					c19ac41b34 | ||
| 
						 | 
					fd85d31cb4 | ||
| 
						 | 
					c9e4d7a564 | ||
| 
						 | 
					9990430e32 | ||
| 
						 | 
					6f5656ab0e | ||
| 
						 | 
					e4a876cee1 | ||
| 
						 | 
					abb534ba7a | ||
| 
						 | 
					65dceb6a40 | ||
| 
						 | 
					db26f0aca9 | ||
| 
						 | 
					76878fd69b | ||
| 
						 | 
					3444fa2dd7 | ||
| 
						 | 
					caa2aeaa52 | ||
| 
						 | 
					11300ee582 | ||
| 
						 | 
					c6b78c3d31 | ||
| 
						 | 
					4c40aa5be9 | ||
| 
						 | 
					50f2e90b76 | ||
| 
						 | 
					5d11ccc9e1 | ||
| 
						 | 
					93860af542 | ||
| 
						 | 
					7bf5834f2c | ||
| 
						 | 
					1fbdd9335f | ||
| 
						 | 
					e9061a537c | ||
| 
						 | 
					ed664a9e1d | ||
| 
						 | 
					4cb18601ff | ||
| 
						 | 
					3abb25166c | ||
| 
						 | 
					9e6ad64d48 | ||
| 
						 | 
					b51d7c459e | ||
| 
						 | 
					d3b6f001fe | ||
| 
						 | 
					e938f1d945 | ||
| 
						 | 
					7284327a00 | ||
| 
						 | 
					919f3f11e2 | ||
| 
						 | 
					3cee15e6f9 | ||
| 
						 | 
					34e3644ada | ||
| 
						 | 
					14bd120cdc | ||
| 
						 | 
					3e40f8bebc | ||
| 
						 | 
					df5f1d9dca | ||
| 
						 | 
					457ee1ab5a | ||
| 
						 | 
					4f64688902 | ||
| 
						 | 
					117dcf1c02 | ||
| 
						 | 
					4529a262c0 | ||
| 
						 | 
					ff24f81a05 | ||
| 
						 | 
					e3cb4f9d0e | ||
| 
						 | 
					9b7890f1cc | ||
| 
						 | 
					eb064dfda2 | ||
| 
						 | 
					f9e66e5a46 | ||
| 
						 | 
					ef89260cf1 | ||
| 
						 | 
					315d928626 | ||
| 
						 | 
					5525452bdf | ||
| 
						 | 
					987cd277f6 | ||
| 
						 | 
					5cdfde2ebf | ||
| 
						 | 
					1cd6233cef | ||
| 
						 | 
					cb81e39f7a | ||
| 
						 | 
					8efd6b32e2 | ||
| 
						 | 
					c95d9603ea | ||
| 
						 | 
					9169b39458 | ||
| 
						 | 
					80eb50655a | ||
| 
						 | 
					b16c555541 | ||
| 
						 | 
					b5b44364e3 | ||
| 
						 | 
					6af58022c8 | ||
| 
						 | 
					e48b460a0a | ||
| 
						 | 
					2cd2614eaa | ||
| 
						 | 
					0129e76ef5 | ||
| 
						 | 
					6896dad675 | ||
| 
						 | 
					1ed4323005 | ||
| 
						 | 
					049af0d3d0 | ||
| 
						 | 
					f5727d83dd | ||
| 
						 | 
					912ce27421 | ||
| 
						 | 
					b3549bb5ec | ||
| 
						 | 
					491cbeca67 | ||
| 
						 | 
					895d92ffe5 | ||
| 
						 | 
					4b11f967bd | ||
| 
						 | 
					1e73dd2446 | ||
| 
						 | 
					315026c2c5 | ||
| 
						 | 
					16dfd9ffbe | ||
| 
						 | 
					16f7b43903 | ||
| 
						 | 
					043febdbc9 | ||
| 
						 | 
					60f91d56f0 | ||
| 
						 | 
					16fc15ae6a | ||
| 
						 | 
					ef8f6d99f1 | ||
| 
						 | 
					4b688135f9 | ||
| 
						 | 
					e24861a546 | ||
| 
						 | 
					128cc34344 | ||
| 
						 | 
					f82a805478 | ||
| 
						 | 
					0dced15c1a | ||
| 
						 | 
					db9342c854 | ||
| 
						 | 
					79c1d48532 | ||
| 
						 | 
					05b9864086 | ||
| 
						 | 
					ff508c9c9b | ||
| 
						 | 
					f96c1a2c79 | ||
| 
						 | 
					ce756ee89f | ||
| 
						 | 
					f2e9d4b851 | ||
| 
						 | 
					e878d743f4 | ||
| 
						 | 
					3fa14d89a2 | ||
| 
						 | 
					bcb722daec | ||
| 
						 | 
					8add1dfacc | ||
| 
						 | 
					aa6ed1b7c1 | ||
| 
						 | 
					95cb921097 | ||
| 
						 | 
					6730df9e8c | ||
| 
						 | 
					b577500a54 | ||
| 
						 | 
					fe46185407 | ||
| 
						 | 
					69a2a29c33 | ||
| 
						 | 
					f766719895 | ||
| 
						 | 
					e2ddc42377 | ||
| 
						 | 
					3521177a34 | ||
| 
						 | 
					c8bb0ecf52 | ||
| 
						 | 
					dbe6136348 | ||
| 
						 | 
					6d1f7e90cf | ||
| 
						 | 
					42663a687c | ||
| 
						 | 
					73c90c26d4 | ||
| 
						 | 
					c579ad92b5 | ||
| 
						 | 
					602c5da953 | ||
| 
						 | 
					1980e59ac2 | ||
| 
						 | 
					28508792ba | ||
| 
						 | 
					3e23dad075 | ||
| 
						 | 
					b13b9d3dbd | ||
| 
						 | 
					4072f28e60 | ||
| 
						 | 
					dbeef6bb02 | ||
| 
						 | 
					fec35440db | ||
| 
						 | 
					f8ea50cc7a | ||
| 
						 | 
					0e53a16cca | ||
| 
						 | 
					7eaba6ba8a | ||
| 
						 | 
					ff16099c6d | ||
| 
						 | 
					a516a7ba0f | ||
| 
						 | 
					11bce6fd3d | ||
| 
						 | 
					3fb906dc02 | ||
| 
						 | 
					3a00a690c9 | ||
| 
						 | 
					a2b7cc1bb1 | ||
| 
						 | 
					04a77b1f42 | ||
| 
						 | 
					f523372d07 | ||
| 
						 | 
					e39c238ef4 | 
@@ -1,44 +1,57 @@
 | 
				
			|||||||
 | 
					# The full repository name
 | 
				
			||||||
repo: go-gitea/gitea
 | 
					repo: go-gitea/gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Service type (gitea or github)
 | 
				
			||||||
 | 
					service: github
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Base URL for Gitea instance if using gitea service type (optional)
 | 
				
			||||||
 | 
					# Default: https://gitea.com
 | 
				
			||||||
 | 
					base-url:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Changelog groups and which labeled PRs to add to each group
 | 
				
			||||||
groups:
 | 
					groups:
 | 
				
			||||||
  - 
 | 
					  -
 | 
				
			||||||
    name: BREAKING
 | 
					    name: BREAKING
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/breaking
 | 
					      - kind/breaking
 | 
				
			||||||
  - 
 | 
					  -
 | 
				
			||||||
    name: FEATURE
 | 
					    name: FEATURE
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/feature
 | 
					      - kind/feature
 | 
				
			||||||
 | 
					  -
 | 
				
			||||||
 | 
					    name: SECURITY
 | 
				
			||||||
 | 
					    labels:
 | 
				
			||||||
 | 
					      - kind/security
 | 
				
			||||||
  -
 | 
					  -
 | 
				
			||||||
    name: BUGFIXES
 | 
					    name: BUGFIXES
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/bug
 | 
					      - kind/bug
 | 
				
			||||||
  - 
 | 
					  -
 | 
				
			||||||
    name: ENHANCEMENT
 | 
					    name: ENHANCEMENT
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/enhancement
 | 
					      - kind/enhancement
 | 
				
			||||||
      - kind/refactor
 | 
					      - kind/refactor
 | 
				
			||||||
      - kind/ui
 | 
					      - kind/ui
 | 
				
			||||||
  -
 | 
					  -
 | 
				
			||||||
    name: SECURITY
 | 
					 | 
				
			||||||
    labels:
 | 
					 | 
				
			||||||
      - kind/security
 | 
					 | 
				
			||||||
  - 
 | 
					 | 
				
			||||||
    name: TESTING
 | 
					    name: TESTING
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/testing
 | 
					      - kind/testing
 | 
				
			||||||
  - 
 | 
					  -
 | 
				
			||||||
    name: TRANSLATION
 | 
					    name: TRANSLATION
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/translation
 | 
					      - kind/translation
 | 
				
			||||||
  - 
 | 
					  -
 | 
				
			||||||
    name: BUILD
 | 
					    name: BUILD
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/build
 | 
					      - kind/build
 | 
				
			||||||
      - kind/lint
 | 
					      - kind/lint
 | 
				
			||||||
  - 
 | 
					  -
 | 
				
			||||||
    name: DOCS
 | 
					    name: DOCS
 | 
				
			||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - kind/docs
 | 
					      - kind/docs
 | 
				
			||||||
  - 
 | 
					  -
 | 
				
			||||||
    name: MISC
 | 
					    name: MISC
 | 
				
			||||||
    default: true
 | 
					    default: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# regex indicating which labels to skip for the changelog
 | 
				
			||||||
 | 
					skip-labels: skip-changelog|backport\/.+
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										413
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										413
									
								
								.drone.yml
									
									
									
									
									
								
							@@ -1,6 +1,61 @@
 | 
				
			|||||||
---
 | 
					---
 | 
				
			||||||
kind: pipeline
 | 
					kind: pipeline
 | 
				
			||||||
name: testing
 | 
					name: compliance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					platform:
 | 
				
			||||||
 | 
					  os: linux
 | 
				
			||||||
 | 
					  arch: arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					workspace:
 | 
				
			||||||
 | 
					  base: /go
 | 
				
			||||||
 | 
					  path: src/code.gitea.io/gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					steps:
 | 
				
			||||||
 | 
					  - name: pre-build
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: node:10 # this step is kept at the lowest version of node that we support
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - make css
 | 
				
			||||||
 | 
					      - make js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: build-without-gcc
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: golang:1.11 # this step is kept as the lowest version of golang that we support
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      GO111MODULE: on
 | 
				
			||||||
 | 
					      GOPROXY: off
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - go build -mod=vendor -o gitea_no_gcc # test if build succeeds without the sqlite tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: build-linux-386
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: golang:1.13
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      GO111MODULE: on
 | 
				
			||||||
 | 
					      GOPROXY: off
 | 
				
			||||||
 | 
					      GOOS: linux
 | 
				
			||||||
 | 
					      GOARCH: 386
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - go build -mod=vendor -o gitea_linux_386 # test if compatible with 32 bit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: check
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: golang:1.13
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - make clean
 | 
				
			||||||
 | 
					      - make golangci-lint
 | 
				
			||||||
 | 
					      - make revive
 | 
				
			||||||
 | 
					      - make swagger-check
 | 
				
			||||||
 | 
					      - make swagger-validate
 | 
				
			||||||
 | 
					      - make test-vendor
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
 | 
				
			||||||
 | 
					      GOSUMDB: sum.golang.org
 | 
				
			||||||
 | 
					      TAGS: bindata sqlite sqlite_unlock_notify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					kind: pipeline
 | 
				
			||||||
 | 
					name: testing-amd64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
platform:
 | 
					platform:
 | 
				
			||||||
  os: linux
 | 
					  os: linux
 | 
				
			||||||
@@ -25,15 +80,9 @@ services:
 | 
				
			|||||||
      MYSQL_ALLOW_EMPTY_PASSWORD: yes
 | 
					      MYSQL_ALLOW_EMPTY_PASSWORD: yes
 | 
				
			||||||
      MYSQL_DATABASE: testgitea
 | 
					      MYSQL_DATABASE: testgitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: pgsql
 | 
					 | 
				
			||||||
    pull: default
 | 
					 | 
				
			||||||
    image: postgres:9.5
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      POSTGRES_DB: test
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: mssql
 | 
					  - name: mssql
 | 
				
			||||||
    pull: default
 | 
					    pull: default
 | 
				
			||||||
    image: microsoft/mssql-server-linux:latest
 | 
					    image: mcr.microsoft.com/mssql/server:latest
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      ACCEPT_EULA: Y
 | 
					      ACCEPT_EULA: Y
 | 
				
			||||||
      MSSQL_PID: Standard
 | 
					      MSSQL_PID: Standard
 | 
				
			||||||
@@ -54,52 +103,23 @@ steps:
 | 
				
			|||||||
        exclude:
 | 
					        exclude:
 | 
				
			||||||
          - pull_request
 | 
					          - pull_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: pre-build
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: node:10 # this step is kept at the lowest version of node that we support
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - make css
 | 
					 | 
				
			||||||
      - make js
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: build-without-gcc
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: golang:1.11 # this step is kept as the lowest version of golang that we support
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      GO111MODULE: on
 | 
					 | 
				
			||||||
      GOPROXY: off
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
					 | 
				
			||||||
      - go build -mod=vendor -o gitea_no_gcc # test if build succeeds without the sqlite tag
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: build-linux-386
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: golang:1.13
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      GO111MODULE: on
 | 
					 | 
				
			||||||
      GOPROXY: off
 | 
					 | 
				
			||||||
      GOOS: linux
 | 
					 | 
				
			||||||
      GOARCH: 386
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
					 | 
				
			||||||
      - go build -mod=vendor -o gitea_linux_386 # test if compatible with 32 bit
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: build
 | 
					  - name: build
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: golang:1.13
 | 
					    image: golang:1.13
 | 
				
			||||||
    commands:
 | 
					    commands:
 | 
				
			||||||
      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
					      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
				
			||||||
      - make clean
 | 
					 | 
				
			||||||
      - make golangci-lint
 | 
					 | 
				
			||||||
      - make revive
 | 
					 | 
				
			||||||
      - make swagger-check
 | 
					 | 
				
			||||||
      - make swagger-validate
 | 
					 | 
				
			||||||
      - make test-vendor
 | 
					 | 
				
			||||||
      - make build
 | 
					      - make build
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
 | 
					      GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
 | 
				
			||||||
      GOSUMDB: sum.golang.org
 | 
					      GOSUMDB: sum.golang.org
 | 
				
			||||||
      TAGS: bindata sqlite sqlite_unlock_notify
 | 
					      TAGS: bindata sqlite sqlite_unlock_notify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: tag-pre-condition
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: alpine/git
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - git update-ref refs/heads/tag_test ${DRONE_COMMIT_SHA}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: unit-test
 | 
					  - name: unit-test
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: golang:1.13
 | 
					    image: golang:1.13
 | 
				
			||||||
@@ -108,70 +128,8 @@ steps:
 | 
				
			|||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      GOPROXY: off
 | 
					      GOPROXY: off
 | 
				
			||||||
      TAGS: bindata sqlite sqlite_unlock_notify
 | 
					      TAGS: bindata sqlite sqlite_unlock_notify
 | 
				
			||||||
    depends_on:
 | 
					      GITHUB_READ_TOKEN:
 | 
				
			||||||
      - build
 | 
					        from_secret: github_read_token
 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      branch:
 | 
					 | 
				
			||||||
        - master
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        - push
 | 
					 | 
				
			||||||
        - pull_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: release-test
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: golang:1.13
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - make test
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      GOPROXY: off
 | 
					 | 
				
			||||||
      TAGS: bindata sqlite sqlite_unlock_notify
 | 
					 | 
				
			||||||
    depends_on:
 | 
					 | 
				
			||||||
      - build
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      branch:
 | 
					 | 
				
			||||||
        - "release/*"
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        - push
 | 
					 | 
				
			||||||
        - pull_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: tag-pre-condition
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: alpine/git
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - git update-ref refs/heads/tag_test ${DRONE_COMMIT_SHA}
 | 
					 | 
				
			||||||
    depends_on:
 | 
					 | 
				
			||||||
      - build
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        - tag
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: tag-test
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: golang:1.13
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - make test
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      GOPROXY: off
 | 
					 | 
				
			||||||
      TAGS: bindata
 | 
					 | 
				
			||||||
    depends_on:
 | 
					 | 
				
			||||||
      - tag-pre-condition
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        - tag
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: test-sqlite
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: golang:1.13
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
 | 
					 | 
				
			||||||
      - apt-get install -y git-lfs
 | 
					 | 
				
			||||||
      - timeout -s ABRT 20m make test-sqlite-migration
 | 
					 | 
				
			||||||
      - timeout -s ABRT 20m make test-sqlite
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      GOPROXY: off
 | 
					 | 
				
			||||||
      TAGS: bindata
 | 
					 | 
				
			||||||
    depends_on:
 | 
					 | 
				
			||||||
      - build
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: test-mysql
 | 
					  - name: test-mysql
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
@@ -187,30 +145,6 @@ steps:
 | 
				
			|||||||
      TEST_LDAP: 1
 | 
					      TEST_LDAP: 1
 | 
				
			||||||
    depends_on:
 | 
					    depends_on:
 | 
				
			||||||
      - build
 | 
					      - build
 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      branch:
 | 
					 | 
				
			||||||
        - master
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        - push
 | 
					 | 
				
			||||||
        - pull_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: tag-test-mysql
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: golang:1.13
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
 | 
					 | 
				
			||||||
      - apt-get install -y git-lfs
 | 
					 | 
				
			||||||
      - timeout -s ABRT 20m make test-mysql-migration
 | 
					 | 
				
			||||||
      - timeout -s ABRT 20m make test-mysql
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      GOPROXY: off
 | 
					 | 
				
			||||||
      TAGS: bindata
 | 
					 | 
				
			||||||
      TEST_LDAP: 1
 | 
					 | 
				
			||||||
    depends_on:
 | 
					 | 
				
			||||||
      - build
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        - tag
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: test-mysql8
 | 
					  - name: test-mysql8
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
@@ -227,21 +161,6 @@ steps:
 | 
				
			|||||||
    depends_on:
 | 
					    depends_on:
 | 
				
			||||||
      - build
 | 
					      - build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: test-pgsql
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: golang:1.13
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
 | 
					 | 
				
			||||||
      - apt-get install -y git-lfs
 | 
					 | 
				
			||||||
      - timeout -s ABRT 20m make test-pgsql-migration
 | 
					 | 
				
			||||||
      - timeout -s ABRT 20m make test-pgsql
 | 
					 | 
				
			||||||
    environment:
 | 
					 | 
				
			||||||
      GOPROXY: off
 | 
					 | 
				
			||||||
      TAGS: bindata
 | 
					 | 
				
			||||||
      TEST_LDAP: 1
 | 
					 | 
				
			||||||
    depends_on:
 | 
					 | 
				
			||||||
      - build
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: test-mssql
 | 
					  - name: test-mssql
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: golang:1.13
 | 
					    image: golang:1.13
 | 
				
			||||||
@@ -293,13 +212,90 @@ steps:
 | 
				
			|||||||
        - push
 | 
					        - push
 | 
				
			||||||
        - pull_request
 | 
					        - pull_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					kind: pipeline
 | 
				
			||||||
 | 
					name: testing-arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					platform:
 | 
				
			||||||
 | 
					  os: linux
 | 
				
			||||||
 | 
					  arch: arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					workspace:
 | 
				
			||||||
 | 
					  base: /go
 | 
				
			||||||
 | 
					  path: src/code.gitea.io/gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  - name: pgsql
 | 
				
			||||||
 | 
					    pull: default
 | 
				
			||||||
 | 
					    image: postgres:9.5
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      POSTGRES_DB: test
 | 
				
			||||||
 | 
					      POSTGRES_PASSWORD: postgres
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: ldap
 | 
				
			||||||
 | 
					    pull: default
 | 
				
			||||||
 | 
					    image: gitea/test-openldap:latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					steps:
 | 
				
			||||||
 | 
					  - name: fetch-tags
 | 
				
			||||||
 | 
					    pull: default
 | 
				
			||||||
 | 
					    image: docker:git
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - git fetch --tags --force
 | 
				
			||||||
 | 
					    when:
 | 
				
			||||||
 | 
					      event:
 | 
				
			||||||
 | 
					        exclude:
 | 
				
			||||||
 | 
					          - pull_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: build
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: golang:1.13
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
				
			||||||
 | 
					      - make build
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
 | 
				
			||||||
 | 
					      GOSUMDB: sum.golang.org
 | 
				
			||||||
 | 
					      TAGS: bindata sqlite sqlite_unlock_notify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: test-sqlite
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: golang:1.13
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
 | 
				
			||||||
 | 
					      - apt-get install -y git-lfs
 | 
				
			||||||
 | 
					      - timeout -s ABRT 20m make test-sqlite-migration
 | 
				
			||||||
 | 
					      - timeout -s ABRT 20m make test-sqlite
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      GOPROXY: off
 | 
				
			||||||
 | 
					      TAGS: bindata
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - name: test-pgsql
 | 
				
			||||||
 | 
					    pull: always
 | 
				
			||||||
 | 
					    image: golang:1.13
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
 | 
				
			||||||
 | 
					      - apt-get install -y git-lfs
 | 
				
			||||||
 | 
					      - timeout -s ABRT 20m make test-pgsql-migration
 | 
				
			||||||
 | 
					      - timeout -s ABRT 20m make test-pgsql
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      GOPROXY: off
 | 
				
			||||||
 | 
					      TAGS: bindata
 | 
				
			||||||
 | 
					      TEST_LDAP: 1
 | 
				
			||||||
 | 
					    depends_on:
 | 
				
			||||||
 | 
					      - build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
kind: pipeline
 | 
					kind: pipeline
 | 
				
			||||||
name: translations
 | 
					name: translations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
platform:
 | 
					platform:
 | 
				
			||||||
  os: linux
 | 
					  os: linux
 | 
				
			||||||
  arch: amd64
 | 
					  arch: arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
workspace:
 | 
					workspace:
 | 
				
			||||||
  base: /go
 | 
					  base: /go
 | 
				
			||||||
@@ -378,7 +374,8 @@ trigger:
 | 
				
			|||||||
    - push
 | 
					    - push
 | 
				
			||||||
 | 
					
 | 
				
			||||||
depends_on:
 | 
					depends_on:
 | 
				
			||||||
  - testing
 | 
					  - testing-amd64
 | 
				
			||||||
 | 
					  - testing-arm64
 | 
				
			||||||
  - translations
 | 
					  - translations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
@@ -390,7 +387,7 @@ steps:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  - name: static
 | 
					  - name: static
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: techknowlogick/xgo:latest
 | 
					    image: techknowlogick/xgo:go-1.13.x
 | 
				
			||||||
    commands:
 | 
					    commands:
 | 
				
			||||||
      - apt update && apt -y install curl
 | 
					      - apt update && apt -y install curl
 | 
				
			||||||
      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
					      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
				
			||||||
@@ -476,7 +473,8 @@ trigger:
 | 
				
			|||||||
    - tag
 | 
					    - tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
depends_on:
 | 
					depends_on:
 | 
				
			||||||
  - testing
 | 
					  - testing-arm64
 | 
				
			||||||
 | 
					  - testing-amd64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
  - name: fetch-tags
 | 
					  - name: fetch-tags
 | 
				
			||||||
@@ -487,7 +485,7 @@ steps:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  - name: static
 | 
					  - name: static
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: techknowlogick/xgo:latest
 | 
					    image: techknowlogick/xgo:go-1.13.x
 | 
				
			||||||
    commands:
 | 
					    commands:
 | 
				
			||||||
      - apt update && apt -y install curl
 | 
					      - apt update && apt -y install curl
 | 
				
			||||||
      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
					      - curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
 | 
				
			||||||
@@ -545,17 +543,14 @@ name: docs
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
platform:
 | 
					platform:
 | 
				
			||||||
  os: linux
 | 
					  os: linux
 | 
				
			||||||
  arch: amd64
 | 
					  arch: arm64
 | 
				
			||||||
 | 
					 | 
				
			||||||
workspace:
 | 
					 | 
				
			||||||
  base: /go
 | 
					 | 
				
			||||||
  path: src/code.gitea.io/gitea
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
  - name: build-docs
 | 
					  - name: build-docs
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: webhippie/hugo:latest
 | 
					    image: plugins/hugo:latest
 | 
				
			||||||
    commands:
 | 
					    commands:
 | 
				
			||||||
 | 
					      - apk add --no-cache make bash curl
 | 
				
			||||||
      - cd docs
 | 
					      - cd docs
 | 
				
			||||||
      - make trans-copy
 | 
					      - make trans-copy
 | 
				
			||||||
      - make clean
 | 
					      - make clean
 | 
				
			||||||
@@ -563,7 +558,7 @@ steps:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  - name: publish-docs
 | 
					  - name: publish-docs
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: lucap/drone-netlify:latest
 | 
					    image: techknowlogick/drone-netlify:latest
 | 
				
			||||||
    settings:
 | 
					    settings:
 | 
				
			||||||
      path: docs/public/
 | 
					      path: docs/public/
 | 
				
			||||||
      site_id: d2260bae-7861-4c02-8646-8f6440b12672
 | 
					      site_id: d2260bae-7861-4c02-8646-8f6440b12672
 | 
				
			||||||
@@ -578,7 +573,7 @@ steps:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
kind: pipeline
 | 
					kind: pipeline
 | 
				
			||||||
name: docker-linux-amd64
 | 
					name: docker-linux-amd64-release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
platform:
 | 
					platform:
 | 
				
			||||||
  os: linux
 | 
					  os: linux
 | 
				
			||||||
@@ -589,13 +584,13 @@ workspace:
 | 
				
			|||||||
  path: src/code.gitea.io/gitea
 | 
					  path: src/code.gitea.io/gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
depends_on:
 | 
					depends_on:
 | 
				
			||||||
  - testing
 | 
					  - testing-amd64
 | 
				
			||||||
 | 
					  - testing-arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
trigger:
 | 
					trigger:
 | 
				
			||||||
  ref:
 | 
					  ref:
 | 
				
			||||||
  - refs/heads/master
 | 
					  - refs/heads/master
 | 
				
			||||||
  - "refs/tags/**"
 | 
					  - "refs/tags/**"
 | 
				
			||||||
  - "refs/pull/**"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
  - name: fetch-tags
 | 
					  - name: fetch-tags
 | 
				
			||||||
@@ -603,23 +598,6 @@ steps:
 | 
				
			|||||||
    image: docker:git
 | 
					    image: docker:git
 | 
				
			||||||
    commands:
 | 
					    commands:
 | 
				
			||||||
      - git fetch --tags --force
 | 
					      - git fetch --tags --force
 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        exclude:
 | 
					 | 
				
			||||||
          - pull_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: dryrun
 | 
					 | 
				
			||||||
    pull: always
 | 
					 | 
				
			||||||
    image: plugins/docker:linux-amd64
 | 
					 | 
				
			||||||
    settings:
 | 
					 | 
				
			||||||
      dry_run: true
 | 
					 | 
				
			||||||
      repo: gitea/gitea
 | 
					 | 
				
			||||||
      tags: linux-amd64
 | 
					 | 
				
			||||||
      build_args:
 | 
					 | 
				
			||||||
        - GOPROXY=off
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        - pull_request
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: publish
 | 
					  - name: publish
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
@@ -641,7 +619,7 @@ steps:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
kind: pipeline
 | 
					kind: pipeline
 | 
				
			||||||
name: docker-linux-arm64
 | 
					name: docker-linux-arm64-dry-run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
platform:
 | 
					platform:
 | 
				
			||||||
  os: linux
 | 
					  os: linux
 | 
				
			||||||
@@ -652,25 +630,13 @@ workspace:
 | 
				
			|||||||
  path: src/code.gitea.io/gitea
 | 
					  path: src/code.gitea.io/gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
depends_on:
 | 
					depends_on:
 | 
				
			||||||
  - testing
 | 
					  - compliance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
trigger:
 | 
					trigger:
 | 
				
			||||||
  ref:
 | 
					  ref:
 | 
				
			||||||
  - refs/heads/master
 | 
					 | 
				
			||||||
  - "refs/tags/**"
 | 
					 | 
				
			||||||
  - "refs/pull/**"
 | 
					  - "refs/pull/**"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
  - name: fetch-tags
 | 
					 | 
				
			||||||
    pull: default
 | 
					 | 
				
			||||||
    image: docker:git
 | 
					 | 
				
			||||||
    commands:
 | 
					 | 
				
			||||||
      - git fetch --tags --force
 | 
					 | 
				
			||||||
    when:
 | 
					 | 
				
			||||||
      event:
 | 
					 | 
				
			||||||
        exclude:
 | 
					 | 
				
			||||||
          - pull_request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  - name: dryrun
 | 
					  - name: dryrun
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: plugins/docker:linux-arm64
 | 
					    image: plugins/docker:linux-arm64
 | 
				
			||||||
@@ -684,6 +650,33 @@ steps:
 | 
				
			|||||||
      event:
 | 
					      event:
 | 
				
			||||||
        - pull_request
 | 
					        - pull_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					kind: pipeline
 | 
				
			||||||
 | 
					name: docker-linux-arm64-release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					platform:
 | 
				
			||||||
 | 
					  os: linux
 | 
				
			||||||
 | 
					  arch: arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					workspace:
 | 
				
			||||||
 | 
					  base: /go
 | 
				
			||||||
 | 
					  path: src/code.gitea.io/gitea
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					depends_on:
 | 
				
			||||||
 | 
					  - testing-amd64
 | 
				
			||||||
 | 
					  - testing-arm64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trigger:
 | 
				
			||||||
 | 
					  ref:
 | 
				
			||||||
 | 
					  - refs/heads/master
 | 
				
			||||||
 | 
					  - "refs/tags/**"
 | 
				
			||||||
 | 
					steps:
 | 
				
			||||||
 | 
					  - name: fetch-tags
 | 
				
			||||||
 | 
					    pull: default
 | 
				
			||||||
 | 
					    image: docker:git
 | 
				
			||||||
 | 
					    commands:
 | 
				
			||||||
 | 
					      - git fetch --tags --force
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - name: publish
 | 
					  - name: publish
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: plugins/docker:linux-arm64
 | 
					    image: plugins/docker:linux-arm64
 | 
				
			||||||
@@ -729,45 +722,49 @@ trigger:
 | 
				
			|||||||
  - "refs/tags/**"
 | 
					  - "refs/tags/**"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
depends_on:
 | 
					depends_on:
 | 
				
			||||||
  - docker-linux-amd64
 | 
					  - docker-linux-amd64-release
 | 
				
			||||||
  - docker-linux-arm64
 | 
					  - docker-linux-arm64-release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
kind: pipeline
 | 
					kind: pipeline
 | 
				
			||||||
name: notify
 | 
					name: notifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
platform:
 | 
					platform:
 | 
				
			||||||
  os: linux
 | 
					  os: linux
 | 
				
			||||||
  arch: amd64
 | 
					  arch: arm64
 | 
				
			||||||
 | 
					 | 
				
			||||||
workspace:
 | 
					 | 
				
			||||||
  base: /go
 | 
					 | 
				
			||||||
  path: src/code.gitea.io/gitea
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
clone:
 | 
					clone:
 | 
				
			||||||
  disable: true
 | 
					  disable: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
when:
 | 
					trigger:
 | 
				
			||||||
 | 
					  branch:
 | 
				
			||||||
 | 
					    - master
 | 
				
			||||||
 | 
					    - "release/*"
 | 
				
			||||||
 | 
					  event:
 | 
				
			||||||
 | 
					    - push
 | 
				
			||||||
 | 
					    - tag
 | 
				
			||||||
  status:
 | 
					  status:
 | 
				
			||||||
    - success
 | 
					    - success
 | 
				
			||||||
    - failure
 | 
					    - failure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
depends_on:
 | 
					depends_on:
 | 
				
			||||||
  - testing
 | 
					  - testing-amd64
 | 
				
			||||||
 | 
					  - testing-arm64
 | 
				
			||||||
  - translations
 | 
					  - translations
 | 
				
			||||||
  - release-version
 | 
					  - release-version
 | 
				
			||||||
  - release-master
 | 
					  - release-master
 | 
				
			||||||
  - docker-linux-amd64
 | 
					  - docker-linux-amd64-release
 | 
				
			||||||
  - docker-linux-arm64
 | 
					  - docker-linux-arm64-release
 | 
				
			||||||
  - docker-manifest
 | 
					  - docker-manifest
 | 
				
			||||||
  - docs
 | 
					  - docs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
steps:
 | 
					steps:
 | 
				
			||||||
  - name: discord
 | 
					  - name: discord
 | 
				
			||||||
    pull: always
 | 
					    pull: always
 | 
				
			||||||
    image: appleboy/drone-discord:1.0.0
 | 
					    image: appleboy/drone-discord:1.2.4
 | 
				
			||||||
    environment:
 | 
					    settings:
 | 
				
			||||||
      DISCORD_WEBHOOK_ID:
 | 
					      message: "{{#success build.status}} ✅  Build #{{build.number}} of `{{repo.name}}` succeeded.\n\n📝 Commit by {{commit.author}} on `{{commit.branch}}`:\n``` {{commit.message}} ```\n\n🌐 {{ build.link }} {{else}} ❌  Build #{{build.number}} of `{{repo.name}}` failed.\n\n📝 Commit by {{commit.author}} on `{{commit.branch}}`:\n``` {{commit.message}} ```\n\n🌐 {{ build.link }} {{/success}}\n"
 | 
				
			||||||
 | 
					      webhook_id:
 | 
				
			||||||
        from_secret: discord_webhook_id
 | 
					        from_secret: discord_webhook_id
 | 
				
			||||||
      DISCORD_WEBHOOK_TOKEN:
 | 
					      webhook_token:
 | 
				
			||||||
        from_secret: discord_webhook_token
 | 
					        from_secret: discord_webhook_token
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -69,6 +69,7 @@ coverage.all
 | 
				
			|||||||
/yarn.lock
 | 
					/yarn.lock
 | 
				
			||||||
/public/js
 | 
					/public/js
 | 
				
			||||||
/public/css
 | 
					/public/css
 | 
				
			||||||
 | 
					/VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Snapcraft
 | 
					# Snapcraft
 | 
				
			||||||
snap/.snapcraft/
 | 
					snap/.snapcraft/
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										211
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -4,13 +4,102 @@ This changelog goes through all the changes that have been made in each release
 | 
				
			|||||||
without substantial changes to our git log; to see the highlights of what has
 | 
					without substantial changes to our git log; to see the highlights of what has
 | 
				
			||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
					been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.11.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.11.0-rc1) - 2020-01-07
 | 
					## [1.11.4](https://github.com/go-gitea/gitea/releases/tag/v1.11.4) - 2020-04-01
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Only update merge_base if not already merged (#10909)
 | 
				
			||||||
 | 
					  * Fix milestones too many SQL variables bug (#10880) (#10904)
 | 
				
			||||||
 | 
					  * Protect against NPEs in notifications list (#10879) (#10883)
 | 
				
			||||||
 | 
					  * Convert plumbing.ErrObjectNotFound to git.ErrNotExist in getCommit (#10862) (#10868)
 | 
				
			||||||
 | 
					  * Convert plumbing.ErrReferenceNotFound to git.ErrNotExist in GetRefCommitID (#10676) (#10797)
 | 
				
			||||||
 | 
					  * Account for empty lines in receive-hook message (#10773) (#10784)
 | 
				
			||||||
 | 
					  * Fix bug on branch API (#10767) (#10775)
 | 
				
			||||||
 | 
					  * Migrate to go-git/go-git v5.0.0 (#10735) (#10753)
 | 
				
			||||||
 | 
					  * Fix hiding of fields in authorization source page (#10734) (#10752)
 | 
				
			||||||
 | 
					  * Prevent default for linkAction (#10742) (#10743)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.11.3](https://github.com/go-gitea/gitea/releases/tag/v1.11.3) - 2020-03-10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Prevent panic in stopwatch (#10670) (#10673)
 | 
				
			||||||
 | 
					  * Fix bug on pull view when required status check no ci result (#10648) (#10651)
 | 
				
			||||||
 | 
					  * Build explicitly with Go 1.13 (#10684)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.11.2](https://github.com/go-gitea/gitea/releases/tag/v1.11.2) - 2020-03-06
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* BREAKING
 | 
					* BREAKING
 | 
				
			||||||
 | 
					  * Various fixes in login sources (#10428) (#10429)
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Ensure only own addresses are updated (#10397) (#10399)
 | 
				
			||||||
 | 
					  * Logout POST action (#10582) (#10585)
 | 
				
			||||||
 | 
					  * Org action fixes and form cleanup (#10512) (#10514)
 | 
				
			||||||
 | 
					  * Change action GETs to POST (#10462) (#10464)
 | 
				
			||||||
 | 
					  * Fix admin notices (#10480) (#10483)
 | 
				
			||||||
 | 
					  * Change admin dashboard to POST (#10465) (#10466)
 | 
				
			||||||
 | 
					  * Update markbates/goth (#10444) (#10445)
 | 
				
			||||||
 | 
					  * Update crypto vendors (#10385) (#10398)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Allow users with write permissions to modify issue descriptions and comments. (#10623) (#10626)
 | 
				
			||||||
 | 
					  * Handle deleted base branch in PR (#10618) (#10619)
 | 
				
			||||||
 | 
					  * Delete dependencies when deleting a repository (#10608) (#10616)
 | 
				
			||||||
 | 
					  * Ensure executable bit is kept on the web editor (#10607) (#10614)
 | 
				
			||||||
 | 
					  * Update mergebase in pr checker (#10586) (#10605)
 | 
				
			||||||
 | 
					  * Fix release attachments being deleted while upgrading (#10572) (#10573)
 | 
				
			||||||
 | 
					  * Fix redirection path if Slack webhook channel is invalid (#10566)
 | 
				
			||||||
 | 
					  * Fix head.tmpl og:image picture location (#10531) (#10556)
 | 
				
			||||||
 | 
					  * Fix 404 after activating secondary email (#10547) (#10553)
 | 
				
			||||||
 | 
					  * Show Signer in commit lists and add basic trust (#10425 & #10511) (#10524)
 | 
				
			||||||
 | 
					  * Fix potential bugs (#10513) (#10518)
 | 
				
			||||||
 | 
					  * Use \[:space:\] instead of \\s (#10508) (#10509)
 | 
				
			||||||
 | 
					  * Avoid mailing users that have explicitly unwatched an issue (#10475) (#10500)
 | 
				
			||||||
 | 
					  * Handle push rejection message in Merge & Web Editor (#10373) (#10497)
 | 
				
			||||||
 | 
					  * Fix SQLite concurrency problems by using BEGIN IMMEDIATE (#10368) (#10493)
 | 
				
			||||||
 | 
					  * Fix double PR notification from API (#10482) (#10486)
 | 
				
			||||||
 | 
					  * Show the username as a fallback on feeds if full name is blank (#10461)
 | 
				
			||||||
 | 
					  * Trigger webhooks on issue label-change via API too (#10421) (#10439)
 | 
				
			||||||
 | 
					  * Fix git reference type in webhooks (#10427) (#10432)
 | 
				
			||||||
 | 
					  * Prevent panic on merge to PR (#10403) (#10408)
 | 
				
			||||||
 | 
					  * Fix wrong num closed issues on repository when close issue via commit… (#10364) (#10380)
 | 
				
			||||||
 | 
					  * Reading pull attachments should depend on read UnitTypePullRequests (#10346) (#10354)
 | 
				
			||||||
 | 
					  * Set max-width on review-box comment box (#10348) (#10353)
 | 
				
			||||||
 | 
					  * Prevent nil pointer in GetPullRequestCommitStatusState (#10342) (#10344)
 | 
				
			||||||
 | 
					  * Fix protected branch status check settings (#10341) (#10343)
 | 
				
			||||||
 | 
					  * Truncate long commit message header (#10301) (#10319)
 | 
				
			||||||
 | 
					  * Set the initial commit status to Success otherwise it will always be Pending (#10317) (#10318)
 | 
				
			||||||
 | 
					  * Don't manually replace whitespace during render (#10291) (#10315)
 | 
				
			||||||
 | 
					* ENHANCEMENT
 | 
				
			||||||
 | 
					  * Admin page for managing user e-mail activation (#10557) (#10579)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.11.1](https://github.com/go-gitea/gitea/releases/tag/v1.11.1) - 2020-02-15
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Repo name added to automatically generated commit message when merging (#9997) (#10285)
 | 
				
			||||||
 | 
					  * Fix Workerpool deadlock (#10283) (#10284)
 | 
				
			||||||
 | 
					  * Divide GetIssueStats query in smaller chunks (#10176) (#10282)
 | 
				
			||||||
 | 
					  * Fix reply on code review (#10257)
 | 
				
			||||||
 | 
					  * Stop hanging issue indexer initialisation from preventing shutdown (#10243) (#10249)
 | 
				
			||||||
 | 
					  * Fix filter label emoji width (#10241) (#10244)
 | 
				
			||||||
 | 
					  * Fix issue sidebar menus having an infinite height (#10239) (#10240)
 | 
				
			||||||
 | 
					  * Fix commit between two commits calculation if there is only last commit (#10225) (#10226)
 | 
				
			||||||
 | 
					  * Only check for conflicts/merging if the PR has not been merged in the interim (#10132) (#10206)
 | 
				
			||||||
 | 
					  * Blacklist manifest.json & milestones user (#10292) (#10293)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.11.0](https://github.com/go-gitea/gitea/releases/tag/v1.11.0) - 2020-02-10
 | 
				
			||||||
 | 
					* BREAKING
 | 
				
			||||||
 | 
					  * Fix followers and following tabs in profile (#10202) (#10203)
 | 
				
			||||||
 | 
					  * Make CertFile and KeyFile relative to CustomPath (#9868) (#9874)
 | 
				
			||||||
  * Remove unused endpoints (#9538)
 | 
					  * Remove unused endpoints (#9538)
 | 
				
			||||||
  * Prefix all user-generated IDs in markup (#9477)
 | 
					  * Prefix all user-generated IDs in markup (#9477)
 | 
				
			||||||
  * Enforce Gitea environment for pushes (#8982)
 | 
					  * Enforce Gitea environment for pushes (#8982)
 | 
				
			||||||
  * Hide some user information via API if user have no enough permission (#8655)
 | 
					  * Hide some user information via API if user have not enough permissions (#8655)
 | 
				
			||||||
  * Move startpage/homepage translation to crowdin (#8596)
 | 
					  * Move startpage/homepage translation to crowdin (#8596)
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Never allow an empty password to validate (#9682) (#9683)
 | 
				
			||||||
 | 
					  * Prevent redirect to Host (#9678) (#9679)
 | 
				
			||||||
 | 
					  * Swagger hide search field (#9554)
 | 
				
			||||||
 | 
					  * Add "search" to reserved usernames (#9063)
 | 
				
			||||||
 | 
					  * Switch to fomantic-ui (#9374)
 | 
				
			||||||
 | 
					  * Only serve attachments when linked to issue/release and if accessible by user (#9340)
 | 
				
			||||||
* FEATURES
 | 
					* FEATURES
 | 
				
			||||||
  * Webhooks should only show sender if it makes sense (#9601)
 | 
					  * Webhooks should only show sender if it makes sense (#9601)
 | 
				
			||||||
  * Provide Default messages for merges (#9393)
 | 
					  * Provide Default messages for merges (#9393)
 | 
				
			||||||
@@ -44,6 +133,68 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
 | 
					  * Sign merges, CRUD, Wiki and Repository initialisation with gpg key (#7631)
 | 
				
			||||||
  * Add basic repository lfs management (#7199)
 | 
					  * Add basic repository lfs management (#7199)
 | 
				
			||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix code-expansion arc-green theme bug (#10180) (#10185)
 | 
				
			||||||
 | 
					  * Prevent double wait-group decrement (#10170) (#10175)
 | 
				
			||||||
 | 
					  * Allow emoji on review head comments (#10159) (#10174)
 | 
				
			||||||
 | 
					  * Fix issue/pull link (#10158) (#10173)
 | 
				
			||||||
 | 
					  * Fix push-create SSH bugs (#10145) (#10151)
 | 
				
			||||||
 | 
					  * Prevent DeleteUser API abuse (#10125) (#10128)
 | 
				
			||||||
 | 
					  * Fix issues/pulls dashboard paging error (#10114) (#10115)
 | 
				
			||||||
 | 
					  * Add button to revert SimpleMDE to plain textarea (#10099) (#10102)
 | 
				
			||||||
 | 
					  * Fix branch page pull request title and link error (#10092) (#10097)
 | 
				
			||||||
 | 
					  * Fix PR API: Only try to get HeadBranch if HeadRepo exist (#10029) (#10088)
 | 
				
			||||||
 | 
					  * Update topics repo count when deleting repository (#10051) (#10081)
 | 
				
			||||||
 | 
					  * Show pull icon on pull requests (#10061) (#10062)
 | 
				
			||||||
 | 
					  * Fix milestone API state parameter unhandled (#10049) (#10052)
 | 
				
			||||||
 | 
					  * Move to using a temporary repo for pushing new PRs (#10009) (#10042)
 | 
				
			||||||
 | 
					  * Fix wiki raw view on sub path (#10002) (#10040)
 | 
				
			||||||
 | 
					  * Ensure that feeds are appropriately restricted (#10018) (#10019)
 | 
				
			||||||
 | 
					  * Sanitize credentials in mirror form (#9975) (#9991)
 | 
				
			||||||
 | 
					  * Close related pull requests when deleting head repository or head branch (#9927) (#9974)
 | 
				
			||||||
 | 
					  * Switch to use -f instead of -F for sendmail (#9961) (#9970)
 | 
				
			||||||
 | 
					  * Fix file rename/copy not supported by indexer (#9965) (#9967)
 | 
				
			||||||
 | 
					  * Fix repo indexer not updating upon push (#9957) (#9963)
 | 
				
			||||||
 | 
					  * Don't convert ellipsis in markdown (#9905) (#9937)
 | 
				
			||||||
 | 
					  * Fixed repo link in generated comment for cross repository dependency (#9863) (#9935)
 | 
				
			||||||
 | 
					  * Check if diff actually contains sections when rendering (#9926) (#9933)
 | 
				
			||||||
 | 
					  * Fix wrong hint when status checking is running on pull request view (#9886) (#9928)
 | 
				
			||||||
 | 
					  * Fix RocketChat (#9908) (#9921)
 | 
				
			||||||
 | 
					  * Do not try to recreate ldap user if they are already created (#9900) (#9919)
 | 
				
			||||||
 | 
					  * Create terminated channel in queue_redis (#9910) (#9911)
 | 
				
			||||||
 | 
					  * Prevent empty LDAP search result from deactivating all users (#9879) (#9896)
 | 
				
			||||||
 | 
					  * Fix wrong permissions check when issues/prs shared operations (#9885) (#9889)
 | 
				
			||||||
 | 
					  * Check user != nil before checking values (#9881) (#9883)
 | 
				
			||||||
 | 
					  * Allow hyphen in language name (#9873) (#9880)
 | 
				
			||||||
 | 
					  * Ensure that 2fa is checked on reset-password (#9857) (#9876)
 | 
				
			||||||
 | 
					  * Fix issues/pulls dependencies problems (#9842) (#9864)
 | 
				
			||||||
 | 
					  * Fix markdown anchor links (#9673) (#9840)
 | 
				
			||||||
 | 
					  * Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9837)
 | 
				
			||||||
 | 
					  * Fix download file wrong content-type (#9825) (#9834)
 | 
				
			||||||
 | 
					  * Fix wrong poster identity on a migrated pull request when submit review (#9827) (#9830)
 | 
				
			||||||
 | 
					  * Fix database dump when log directory is missing (#9818) (#9819)
 | 
				
			||||||
 | 
					  * Fix compare (#9808) (#9814)
 | 
				
			||||||
 | 
					  * Fix push-to-create (#9772) (#9797)
 | 
				
			||||||
 | 
					  * Fix missing msteam webhook on organization (#9781) (#9794)
 | 
				
			||||||
 | 
					  * Fix missing unlock in uniquequeue (#9790) (#9791)
 | 
				
			||||||
 | 
					  * Fix add team on collaborator page when same name as organization (#9778)
 | 
				
			||||||
 | 
					  * DeleteRepoFile incorrectly handles Delete to new branch (#9769) (#9775)
 | 
				
			||||||
 | 
					  * Fix milestones page (#9771)
 | 
				
			||||||
 | 
					  * Fix SimpleMDE quote reply (#9757) (#9768)
 | 
				
			||||||
 | 
					  * Fix missing updated time on migrated issues and comments (#9744) (#9764)
 | 
				
			||||||
 | 
					  * Move Errored PRs out of StatusChecking (#9675) (#9726)
 | 
				
			||||||
 | 
					  * Make hook status printing configurable with delay (#9641) (#9725)
 | 
				
			||||||
 | 
					  * Fix /repos/issues/search (#9698) (#9724)
 | 
				
			||||||
 | 
					  * Silence fomantic error regarding tabs (#9713) (#9718)
 | 
				
			||||||
 | 
					  * Remove unused lock (#9709) (#9710)
 | 
				
			||||||
 | 
					  * Remove q.lock.Unlock() in setInternal to prevent panic (#9705) (#9706)
 | 
				
			||||||
 | 
					  * Load milestone in API PR list (#9671) (#9700)
 | 
				
			||||||
 | 
					  * Don't attempt to close issue if already closed (#9696) (#9699)
 | 
				
			||||||
 | 
					  * Remove google font call (#9668) (#9681)
 | 
				
			||||||
 | 
					  * Eliminate horizontal scroll caused by footer (#9674)
 | 
				
			||||||
 | 
					  * Fix nil reference in repo generation (#9660) (#9666)
 | 
				
			||||||
 | 
					  * Add HTML URL to API Issues (#9654) (#9661)
 | 
				
			||||||
 | 
					  * Add PR review webhook to Telegram (#9653) (#9655)
 | 
				
			||||||
 | 
					  * Use filepath.IsAbs instead of path.IsAbs (#9651) (#9652)
 | 
				
			||||||
  * Disable remove button on repository teams when have access to all (#9640)
 | 
					  * Disable remove button on repository teams when have access to all (#9640)
 | 
				
			||||||
  * Clean up old references on branch delete (#9614)
 | 
					  * Clean up old references on branch delete (#9614)
 | 
				
			||||||
  * Hide public repos owned by private orgs (#9609)
 | 
					  * Hide public repos owned by private orgs (#9609)
 | 
				
			||||||
@@ -175,6 +326,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Fix migrate mirror 500 bug (#8526)
 | 
					  * Fix migrate mirror 500 bug (#8526)
 | 
				
			||||||
  * Fix password complexity regex for special characters (on master) (#8525)
 | 
					  * Fix password complexity regex for special characters (on master) (#8525)
 | 
				
			||||||
* ENHANCEMENTS
 | 
					* ENHANCEMENTS
 | 
				
			||||||
 | 
					  * Explicitly refer to PR in squash-merge commit message in case of external tracker (#9844) (#9855)
 | 
				
			||||||
  * Add a /user/login landing page option (#9622)
 | 
					  * Add a /user/login landing page option (#9622)
 | 
				
			||||||
  * Some more e-mail notification fixes (#9596)
 | 
					  * Some more e-mail notification fixes (#9596)
 | 
				
			||||||
  * Add branch protection option to block merge on requested changes. (#9592)
 | 
					  * Add branch protection option to block merge on requested changes. (#9592)
 | 
				
			||||||
@@ -291,12 +443,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
 | 
					  * wiki - add 'write' 'preview' buttons to wiki edit like in issues (#7241)
 | 
				
			||||||
  * Change target branch for pull request (#6488)
 | 
					  * Change target branch for pull request (#6488)
 | 
				
			||||||
  * Display PR commits and diffs using base repo rather than forked (#3648)
 | 
					  * Display PR commits and diffs using base repo rather than forked (#3648)
 | 
				
			||||||
* SECURITY
 | 
					 | 
				
			||||||
  * Swagger hide search field (#9554)
 | 
					 | 
				
			||||||
  * Add "search" to reserved usernames (#9063)
 | 
					 | 
				
			||||||
  * Switch to fomantic-ui (#9374)
 | 
					 | 
				
			||||||
  * Only serve attachments when linked to issue/release and if accessible by user (#9340)
 | 
					 | 
				
			||||||
  * Hide credentials when submitting migration through API (#9102)
 | 
					 | 
				
			||||||
* TESTING
 | 
					* TESTING
 | 
				
			||||||
  * Add debug option to serv to help debug problems (#9492)
 | 
					  * Add debug option to serv to help debug problems (#9492)
 | 
				
			||||||
  * Fix the intermittent TestGPGGit failures (#9360)
 | 
					  * Fix the intermittent TestGPGGit failures (#9360)
 | 
				
			||||||
@@ -310,10 +456,12 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Update Github Migration Tests (#8893) (#8938)
 | 
					  * Update Github Migration Tests (#8893) (#8938)
 | 
				
			||||||
  * Update heatmap fixtures to restore tests (#8615)
 | 
					  * Update heatmap fixtures to restore tests (#8615)
 | 
				
			||||||
* TRANSLATION
 | 
					* TRANSLATION
 | 
				
			||||||
 | 
					  * Fix Korean locales (#9761) (#9780)
 | 
				
			||||||
  * Fix placeholders in the error message (#9060)
 | 
					  * Fix placeholders in the error message (#9060)
 | 
				
			||||||
  * Fix spelling of admin.users.max_repo_creation (#8934)
 | 
					  * Fix spelling of admin.users.max_repo_creation (#8934)
 | 
				
			||||||
  * Improve german translation of homepage (#8549)
 | 
					  * Improve german translation of homepage (#8549)
 | 
				
			||||||
* BUILD
 | 
					* BUILD
 | 
				
			||||||
 | 
					  * Fix webpack polyfills (#9735) (#9738)
 | 
				
			||||||
  * Update gitea.com/macaron to 1.4.0 (#9608)
 | 
					  * Update gitea.com/macaron to 1.4.0 (#9608)
 | 
				
			||||||
  * Upgrade lato fonts to v16. (#9498)
 | 
					  * Upgrade lato fonts to v16. (#9498)
 | 
				
			||||||
  * Update alpine to 3.11 (#9440)
 | 
					  * Update alpine to 3.11 (#9440)
 | 
				
			||||||
@@ -344,6 +492,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Update the provided gitea.service to mention socket activation (#8531)
 | 
					  * Update the provided gitea.service to mention socket activation (#8531)
 | 
				
			||||||
  * Doc added how to setup email (#8520)
 | 
					  * Doc added how to setup email (#8520)
 | 
				
			||||||
* MISC
 | 
					* MISC
 | 
				
			||||||
 | 
					  * Backport Locales [2020-01-14] (#9773)
 | 
				
			||||||
  * Add translatable Powered by Gitea text in footer (#9600)
 | 
					  * Add translatable Powered by Gitea text in footer (#9600)
 | 
				
			||||||
  * Add contrib/environment-to-ini (#9519)
 | 
					  * Add contrib/environment-to-ini (#9519)
 | 
				
			||||||
  * Remove unnecessary loading of settings in update hook (#9496)
 | 
					  * Remove unnecessary loading of settings in update hook (#9496)
 | 
				
			||||||
@@ -384,6 +533,48 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
  * Update CodeMirror to version 5.49.0 (#8381)
 | 
					  * Update CodeMirror to version 5.49.0 (#8381)
 | 
				
			||||||
  * Wiki editor: enable side-by-side button (#7242)
 | 
					  * Wiki editor: enable side-by-side button (#7242)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.10.6](https://github.com/go-gitea/gitea/releases/tag/v1.10.6) - 2020-03-10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is a re-tag version of v1.10.5 and also explicitly built with Go 1.13.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					WARNING: v1.10.5 is incorrectly tagged targeting 1.12-dev and should **not** be used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.10.5](https://github.com/go-gitea/gitea/releases/tag/v1.10.5) - 2020-03-06
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix release attachments being deleted while upgrading (#10572) (#10574)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.10.4](https://github.com/go-gitea/gitea/releases/tag/v1.10.4) - 2020-02-16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* FEATURE
 | 
				
			||||||
 | 
					  * Prevent empty LDAP search from deactivating all users (#9879) (#9890)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Fix reply on code review (#10261) (#10227)
 | 
				
			||||||
 | 
					  * Fix branch page pull request title and link error (#10092) (#10098)
 | 
				
			||||||
 | 
					  * Fix milestone API state parameter unhandled (#10049) (#10053)
 | 
				
			||||||
 | 
					  * Fix wiki raw view on sub path (#10002) (#10041)
 | 
				
			||||||
 | 
					  * Fix RocketChat Webhook (#9908) (#9921) (#9925)
 | 
				
			||||||
 | 
					  * Fix bug about wrong dependencies permissions check and other wrong permissions check (#9884) (Partial backport #9842)
 | 
				
			||||||
 | 
					  * Ensure that 2fa is checked on reset-password (#9857) (#9877)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## [1.10.3](https://github.com/go-gitea/gitea/releases/tag/v1.10.3) - 2020-01-17
 | 
				
			||||||
 | 
					* SECURITY
 | 
				
			||||||
 | 
					  * Hide credentials when submitting migration (#9102) (#9704)
 | 
				
			||||||
 | 
					  * Never allow an empty password to validate (#9682) (#9684)
 | 
				
			||||||
 | 
					  * Prevent redirect to Host (#9678) (#9680)
 | 
				
			||||||
 | 
					  * Hide public repos owned by private orgs (#9609) (#9616)
 | 
				
			||||||
 | 
					* BUGFIXES
 | 
				
			||||||
 | 
					  * Allow assignee on Pull Creation when Issue Unit is deactivated (#9836) (#9838)
 | 
				
			||||||
 | 
					  * Fix download file wrong content-type (#9825) (#9835)
 | 
				
			||||||
 | 
					  * Fix wrong identify poster on a migrated pull request when submit review (#9827) (#9831)
 | 
				
			||||||
 | 
					  * Fix dump non-exist log directory (#9818) (#9820)
 | 
				
			||||||
 | 
					  * Fix compare (#9808) (#9815)
 | 
				
			||||||
 | 
					  * Fix missing msteam webhook on organization (#9781) (#9795)
 | 
				
			||||||
 | 
					  * Fix add team on collaborator page when same name as organization (#9783)
 | 
				
			||||||
 | 
					  * Fix cache problem on dashboard (#9358) (#9703)
 | 
				
			||||||
 | 
					  * Send tag create and push webhook when release created on UI (#8671) (#9702)
 | 
				
			||||||
 | 
					  * Branches not at ref commit ID should not be listed as Merged (#9614) (#9639)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
 | 
					## [1.10.2](https://github.com/go-gitea/gitea/releases/tag/v1.10.2) - 2020-01-02
 | 
				
			||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
  * Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
 | 
					  * Allow only specific Columns to be updated on Issue via API (#9539) (#9580)
 | 
				
			||||||
@@ -1483,13 +1674,13 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 | 
				
			|||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
  * Allow resend of confirmation email when logged in (#6482) (#6487)
 | 
					  * Allow resend of confirmation email when logged in (#6482) (#6487)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27  
 | 
					## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27
 | 
				
			||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
  * Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423)
 | 
					  * Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423)
 | 
				
			||||||
  * Fix bug where manifest.json was being requested without cookies and continuously creating new sessions (#6372) (#6383)
 | 
					  * Fix bug where manifest.json was being requested without cookies and continuously creating new sessions (#6372) (#6383)
 | 
				
			||||||
  * Fix ParsePatch function to work with quoted diff --git strings (#6323) (#6332)
 | 
					  * Fix ParsePatch function to work with quoted diff --git strings (#6323) (#6332)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## [1.7.4](https://github.com/go-gitea/gitea/releases/tag/v1.7.4) - 2019-03-12  
 | 
					## [1.7.4](https://github.com/go-gitea/gitea/releases/tag/v1.7.4) - 2019-03-12
 | 
				
			||||||
* SECURITY
 | 
					* SECURITY
 | 
				
			||||||
  * Fix potential XSS vulnerability in repository description. (#6306) (#6308)
 | 
					  * Fix potential XSS vulnerability in repository description. (#6306) (#6308)
 | 
				
			||||||
* BUGFIXES
 | 
					* BUGFIXES
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										65
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								Makefile
									
									
									
									
									
								
							@@ -29,6 +29,8 @@ EXTRA_GOFLAGS ?=
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
 | 
					MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STORED_VERSION_FILE := VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ifneq ($(DRONE_TAG),)
 | 
					ifneq ($(DRONE_TAG),)
 | 
				
			||||||
	VERSION ?= $(subst v,,$(DRONE_TAG))
 | 
						VERSION ?= $(subst v,,$(DRONE_TAG))
 | 
				
			||||||
	GITEA_VERSION ?= $(VERSION)
 | 
						GITEA_VERSION ?= $(VERSION)
 | 
				
			||||||
@@ -38,7 +40,13 @@ else
 | 
				
			|||||||
	else
 | 
						else
 | 
				
			||||||
		VERSION ?= master
 | 
							VERSION ?= master
 | 
				
			||||||
	endif
 | 
						endif
 | 
				
			||||||
	GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
 | 
					
 | 
				
			||||||
 | 
						STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
 | 
				
			||||||
 | 
						ifneq ($(STORED_VERSION),)
 | 
				
			||||||
 | 
							GITEA_VERSION ?= $(STORED_VERSION)
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
 | 
				
			||||||
 | 
						endif
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
 | 
					LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
 | 
				
			||||||
@@ -96,13 +104,15 @@ include docker/Makefile
 | 
				
			|||||||
help:
 | 
					help:
 | 
				
			||||||
	@echo "Make Routines:"
 | 
						@echo "Make Routines:"
 | 
				
			||||||
	@echo " - \"\"                equivalent to \"build\""
 | 
						@echo " - \"\"                equivalent to \"build\""
 | 
				
			||||||
	@echo " - build             creates the entire project"
 | 
						@echo " - build             build everything"
 | 
				
			||||||
	@echo " - clean             delete integration files and build files but not css and js files"
 | 
						@echo " - frontend          build frontend files"
 | 
				
			||||||
	@echo " - clean-all         delete all generated files (integration test, build, css and js files)"
 | 
						@echo " - backend           build backend files"
 | 
				
			||||||
 | 
						@echo " - clean             delete backend and integration files"
 | 
				
			||||||
 | 
						@echo " - clean-all         delete backend, frontend and integration files"
 | 
				
			||||||
	@echo " - css               rebuild only css files"
 | 
						@echo " - css               rebuild only css files"
 | 
				
			||||||
	@echo " - js                rebuild only js files"
 | 
						@echo " - js                rebuild only js files"
 | 
				
			||||||
	@echo " - generate          run \"make css js\" and \"go generate\""
 | 
						@echo " - generate          run \"go generate\""
 | 
				
			||||||
	@echo " - fmt               format the code"
 | 
						@echo " - fmt               format the Go code"
 | 
				
			||||||
	@echo " - generate-swagger  generate the swagger spec from code comments"
 | 
						@echo " - generate-swagger  generate the swagger spec from code comments"
 | 
				
			||||||
	@echo " - swagger-validate  check if the swagger spec is valide"
 | 
						@echo " - swagger-validate  check if the swagger spec is valide"
 | 
				
			||||||
	@echo " - revive            run code linter revive"
 | 
						@echo " - revive            run code linter revive"
 | 
				
			||||||
@@ -113,12 +123,19 @@ help:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.PHONY: go-check
 | 
					.PHONY: go-check
 | 
				
			||||||
go-check:
 | 
					go-check:
 | 
				
			||||||
	$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell go version | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?\s' | tr '.' ' ');))
 | 
						$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell go version | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?[[:space:]]' | tr '.' ' ');))
 | 
				
			||||||
	@if [ "$(GO_VERSION)" -lt "001011000" ]; then \
 | 
						@if [ "$(GO_VERSION)" -lt "001011000" ]; then \
 | 
				
			||||||
		echo "Gitea requires Go 1.11.0 or greater to build. You can get it at https://golang.org/dl/"; \
 | 
							echo "Gitea requires Go 1.11.0 or greater to build. You can get it at https://golang.org/dl/"; \
 | 
				
			||||||
		exit 1; \
 | 
							exit 1; \
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: git-check
 | 
				
			||||||
 | 
					git-check:
 | 
				
			||||||
 | 
						@if git lfs >/dev/null 2>&1 ; then : ; else \
 | 
				
			||||||
 | 
							echo "Gitea requires git with lfs support to run tests." ; \
 | 
				
			||||||
 | 
							exit 1; \
 | 
				
			||||||
 | 
						fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: node-check
 | 
					.PHONY: node-check
 | 
				
			||||||
node-check:
 | 
					node-check:
 | 
				
			||||||
	$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?' | tr '.' ' ');))
 | 
						$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?' | tr '.' ' ');))
 | 
				
			||||||
@@ -149,10 +166,6 @@ fmt:
 | 
				
			|||||||
vet:
 | 
					vet:
 | 
				
			||||||
	$(GO) vet $(PACKAGES)
 | 
						$(GO) vet $(PACKAGES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: generate
 | 
					 | 
				
			||||||
generate: js css
 | 
					 | 
				
			||||||
	GO111MODULE=on $(GO) generate -mod=vendor $(PACKAGES)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.PHONY: generate-swagger
 | 
					.PHONY: generate-swagger
 | 
				
			||||||
generate-swagger:
 | 
					generate-swagger:
 | 
				
			||||||
	$(SWAGGER) generate spec -o './$(SWAGGER_SPEC)'
 | 
						$(SWAGGER) generate spec -o './$(SWAGGER_SPEC)'
 | 
				
			||||||
@@ -233,7 +246,7 @@ coverage:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.PHONY: unit-test-coverage
 | 
					.PHONY: unit-test-coverage
 | 
				
			||||||
unit-test-coverage:
 | 
					unit-test-coverage:
 | 
				
			||||||
	$(GO) test -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
 | 
						GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: vendor
 | 
					.PHONY: vendor
 | 
				
			||||||
vendor:
 | 
					vendor:
 | 
				
			||||||
@@ -376,7 +389,7 @@ integrations.mssql.test: $(GO_SOURCES)
 | 
				
			|||||||
integrations.sqlite.test: $(GO_SOURCES)
 | 
					integrations.sqlite.test: $(GO_SOURCES)
 | 
				
			||||||
	GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
 | 
						GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
integrations.cover.test: $(GO_SOURCES)
 | 
					integrations.cover.test: git-check $(GO_SOURCES)
 | 
				
			||||||
	GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(PACKAGES) | tr ' ' ',') -o integrations.cover.test
 | 
						GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(PACKAGES) | tr ' ' ',') -o integrations.cover.test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: migrations.mysql.test
 | 
					.PHONY: migrations.mysql.test
 | 
				
			||||||
@@ -407,13 +420,23 @@ install: $(wildcard *.go)
 | 
				
			|||||||
	$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
 | 
						$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: build
 | 
					.PHONY: build
 | 
				
			||||||
build: go-check generate $(EXECUTABLE)
 | 
					build: frontend backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: frontend
 | 
				
			||||||
 | 
					frontend: node-check js css
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: backend
 | 
				
			||||||
 | 
					backend: go-check generate $(EXECUTABLE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: generate
 | 
				
			||||||
 | 
					generate:
 | 
				
			||||||
 | 
						GO111MODULE=on $(GO) generate -mod=vendor $(PACKAGES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(EXECUTABLE): $(GO_SOURCES)
 | 
					$(EXECUTABLE): $(GO_SOURCES)
 | 
				
			||||||
	GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
 | 
						GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: release
 | 
					.PHONY: release
 | 
				
			||||||
release: generate release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
 | 
					release: frontend generate release-dirs release-windows release-linux release-darwin release-copy release-compress release-sources release-check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: release-dirs
 | 
					.PHONY: release-dirs
 | 
				
			||||||
release-dirs:
 | 
					release-dirs:
 | 
				
			||||||
@@ -424,7 +447,7 @@ release-windows:
 | 
				
			|||||||
	@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
						@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
				
			||||||
		$(GO) get -u src.techknowlogick.com/xgo; \
 | 
							$(GO) get -u src.techknowlogick.com/xgo; \
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
	xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
 | 
						xgo -go go-1.13 -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
 | 
				
			||||||
ifeq ($(CI),drone)
 | 
					ifeq ($(CI),drone)
 | 
				
			||||||
	cp /build/* $(DIST)/binaries
 | 
						cp /build/* $(DIST)/binaries
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
@@ -434,7 +457,7 @@ release-linux:
 | 
				
			|||||||
	@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
						@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
				
			||||||
		$(GO) get -u src.techknowlogick.com/xgo; \
 | 
							$(GO) get -u src.techknowlogick.com/xgo; \
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
	xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
 | 
						xgo -go go-1.13 -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
 | 
				
			||||||
ifeq ($(CI),drone)
 | 
					ifeq ($(CI),drone)
 | 
				
			||||||
	cp /build/* $(DIST)/binaries
 | 
						cp /build/* $(DIST)/binaries
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
@@ -444,7 +467,7 @@ release-darwin:
 | 
				
			|||||||
	@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
						@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 | 
				
			||||||
		$(GO) get -u src.techknowlogick.com/xgo; \
 | 
							$(GO) get -u src.techknowlogick.com/xgo; \
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
	xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
 | 
						xgo -go go-1.13 -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
 | 
				
			||||||
ifeq ($(CI),drone)
 | 
					ifeq ($(CI),drone)
 | 
				
			||||||
	cp /build/* $(DIST)/binaries
 | 
						cp /build/* $(DIST)/binaries
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
@@ -464,6 +487,12 @@ release-compress:
 | 
				
			|||||||
	fi
 | 
						fi
 | 
				
			||||||
	cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
 | 
						cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: release-sources
 | 
				
			||||||
 | 
					release-sources: | node_modules
 | 
				
			||||||
 | 
						echo $(VERSION) > $(STORED_VERSION_FILE)
 | 
				
			||||||
 | 
						tar --exclude=./$(DIST) --exclude=./.git --exclude=./node_modules/.cache -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
 | 
				
			||||||
 | 
						rm -f $(STORED_VERSION_FILE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
node_modules: package-lock.json
 | 
					node_modules: package-lock.json
 | 
				
			||||||
	npm install --no-save
 | 
						npm install --no-save
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,15 @@ From the root of the source tree, run:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    TAGS="bindata" make build
 | 
					    TAGS="bindata" make build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `build` target is split into two sub-targets:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `make backend` which requires [Go 1.11](https://golang.org/dl/) or greater.
 | 
				
			||||||
 | 
					- `make frontend` which requires [Node.js 10.0.0](https://nodejs.org/en/download/) or greater.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If pre-built frontend files are present it is possible to only build the backend:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TAGS="bindata" make backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
More info: https://docs.gitea.io/en-us/install-from-source/
 | 
					More info: https://docs.gitea.io/en-us/install-from-source/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Using
 | 
					## Using
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,6 +61,10 @@ var (
 | 
				
			|||||||
			Name:  "admin-filter",
 | 
								Name:  "admin-filter",
 | 
				
			||||||
			Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
 | 
								Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							cli.BoolFlag{
 | 
				
			||||||
 | 
								Name:  "allow-deactivate-all",
 | 
				
			||||||
 | 
								Usage: "Allow empty search results to deactivate all users.",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		cli.StringFlag{
 | 
							cli.StringFlag{
 | 
				
			||||||
			Name:  "username-attribute",
 | 
								Name:  "username-attribute",
 | 
				
			||||||
			Usage: "The attribute of the user’s LDAP record containing the user name.",
 | 
								Usage: "The attribute of the user’s LDAP record containing the user name.",
 | 
				
			||||||
@@ -231,6 +235,9 @@ func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
 | 
				
			|||||||
	if c.IsSet("admin-filter") {
 | 
						if c.IsSet("admin-filter") {
 | 
				
			||||||
		config.Source.AdminFilter = c.String("admin-filter")
 | 
							config.Source.AdminFilter = c.String("admin-filter")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if c.IsSet("allow-deactivate-all") {
 | 
				
			||||||
 | 
							config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -151,8 +151,10 @@ func runDump(ctx *cli.Context) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := z.AddDir("log", setting.LogRootPath); err != nil {
 | 
						if com.IsExist(setting.LogRootPath) {
 | 
				
			||||||
		fatal("Failed to include log: %v", err)
 | 
							if err := z.AddDir("log", setting.LogRootPath); err != nil {
 | 
				
			||||||
 | 
								fatal("Failed to include log: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = z.Close(); err != nil {
 | 
						if err = z.Close(); err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										148
									
								
								cmd/hook.go
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								cmd/hook.go
									
									
									
									
									
								
							@@ -8,10 +8,12 @@ import (
 | 
				
			|||||||
	"bufio"
 | 
						"bufio"
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
@@ -58,6 +60,85 @@ var (
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type delayWriter struct {
 | 
				
			||||||
 | 
						internal io.Writer
 | 
				
			||||||
 | 
						buf      *bytes.Buffer
 | 
				
			||||||
 | 
						timer    *time.Timer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newDelayWriter(internal io.Writer, delay time.Duration) *delayWriter {
 | 
				
			||||||
 | 
						timer := time.NewTimer(delay)
 | 
				
			||||||
 | 
						return &delayWriter{
 | 
				
			||||||
 | 
							internal: internal,
 | 
				
			||||||
 | 
							buf:      &bytes.Buffer{},
 | 
				
			||||||
 | 
							timer:    timer,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *delayWriter) Write(p []byte) (n int, err error) {
 | 
				
			||||||
 | 
						if d.buf != nil {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-d.timer.C:
 | 
				
			||||||
 | 
								_, err := d.internal.Write(d.buf.Bytes())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return 0, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								d.buf = nil
 | 
				
			||||||
 | 
								return d.internal.Write(p)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return d.buf.Write(p)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d.internal.Write(p)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *delayWriter) WriteString(s string) (n int, err error) {
 | 
				
			||||||
 | 
						if d.buf != nil {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-d.timer.C:
 | 
				
			||||||
 | 
								_, err := d.internal.Write(d.buf.Bytes())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return 0, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								d.buf = nil
 | 
				
			||||||
 | 
								return d.internal.Write([]byte(s))
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return d.buf.WriteString(s)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d.internal.Write([]byte(s))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *delayWriter) Close() error {
 | 
				
			||||||
 | 
						if d == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						stopped := d.timer.Stop()
 | 
				
			||||||
 | 
						if stopped {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case <-d.timer.C:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.buf == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, err := d.internal.Write(d.buf.Bytes())
 | 
				
			||||||
 | 
						d.buf = nil
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type nilWriter struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *nilWriter) Write(p []byte) (int, error) {
 | 
				
			||||||
 | 
						return len(p), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *nilWriter) WriteString(s string) (int, error) {
 | 
				
			||||||
 | 
						return len(s), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func runHookPreReceive(c *cli.Context) error {
 | 
					func runHookPreReceive(c *cli.Context) error {
 | 
				
			||||||
	if os.Getenv(models.EnvIsInternal) == "true" {
 | 
						if os.Getenv(models.EnvIsInternal) == "true" {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -101,6 +182,18 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
	total := 0
 | 
						total := 0
 | 
				
			||||||
	lastline := 0
 | 
						lastline := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out io.Writer
 | 
				
			||||||
 | 
						out = &nilWriter{}
 | 
				
			||||||
 | 
						if setting.Git.VerbosePush {
 | 
				
			||||||
 | 
							if setting.Git.VerbosePushDelay > 0 {
 | 
				
			||||||
 | 
								dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
 | 
				
			||||||
 | 
								defer dWriter.Close()
 | 
				
			||||||
 | 
								out = dWriter
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								out = os.Stdout
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for scanner.Scan() {
 | 
						for scanner.Scan() {
 | 
				
			||||||
		// TODO: support news feeds for wiki
 | 
							// TODO: support news feeds for wiki
 | 
				
			||||||
		if isWiki {
 | 
							if isWiki {
 | 
				
			||||||
@@ -124,12 +217,10 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
			newCommitIDs[count] = newCommitID
 | 
								newCommitIDs[count] = newCommitID
 | 
				
			||||||
			refFullNames[count] = refFullName
 | 
								refFullNames[count] = refFullName
 | 
				
			||||||
			count++
 | 
								count++
 | 
				
			||||||
			fmt.Fprintf(os.Stdout, "*")
 | 
								fmt.Fprintf(out, "*")
 | 
				
			||||||
			os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if count >= hookBatchSize {
 | 
								if count >= hookBatchSize {
 | 
				
			||||||
				fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
 | 
									fmt.Fprintf(out, " Checking %d branches\n", count)
 | 
				
			||||||
				os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				hookOptions.OldCommitIDs = oldCommitIDs
 | 
									hookOptions.OldCommitIDs = oldCommitIDs
 | 
				
			||||||
				hookOptions.NewCommitIDs = newCommitIDs
 | 
									hookOptions.NewCommitIDs = newCommitIDs
 | 
				
			||||||
@@ -147,12 +238,10 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
				lastline = 0
 | 
									lastline = 0
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			fmt.Fprintf(os.Stdout, ".")
 | 
								fmt.Fprintf(out, ".")
 | 
				
			||||||
			os.Stdout.Sync()
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if lastline >= hookBatchSize {
 | 
							if lastline >= hookBatchSize {
 | 
				
			||||||
			fmt.Fprintf(os.Stdout, "\n")
 | 
								fmt.Fprintf(out, "\n")
 | 
				
			||||||
			os.Stdout.Sync()
 | 
					 | 
				
			||||||
			lastline = 0
 | 
								lastline = 0
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -162,8 +251,7 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
		hookOptions.NewCommitIDs = newCommitIDs[:count]
 | 
							hookOptions.NewCommitIDs = newCommitIDs[:count]
 | 
				
			||||||
		hookOptions.RefFullNames = refFullNames[:count]
 | 
							hookOptions.RefFullNames = refFullNames[:count]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
 | 
							fmt.Fprintf(out, " Checking %d branches\n", count)
 | 
				
			||||||
		os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
 | 
							statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
 | 
				
			||||||
		switch statusCode {
 | 
							switch statusCode {
 | 
				
			||||||
@@ -173,14 +261,11 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
			fail(msg, "")
 | 
								fail(msg, "")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if lastline > 0 {
 | 
						} else if lastline > 0 {
 | 
				
			||||||
		fmt.Fprintf(os.Stdout, "\n")
 | 
							fmt.Fprintf(out, "\n")
 | 
				
			||||||
		os.Stdout.Sync()
 | 
					 | 
				
			||||||
		lastline = 0
 | 
							lastline = 0
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Fprintf(os.Stdout, "Checked %d references in total\n", total)
 | 
						fmt.Fprintf(out, "Checked %d references in total\n", total)
 | 
				
			||||||
	os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -206,6 +291,19 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out io.Writer
 | 
				
			||||||
 | 
						var dWriter *delayWriter
 | 
				
			||||||
 | 
						out = &nilWriter{}
 | 
				
			||||||
 | 
						if setting.Git.VerbosePush {
 | 
				
			||||||
 | 
							if setting.Git.VerbosePushDelay > 0 {
 | 
				
			||||||
 | 
								dWriter = newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
 | 
				
			||||||
 | 
								defer dWriter.Close()
 | 
				
			||||||
 | 
								out = dWriter
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								out = os.Stdout
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// the environment setted on serv command
 | 
						// the environment setted on serv command
 | 
				
			||||||
	repoUser := os.Getenv(models.EnvRepoUsername)
 | 
						repoUser := os.Getenv(models.EnvRepoUsername)
 | 
				
			||||||
	isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
 | 
						isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
 | 
				
			||||||
@@ -241,7 +339,7 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fmt.Fprintf(os.Stdout, ".")
 | 
							fmt.Fprintf(out, ".")
 | 
				
			||||||
		oldCommitIDs[count] = string(fields[0])
 | 
							oldCommitIDs[count] = string(fields[0])
 | 
				
			||||||
		newCommitIDs[count] = string(fields[1])
 | 
							newCommitIDs[count] = string(fields[1])
 | 
				
			||||||
		refFullNames[count] = string(fields[2])
 | 
							refFullNames[count] = string(fields[2])
 | 
				
			||||||
@@ -250,16 +348,15 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		count++
 | 
							count++
 | 
				
			||||||
		total++
 | 
							total++
 | 
				
			||||||
		os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if count >= hookBatchSize {
 | 
							if count >= hookBatchSize {
 | 
				
			||||||
			fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
 | 
								fmt.Fprintf(out, " Processing %d references\n", count)
 | 
				
			||||||
			os.Stdout.Sync()
 | 
					 | 
				
			||||||
			hookOptions.OldCommitIDs = oldCommitIDs
 | 
								hookOptions.OldCommitIDs = oldCommitIDs
 | 
				
			||||||
			hookOptions.NewCommitIDs = newCommitIDs
 | 
								hookOptions.NewCommitIDs = newCommitIDs
 | 
				
			||||||
			hookOptions.RefFullNames = refFullNames
 | 
								hookOptions.RefFullNames = refFullNames
 | 
				
			||||||
			resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
 | 
								resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
 | 
				
			||||||
			if resp == nil {
 | 
								if resp == nil {
 | 
				
			||||||
 | 
									_ = dWriter.Close()
 | 
				
			||||||
				hookPrintResults(results)
 | 
									hookPrintResults(results)
 | 
				
			||||||
				fail("Internal Server Error", err)
 | 
									fail("Internal Server Error", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -277,9 +374,9 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
				fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
 | 
									fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
 | 
							fmt.Fprintf(out, "Processed %d references in total\n", total)
 | 
				
			||||||
		os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_ = dWriter.Close()
 | 
				
			||||||
		hookPrintResults(results)
 | 
							hookPrintResults(results)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -288,19 +385,18 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
	hookOptions.NewCommitIDs = newCommitIDs[:count]
 | 
						hookOptions.NewCommitIDs = newCommitIDs[:count]
 | 
				
			||||||
	hookOptions.RefFullNames = refFullNames[:count]
 | 
						hookOptions.RefFullNames = refFullNames[:count]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
 | 
						fmt.Fprintf(out, " Processing %d references\n", count)
 | 
				
			||||||
	os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
 | 
						resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
 | 
				
			||||||
	if resp == nil {
 | 
						if resp == nil {
 | 
				
			||||||
 | 
							_ = dWriter.Close()
 | 
				
			||||||
		hookPrintResults(results)
 | 
							hookPrintResults(results)
 | 
				
			||||||
		fail("Internal Server Error", err)
 | 
							fail("Internal Server Error", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wasEmpty = wasEmpty || resp.RepoWasEmpty
 | 
						wasEmpty = wasEmpty || resp.RepoWasEmpty
 | 
				
			||||||
	results = append(results, resp.Results...)
 | 
						results = append(results, resp.Results...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
 | 
						fmt.Fprintf(out, "Processed %d references in total\n", total)
 | 
				
			||||||
	os.Stdout.Sync()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if wasEmpty && masterPushed {
 | 
						if wasEmpty && masterPushed {
 | 
				
			||||||
		// We need to tell the repo to reset the default branch to master
 | 
							// We need to tell the repo to reset the default branch to master
 | 
				
			||||||
@@ -309,7 +405,7 @@ Gitea or set your environment appropriately.`, "")
 | 
				
			|||||||
			fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
 | 
								fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						_ = dWriter.Close()
 | 
				
			||||||
	hookPrintResults(results)
 | 
						hookPrintResults(results)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -72,6 +73,7 @@ var (
 | 
				
			|||||||
		"git-receive-pack":   models.AccessModeWrite,
 | 
							"git-receive-pack":   models.AccessModeWrite,
 | 
				
			||||||
		lfsAuthenticateVerb:  models.AccessModeNone,
 | 
							lfsAuthenticateVerb:  models.AccessModeNone,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fail(userMessage, logMessage string, args ...interface{}) {
 | 
					func fail(userMessage, logMessage string, args ...interface{}) {
 | 
				
			||||||
@@ -147,6 +149,10 @@ func runServ(c *cli.Context) error {
 | 
				
			|||||||
	username := strings.ToLower(rr[0])
 | 
						username := strings.ToLower(rr[0])
 | 
				
			||||||
	reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
 | 
						reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if alphaDashDotPattern.MatchString(reponame) {
 | 
				
			||||||
 | 
							fail("Invalid repo name", "Invalid repo name: %s", reponame)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if setting.EnablePprof || c.Bool("enable-pprof") {
 | 
						if setting.EnablePprof || c.Bool("enable-pprof") {
 | 
				
			||||||
		if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
 | 
							if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
 | 
				
			||||||
			fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
 | 
								fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,11 +28,11 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/routers"
 | 
						"code.gitea.io/gitea/routers"
 | 
				
			||||||
	"code.gitea.io/gitea/routers/routes"
 | 
						"code.gitea.io/gitea/routers/routes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/config"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	context2 "github.com/gorilla/context"
 | 
						context2 "github.com/gorilla/context"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
						"github.com/unknwon/com"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4"
 | 
					 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/config"
 | 
					 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
					 | 
				
			||||||
	"gopkg.in/testfixtures.v2"
 | 
						"gopkg.in/testfixtures.v2"
 | 
				
			||||||
	"xorm.io/xorm"
 | 
						"xorm.io/xorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -275,8 +275,9 @@ DISABLE_ROUTER_LOG = false
 | 
				
			|||||||
; not forget to export the private key):
 | 
					; not forget to export the private key):
 | 
				
			||||||
; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
 | 
					; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
 | 
				
			||||||
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
 | 
					; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
 | 
				
			||||||
CERT_FILE = custom/https/cert.pem
 | 
					; Paths are relative to CUSTOM_PATH
 | 
				
			||||||
KEY_FILE = custom/https/key.pem
 | 
					CERT_FILE = https/cert.pem
 | 
				
			||||||
 | 
					KEY_FILE = https/key.pem
 | 
				
			||||||
; Root directory containing templates and static files.
 | 
					; Root directory containing templates and static files.
 | 
				
			||||||
; default is the path where Gitea is executed
 | 
					; default is the path where Gitea is executed
 | 
				
			||||||
STATIC_ROOT_PATH =
 | 
					STATIC_ROOT_PATH =
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ params:
 | 
				
			|||||||
  description: Git with a cup of tea
 | 
					  description: Git with a cup of tea
 | 
				
			||||||
  author: The Gitea Authors
 | 
					  author: The Gitea Authors
 | 
				
			||||||
  website: https://docs.gitea.io
 | 
					  website: https://docs.gitea.io
 | 
				
			||||||
  version: 1.10.2
 | 
					  version: 1.11.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
outputs:
 | 
					outputs:
 | 
				
			||||||
  home:
 | 
					  home:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -181,8 +181,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 | 
				
			|||||||
- `SSH_LISTEN_PORT`: **%(SSH\_PORT)s**: Port for the built-in SSH server.
 | 
					- `SSH_LISTEN_PORT`: **%(SSH\_PORT)s**: Port for the built-in SSH server.
 | 
				
			||||||
- `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures.
 | 
					- `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures.
 | 
				
			||||||
- `DISABLE_ROUTER_LOG`: **false**: Mute printing of the router log.
 | 
					- `DISABLE_ROUTER_LOG`: **false**: Mute printing of the router log.
 | 
				
			||||||
- `CERT_FILE`: **custom/https/cert.pem**: Cert file path used for HTTPS.
 | 
					- `CERT_FILE`: **https/cert.pem**: Cert file path used for HTTPS. From 1.11 paths are relative to `CUSTOM_PATH`.
 | 
				
			||||||
- `KEY_FILE`: **custom/https/key.pem**: Key file path used for HTTPS.
 | 
					- `KEY_FILE`: **https/key.pem**: Key file path used for HTTPS. From 1.11 paths are relative to `CUSTOM_PATH`.
 | 
				
			||||||
- `STATIC_ROOT_PATH`: **./**: Upper level of template and static files path.
 | 
					- `STATIC_ROOT_PATH`: **./**: Upper level of template and static files path.
 | 
				
			||||||
- `STATIC_CACHE_TIME`: **6h**: Web browser cache time for static resources on `custom/`, `public/` and all uploaded avatars.
 | 
					- `STATIC_CACHE_TIME`: **6h**: Web browser cache time for static resources on `custom/`, `public/` and all uploaded avatars.
 | 
				
			||||||
- `ENABLE_GZIP`: **false**: Enables application-level GZIP support.
 | 
					- `ENABLE_GZIP`: **false**: Enables application-level GZIP support.
 | 
				
			||||||
@@ -522,6 +522,8 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
 | 
				
			|||||||
- `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view.
 | 
					- `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view.
 | 
				
			||||||
- `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/
 | 
					- `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/
 | 
				
			||||||
- `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1
 | 
					- `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1
 | 
				
			||||||
 | 
					- `VERBOSE_PUSH`: **true**: Print status information about pushes as they are being processed.
 | 
				
			||||||
 | 
					- `VERBOSE_PUSH_DELAY`: **5s**: Only print verbose information if push takes longer than this delay.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Git - Timeout settings (`git.timeout`)
 | 
					## Git - Timeout settings (`git.timeout`)
 | 
				
			||||||
- `DEFAUlT`: **360**: Git operations default timeout seconds.
 | 
					- `DEFAUlT`: **360**: Git operations default timeout seconds.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,7 @@ _Symbols used in table:_
 | 
				
			|||||||
| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ⁄ | ✓ |
 | 
					| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ⁄ | ✓ |
 | 
				
			||||||
| Group Milestones | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
 | 
					| Group Milestones | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
 | 
				
			||||||
| Granular user roles (Code, Issues, Wiki etc) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
 | 
					| Granular user roles (Code, Issues, Wiki etc) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
 | 
				
			||||||
| Verified Committer | ✘ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
 | 
					| Verified Committer | ⁄ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
 | 
				
			||||||
| GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
 | 
					| GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
 | 
				
			||||||
| Reject unsigned commits | [✘](https://github.com/go-gitea/gitea/issues/2770) | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
 | 
					| Reject unsigned commits | [✘](https://github.com/go-gitea/gitea/issues/2770) | ✘ | ✓ | ✓ | ✓ | ✘ | ✓ |
 | 
				
			||||||
| Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
 | 
					| Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,6 +114,17 @@ recommended way to build from source is therefore:
 | 
				
			|||||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
 | 
					TAGS="bindata sqlite sqlite_unlock_notify" make build
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `build` target is split into two sub-targets:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `make backend` which requires [Go 1.11](https://golang.org/dl/) or greater.
 | 
				
			||||||
 | 
					- `make frontend` which requires [Node.js 10.0.0](https://nodejs.org/en/download/) or greater.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If pre-built frontend files are present it is possible to only build the backend:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					TAGS="bindata" make backend
 | 
				
			||||||
 | 
					``
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Test
 | 
					## Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
After following the steps above, a `gitea` binary will be available in the working directory.
 | 
					After following the steps above, a `gitea` binary will be available in the working directory.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -136,7 +136,8 @@ the `!` marker to identify pull requests. For example:
 | 
				
			|||||||
> This is pull request [!1234](#), and links to a pull request in Gitea.
 | 
					> This is pull request [!1234](#), and links to a pull request in Gitea.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `!` and `#` can be used interchangeably for issues and pull request _except_
 | 
					The `!` and `#` can be used interchangeably for issues and pull request _except_
 | 
				
			||||||
for this case, where a distinction is required.
 | 
					for this case, where a distinction is required. If the repository uses external
 | 
				
			||||||
 | 
					tracker, commit message for squash merge will use `!` as reference by default.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Issues and Pull Requests References Summary
 | 
					## Issues and Pull Requests References Summary
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								go.mod
									
									
									
									
									
								
							@@ -41,6 +41,8 @@ require (
 | 
				
			|||||||
	github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
 | 
						github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
 | 
				
			||||||
	github.com/gliderlabs/ssh v0.2.2
 | 
						github.com/gliderlabs/ssh v0.2.2
 | 
				
			||||||
	github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
 | 
						github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
 | 
				
			||||||
 | 
						github.com/go-git/go-billy/v5 v5.0.0
 | 
				
			||||||
 | 
						github.com/go-git/go-git/v5 v5.0.0
 | 
				
			||||||
	github.com/go-openapi/jsonreference v0.19.3 // indirect
 | 
						github.com/go-openapi/jsonreference v0.19.3 // indirect
 | 
				
			||||||
	github.com/go-redis/redis v6.15.2+incompatible
 | 
						github.com/go-redis/redis v6.15.2+incompatible
 | 
				
			||||||
	github.com/go-sql-driver/mysql v1.4.1
 | 
						github.com/go-sql-driver/mysql v1.4.1
 | 
				
			||||||
@@ -62,7 +64,7 @@ require (
 | 
				
			|||||||
	github.com/lib/pq v1.2.0
 | 
						github.com/lib/pq v1.2.0
 | 
				
			||||||
	github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
 | 
						github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
 | 
				
			||||||
	github.com/mailru/easyjson v0.7.0 // indirect
 | 
						github.com/mailru/easyjson v0.7.0 // indirect
 | 
				
			||||||
	github.com/markbates/goth v1.56.0
 | 
						github.com/markbates/goth v1.61.2
 | 
				
			||||||
	github.com/mattn/go-isatty v0.0.7
 | 
						github.com/mattn/go-isatty v0.0.7
 | 
				
			||||||
	github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
 | 
						github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
 | 
				
			||||||
	github.com/mattn/go-sqlite3 v1.11.0
 | 
						github.com/mattn/go-sqlite3 v1.11.0
 | 
				
			||||||
@@ -80,7 +82,7 @@ require (
 | 
				
			|||||||
	github.com/quasoft/websspi v1.0.0
 | 
						github.com/quasoft/websspi v1.0.0
 | 
				
			||||||
	github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
 | 
				
			||||||
	github.com/satori/go.uuid v1.2.0
 | 
						github.com/satori/go.uuid v1.2.0
 | 
				
			||||||
	github.com/sergi/go-diff v1.0.0
 | 
						github.com/sergi/go-diff v1.1.0
 | 
				
			||||||
	github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
 | 
						github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
 | 
				
			||||||
	github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
 | 
						github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
 | 
				
			||||||
	github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect
 | 
						github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect
 | 
				
			||||||
@@ -95,10 +97,10 @@ require (
 | 
				
			|||||||
	github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
 | 
						github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53
 | 
				
			||||||
	github.com/yuin/goldmark v1.1.19
 | 
						github.com/yuin/goldmark v1.1.19
 | 
				
			||||||
	go.etcd.io/bbolt v1.3.3 // indirect
 | 
						go.etcd.io/bbolt v1.3.3 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
 | 
						golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
 | 
				
			||||||
	golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
 | 
						golang.org/x/net v0.0.0-20200301022130-244492dfa37a
 | 
				
			||||||
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
						golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
 | 
				
			||||||
	golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2
 | 
						golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527
 | 
				
			||||||
	golang.org/x/text v0.3.2
 | 
						golang.org/x/text v0.3.2
 | 
				
			||||||
	golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect
 | 
						golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect
 | 
				
			||||||
	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 | 
						gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 | 
				
			||||||
@@ -106,8 +108,6 @@ require (
 | 
				
			|||||||
	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 | 
						gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 | 
				
			||||||
	gopkg.in/ini.v1 v1.51.1
 | 
						gopkg.in/ini.v1 v1.51.1
 | 
				
			||||||
	gopkg.in/ldap.v3 v3.0.2
 | 
						gopkg.in/ldap.v3 v3.0.2
 | 
				
			||||||
	gopkg.in/src-d/go-billy.v4 v4.3.2
 | 
					 | 
				
			||||||
	gopkg.in/src-d/go-git.v4 v4.13.1
 | 
					 | 
				
			||||||
	gopkg.in/testfixtures.v2 v2.5.0
 | 
						gopkg.in/testfixtures.v2 v2.5.0
 | 
				
			||||||
	mvdan.cc/xurls/v2 v2.1.0
 | 
						mvdan.cc/xurls/v2 v2.1.0
 | 
				
			||||||
	strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
 | 
						strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										59
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								go.sum
									
									
									
									
									
								
							@@ -110,7 +110,7 @@ github.com/couchbase/vellum v0.0.0-20190829182332-ef2e028c01fd/go.mod h1:xbc8Ff/
 | 
				
			|||||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 h1:1XjEY/gnjQ+AfXef2U6dxCquhiRzkEpxZuWqs+QxTL8=
 | 
					github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 h1:1XjEY/gnjQ+AfXef2U6dxCquhiRzkEpxZuWqs+QxTL8=
 | 
				
			||||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
 | 
					github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
 | 
				
			||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 | 
					github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 | 
				
			||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 | 
					github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
				
			||||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
 | 
					github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
 | 
				
			||||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
 | 
					github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
 | 
				
			||||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
 | 
					github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
 | 
				
			||||||
@@ -164,6 +164,14 @@ github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a h1:FQqo
 | 
				
			|||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 | 
					github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 | 
				
			||||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
 | 
					github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
 | 
				
			||||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
 | 
					github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
 | 
				
			||||||
 | 
					github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 | 
				
			||||||
 | 
					github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
 | 
				
			||||||
 | 
					github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
 | 
				
			||||||
 | 
					github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
 | 
				
			||||||
 | 
					github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
 | 
				
			||||||
 | 
					github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
 | 
				
			||||||
 | 
					github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
 | 
				
			||||||
 | 
					github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
 | 
				
			||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
					github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
				
			||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
					github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
				
			||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
					github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
				
			||||||
@@ -346,11 +354,13 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
				
			|||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
					github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
				
			||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
					github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
				
			||||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
					github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
				
			||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 | 
					 | 
				
			||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
					github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
				
			||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
					github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
				
			||||||
 | 
					github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
				
			||||||
 | 
					github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
				
			||||||
github.com/lafriks/xormstore v1.3.2 h1:hqi3F8s/B4rz8GuEZZDuHuOxRjeuOpEI/cC7vcnWwH4=
 | 
					github.com/lafriks/xormstore v1.3.2 h1:hqi3F8s/B4rz8GuEZZDuHuOxRjeuOpEI/cC7vcnWwH4=
 | 
				
			||||||
github.com/lafriks/xormstore v1.3.2/go.mod h1:mVNIwIa25QIr8rfR7YlVjrqN/apswHkVdtLCyVYBzXw=
 | 
					github.com/lafriks/xormstore v1.3.2/go.mod h1:mVNIwIa25QIr8rfR7YlVjrqN/apswHkVdtLCyVYBzXw=
 | 
				
			||||||
 | 
					github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk=
 | 
				
			||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
					github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
				
			||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 | 
					github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 | 
				
			||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
					github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
				
			||||||
@@ -370,8 +380,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
 | 
				
			|||||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
 | 
					github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 | 
					github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 | 
				
			||||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
 | 
					github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
 | 
				
			||||||
github.com/markbates/goth v1.56.0 h1:XEYedCgMNz5pi3ojXI8z2XUmXtBnMeuKUpx4Z6HlNj8=
 | 
					github.com/markbates/goth v1.61.2 h1:jDowrUH5qw8KGuQdKwFhLzkXkTYCIPfz3LHADJsiPIs=
 | 
				
			||||||
github.com/markbates/goth v1.56.0/go.mod h1:zZmAw0Es0Dpm7TT/4AdN14QrkiWLMrrU9Xei1o+/mdA=
 | 
					github.com/markbates/goth v1.61.2/go.mod h1:qh2QfwZoWRucQ+DR5KVKC6dUGkNCToWh4vS45GIzFsY=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
 | 
					github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
					github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 | 
				
			||||||
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g=
 | 
					github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d h1:m+dSK37rFf2fqppZhg15yI2IwC9BtucBiRwSDm9VL8g=
 | 
				
			||||||
@@ -402,6 +412,8 @@ github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc/go.mod h1:np1wUFZ6ty
 | 
				
			|||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
					github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
				
			||||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
 | 
					github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
 | 
				
			||||||
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
 | 
					github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
 | 
				
			||||||
 | 
					github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 | 
				
			||||||
 | 
					github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 | 
				
			||||||
github.com/niklasfasching/go-org v0.1.8 h1:Kjvs6lP+LIILHhc9zIJ4Gu90a/pVY483if2Qmu8v4Fg=
 | 
					github.com/niklasfasching/go-org v0.1.8 h1:Kjvs6lP+LIILHhc9zIJ4Gu90a/pVY483if2Qmu8v4Fg=
 | 
				
			||||||
github.com/niklasfasching/go-org v0.1.8/go.mod h1:AsLD6X7djzRIz4/RFZu8vwRL0VGjUvGZCCH1Nz0VdrU=
 | 
					github.com/niklasfasching/go-org v0.1.8/go.mod h1:AsLD6X7djzRIz4/RFZu8vwRL0VGjUvGZCCH1Nz0VdrU=
 | 
				
			||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
					github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
				
			||||||
@@ -416,7 +428,6 @@ github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
 | 
				
			|||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
					github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
				
			||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 | 
					github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 | 
				
			||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
					github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
				
			||||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
 | 
					 | 
				
			||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
					github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
				
			||||||
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
 | 
					github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
 | 
				
			||||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
					github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
 | 
				
			||||||
@@ -462,12 +473,11 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn
 | 
				
			|||||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
 | 
				
			||||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
					github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 | 
				
			||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
					github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
				
			||||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
 | 
					 | 
				
			||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
					github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
				
			||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 | 
					github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 | 
				
			||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 | 
					github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 | 
				
			||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 | 
					github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
 | 
				
			||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
					github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 | 
				
			||||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
 | 
					github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
 | 
				
			||||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 | 
					github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 | 
				
			||||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
 | 
					github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0=
 | 
				
			||||||
@@ -502,13 +512,10 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 | 
				
			|||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
					github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
				
			||||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
 | 
					github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
 | 
				
			||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 | 
					github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 | 
				
			||||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
 | 
					 | 
				
			||||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
 | 
					 | 
				
			||||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 h1:JNEGSiWg6D3lcBCMCBqN3ELniXujt+0QNHLhNnO0w3s=
 | 
					github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 h1:JNEGSiWg6D3lcBCMCBqN3ELniXujt+0QNHLhNnO0w3s=
 | 
				
			||||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2/go.mod h1:mjqs7N0Q6m5HpR7QfXVBZXZWSqTjQLeTujjA/xUp2uw=
 | 
					github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2/go.mod h1:mjqs7N0Q6m5HpR7QfXVBZXZWSqTjQLeTujjA/xUp2uw=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 | 
					 | 
				
			||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
					github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
				
			||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
@@ -532,7 +539,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
 | 
				
			|||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
					github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
				
			||||||
github.com/unknwon/cae v0.0.0-20190822084630-55a0b64484a1 h1:SpoCl3+Pta5/ubQyF+Fmx65obtpfkyzeaOIneCE3MTw=
 | 
					github.com/unknwon/cae v0.0.0-20190822084630-55a0b64484a1 h1:SpoCl3+Pta5/ubQyF+Fmx65obtpfkyzeaOIneCE3MTw=
 | 
				
			||||||
github.com/unknwon/cae v0.0.0-20190822084630-55a0b64484a1/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU=
 | 
					github.com/unknwon/cae v0.0.0-20190822084630-55a0b64484a1/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU=
 | 
				
			||||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM=
 | 
					 | 
				
			||||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
 | 
					github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
 | 
				
			||||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
 | 
					github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
 | 
				
			||||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
 | 
					github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
 | 
				
			||||||
@@ -578,11 +584,10 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
 | 
				
			|||||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
 | 
					 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
 | 
					golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
					golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
				
			||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
					golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
				
			||||||
@@ -614,8 +619,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
 | 
				
			|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
				
			||||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 h1:DPz9iiH3YoKiKhX/ijjoZvT0VFwK2c6CWYWQ7Zyr8TU=
 | 
					golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
 | 
				
			||||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
					golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
@@ -645,15 +650,13 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
 | 
				
			|||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 h1:/J2nHFg1MTqaRLFO7M+J78ASNsJoz3r0cvHBPQ77fsE=
 | 
					golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
					golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
					golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
				
			||||||
@@ -675,7 +678,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
 | 
				
			|||||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
					golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
					golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
					golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
 | 
					 | 
				
			||||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
					golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM=
 | 
					golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 h1:kJQZhwFzSwJS2BxboKjdZzWczQOZx8VuH7Y8hhuGUtM=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
					golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
				
			||||||
@@ -715,8 +717,9 @@ gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8
 | 
				
			|||||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
 | 
					gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 | 
					 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
					gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
				
			||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
					gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
				
			||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 | 
					gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 | 
				
			||||||
@@ -731,12 +734,6 @@ gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
 | 
				
			|||||||
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
 | 
					gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
 | 
				
			||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
					gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
				
			||||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
					gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
				
			||||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
 | 
					 | 
				
			||||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
 | 
					 | 
				
			||||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
 | 
					 | 
				
			||||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 | 
					 | 
				
			||||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
 | 
					 | 
				
			||||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
 | 
					 | 
				
			||||||
gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw=
 | 
					gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw=
 | 
				
			||||||
gopkg.in/testfixtures.v2 v2.5.0/go.mod h1:vyAq+MYCgNpR29qitQdLZhdbLFf4mR/2MFJRFoQZZ2M=
 | 
					gopkg.in/testfixtures.v2 v2.5.0/go.mod h1:vyAq+MYCgNpR29qitQdLZhdbLFf4mR/2MFJRFoQZZ2M=
 | 
				
			||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
					gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
				
			||||||
@@ -745,8 +742,9 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 | 
				
			|||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
					gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
					gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 | 
					 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
					honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
				
			||||||
@@ -760,7 +758,6 @@ xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
 | 
				
			|||||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
 | 
					xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
 | 
				
			||||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
 | 
					xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
 | 
				
			||||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
 | 
					xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
 | 
				
			||||||
xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
 | 
					 | 
				
			||||||
xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
 | 
					xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
 | 
				
			||||||
xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ=
 | 
					xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ=
 | 
				
			||||||
xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
 | 
					xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,8 @@ func testAPIGetBranch(t *testing.T, branchName string, exists bool) {
 | 
				
			|||||||
	var branch api.Branch
 | 
						var branch api.Branch
 | 
				
			||||||
	DecodeJSON(t, resp, &branch)
 | 
						DecodeJSON(t, resp, &branch)
 | 
				
			||||||
	assert.EqualValues(t, branchName, branch.Name)
 | 
						assert.EqualValues(t, branchName, branch.Name)
 | 
				
			||||||
 | 
						assert.True(t, branch.UserCanPush)
 | 
				
			||||||
 | 
						assert.True(t, branch.UserCanMerge)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAPIGetBranch(t *testing.T) {
 | 
					func TestAPIGetBranch(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								integrations/api_issue_milestone_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								integrations/api_issue_milestone_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					// Copyright 2020 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPIIssuesMilestone(t *testing.T) {
 | 
				
			||||||
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						milestone := models.AssertExistsAndLoadBean(t, &models.Milestone{ID: 1}).(*models.Milestone)
 | 
				
			||||||
 | 
						repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: milestone.RepoID}).(*models.Repository)
 | 
				
			||||||
 | 
						owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
 | 
				
			||||||
 | 
						assert.Equal(t, int64(1), int64(milestone.NumIssues))
 | 
				
			||||||
 | 
						assert.Equal(t, structs.StateOpen, milestone.State())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session := loginUser(t, owner.Name)
 | 
				
			||||||
 | 
						token := getTokenForLoggedInUser(t, session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// update values of issue
 | 
				
			||||||
 | 
						milestoneState := "closed"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%d?token=%s", owner.Name, repo.Name, milestone.ID, token)
 | 
				
			||||||
 | 
						req := NewRequestWithJSON(t, "PATCH", urlStr, structs.EditMilestoneOption{
 | 
				
			||||||
 | 
							State: &milestoneState,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var apiMilestone structs.Milestone
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiMilestone)
 | 
				
			||||||
 | 
						assert.EqualValues(t, "closed", apiMilestone.State)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", urlStr)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var apiMilestone2 structs.Milestone
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiMilestone2)
 | 
				
			||||||
 | 
						assert.EqualValues(t, "closed", apiMilestone2.State)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,6 +7,7 @@ package integrations
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
@@ -38,12 +39,12 @@ func TestAPICreateIssue(t *testing.T) {
 | 
				
			|||||||
	defer prepareTestEnv(t)()
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
	const body, title = "apiTestBody", "apiTestTitle"
 | 
						const body, title = "apiTestBody", "apiTestTitle"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
						repoBefore := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
 | 
						owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repoBefore.OwnerID}).(*models.User)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, owner.Name)
 | 
						session := loginUser(t, owner.Name)
 | 
				
			||||||
	token := getTokenForLoggedInUser(t, session)
 | 
						token := getTokenForLoggedInUser(t, session)
 | 
				
			||||||
	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repo.Name, token)
 | 
						urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues?state=all&token=%s", owner.Name, repoBefore.Name, token)
 | 
				
			||||||
	req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{
 | 
						req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateIssueOption{
 | 
				
			||||||
		Body:     body,
 | 
							Body:     body,
 | 
				
			||||||
		Title:    title,
 | 
							Title:    title,
 | 
				
			||||||
@@ -56,19 +57,23 @@ func TestAPICreateIssue(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, apiIssue.Title, title)
 | 
						assert.Equal(t, apiIssue.Title, title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	models.AssertExistsAndLoadBean(t, &models.Issue{
 | 
						models.AssertExistsAndLoadBean(t, &models.Issue{
 | 
				
			||||||
		RepoID:     repo.ID,
 | 
							RepoID:     repoBefore.ID,
 | 
				
			||||||
		AssigneeID: owner.ID,
 | 
							AssigneeID: owner.ID,
 | 
				
			||||||
		Content:    body,
 | 
							Content:    body,
 | 
				
			||||||
		Title:      title,
 | 
							Title:      title,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repoAfter := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
 | 
						assert.Equal(t, repoBefore.NumIssues+1, repoAfter.NumIssues)
 | 
				
			||||||
 | 
						assert.Equal(t, repoBefore.NumClosedIssues, repoAfter.NumClosedIssues)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAPIEditIssue(t *testing.T) {
 | 
					func TestAPIEditIssue(t *testing.T) {
 | 
				
			||||||
	defer prepareTestEnv(t)()
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
 | 
						issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
 | 
				
			||||||
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
 | 
						repoBefore := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
 | 
				
			||||||
	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
 | 
						owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repoBefore.OwnerID}).(*models.User)
 | 
				
			||||||
	assert.NoError(t, issueBefore.LoadAttributes())
 | 
						assert.NoError(t, issueBefore.LoadAttributes())
 | 
				
			||||||
	assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
 | 
						assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
 | 
				
			||||||
	assert.Equal(t, api.StateOpen, issueBefore.State())
 | 
						assert.Equal(t, api.StateOpen, issueBefore.State())
 | 
				
			||||||
@@ -83,7 +88,7 @@ func TestAPIEditIssue(t *testing.T) {
 | 
				
			|||||||
	body := "new content!"
 | 
						body := "new content!"
 | 
				
			||||||
	title := "new title from api set"
 | 
						title := "new title from api set"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
 | 
						urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repoBefore.Name, issueBefore.Index, token)
 | 
				
			||||||
	req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
 | 
						req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
 | 
				
			||||||
		State:          &issueState,
 | 
							State:          &issueState,
 | 
				
			||||||
		RemoveDeadline: &removeDeadline,
 | 
							RemoveDeadline: &removeDeadline,
 | 
				
			||||||
@@ -98,6 +103,7 @@ func TestAPIEditIssue(t *testing.T) {
 | 
				
			|||||||
	DecodeJSON(t, resp, &apiIssue)
 | 
						DecodeJSON(t, resp, &apiIssue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
 | 
						issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
 | 
				
			||||||
 | 
						repoAfter := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// check deleted user
 | 
						// check deleted user
 | 
				
			||||||
	assert.Equal(t, int64(500), issueAfter.PosterID)
 | 
						assert.Equal(t, int64(500), issueAfter.PosterID)
 | 
				
			||||||
@@ -106,6 +112,9 @@ func TestAPIEditIssue(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, int64(-1), issueBefore.PosterID)
 | 
						assert.Equal(t, int64(-1), issueBefore.PosterID)
 | 
				
			||||||
	assert.Equal(t, int64(-1), apiIssue.Poster.ID)
 | 
						assert.Equal(t, int64(-1), apiIssue.Poster.ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check repo change
 | 
				
			||||||
 | 
						assert.Equal(t, repoBefore.NumClosedIssues+1, repoAfter.NumClosedIssues)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// API response
 | 
						// API response
 | 
				
			||||||
	assert.Equal(t, api.StateClosed, apiIssue.State)
 | 
						assert.Equal(t, api.StateClosed, apiIssue.State)
 | 
				
			||||||
	assert.Equal(t, milestone, apiIssue.Milestone.ID)
 | 
						assert.Equal(t, milestone, apiIssue.Milestone.ID)
 | 
				
			||||||
@@ -120,3 +129,47 @@ func TestAPIEditIssue(t *testing.T) {
 | 
				
			|||||||
	assert.Equal(t, body, issueAfter.Content)
 | 
						assert.Equal(t, body, issueAfter.Content)
 | 
				
			||||||
	assert.Equal(t, title, issueAfter.Title)
 | 
						assert.Equal(t, title, issueAfter.Title)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAPISearchIssue(t *testing.T) {
 | 
				
			||||||
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
						token := getTokenForLoggedInUser(t, session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						link, _ := url.Parse("/api/v1/repos/issues/search")
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", link.String())
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						var apiIssues []*api.Issue
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiIssues)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Len(t, apiIssues, 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query := url.Values{}
 | 
				
			||||||
 | 
						query.Add("token", token)
 | 
				
			||||||
 | 
						link.RawQuery = query.Encode()
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", link.String())
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiIssues)
 | 
				
			||||||
 | 
						assert.Len(t, apiIssues, 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query.Add("state", "closed")
 | 
				
			||||||
 | 
						link.RawQuery = query.Encode()
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", link.String())
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiIssues)
 | 
				
			||||||
 | 
						assert.Len(t, apiIssues, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query.Set("state", "all")
 | 
				
			||||||
 | 
						link.RawQuery = query.Encode()
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", link.String())
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiIssues)
 | 
				
			||||||
 | 
						assert.Len(t, apiIssues, 10) //there are more but 10 is page item limit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						query.Add("page", "2")
 | 
				
			||||||
 | 
						link.RawQuery = query.Encode()
 | 
				
			||||||
 | 
						req = NewRequest(t, "GET", link.String())
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiIssues)
 | 
				
			||||||
 | 
						assert.Len(t, apiIssues, 0)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -351,6 +351,17 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
 | 
				
			|||||||
			pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t)
 | 
								pr, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "protected", "unprotected")(t)
 | 
				
			||||||
			assert.NoError(t, err)
 | 
								assert.NoError(t, err)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
							t.Run("GenerateCommit", func(t *testing.T) {
 | 
				
			||||||
 | 
								_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected-2"))
 | 
				
			||||||
 | 
							var pr2 api.PullRequest
 | 
				
			||||||
 | 
							t.Run("CreatePullRequest", func(t *testing.T) {
 | 
				
			||||||
 | 
								pr2, err = doAPICreatePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, "unprotected", "unprotected-2")(t)
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							t.Run("MergePR2", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr2.Index))
 | 
				
			||||||
		t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index))
 | 
							t.Run("MergePR", doAPIMergePullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index))
 | 
				
			||||||
		t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
 | 
							t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
 | 
				
			||||||
		t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username))
 | 
							t.Run("ProtectProtectedBranchWhitelist", doProtectBranch(ctx, "protected", baseCtx.Username))
 | 
				
			||||||
@@ -422,6 +433,9 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
 | 
				
			|||||||
		tmpDir, err := ioutil.TempDir("", ctx.Reponame)
 | 
							tmpDir, err := ioutil.TempDir("", ctx.Reponame)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_, err = git.NewCommand("clone", u.String()).RunInDir(tmpDir)
 | 
				
			||||||
 | 
							assert.Error(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err = git.InitRepository(tmpDir, false)
 | 
							err = git.InitRepository(tmpDir, false)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -449,6 +463,13 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
 | 
				
			|||||||
		_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
 | 
							_, err = git.NewCommand("remote", "add", "origin", u.String()).RunInDir(tmpDir)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							invalidCtx := ctx
 | 
				
			||||||
 | 
							invalidCtx.Reponame = fmt.Sprintf("invalid/repo-tmp-push-create-%s", u.Scheme)
 | 
				
			||||||
 | 
							u.Path = invalidCtx.GitPath()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_, err = git.NewCommand("remote", "add", "invalid", u.String()).RunInDir(tmpDir)
 | 
				
			||||||
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Push to create disabled
 | 
							// Push to create disabled
 | 
				
			||||||
		setting.Repository.EnablePushCreateUser = false
 | 
							setting.Repository.EnablePushCreateUser = false
 | 
				
			||||||
		_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
 | 
							_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
 | 
				
			||||||
@@ -456,6 +477,12 @@ func doPushCreate(ctx APITestContext, u *url.URL) func(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Push to create enabled
 | 
							// Push to create enabled
 | 
				
			||||||
		setting.Repository.EnablePushCreateUser = true
 | 
							setting.Repository.EnablePushCreateUser = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Invalid repo
 | 
				
			||||||
 | 
							_, err = git.NewCommand("push", "invalid", "master").RunInDir(tmpDir)
 | 
				
			||||||
 | 
							assert.Error(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Valid repo
 | 
				
			||||||
		_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
 | 
							_, err = git.NewCommand("push", "origin", "master").RunInDir(tmpDir)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1 +1 @@
 | 
				
			|||||||
0cf15c3f66ec8384480ed9c3cf87c9e97fbb0ec3
 | 
					423313fbd38093bb10d0c8387db9105409c6f196
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -126,7 +126,7 @@ func restoreOldDB(t *testing.T, version string) bool {
 | 
				
			|||||||
		err := os.MkdirAll(path.Dir(setting.Database.Path), os.ModePerm)
 | 
							err := os.MkdirAll(path.Dir(setting.Database.Path), os.ModePerm)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d", setting.Database.Path, setting.Database.Timeout))
 | 
							db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate", setting.Database.Path, setting.Database.Timeout))
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		defer db.Close()
 | 
							defer db.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,3 +106,57 @@ func TestPullCreate_TitleEscape(t *testing.T) {
 | 
				
			|||||||
		assert.Equal(t, "<u>XSS PR</u>", titleHTML)
 | 
							assert.Equal(t, "<u>XSS PR</u>", titleHTML)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testUIDeleteBranch(t *testing.T, session *TestSession, ownerName, repoName, branchName string) {
 | 
				
			||||||
 | 
						relURL := "/" + path.Join(ownerName, repoName, "branches")
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", relURL)
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequestWithValues(t, "POST", relURL+"/delete", map[string]string{
 | 
				
			||||||
 | 
							"_csrf": getCsrf(t, htmlDoc.doc),
 | 
				
			||||||
 | 
							"name":  branchName,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testDeleteRepository(t *testing.T, session *TestSession, ownerName, repoName string) {
 | 
				
			||||||
 | 
						relURL := "/" + path.Join(ownerName, repoName, "settings")
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", relURL)
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = NewRequestWithValues(t, "POST", relURL+"?action=delete", map[string]string{
 | 
				
			||||||
 | 
							"_csrf":     getCsrf(t, htmlDoc.doc),
 | 
				
			||||||
 | 
							"repo_name": repoName,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusFound)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPullBranchDelete(t *testing.T) {
 | 
				
			||||||
 | 
						onGiteaRun(t, func(t *testing.T, u *url.URL) {
 | 
				
			||||||
 | 
							defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							session := loginUser(t, "user1")
 | 
				
			||||||
 | 
							testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
 | 
							testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusFound)
 | 
				
			||||||
 | 
							testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
 | 
							resp := testPullCreate(t, session, "user1", "repo1", "master1", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// check the redirected URL
 | 
				
			||||||
 | 
							url := resp.HeaderMap.Get("Location")
 | 
				
			||||||
 | 
							assert.Regexp(t, "^/user2/repo1/pulls/[0-9]*$", url)
 | 
				
			||||||
 | 
							req := NewRequest(t, "GET", url)
 | 
				
			||||||
 | 
							session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// delete head branch and confirm pull page is ok
 | 
				
			||||||
 | 
							testUIDeleteBranch(t, session, "user1", "repo1", "master1")
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", url)
 | 
				
			||||||
 | 
							session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// delete head repository and confirm pull page is ok
 | 
				
			||||||
 | 
							testDeleteRepository(t, session, "user1", "repo1")
 | 
				
			||||||
 | 
							req = NewRequest(t, "GET", url)
 | 
				
			||||||
 | 
							session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -105,8 +105,6 @@ func TestPullRebase(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestPullRebaseMerge(t *testing.T) {
 | 
					func TestPullRebaseMerge(t *testing.T) {
 | 
				
			||||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
						onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
				
			||||||
		defer prepareTestEnv(t)()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
							hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		hookTasksLenBefore := len(hookTasks)
 | 
							hookTasksLenBefore := len(hookTasks)
 | 
				
			||||||
@@ -129,8 +127,6 @@ func TestPullRebaseMerge(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestPullSquash(t *testing.T) {
 | 
					func TestPullSquash(t *testing.T) {
 | 
				
			||||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
						onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
				
			||||||
		defer prepareTestEnv(t)()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
							hookTasks, err := models.HookTasks(1, 1) //Retrieve previous hook number
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		hookTasksLenBefore := len(hookTasks)
 | 
							hookTasksLenBefore := len(hookTasks)
 | 
				
			||||||
@@ -154,10 +150,9 @@ func TestPullSquash(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestPullCleanUpAfterMerge(t *testing.T) {
 | 
					func TestPullCleanUpAfterMerge(t *testing.T) {
 | 
				
			||||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
						onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
				
			||||||
		defer prepareTestEnv(t)()
 | 
					 | 
				
			||||||
		session := loginUser(t, "user1")
 | 
							session := loginUser(t, "user1")
 | 
				
			||||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
							testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
 | 
							testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited - TestPullCleanUpAfterMerge)\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
 | 
							resp := testPullCreate(t, session, "user1", "repo1", "feature/test", "This is a pull title")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -190,7 +185,6 @@ func TestPullCleanUpAfterMerge(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestCantMergeWorkInProgress(t *testing.T) {
 | 
					func TestCantMergeWorkInProgress(t *testing.T) {
 | 
				
			||||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
						onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
				
			||||||
		defer prepareTestEnv(t)()
 | 
					 | 
				
			||||||
		session := loginUser(t, "user1")
 | 
							session := loginUser(t, "user1")
 | 
				
			||||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
							testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
		testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
							testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
@@ -212,7 +206,6 @@ func TestCantMergeWorkInProgress(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestCantMergeConflict(t *testing.T) {
 | 
					func TestCantMergeConflict(t *testing.T) {
 | 
				
			||||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
						onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
				
			||||||
		defer prepareTestEnv(t)()
 | 
					 | 
				
			||||||
		session := loginUser(t, "user1")
 | 
							session := loginUser(t, "user1")
 | 
				
			||||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
							testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
 | 
							testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
 | 
				
			||||||
@@ -258,7 +251,6 @@ func TestCantMergeConflict(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestCantMergeUnrelated(t *testing.T) {
 | 
					func TestCantMergeUnrelated(t *testing.T) {
 | 
				
			||||||
	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
						onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 | 
				
			||||||
		defer prepareTestEnv(t)()
 | 
					 | 
				
			||||||
		session := loginUser(t, "user1")
 | 
							session := loginUser(t, "user1")
 | 
				
			||||||
		testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
							testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
 | 
				
			||||||
		testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
 | 
							testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,6 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
					 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -48,20 +47,20 @@ func TestPullCreate_CommitStatus(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		commitID := path.Base(commitURL)
 | 
							commitID := path.Base(commitURL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		statusList := []models.CommitStatusState{
 | 
							statusList := []api.CommitStatusState{
 | 
				
			||||||
			models.CommitStatusPending,
 | 
								api.CommitStatusPending,
 | 
				
			||||||
			models.CommitStatusError,
 | 
								api.CommitStatusError,
 | 
				
			||||||
			models.CommitStatusFailure,
 | 
								api.CommitStatusFailure,
 | 
				
			||||||
			models.CommitStatusWarning,
 | 
								api.CommitStatusWarning,
 | 
				
			||||||
			models.CommitStatusSuccess,
 | 
								api.CommitStatusSuccess,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		statesIcons := map[models.CommitStatusState]string{
 | 
							statesIcons := map[api.CommitStatusState]string{
 | 
				
			||||||
			models.CommitStatusPending: "circle icon yellow",
 | 
								api.CommitStatusPending: "circle icon yellow",
 | 
				
			||||||
			models.CommitStatusSuccess: "check icon green",
 | 
								api.CommitStatusSuccess: "check icon green",
 | 
				
			||||||
			models.CommitStatusError:   "warning icon red",
 | 
								api.CommitStatusError:   "warning icon red",
 | 
				
			||||||
			models.CommitStatusFailure: "remove icon red",
 | 
								api.CommitStatusFailure: "remove icon red",
 | 
				
			||||||
			models.CommitStatusWarning: "warning sign icon yellow",
 | 
								api.CommitStatusWarning: "warning sign icon yellow",
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Update commit status, and check if icon is updated as well
 | 
							// Update commit status, and check if icon is updated as well
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title st
 | 
				
			|||||||
	resp := session.MakeRequest(t, req, http.StatusOK)
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
	htmlDoc := NewHTMLParser(t, resp.Body)
 | 
						htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	link, exists := htmlDoc.doc.Find("form").Attr("action")
 | 
						link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
 | 
				
			||||||
	assert.True(t, exists, "The template has changed")
 | 
						assert.True(t, exists, "The template has changed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	postData := map[string]string{
 | 
						postData := map[string]string{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ func TestSignOut(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	session := loginUser(t, "user2")
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req := NewRequest(t, "GET", "/user/logout")
 | 
						req := NewRequest(t, "POST", "/user/logout")
 | 
				
			||||||
	session.MakeRequest(t, req, http.StatusFound)
 | 
						session.MakeRequest(t, req, http.StatusFound)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// try to view a private repo, should fail
 | 
						// try to view a private repo, should fail
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -122,10 +122,13 @@ func (a *Action) ShortActUserName() string {
 | 
				
			|||||||
	return base.EllipsisString(a.GetActUserName(), 20)
 | 
						return base.EllipsisString(a.GetActUserName(), 20)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME
 | 
					// GetDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
 | 
				
			||||||
func (a *Action) GetDisplayName() string {
 | 
					func (a *Action) GetDisplayName() string {
 | 
				
			||||||
	if setting.UI.DefaultShowFullName {
 | 
						if setting.UI.DefaultShowFullName {
 | 
				
			||||||
		return a.GetActFullName()
 | 
							trimmedFullName := strings.TrimSpace(a.GetActFullName())
 | 
				
			||||||
 | 
							if len(trimmedFullName) > 0 {
 | 
				
			||||||
 | 
								return trimmedFullName
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return a.ShortActUserName()
 | 
						return a.ShortActUserName()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -212,7 +215,7 @@ func (a *Action) getCommentLink(e Engine) string {
 | 
				
			|||||||
		return "#"
 | 
							return "#"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if a.Comment == nil && a.CommentID != 0 {
 | 
						if a.Comment == nil && a.CommentID != 0 {
 | 
				
			||||||
		a.Comment, _ = GetCommentByID(a.CommentID)
 | 
							a.Comment, _ = getCommentByID(e, a.CommentID)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if a.Comment != nil {
 | 
						if a.Comment != nil {
 | 
				
			||||||
		return a.Comment.HTMLURL()
 | 
							return a.Comment.HTMLURL()
 | 
				
			||||||
@@ -432,6 +435,8 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cond = cond.And(builder.In("repo_id", repoIDs))
 | 
							cond = cond.And(builder.In("repo_id", repoIDs))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							cond = cond.And(builder.In("repo_id", AccessibleRepoIDsQuery(opts.RequestingUserID)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
 | 
						cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,7 +79,11 @@ func (a *Attachment) LinkedRepository() (*Repository, UnitType, error) {
 | 
				
			|||||||
			return nil, UnitTypeIssues, err
 | 
								return nil, UnitTypeIssues, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		repo, err := GetRepositoryByID(iss.RepoID)
 | 
							repo, err := GetRepositoryByID(iss.RepoID)
 | 
				
			||||||
		return repo, UnitTypeIssues, err
 | 
							unitType := UnitTypeIssues
 | 
				
			||||||
 | 
							if iss.IsPull {
 | 
				
			||||||
 | 
								unitType = UnitTypePullRequests
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return repo, unitType, err
 | 
				
			||||||
	} else if a.ReleaseID != 0 {
 | 
						} else if a.ReleaseID != 0 {
 | 
				
			||||||
		rel, err := GetReleaseByID(a.ReleaseID)
 | 
							rel, err := GetReleaseByID(a.ReleaseID)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -195,7 +199,7 @@ func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
 | 
					func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
 | 
				
			||||||
	attachments := make([]*Attachment, 0, 10)
 | 
						attachments := make([]*Attachment, 0, 10)
 | 
				
			||||||
	return attachments, x.Where("comment_id=?", commentID).Find(&attachments)
 | 
						return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getAttachmentByReleaseIDFileName return a file based on the the following infos:
 | 
					// getAttachmentByReleaseIDFileName return a file based on the the following infos:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -138,7 +138,7 @@ func TestLinkedRepository(t *testing.T) {
 | 
				
			|||||||
		expectedUnitType UnitType
 | 
							expectedUnitType UnitType
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"LinkedIssue", 1, &Repository{ID: 1}, UnitTypeIssues},
 | 
							{"LinkedIssue", 1, &Repository{ID: 1}, UnitTypeIssues},
 | 
				
			||||||
		{"LinkedComment", 3, &Repository{ID: 1}, UnitTypeIssues},
 | 
							{"LinkedComment", 3, &Repository{ID: 1}, UnitTypePullRequests},
 | 
				
			||||||
		{"LinkedRelease", 9, &Repository{ID: 1}, UnitTypeReleases},
 | 
							{"LinkedRelease", 9, &Repository{ID: 1}, UnitTypeReleases},
 | 
				
			||||||
		{"Notlinked", 10, nil, -1},
 | 
							{"Notlinked", 10, nil, -1},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,6 +113,28 @@ func (protectBranch *ProtectedBranch) CanUserMerge(userID int64) bool {
 | 
				
			|||||||
	return in
 | 
						return in
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
 | 
				
			||||||
 | 
					func (protectBranch *ProtectedBranch) IsUserMergeWhitelisted(userID int64) bool {
 | 
				
			||||||
 | 
						if !protectBranch.EnableMergeWhitelist {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if base.Int64sContains(protectBranch.MergeWhitelistUserIDs, userID) {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(protectBranch.MergeWhitelistTeamIDs) == 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						in, err := IsUserInTeams(userID, protectBranch.MergeWhitelistTeamIDs)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Error("IsUserInTeams: %v", err)
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return in
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsUserOfficialReviewer check if user is official reviewer for the branch (counts towards required approvals)
 | 
					// IsUserOfficialReviewer check if user is official reviewer for the branch (counts towards required approvals)
 | 
				
			||||||
func (protectBranch *ProtectedBranch) IsUserOfficialReviewer(user *User) (bool, error) {
 | 
					func (protectBranch *ProtectedBranch) IsUserOfficialReviewer(user *User) (bool, error) {
 | 
				
			||||||
	return protectBranch.isUserOfficialReviewer(x, user)
 | 
						return protectBranch.isUserOfficialReviewer(x, user)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,52 +19,19 @@ import (
 | 
				
			|||||||
	"xorm.io/xorm"
 | 
						"xorm.io/xorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommitStatusState holds the state of a Status
 | 
					 | 
				
			||||||
// It can be "pending", "success", "error", "failure", and "warning"
 | 
					 | 
				
			||||||
type CommitStatusState string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsWorseThan returns true if this State is worse than the given State
 | 
					 | 
				
			||||||
func (css CommitStatusState) IsWorseThan(css2 CommitStatusState) bool {
 | 
					 | 
				
			||||||
	switch css {
 | 
					 | 
				
			||||||
	case CommitStatusError:
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	case CommitStatusFailure:
 | 
					 | 
				
			||||||
		return css2 != CommitStatusError
 | 
					 | 
				
			||||||
	case CommitStatusWarning:
 | 
					 | 
				
			||||||
		return css2 != CommitStatusError && css2 != CommitStatusFailure
 | 
					 | 
				
			||||||
	case CommitStatusSuccess:
 | 
					 | 
				
			||||||
		return css2 != CommitStatusError && css2 != CommitStatusFailure && css2 != CommitStatusWarning
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		return css2 != CommitStatusError && css2 != CommitStatusFailure && css2 != CommitStatusWarning && css2 != CommitStatusSuccess
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	// CommitStatusPending is for when the Status is Pending
 | 
					 | 
				
			||||||
	CommitStatusPending CommitStatusState = "pending"
 | 
					 | 
				
			||||||
	// CommitStatusSuccess is for when the Status is Success
 | 
					 | 
				
			||||||
	CommitStatusSuccess CommitStatusState = "success"
 | 
					 | 
				
			||||||
	// CommitStatusError is for when the Status is Error
 | 
					 | 
				
			||||||
	CommitStatusError CommitStatusState = "error"
 | 
					 | 
				
			||||||
	// CommitStatusFailure is for when the Status is Failure
 | 
					 | 
				
			||||||
	CommitStatusFailure CommitStatusState = "failure"
 | 
					 | 
				
			||||||
	// CommitStatusWarning is for when the Status is Warning
 | 
					 | 
				
			||||||
	CommitStatusWarning CommitStatusState = "warning"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CommitStatus holds a single Status of a single Commit
 | 
					// CommitStatus holds a single Status of a single Commit
 | 
				
			||||||
type CommitStatus struct {
 | 
					type CommitStatus struct {
 | 
				
			||||||
	ID          int64             `xorm:"pk autoincr"`
 | 
						ID          int64                 `xorm:"pk autoincr"`
 | 
				
			||||||
	Index       int64             `xorm:"INDEX UNIQUE(repo_sha_index)"`
 | 
						Index       int64                 `xorm:"INDEX UNIQUE(repo_sha_index)"`
 | 
				
			||||||
	RepoID      int64             `xorm:"INDEX UNIQUE(repo_sha_index)"`
 | 
						RepoID      int64                 `xorm:"INDEX UNIQUE(repo_sha_index)"`
 | 
				
			||||||
	Repo        *Repository       `xorm:"-"`
 | 
						Repo        *Repository           `xorm:"-"`
 | 
				
			||||||
	State       CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
 | 
						State       api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"`
 | 
				
			||||||
	SHA         string            `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
 | 
						SHA         string                `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
 | 
				
			||||||
	TargetURL   string            `xorm:"TEXT"`
 | 
						TargetURL   string                `xorm:"TEXT"`
 | 
				
			||||||
	Description string            `xorm:"TEXT"`
 | 
						Description string                `xorm:"TEXT"`
 | 
				
			||||||
	ContextHash string            `xorm:"char(40) index"`
 | 
						ContextHash string                `xorm:"char(40) index"`
 | 
				
			||||||
	Context     string            `xorm:"TEXT"`
 | 
						Context     string                `xorm:"TEXT"`
 | 
				
			||||||
	Creator     *User             `xorm:"-"`
 | 
						Creator     *User                 `xorm:"-"`
 | 
				
			||||||
	CreatorID   int64
 | 
						CreatorID   int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
 | 
						CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
 | 
				
			||||||
@@ -118,9 +85,9 @@ func (status *CommitStatus) APIFormat() *api.Status {
 | 
				
			|||||||
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
 | 
					// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
 | 
				
			||||||
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
 | 
					func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
 | 
				
			||||||
	var lastStatus *CommitStatus
 | 
						var lastStatus *CommitStatus
 | 
				
			||||||
	var state CommitStatusState
 | 
						var state api.CommitStatusState
 | 
				
			||||||
	for _, status := range statuses {
 | 
						for _, status := range statuses {
 | 
				
			||||||
		if status.State.IsWorseThan(state) {
 | 
							if status.State.NoBetterThan(state) {
 | 
				
			||||||
			state = status.State
 | 
								state = status.State
 | 
				
			||||||
			lastStatus = status
 | 
								lastStatus = status
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package models
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,22 +24,22 @@ func TestGetCommitStatuses(t *testing.T) {
 | 
				
			|||||||
	assert.Len(t, statuses, 5)
 | 
						assert.Len(t, statuses, 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t, "ci/awesomeness", statuses[0].Context)
 | 
						assert.Equal(t, "ci/awesomeness", statuses[0].Context)
 | 
				
			||||||
	assert.Equal(t, CommitStatusPending, statuses[0].State)
 | 
						assert.Equal(t, structs.CommitStatusPending, statuses[0].State)
 | 
				
			||||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[0].APIURL())
 | 
						assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[0].APIURL())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t, "cov/awesomeness", statuses[1].Context)
 | 
						assert.Equal(t, "cov/awesomeness", statuses[1].Context)
 | 
				
			||||||
	assert.Equal(t, CommitStatusWarning, statuses[1].State)
 | 
						assert.Equal(t, structs.CommitStatusWarning, statuses[1].State)
 | 
				
			||||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[1].APIURL())
 | 
						assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[1].APIURL())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t, "cov/awesomeness", statuses[2].Context)
 | 
						assert.Equal(t, "cov/awesomeness", statuses[2].Context)
 | 
				
			||||||
	assert.Equal(t, CommitStatusSuccess, statuses[2].State)
 | 
						assert.Equal(t, structs.CommitStatusSuccess, statuses[2].State)
 | 
				
			||||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[2].APIURL())
 | 
						assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[2].APIURL())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t, "ci/awesomeness", statuses[3].Context)
 | 
						assert.Equal(t, "ci/awesomeness", statuses[3].Context)
 | 
				
			||||||
	assert.Equal(t, CommitStatusFailure, statuses[3].State)
 | 
						assert.Equal(t, structs.CommitStatusFailure, statuses[3].State)
 | 
				
			||||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[3].APIURL())
 | 
						assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[3].APIURL())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
 | 
						assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
 | 
				
			||||||
	assert.Equal(t, CommitStatusError, statuses[4].State)
 | 
						assert.Equal(t, structs.CommitStatusError, statuses[4].State)
 | 
				
			||||||
	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL())
 | 
						assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -56,6 +57,21 @@ func (err ErrNamePatternNotAllowed) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
 | 
						return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrNameCharsNotAllowed represents a "character not allowed in name" error.
 | 
				
			||||||
 | 
					type ErrNameCharsNotAllowed struct {
 | 
				
			||||||
 | 
						Name string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrNameCharsNotAllowed checks if an error is an ErrNameCharsNotAllowed.
 | 
				
			||||||
 | 
					func IsErrNameCharsNotAllowed(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrNameCharsNotAllowed)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrNameCharsNotAllowed) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("User name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrSSHDisabled represents an "SSH disabled" error.
 | 
					// ErrSSHDisabled represents an "SSH disabled" error.
 | 
				
			||||||
type ErrSSHDisabled struct {
 | 
					type ErrSSHDisabled struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1355,6 +1371,53 @@ func (err ErrMergePushOutOfDate) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("Merge PushOutOfDate Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
 | 
						return fmt.Sprintf("Merge PushOutOfDate Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrPushRejected represents an error if merging fails due to rejection from a hook
 | 
				
			||||||
 | 
					type ErrPushRejected struct {
 | 
				
			||||||
 | 
						Style   MergeStyle
 | 
				
			||||||
 | 
						Message string
 | 
				
			||||||
 | 
						StdOut  string
 | 
				
			||||||
 | 
						StdErr  string
 | 
				
			||||||
 | 
						Err     error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrPushRejected checks if an error is a ErrPushRejected.
 | 
				
			||||||
 | 
					func IsErrPushRejected(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrPushRejected)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrPushRejected) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("Merge PushRejected Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GenerateMessage generates the remote message from the stderr
 | 
				
			||||||
 | 
					func (err *ErrPushRejected) GenerateMessage() {
 | 
				
			||||||
 | 
						messageBuilder := &strings.Builder{}
 | 
				
			||||||
 | 
						i := strings.Index(err.StdErr, "remote: ")
 | 
				
			||||||
 | 
						if i < 0 {
 | 
				
			||||||
 | 
							err.Message = ""
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							if len(err.StdErr) <= i+8 {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err.StdErr[i:i+8] != "remote: " {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							i += 8
 | 
				
			||||||
 | 
							nl := strings.IndexByte(err.StdErr[i:], '\n')
 | 
				
			||||||
 | 
							if nl >= 0 {
 | 
				
			||||||
 | 
								messageBuilder.WriteString(err.StdErr[i : i+nl+1])
 | 
				
			||||||
 | 
								i = i + nl + 1
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								messageBuilder.WriteString(err.StdErr[i:])
 | 
				
			||||||
 | 
								i = len(err.StdErr)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err.Message = strings.TrimSpace(messageBuilder.String())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrRebaseConflicts represents an error if rebase fails with a conflict
 | 
					// ErrRebaseConflicts represents an error if rebase fails with a conflict
 | 
				
			||||||
type ErrRebaseConflicts struct {
 | 
					type ErrRebaseConflicts struct {
 | 
				
			||||||
	Style     MergeStyle
 | 
						Style     MergeStyle
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -369,6 +369,7 @@ type CommitVerification struct {
 | 
				
			|||||||
	CommittingUser *User
 | 
						CommittingUser *User
 | 
				
			||||||
	SigningEmail   string
 | 
						SigningEmail   string
 | 
				
			||||||
	SigningKey     *GPGKey
 | 
						SigningKey     *GPGKey
 | 
				
			||||||
 | 
						TrustStatus    string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SignCommit represents a commit with validation of signature.
 | 
					// SignCommit represents a commit with validation of signature.
 | 
				
			||||||
@@ -754,18 +755,54 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
 | 
					// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
 | 
				
			||||||
func ParseCommitsWithSignature(oldCommits *list.List) *list.List {
 | 
					func ParseCommitsWithSignature(oldCommits *list.List, repository *Repository) *list.List {
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		newCommits = list.New()
 | 
							newCommits = list.New()
 | 
				
			||||||
		e          = oldCommits.Front()
 | 
							e          = oldCommits.Front()
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
						memberMap := map[int64]bool{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for e != nil {
 | 
						for e != nil {
 | 
				
			||||||
		c := e.Value.(UserCommit)
 | 
							c := e.Value.(UserCommit)
 | 
				
			||||||
		newCommits.PushBack(SignCommit{
 | 
							signCommit := SignCommit{
 | 
				
			||||||
			UserCommit:   &c,
 | 
								UserCommit:   &c,
 | 
				
			||||||
			Verification: ParseCommitWithSignature(c.Commit),
 | 
								Verification: ParseCommitWithSignature(c.Commit),
 | 
				
			||||||
		})
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_ = CalculateTrustStatus(signCommit.Verification, repository, &memberMap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newCommits.PushBack(signCommit)
 | 
				
			||||||
		e = e.Next()
 | 
							e = e.Next()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return newCommits
 | 
						return newCommits
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository
 | 
				
			||||||
 | 
					func CalculateTrustStatus(verification *CommitVerification, repository *Repository, memberMap *map[int64]bool) (err error) {
 | 
				
			||||||
 | 
						if verification.Verified {
 | 
				
			||||||
 | 
							verification.TrustStatus = "trusted"
 | 
				
			||||||
 | 
							if verification.SigningUser.ID != 0 {
 | 
				
			||||||
 | 
								var isMember bool
 | 
				
			||||||
 | 
								if memberMap != nil {
 | 
				
			||||||
 | 
									var has bool
 | 
				
			||||||
 | 
									isMember, has = (*memberMap)[verification.SigningUser.ID]
 | 
				
			||||||
 | 
									if !has {
 | 
				
			||||||
 | 
										isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID)
 | 
				
			||||||
 | 
										(*memberMap)[verification.SigningUser.ID] = isMember
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									isMember, err = repository.IsOwnerMemberCollaborator(verification.SigningUser.ID)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !isMember {
 | 
				
			||||||
 | 
									verification.TrustStatus = "untrusted"
 | 
				
			||||||
 | 
									if verification.CommittingUser.ID != verification.SigningUser.ID {
 | 
				
			||||||
 | 
										// The committing user and the signing user are not the same and are not the default key
 | 
				
			||||||
 | 
										// This should be marked as questionable unless the signing user is a collaborator/team member etc.
 | 
				
			||||||
 | 
										verification.TrustStatus = "unmatched"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -381,6 +381,7 @@ func (issue *Issue) apiFormat(e Engine) *api.Issue {
 | 
				
			|||||||
	apiIssue := &api.Issue{
 | 
						apiIssue := &api.Issue{
 | 
				
			||||||
		ID:       issue.ID,
 | 
							ID:       issue.ID,
 | 
				
			||||||
		URL:      issue.APIURL(),
 | 
							URL:      issue.APIURL(),
 | 
				
			||||||
 | 
							HTMLURL:  issue.HTMLURL(),
 | 
				
			||||||
		Index:    issue.Index,
 | 
							Index:    issue.Index,
 | 
				
			||||||
		Poster:   issue.Poster.APIFormat(),
 | 
							Poster:   issue.Poster.APIFormat(),
 | 
				
			||||||
		Title:    issue.Title,
 | 
							Title:    issue.Title,
 | 
				
			||||||
@@ -402,11 +403,12 @@ func (issue *Issue) apiFormat(e Engine) *api.Issue {
 | 
				
			|||||||
		apiIssue.Closed = issue.ClosedUnix.AsTimePtr()
 | 
							apiIssue.Closed = issue.ClosedUnix.AsTimePtr()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						issue.loadMilestone(e)
 | 
				
			||||||
	if issue.Milestone != nil {
 | 
						if issue.Milestone != nil {
 | 
				
			||||||
		apiIssue.Milestone = issue.Milestone.APIFormat()
 | 
							apiIssue.Milestone = issue.Milestone.APIFormat()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	issue.loadAssignees(e)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						issue.loadAssignees(e)
 | 
				
			||||||
	if len(issue.Assignees) > 0 {
 | 
						if len(issue.Assignees) > 0 {
 | 
				
			||||||
		for _, assignee := range issue.Assignees {
 | 
							for _, assignee := range issue.Assignees {
 | 
				
			||||||
			apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat())
 | 
								apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat())
 | 
				
			||||||
@@ -436,7 +438,7 @@ func (issue *Issue) HashTag() string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsPoster returns true if given user by ID is the poster.
 | 
					// IsPoster returns true if given user by ID is the poster.
 | 
				
			||||||
func (issue *Issue) IsPoster(uid int64) bool {
 | 
					func (issue *Issue) IsPoster(uid int64) bool {
 | 
				
			||||||
	return issue.PosterID == uid
 | 
						return issue.OriginalAuthorID == 0 && issue.PosterID == uid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (issue *Issue) hasLabel(e Engine, labelID int64) bool {
 | 
					func (issue *Issue) hasLabel(e Engine, labelID int64) bool {
 | 
				
			||||||
@@ -671,6 +673,10 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (*C
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := issue.updateClosedNum(e); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// New action comment
 | 
						// New action comment
 | 
				
			||||||
	cmtType := CommentTypeClose
 | 
						cmtType := CommentTypeClose
 | 
				
			||||||
	if !issue.IsClosed {
 | 
						if !issue.IsClosed {
 | 
				
			||||||
@@ -1334,6 +1340,36 @@ type IssueStatsOptions struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetIssueStats returns issue statistic information by given conditions.
 | 
					// GetIssueStats returns issue statistic information by given conditions.
 | 
				
			||||||
func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
 | 
					func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
 | 
				
			||||||
 | 
						if len(opts.IssueIDs) <= maxQueryParameters {
 | 
				
			||||||
 | 
							return getIssueStatsChunk(opts, opts.IssueIDs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If too long a list of IDs is provided, we get the statistics in
 | 
				
			||||||
 | 
						// smaller chunks and get accumulates. Note: this could potentially
 | 
				
			||||||
 | 
						// get us invalid results. The alternative is to insert the list of
 | 
				
			||||||
 | 
						// ids in a temporary table and join from them.
 | 
				
			||||||
 | 
						accum := &IssueStats{}
 | 
				
			||||||
 | 
						for i := 0; i < len(opts.IssueIDs); {
 | 
				
			||||||
 | 
							chunk := i + maxQueryParameters
 | 
				
			||||||
 | 
							if chunk > len(opts.IssueIDs) {
 | 
				
			||||||
 | 
								chunk = len(opts.IssueIDs)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							stats, err := getIssueStatsChunk(opts, opts.IssueIDs[i:chunk])
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							accum.OpenCount += stats.OpenCount
 | 
				
			||||||
 | 
							accum.ClosedCount += stats.ClosedCount
 | 
				
			||||||
 | 
							accum.YourRepositoriesCount += stats.YourRepositoriesCount
 | 
				
			||||||
 | 
							accum.AssignCount += stats.AssignCount
 | 
				
			||||||
 | 
							accum.CreateCount += stats.CreateCount
 | 
				
			||||||
 | 
							accum.OpenCount += stats.MentionCount
 | 
				
			||||||
 | 
							i = chunk
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return accum, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) {
 | 
				
			||||||
	stats := &IssueStats{}
 | 
						stats := &IssueStats{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	countSession := func(opts *IssueStatsOptions) *xorm.Session {
 | 
						countSession := func(opts *IssueStatsOptions) *xorm.Session {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -749,8 +749,12 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetCommentByID returns the comment by given ID.
 | 
					// GetCommentByID returns the comment by given ID.
 | 
				
			||||||
func GetCommentByID(id int64) (*Comment, error) {
 | 
					func GetCommentByID(id int64) (*Comment, error) {
 | 
				
			||||||
 | 
						return getCommentByID(x, id)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getCommentByID(e Engine, id int64) (*Comment, error) {
 | 
				
			||||||
	c := new(Comment)
 | 
						c := new(Comment)
 | 
				
			||||||
	has, err := x.ID(id).Get(c)
 | 
						has, err := e.ID(id).Get(c)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	} else if !has {
 | 
						} else if !has {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -376,6 +376,11 @@ func (comments CommentList) loadDependentIssues(e Engine) error {
 | 
				
			|||||||
	for _, comment := range comments {
 | 
						for _, comment := range comments {
 | 
				
			||||||
		if comment.DependentIssue == nil {
 | 
							if comment.DependentIssue == nil {
 | 
				
			||||||
			comment.DependentIssue = issues[comment.DependentIssueID]
 | 
								comment.DependentIssue = issues[comment.DependentIssueID]
 | 
				
			||||||
 | 
								if comment.DependentIssue != nil {
 | 
				
			||||||
 | 
									if err := comment.DependentIssue.loadRepo(e); err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ package models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
@@ -95,6 +96,8 @@ func NewMilestone(m *Milestone) (err error) {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m.Name = strings.TrimSpace(m.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err = sess.Insert(m); err != nil {
 | 
						if _, err = sess.Insert(m); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -268,6 +271,7 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (Mile
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func updateMilestone(e Engine, m *Milestone) error {
 | 
					func updateMilestone(e Engine, m *Milestone) error {
 | 
				
			||||||
 | 
						m.Name = strings.TrimSpace(m.Name)
 | 
				
			||||||
	_, err := e.ID(m.ID).AllCols().
 | 
						_, err := e.ID(m.ID).AllCols().
 | 
				
			||||||
		SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
 | 
							SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
 | 
				
			||||||
			builder.Eq{"milestone_id": m.ID},
 | 
								builder.Eq{"milestone_id": m.ID},
 | 
				
			||||||
@@ -283,12 +287,33 @@ func updateMilestone(e Engine, m *Milestone) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateMilestone updates information of given milestone.
 | 
					// UpdateMilestone updates information of given milestone.
 | 
				
			||||||
func UpdateMilestone(m *Milestone) error {
 | 
					func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
 | 
				
			||||||
	if err := updateMilestone(x, m); err != nil {
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return updateMilestoneCompleteness(x, m.ID)
 | 
						if m.IsClosed && !oldIsClosed {
 | 
				
			||||||
 | 
							m.ClosedDateUnix = timeutil.TimeStampNow()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := updateMilestone(sess, m); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := updateMilestoneCompleteness(sess, m.ID); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if IsClosed changed, update milestone numbers of repository
 | 
				
			||||||
 | 
						if oldIsClosed != m.IsClosed {
 | 
				
			||||||
 | 
							if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
 | 
					func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
 | 
				
			||||||
@@ -496,10 +521,12 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
 | 
				
			|||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CountMilestonesByRepoIDs map from repoIDs to number of milestones matching the options`
 | 
					// CountMilestones map from repo conditions to number of milestones matching the options`
 | 
				
			||||||
func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64, error) {
 | 
					func CountMilestones(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) {
 | 
				
			||||||
	sess := x.Where("is_closed = ?", isClosed)
 | 
						sess := x.Where("is_closed = ?", isClosed)
 | 
				
			||||||
	sess.In("repo_id", repoIDs)
 | 
						if repoCond.IsValid() {
 | 
				
			||||||
 | 
							sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	countsSlice := make([]*struct {
 | 
						countsSlice := make([]*struct {
 | 
				
			||||||
		RepoID int64
 | 
							RepoID int64
 | 
				
			||||||
@@ -519,11 +546,21 @@ func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64,
 | 
				
			|||||||
	return countMap, nil
 | 
						return countMap, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetMilestonesByRepoIDs returns a list of milestones of given repositories and status.
 | 
					// CountMilestonesByRepoIDs map from repoIDs to number of milestones matching the options`
 | 
				
			||||||
func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType string) (MilestoneList, error) {
 | 
					func CountMilestonesByRepoIDs(repoIDs []int64, isClosed bool) (map[int64]int64, error) {
 | 
				
			||||||
 | 
						return CountMilestones(
 | 
				
			||||||
 | 
							builder.In("repo_id", repoIDs),
 | 
				
			||||||
 | 
							isClosed,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchMilestones search milestones
 | 
				
			||||||
 | 
					func SearchMilestones(repoCond builder.Cond, page int, isClosed bool, sortType string) (MilestoneList, error) {
 | 
				
			||||||
	miles := make([]*Milestone, 0, setting.UI.IssuePagingNum)
 | 
						miles := make([]*Milestone, 0, setting.UI.IssuePagingNum)
 | 
				
			||||||
	sess := x.Where("is_closed = ?", isClosed)
 | 
						sess := x.Where("is_closed = ?", isClosed)
 | 
				
			||||||
	sess.In("repo_id", repoIDs)
 | 
						if repoCond.IsValid() {
 | 
				
			||||||
 | 
							sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if page > 0 {
 | 
						if page > 0 {
 | 
				
			||||||
		sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
 | 
							sess = sess.Limit(setting.UI.IssuePagingNum, (page-1)*setting.UI.IssuePagingNum)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -545,25 +582,45 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s
 | 
				
			|||||||
	return miles, sess.Find(&miles)
 | 
						return miles, sess.Find(&miles)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetMilestonesByRepoIDs returns a list of milestones of given repositories and status.
 | 
				
			||||||
 | 
					func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType string) (MilestoneList, error) {
 | 
				
			||||||
 | 
						return SearchMilestones(
 | 
				
			||||||
 | 
							builder.In("repo_id", repoIDs),
 | 
				
			||||||
 | 
							page,
 | 
				
			||||||
 | 
							isClosed,
 | 
				
			||||||
 | 
							sortType,
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MilestonesStats represents milestone statistic information.
 | 
					// MilestonesStats represents milestone statistic information.
 | 
				
			||||||
type MilestonesStats struct {
 | 
					type MilestonesStats struct {
 | 
				
			||||||
	OpenCount, ClosedCount int64
 | 
						OpenCount, ClosedCount int64
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Total returns the total counts of milestones
 | 
				
			||||||
 | 
					func (m MilestonesStats) Total() int64 {
 | 
				
			||||||
 | 
						return m.OpenCount + m.ClosedCount
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetMilestonesStats returns milestone statistic information for dashboard by given conditions.
 | 
					// GetMilestonesStats returns milestone statistic information for dashboard by given conditions.
 | 
				
			||||||
func GetMilestonesStats(userRepoIDs []int64) (*MilestonesStats, error) {
 | 
					func GetMilestonesStats(repoCond builder.Cond) (*MilestonesStats, error) {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	stats := &MilestonesStats{}
 | 
						stats := &MilestonesStats{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	stats.OpenCount, err = x.Where("is_closed = ?", false).
 | 
						sess := x.Where("is_closed = ?", false)
 | 
				
			||||||
		And(builder.In("repo_id", userRepoIDs)).
 | 
						if repoCond.IsValid() {
 | 
				
			||||||
		Count(new(Milestone))
 | 
							sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond)))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						stats.OpenCount, err = sess.Count(new(Milestone))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	stats.ClosedCount, err = x.Where("is_closed = ?", true).
 | 
					
 | 
				
			||||||
		And(builder.In("repo_id", userRepoIDs)).
 | 
						sess = x.Where("is_closed = ?", true)
 | 
				
			||||||
		Count(new(Milestone))
 | 
						if repoCond.IsValid() {
 | 
				
			||||||
 | 
							sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond)))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						stats.ClosedCount, err = sess.Count(new(Milestone))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
 | 
						"xorm.io/builder"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -158,10 +159,11 @@ func TestUpdateMilestone(t *testing.T) {
 | 
				
			|||||||
	assert.NoError(t, PrepareTestDatabase())
 | 
						assert.NoError(t, PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
 | 
						milestone := AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
 | 
				
			||||||
	milestone.Name = "newMilestoneName"
 | 
						milestone.Name = " newMilestoneName  "
 | 
				
			||||||
	milestone.Content = "newMilestoneContent"
 | 
						milestone.Content = "newMilestoneContent"
 | 
				
			||||||
	assert.NoError(t, UpdateMilestone(milestone))
 | 
						assert.NoError(t, UpdateMilestone(milestone, milestone.IsClosed))
 | 
				
			||||||
	AssertExistsAndLoadBean(t, milestone)
 | 
						milestone = AssertExistsAndLoadBean(t, &Milestone{ID: 1}).(*Milestone)
 | 
				
			||||||
 | 
						assert.EqualValues(t, "newMilestoneName", milestone.Name)
 | 
				
			||||||
	CheckConsistencyFor(t, &Milestone{})
 | 
						CheckConsistencyFor(t, &Milestone{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -369,7 +371,7 @@ func TestGetMilestonesStats(t *testing.T) {
 | 
				
			|||||||
	repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 | 
						repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 | 
				
			||||||
	repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
 | 
						repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	milestoneStats, err := GetMilestonesStats([]int64{repo1.ID, repo2.ID})
 | 
						milestoneStats, err := GetMilestonesStats(builder.In("repo_id", []int64{repo1.ID, repo2.ID}))
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount)
 | 
						assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount)
 | 
				
			||||||
	assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount)
 | 
						assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,14 +64,18 @@ func getIssueWatch(e Engine, userID, issueID int64) (iw *IssueWatch, exists bool
 | 
				
			|||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetIssueWatchersIDs returns IDs of subscribers to a given issue id
 | 
					// GetIssueWatchersIDs returns IDs of subscribers or explicit unsubscribers to a given issue id
 | 
				
			||||||
// but avoids joining with `user` for performance reasons
 | 
					// but avoids joining with `user` for performance reasons
 | 
				
			||||||
// User permissions must be verified elsewhere if required
 | 
					// User permissions must be verified elsewhere if required
 | 
				
			||||||
func GetIssueWatchersIDs(issueID int64) ([]int64, error) {
 | 
					func GetIssueWatchersIDs(issueID int64, watching bool) ([]int64, error) {
 | 
				
			||||||
 | 
						return getIssueWatchersIDs(x, issueID, watching)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getIssueWatchersIDs(e Engine, issueID int64, watching bool) ([]int64, error) {
 | 
				
			||||||
	ids := make([]int64, 0, 64)
 | 
						ids := make([]int64, 0, 64)
 | 
				
			||||||
	return ids, x.Table("issue_watch").
 | 
						return ids, e.Table("issue_watch").
 | 
				
			||||||
		Where("issue_id=?", issueID).
 | 
							Where("issue_id=?", issueID).
 | 
				
			||||||
		And("is_watching = ?", true).
 | 
							And("is_watching = ?", watching).
 | 
				
			||||||
		Select("user_id").
 | 
							Select("user_id").
 | 
				
			||||||
		Find(&ids)
 | 
							Find(&ids)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,6 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/smtp"
 | 
						"net/smtp"
 | 
				
			||||||
	"net/textproto"
 | 
						"net/textproto"
 | 
				
			||||||
	"regexp"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/auth/ldap"
 | 
						"code.gitea.io/gitea/modules/auth/ldap"
 | 
				
			||||||
@@ -455,13 +454,9 @@ func composeFullName(firstname, surname, username string) string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					 | 
				
			||||||
	alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 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, password string, source *LoginSource, autoRegister bool) (*User, error) {
 | 
					func LoginViaLDAP(user *User, login, password string, source *LoginSource) (*User, error) {
 | 
				
			||||||
	sr := source.Cfg.(*LDAPConfig).SearchEntry(login, password, source.Type == LoginDLDAP)
 | 
						sr := source.Cfg.(*LDAPConfig).SearchEntry(login, password, source.Type == LoginDLDAP)
 | 
				
			||||||
	if sr == nil {
 | 
						if sr == nil {
 | 
				
			||||||
		// User not in LDAP, do nothing
 | 
							// User not in LDAP, do nothing
 | 
				
			||||||
@@ -473,17 +468,25 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource, autoR
 | 
				
			|||||||
	// Update User admin flag if exist
 | 
						// Update User admin flag if exist
 | 
				
			||||||
	if isExist, err := IsUserExist(0, sr.Username); err != nil {
 | 
						if isExist, err := IsUserExist(0, sr.Username); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	} else if isExist &&
 | 
						} else if isExist {
 | 
				
			||||||
		!user.ProhibitLogin && len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin {
 | 
							if user == nil {
 | 
				
			||||||
		// Change existing admin flag only if AdminFilter option is set
 | 
								user, err = GetUserByName(sr.Username)
 | 
				
			||||||
		user.IsAdmin = sr.IsAdmin
 | 
								if err != nil {
 | 
				
			||||||
		err = UpdateUserCols(user, "is_admin")
 | 
									return nil, err
 | 
				
			||||||
		if err != nil {
 | 
								}
 | 
				
			||||||
			return nil, err
 | 
							}
 | 
				
			||||||
 | 
							if user != nil &&
 | 
				
			||||||
 | 
								!user.ProhibitLogin && len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin {
 | 
				
			||||||
 | 
								// Change existing admin flag only if AdminFilter option is set
 | 
				
			||||||
 | 
								user.IsAdmin = sr.IsAdmin
 | 
				
			||||||
 | 
								err = UpdateUserCols(user, "is_admin")
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !autoRegister {
 | 
						if user != nil {
 | 
				
			||||||
		if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys(user, source, sr.SSHPublicKey) {
 | 
							if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys(user, source, sr.SSHPublicKey) {
 | 
				
			||||||
			return user, RewriteAllPublicKeys()
 | 
								return user, RewriteAllPublicKeys()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -495,10 +498,6 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource, autoR
 | 
				
			|||||||
	if len(sr.Username) == 0 {
 | 
						if len(sr.Username) == 0 {
 | 
				
			||||||
		sr.Username = login
 | 
							sr.Username = login
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Validate username make sure it satisfies requirement.
 | 
					 | 
				
			||||||
	if alphaDashDotPattern.MatchString(sr.Username) {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters", sr.Username)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(sr.Mail) == 0 {
 | 
						if len(sr.Mail) == 0 {
 | 
				
			||||||
		sr.Mail = fmt.Sprintf("%s@localhost", sr.Username)
 | 
							sr.Mail = fmt.Sprintf("%s@localhost", sr.Username)
 | 
				
			||||||
@@ -594,7 +593,7 @@ func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// LoginViaSMTP queries if login/password is valid against the SMTP,
 | 
					// LoginViaSMTP queries if login/password is valid against the SMTP,
 | 
				
			||||||
// and create a local user if success when enabled.
 | 
					// and create a local user if success when enabled.
 | 
				
			||||||
func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
 | 
					func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPConfig) (*User, error) {
 | 
				
			||||||
	// Verify allowed domains.
 | 
						// Verify allowed domains.
 | 
				
			||||||
	if len(cfg.AllowedDomains) > 0 {
 | 
						if len(cfg.AllowedDomains) > 0 {
 | 
				
			||||||
		idx := strings.Index(login, "@")
 | 
							idx := strings.Index(login, "@")
 | 
				
			||||||
@@ -625,7 +624,7 @@ func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPC
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !autoRegister {
 | 
						if user != nil {
 | 
				
			||||||
		return user, nil
 | 
							return user, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -657,33 +656,41 @@ 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) (*User, error) {
 | 
				
			||||||
	if err := pam.Auth(cfg.ServiceName, login, password); err != nil {
 | 
						pamLogin, err := pam.Auth(cfg.ServiceName, login, password)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
		if strings.Contains(err.Error(), "Authentication failure") {
 | 
							if strings.Contains(err.Error(), "Authentication failure") {
 | 
				
			||||||
			return nil, ErrUserNotExist{0, login, 0}
 | 
								return nil, ErrUserNotExist{0, login, 0}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !autoRegister {
 | 
						if user != nil {
 | 
				
			||||||
		return user, nil
 | 
							return user, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Allow PAM sources with `@` in their name, like from Active Directory
 | 
				
			||||||
 | 
						username := pamLogin
 | 
				
			||||||
 | 
						idx := strings.Index(pamLogin, "@")
 | 
				
			||||||
 | 
						if idx > -1 {
 | 
				
			||||||
 | 
							username = pamLogin[:idx]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	user = &User{
 | 
						user = &User{
 | 
				
			||||||
		LowerName:   strings.ToLower(login),
 | 
							LowerName:   strings.ToLower(username),
 | 
				
			||||||
		Name:        login,
 | 
							Name:        username,
 | 
				
			||||||
		Email:       login,
 | 
							Email:       pamLogin,
 | 
				
			||||||
		Passwd:      password,
 | 
							Passwd:      password,
 | 
				
			||||||
		LoginType:   LoginPAM,
 | 
							LoginType:   LoginPAM,
 | 
				
			||||||
		LoginSource: sourceID,
 | 
							LoginSource: sourceID,
 | 
				
			||||||
		LoginName:   login,
 | 
							LoginName:   login, // This is what the user typed in
 | 
				
			||||||
		IsActive:    true,
 | 
							IsActive:    true,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return user, CreateUser(user)
 | 
						return user, CreateUser(user)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ExternalUserLogin attempts a login using external source types.
 | 
					// 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) (*User, error) {
 | 
				
			||||||
	if !source.IsActived {
 | 
						if !source.IsActived {
 | 
				
			||||||
		return nil, ErrLoginSourceNotActived
 | 
							return nil, ErrLoginSourceNotActived
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -691,11 +698,11 @@ func ExternalUserLogin(user *User, login, password string, source *LoginSource,
 | 
				
			|||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	switch source.Type {
 | 
						switch source.Type {
 | 
				
			||||||
	case LoginLDAP, LoginDLDAP:
 | 
						case LoginLDAP, LoginDLDAP:
 | 
				
			||||||
		user, err = LoginViaLDAP(user, login, password, source, autoRegister)
 | 
							user, err = LoginViaLDAP(user, login, password, source)
 | 
				
			||||||
	case LoginSMTP:
 | 
						case LoginSMTP:
 | 
				
			||||||
		user, err = LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
 | 
							user, err = LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig))
 | 
				
			||||||
	case LoginPAM:
 | 
						case LoginPAM:
 | 
				
			||||||
		user, err = LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
 | 
							user, err = LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig))
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return nil, ErrUnsupportedLoginType
 | 
							return nil, ErrUnsupportedLoginType
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -775,7 +782,7 @@ func UserSignIn(username, password string) (*User, error) {
 | 
				
			|||||||
				return nil, ErrLoginSourceNotExist{user.LoginSource}
 | 
									return nil, ErrLoginSourceNotExist{user.LoginSource}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return ExternalUserLogin(user, user.LoginName, password, &source, false)
 | 
								return ExternalUserLogin(user, user.LoginName, password, &source)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -789,7 +796,7 @@ func UserSignIn(username, password string) (*User, error) {
 | 
				
			|||||||
			// don't try to authenticate against OAuth2 and SSPI sources here
 | 
								// don't try to authenticate against OAuth2 and SSPI sources here
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		authUser, err := ExternalUserLogin(nil, username, password, source, true)
 | 
							authUser, err := ExternalUserLogin(nil, username, password, source)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			return authUser, nil
 | 
								return authUser, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,23 +26,38 @@ func deleteOrphanedAttachments(x *xorm.Engine) error {
 | 
				
			|||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sess.Close()
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err := sess.BufferSize(setting.Database.IterateBufferSize).
 | 
						var limit = setting.Database.IterateBufferSize
 | 
				
			||||||
		Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").Cols("uuid").
 | 
						if limit <= 0 {
 | 
				
			||||||
		Iterate(new(Attachment),
 | 
							limit = 50
 | 
				
			||||||
			func(idx int, bean interface{}) error {
 | 
					 | 
				
			||||||
				attachment := bean.(*Attachment)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if err := os.RemoveAll(models.AttachmentLocalPath(attachment.UUID)); err != nil {
 | 
					 | 
				
			||||||
					return err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				_, err := sess.ID(attachment.ID).NoAutoCondition().Delete(attachment)
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return sess.Commit()
 | 
						for {
 | 
				
			||||||
 | 
							attachements := make([]Attachment, 0, limit)
 | 
				
			||||||
 | 
							if err := sess.Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").
 | 
				
			||||||
 | 
								Cols("id, uuid").Limit(limit).
 | 
				
			||||||
 | 
								Asc("id").
 | 
				
			||||||
 | 
								Find(&attachements); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(attachements) == 0 {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var ids = make([]int64, 0, limit)
 | 
				
			||||||
 | 
							for _, attachment := range attachements {
 | 
				
			||||||
 | 
								ids = append(ids, attachment.ID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if _, err := sess.In("id", ids).Delete(new(Attachment)); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for _, attachment := range attachements {
 | 
				
			||||||
 | 
								if err := os.RemoveAll(models.AttachmentLocalPath(attachment.UUID)); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(attachements) < limit {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,6 +46,12 @@ type Engine interface {
 | 
				
			|||||||
	Asc(colNames ...string) *xorm.Session
 | 
						Asc(colNames ...string) *xorm.Session
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// When queries are broken down in parts because of the number
 | 
				
			||||||
 | 
						// of parameters, attempt to break by this amount
 | 
				
			||||||
 | 
						maxQueryParameters = 300
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	x      *xorm.Engine
 | 
						x      *xorm.Engine
 | 
				
			||||||
	tables []interface{}
 | 
						tables []interface{}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ package models
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -281,9 +282,9 @@ func (nl NotificationList) getPendingRepoIDs() []int64 {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadRepos loads repositories from database
 | 
					// LoadRepos loads repositories from database
 | 
				
			||||||
func (nl NotificationList) LoadRepos() (RepositoryList, error) {
 | 
					func (nl NotificationList) LoadRepos() (RepositoryList, []int, error) {
 | 
				
			||||||
	if len(nl) == 0 {
 | 
						if len(nl) == 0 {
 | 
				
			||||||
		return RepositoryList{}, nil
 | 
							return RepositoryList{}, []int{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var repoIDs = nl.getPendingRepoIDs()
 | 
						var repoIDs = nl.getPendingRepoIDs()
 | 
				
			||||||
@@ -298,7 +299,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
 | 
				
			|||||||
			In("id", repoIDs[:limit]).
 | 
								In("id", repoIDs[:limit]).
 | 
				
			||||||
			Rows(new(Repository))
 | 
								Rows(new(Repository))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for rows.Next() {
 | 
							for rows.Next() {
 | 
				
			||||||
@@ -306,7 +307,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
 | 
				
			|||||||
			err = rows.Scan(&repo)
 | 
								err = rows.Scan(&repo)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				rows.Close()
 | 
									rows.Close()
 | 
				
			||||||
				return nil, err
 | 
									return nil, nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			repos[repo.ID] = &repo
 | 
								repos[repo.ID] = &repo
 | 
				
			||||||
@@ -317,14 +318,21 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
 | 
				
			|||||||
		repoIDs = repoIDs[limit:]
 | 
							repoIDs = repoIDs[limit:]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						failed := []int{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var reposList = make(RepositoryList, 0, len(repoIDs))
 | 
						var reposList = make(RepositoryList, 0, len(repoIDs))
 | 
				
			||||||
	for _, notification := range nl {
 | 
						for i, notification := range nl {
 | 
				
			||||||
		if notification.Repository == nil {
 | 
							if notification.Repository == nil {
 | 
				
			||||||
			notification.Repository = repos[notification.RepoID]
 | 
								notification.Repository = repos[notification.RepoID]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if notification.Repository == nil {
 | 
				
			||||||
 | 
								log.Error("Notification[%d]: RepoID: %d not found", notification.ID, notification.RepoID)
 | 
				
			||||||
 | 
								failed = append(failed, i)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		var found bool
 | 
							var found bool
 | 
				
			||||||
		for _, r := range reposList {
 | 
							for _, r := range reposList {
 | 
				
			||||||
			if r.ID == notification.Repository.ID {
 | 
								if r.ID == notification.RepoID {
 | 
				
			||||||
				found = true
 | 
									found = true
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -333,7 +341,7 @@ func (nl NotificationList) LoadRepos() (RepositoryList, error) {
 | 
				
			|||||||
			reposList = append(reposList, notification.Repository)
 | 
								reposList = append(reposList, notification.Repository)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return reposList, nil
 | 
						return reposList, failed, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (nl NotificationList) getPendingIssueIDs() []int64 {
 | 
					func (nl NotificationList) getPendingIssueIDs() []int64 {
 | 
				
			||||||
@@ -350,9 +358,9 @@ func (nl NotificationList) getPendingIssueIDs() []int64 {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadIssues loads issues from database
 | 
					// LoadIssues loads issues from database
 | 
				
			||||||
func (nl NotificationList) LoadIssues() error {
 | 
					func (nl NotificationList) LoadIssues() ([]int, error) {
 | 
				
			||||||
	if len(nl) == 0 {
 | 
						if len(nl) == 0 {
 | 
				
			||||||
		return nil
 | 
							return []int{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var issueIDs = nl.getPendingIssueIDs()
 | 
						var issueIDs = nl.getPendingIssueIDs()
 | 
				
			||||||
@@ -367,7 +375,7 @@ func (nl NotificationList) LoadIssues() error {
 | 
				
			|||||||
			In("id", issueIDs[:limit]).
 | 
								In("id", issueIDs[:limit]).
 | 
				
			||||||
			Rows(new(Issue))
 | 
								Rows(new(Issue))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for rows.Next() {
 | 
							for rows.Next() {
 | 
				
			||||||
@@ -375,7 +383,7 @@ func (nl NotificationList) LoadIssues() error {
 | 
				
			|||||||
			err = rows.Scan(&issue)
 | 
								err = rows.Scan(&issue)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				rows.Close()
 | 
									rows.Close()
 | 
				
			||||||
				return err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			issues[issue.ID] = &issue
 | 
								issues[issue.ID] = &issue
 | 
				
			||||||
@@ -386,13 +394,38 @@ func (nl NotificationList) LoadIssues() error {
 | 
				
			|||||||
		issueIDs = issueIDs[limit:]
 | 
							issueIDs = issueIDs[limit:]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, notification := range nl {
 | 
						failures := []int{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, notification := range nl {
 | 
				
			||||||
		if notification.Issue == nil {
 | 
							if notification.Issue == nil {
 | 
				
			||||||
			notification.Issue = issues[notification.IssueID]
 | 
								notification.Issue = issues[notification.IssueID]
 | 
				
			||||||
 | 
								if notification.Issue == nil {
 | 
				
			||||||
 | 
									log.Error("Notification[%d]: IssueID: %d Not Found", notification.ID, notification.IssueID)
 | 
				
			||||||
 | 
									failures = append(failures, i)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			notification.Issue.Repo = notification.Repository
 | 
								notification.Issue.Repo = notification.Repository
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return failures, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Without returns the notification list without the failures
 | 
				
			||||||
 | 
					func (nl NotificationList) Without(failures []int) NotificationList {
 | 
				
			||||||
 | 
						if len(failures) == 0 {
 | 
				
			||||||
 | 
							return nl
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						remaining := make([]*Notification, 0, len(nl))
 | 
				
			||||||
 | 
						last := -1
 | 
				
			||||||
 | 
						var i int
 | 
				
			||||||
 | 
						for _, i = range failures {
 | 
				
			||||||
 | 
							remaining = append(remaining, nl[last+1:i]...)
 | 
				
			||||||
 | 
							last = i
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(nl) > i {
 | 
				
			||||||
 | 
							remaining = append(remaining, nl[i+1:]...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return remaining
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (nl NotificationList) getPendingCommentIDs() []int64 {
 | 
					func (nl NotificationList) getPendingCommentIDs() []int64 {
 | 
				
			||||||
@@ -409,9 +442,9 @@ func (nl NotificationList) getPendingCommentIDs() []int64 {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadComments loads comments from database
 | 
					// LoadComments loads comments from database
 | 
				
			||||||
func (nl NotificationList) LoadComments() error {
 | 
					func (nl NotificationList) LoadComments() ([]int, error) {
 | 
				
			||||||
	if len(nl) == 0 {
 | 
						if len(nl) == 0 {
 | 
				
			||||||
		return nil
 | 
							return []int{}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var commentIDs = nl.getPendingCommentIDs()
 | 
						var commentIDs = nl.getPendingCommentIDs()
 | 
				
			||||||
@@ -426,7 +459,7 @@ func (nl NotificationList) LoadComments() error {
 | 
				
			|||||||
			In("id", commentIDs[:limit]).
 | 
								In("id", commentIDs[:limit]).
 | 
				
			||||||
			Rows(new(Comment))
 | 
								Rows(new(Comment))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for rows.Next() {
 | 
							for rows.Next() {
 | 
				
			||||||
@@ -434,7 +467,7 @@ func (nl NotificationList) LoadComments() error {
 | 
				
			|||||||
			err = rows.Scan(&comment)
 | 
								err = rows.Scan(&comment)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				rows.Close()
 | 
									rows.Close()
 | 
				
			||||||
				return err
 | 
									return nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			comments[comment.ID] = &comment
 | 
								comments[comment.ID] = &comment
 | 
				
			||||||
@@ -445,13 +478,19 @@ func (nl NotificationList) LoadComments() error {
 | 
				
			|||||||
		commentIDs = commentIDs[limit:]
 | 
							commentIDs = commentIDs[limit:]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, notification := range nl {
 | 
						failures := []int{}
 | 
				
			||||||
 | 
						for i, notification := range nl {
 | 
				
			||||||
		if notification.CommentID > 0 && notification.Comment == nil && comments[notification.CommentID] != nil {
 | 
							if notification.CommentID > 0 && notification.Comment == nil && comments[notification.CommentID] != nil {
 | 
				
			||||||
			notification.Comment = comments[notification.CommentID]
 | 
								notification.Comment = comments[notification.CommentID]
 | 
				
			||||||
 | 
								if notification.Comment == nil {
 | 
				
			||||||
 | 
									log.Error("Notification[%d]: CommentID[%d] failed to load", notification.ID, notification.CommentID)
 | 
				
			||||||
 | 
									failures = append(failures, i)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			notification.Comment.Issue = notification.Issue
 | 
								notification.Comment.Issue = notification.Issue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return failures, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetNotificationCount returns the notification count for user
 | 
					// GetNotificationCount returns the notification count for user
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -399,7 +399,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Create org.
 | 
						// Create org.
 | 
				
			||||||
	org := &User{
 | 
						org := &User{
 | 
				
			||||||
		Name:       "All repo",
 | 
							Name:       "All_repo",
 | 
				
			||||||
		IsActive:   true,
 | 
							IsActive:   true,
 | 
				
			||||||
		Type:       UserTypeOrganization,
 | 
							Type:       UserTypeOrganization,
 | 
				
			||||||
		Visibility: structs.VisibleTypePublic,
 | 
							Visibility: structs.VisibleTypePublic,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										156
									
								
								models/pull.go
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								models/pull.go
									
									
									
									
									
								
							@@ -35,6 +35,7 @@ const (
 | 
				
			|||||||
	PullRequestStatusChecking
 | 
						PullRequestStatusChecking
 | 
				
			||||||
	PullRequestStatusMergeable
 | 
						PullRequestStatusMergeable
 | 
				
			||||||
	PullRequestStatusManuallyMerged
 | 
						PullRequestStatusManuallyMerged
 | 
				
			||||||
 | 
						PullRequestStatusError
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PullRequest represents relation between pull request and repositories.
 | 
					// PullRequest represents relation between pull request and repositories.
 | 
				
			||||||
@@ -67,7 +68,11 @@ type PullRequest struct {
 | 
				
			|||||||
// MustHeadUserName returns the HeadRepo's username if failed return blank
 | 
					// MustHeadUserName returns the HeadRepo's username if failed return blank
 | 
				
			||||||
func (pr *PullRequest) MustHeadUserName() string {
 | 
					func (pr *PullRequest) MustHeadUserName() string {
 | 
				
			||||||
	if err := pr.LoadHeadRepo(); err != nil {
 | 
						if err := pr.LoadHeadRepo(); err != nil {
 | 
				
			||||||
		log.Error("LoadHeadRepo: %v", err)
 | 
							if !IsErrRepoNotExist(err) {
 | 
				
			||||||
 | 
								log.Error("LoadHeadRepo: %v", err)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								log.Warn("LoadHeadRepo %d but repository does not exist: %v", pr.HeadRepoID, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return pr.HeadRepo.MustOwnerName()
 | 
						return pr.HeadRepo.MustOwnerName()
 | 
				
			||||||
@@ -175,7 +180,16 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
 | 
				
			|||||||
			return ""
 | 
								return ""
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.MustHeadUserName(), pr.HeadRepo.Name, pr.BaseBranch)
 | 
						if err := pr.LoadIssue(); err != nil {
 | 
				
			||||||
 | 
							log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pr.BaseRepoID == pr.HeadRepoID {
 | 
				
			||||||
 | 
							return fmt.Sprintf("Merge pull request '%s' (#%d) from %s into %s", pr.Issue.Title, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return fmt.Sprintf("Merge pull request '%s' (#%d) from %s:%s into %s", pr.Issue.Title, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCommitMessages returns the commit messages between head and merge base (if there is one)
 | 
					// GetCommitMessages returns the commit messages between head and merge base (if there is one)
 | 
				
			||||||
@@ -384,6 +398,13 @@ func (pr *PullRequest) GetDefaultSquashMessage() string {
 | 
				
			|||||||
		log.Error("LoadIssue: %v", err)
 | 
							log.Error("LoadIssue: %v", err)
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if err := pr.LoadBaseRepo(); err != nil {
 | 
				
			||||||
 | 
							log.Error("LoadBaseRepo: %v", err)
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if pr.BaseRepo.UnitEnabled(UnitTypeExternalTracker) {
 | 
				
			||||||
 | 
							return fmt.Sprintf("%s (!%d)", pr.Issue.Title, pr.Issue.Index)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index)
 | 
						return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -408,7 +429,7 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
 | 
				
			|||||||
		err        error
 | 
							err        error
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if err = pr.Issue.loadRepo(e); err != nil {
 | 
						if err = pr.Issue.loadRepo(e); err != nil {
 | 
				
			||||||
		log.Error("loadRepo[%d]: %v", pr.ID, err)
 | 
							log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	apiIssue := pr.Issue.apiFormat(e)
 | 
						apiIssue := pr.Issue.apiFormat(e)
 | 
				
			||||||
@@ -419,19 +440,14 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
 | 
				
			|||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if pr.HeadRepo == nil {
 | 
						if pr.HeadRepoID != 0 && pr.HeadRepo == nil {
 | 
				
			||||||
		pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
 | 
							pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil && !IsErrRepoNotExist(err) {
 | 
				
			||||||
			log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
 | 
								log.Error("GetRepositoryById[%d]: %v", pr.ID, err)
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = pr.Issue.loadRepo(e); err != nil {
 | 
					 | 
				
			||||||
		log.Error("pr.Issue.loadRepo[%d]: %v", pr.ID, err)
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	apiPullRequest := &api.PullRequest{
 | 
						apiPullRequest := &api.PullRequest{
 | 
				
			||||||
		ID:        pr.ID,
 | 
							ID:        pr.ID,
 | 
				
			||||||
		URL:       pr.Issue.HTMLURL(),
 | 
							URL:       pr.Issue.HTMLURL(),
 | 
				
			||||||
@@ -483,37 +499,45 @@ func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
 | 
				
			|||||||
		apiPullRequest.Base = apiBaseBranchInfo
 | 
							apiPullRequest.Base = apiBaseBranchInfo
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
 | 
						if pr.HeadRepo != nil {
 | 
				
			||||||
	if err != nil {
 | 
							headBranch, err = pr.HeadRepo.GetBranch(pr.HeadBranch)
 | 
				
			||||||
		if git.IsErrBranchNotExist(err) {
 | 
					 | 
				
			||||||
			apiPullRequest.Head = nil
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		apiHeadBranchInfo := &api.PRBranchInfo{
 | 
					 | 
				
			||||||
			Name:       pr.HeadBranch,
 | 
					 | 
				
			||||||
			Ref:        pr.HeadBranch,
 | 
					 | 
				
			||||||
			RepoID:     pr.HeadRepoID,
 | 
					 | 
				
			||||||
			Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		headCommit, err = headBranch.GetCommit()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if git.IsErrNotExist(err) {
 | 
								if git.IsErrBranchNotExist(err) {
 | 
				
			||||||
				apiHeadBranchInfo.Sha = ""
 | 
									apiPullRequest.Head = nil
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				log.Error("GetCommit[%s]: %v", headBranch.Name, err)
 | 
									log.Error("GetBranch[%s]: %v", pr.HeadBranch, err)
 | 
				
			||||||
				return nil
 | 
									return nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			apiHeadBranchInfo.Sha = headCommit.ID.String()
 | 
								apiHeadBranchInfo := &api.PRBranchInfo{
 | 
				
			||||||
 | 
									Name:       pr.HeadBranch,
 | 
				
			||||||
 | 
									Ref:        pr.HeadBranch,
 | 
				
			||||||
 | 
									RepoID:     pr.HeadRepoID,
 | 
				
			||||||
 | 
									Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								headCommit, err = headBranch.GetCommit()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if git.IsErrNotExist(err) {
 | 
				
			||||||
 | 
										apiHeadBranchInfo.Sha = ""
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										log.Error("GetCommit[%s]: %v", headBranch.Name, err)
 | 
				
			||||||
 | 
										return nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									apiHeadBranchInfo.Sha = headCommit.ID.String()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								apiPullRequest.Head = apiHeadBranchInfo
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							apiPullRequest.Head = &api.PRBranchInfo{
 | 
				
			||||||
 | 
								Name:   pr.HeadBranch,
 | 
				
			||||||
 | 
								Ref:    fmt.Sprintf("refs/pull/%d/head", pr.Index),
 | 
				
			||||||
 | 
								RepoID: -1,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		apiPullRequest.Head = apiHeadBranchInfo
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if pr.Status != PullRequestStatusChecking {
 | 
						if pr.Status != PullRequestStatusChecking {
 | 
				
			||||||
		mergeable := pr.Status != PullRequestStatusConflict && !pr.IsWorkInProgress()
 | 
							mergeable := !(pr.Status == PullRequestStatusConflict || pr.Status == PullRequestStatusError) && !pr.IsWorkInProgress()
 | 
				
			||||||
		apiPullRequest.Mergeable = mergeable
 | 
							apiPullRequest.Mergeable = mergeable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if pr.HasMerged {
 | 
						if pr.HasMerged {
 | 
				
			||||||
@@ -634,44 +658,66 @@ func (pr *PullRequest) CheckUserAllowedToMerge(doer *User) (err error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetMerged sets a pull request to merged and closes the corresponding issue
 | 
					// SetMerged sets a pull request to merged and closes the corresponding issue
 | 
				
			||||||
func (pr *PullRequest) SetMerged() (err error) {
 | 
					func (pr *PullRequest) SetMerged() (bool, error) {
 | 
				
			||||||
	if pr.HasMerged {
 | 
						if pr.HasMerged {
 | 
				
			||||||
		return fmt.Errorf("PullRequest[%d] already merged", pr.Index)
 | 
							return false, fmt.Errorf("PullRequest[%d] already merged", pr.Index)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
 | 
						if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
 | 
				
			||||||
		return fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
 | 
							return false, fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr.HasMerged = true
 | 
						pr.HasMerged = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sess.Close()
 | 
						defer sess.Close()
 | 
				
			||||||
	if err = sess.Begin(); err != nil {
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return false, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = pr.loadIssue(sess); err != nil {
 | 
						if _, err := sess.Exec("UPDATE `issue` SET `repo_id` = `repo_id` WHERE `id` = ?", pr.IssueID); err != nil {
 | 
				
			||||||
		return err
 | 
							return false, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = pr.Issue.loadRepo(sess); err != nil {
 | 
						if _, err := sess.Exec("UPDATE `pull_request` SET `issue_id` = `issue_id` WHERE `id` = ?", pr.ID); err != nil {
 | 
				
			||||||
		return err
 | 
							return false, err
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err = pr.Issue.Repo.getOwner(sess); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err = pr.Issue.changeStatus(sess, pr.Merger, true); err != nil {
 | 
						pr.Issue = nil
 | 
				
			||||||
		return fmt.Errorf("Issue.changeStatus: %v", err)
 | 
						if err := pr.loadIssue(sess); err != nil {
 | 
				
			||||||
	}
 | 
							return false, err
 | 
				
			||||||
	if _, err = sess.ID(pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
 | 
					 | 
				
			||||||
		return fmt.Errorf("update pull request: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = sess.Commit(); err != nil {
 | 
						if tmpPr, err := getPullRequestByID(sess, pr.ID); err != nil {
 | 
				
			||||||
		return fmt.Errorf("Commit: %v", err)
 | 
							return false, err
 | 
				
			||||||
 | 
						} else if tmpPr.HasMerged {
 | 
				
			||||||
 | 
							if pr.Issue.IsClosed {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false, fmt.Errorf("PullRequest[%d] already merged but it's associated issue [%d] is not closed", pr.Index, pr.IssueID)
 | 
				
			||||||
 | 
						} else if pr.Issue.IsClosed {
 | 
				
			||||||
 | 
							return false, fmt.Errorf("PullRequest[%d] already closed", pr.Index)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
					
 | 
				
			||||||
 | 
						if err := pr.Issue.loadRepo(sess); err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := pr.Issue.Repo.getOwner(sess); err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := pr.Issue.changeStatus(sess, pr.Merger, true); err != nil {
 | 
				
			||||||
 | 
							return false, fmt.Errorf("Issue.changeStatus: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
 | 
				
			||||||
 | 
							return false, fmt.Errorf("Failed to update pr[%d]: %v", pr.ID, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := sess.Commit(); err != nil {
 | 
				
			||||||
 | 
							return false, fmt.Errorf("Commit: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewPullRequest creates new pull request with labels for repository.
 | 
					// NewPullRequest creates new pull request with labels for repository.
 | 
				
			||||||
@@ -830,6 +876,12 @@ func (pr *PullRequest) UpdateCols(cols ...string) error {
 | 
				
			|||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged
 | 
				
			||||||
 | 
					func (pr *PullRequest) UpdateColsIfNotMerged(cols ...string) error {
 | 
				
			||||||
 | 
						_, err := x.Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
 | 
					// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
 | 
				
			||||||
func (pr *PullRequest) IsWorkInProgress() bool {
 | 
					func (pr *PullRequest) IsWorkInProgress() bool {
 | 
				
			||||||
	if err := pr.LoadIssue(); err != nil {
 | 
						if err := pr.LoadIssue(); err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -204,6 +204,14 @@ type Repository struct {
 | 
				
			|||||||
	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
 | 
						UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SanitizedOriginalURL returns a sanitized OriginalURL
 | 
				
			||||||
 | 
					func (repo *Repository) SanitizedOriginalURL() string {
 | 
				
			||||||
 | 
						if repo.OriginalURL == "" {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return util.SanitizeURLCredentials(repo.OriginalURL, false)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ColorFormat returns a colored string to represent this repo
 | 
					// ColorFormat returns a colored string to represent this repo
 | 
				
			||||||
func (repo *Repository) ColorFormat(s fmt.State) {
 | 
					func (repo *Repository) ColorFormat(s fmt.State) {
 | 
				
			||||||
	var ownerName interface{}
 | 
						var ownerName interface{}
 | 
				
			||||||
@@ -1848,6 +1856,18 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Dependencies for issues in this repository
 | 
				
			||||||
 | 
						if _, err = sess.In("issue_id", deleteCond).
 | 
				
			||||||
 | 
							Delete(&IssueDependency{}); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete dependencies for issues in other repositories
 | 
				
			||||||
 | 
						if _, err = sess.In("dependency_id", deleteCond).
 | 
				
			||||||
 | 
							Delete(&IssueDependency{}); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err = sess.In("issue_id", deleteCond).
 | 
						if _, err = sess.In("issue_id", deleteCond).
 | 
				
			||||||
		Delete(&IssueUser{}); err != nil {
 | 
							Delete(&IssueUser{}); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -1902,6 +1922,12 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(repo.Topics) > 0 {
 | 
				
			||||||
 | 
							if err = removeTopicsFromRepo(sess, repo.ID); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// FIXME: Remove repository files should be executed after transaction succeed.
 | 
						// FIXME: Remove repository files should be executed after transaction succeed.
 | 
				
			||||||
	repoPath := repo.repoPath(sess)
 | 
						repoPath := repo.repoPath(sess)
 | 
				
			||||||
	removeAllWithNotice(sess, "Delete repository files", repoPath)
 | 
						removeAllWithNotice(sess, "Delete repository files", repoPath)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -202,3 +202,23 @@ func (repo *Repository) getRepoTeams(e Engine) (teams []*Team, err error) {
 | 
				
			|||||||
func (repo *Repository) GetRepoTeams() ([]*Team, error) {
 | 
					func (repo *Repository) GetRepoTeams() ([]*Team, error) {
 | 
				
			||||||
	return repo.getRepoTeams(x)
 | 
						return repo.getRepoTeams(x)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository
 | 
				
			||||||
 | 
					func (repo *Repository) IsOwnerMemberCollaborator(userID int64) (bool, error) {
 | 
				
			||||||
 | 
						if repo.OwnerID == userID {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						teamMember, err := x.Join("INNER", "team_repo", "team_repo.team_id = team_user.team_id").
 | 
				
			||||||
 | 
							Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id").
 | 
				
			||||||
 | 
							Where("team_repo.repo_id = ?", repo.ID).
 | 
				
			||||||
 | 
							And("team_unit.`type` = ?", UnitTypeCode).
 | 
				
			||||||
 | 
							And("team_user.uid = ?", userID).Table("team_user").Exist(&TeamUser{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if teamMember {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return x.Get(&Collaboration{RepoID: repo.ID, UserID: userID})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,41 +124,43 @@ func generateRepoCommit(e Engine, repo, templateRepo, generateRepo *Repository,
 | 
				
			|||||||
		return fmt.Errorf("checkGiteaTemplate: %v", err)
 | 
							return fmt.Errorf("checkGiteaTemplate: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := os.Remove(gt.Path); err != nil {
 | 
						if gt != nil {
 | 
				
			||||||
		return fmt.Errorf("remove .giteatemplate: %v", err)
 | 
							if err := os.Remove(gt.Path); err != nil {
 | 
				
			||||||
	}
 | 
								return fmt.Errorf("remove .giteatemplate: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Avoid walking tree if there are no globs
 | 
							// Avoid walking tree if there are no globs
 | 
				
			||||||
	if len(gt.Globs()) > 0 {
 | 
							if len(gt.Globs()) > 0 {
 | 
				
			||||||
		tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
 | 
								tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
 | 
				
			||||||
		if err := filepath.Walk(tmpDirSlash, func(path string, info os.FileInfo, walkErr error) error {
 | 
								if err := filepath.Walk(tmpDirSlash, func(path string, info os.FileInfo, walkErr error) error {
 | 
				
			||||||
			if walkErr != nil {
 | 
									if walkErr != nil {
 | 
				
			||||||
				return walkErr
 | 
										return walkErr
 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if info.IsDir() {
 | 
					 | 
				
			||||||
				return nil
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash)
 | 
					 | 
				
			||||||
			for _, g := range gt.Globs() {
 | 
					 | 
				
			||||||
				if g.Match(base) {
 | 
					 | 
				
			||||||
					content, err := ioutil.ReadFile(path)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						return err
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					if err := ioutil.WriteFile(path,
 | 
					 | 
				
			||||||
						[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
 | 
					 | 
				
			||||||
						0644); err != nil {
 | 
					 | 
				
			||||||
						return err
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if info.IsDir() {
 | 
				
			||||||
 | 
										return nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash)
 | 
				
			||||||
 | 
									for _, g := range gt.Globs() {
 | 
				
			||||||
 | 
										if g.Match(base) {
 | 
				
			||||||
 | 
											content, err := ioutil.ReadFile(path)
 | 
				
			||||||
 | 
											if err != nil {
 | 
				
			||||||
 | 
												return err
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											if err := ioutil.WriteFile(path,
 | 
				
			||||||
 | 
												[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
 | 
				
			||||||
 | 
												0644); err != nil {
 | 
				
			||||||
 | 
												return err
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}); err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,6 +144,10 @@ type SearchRepoOptions struct {
 | 
				
			|||||||
	TopicOnly bool
 | 
						TopicOnly bool
 | 
				
			||||||
	// include description in keyword search
 | 
						// include description in keyword search
 | 
				
			||||||
	IncludeDescription bool
 | 
						IncludeDescription bool
 | 
				
			||||||
 | 
						// None -> include has milestones AND has no milestone
 | 
				
			||||||
 | 
						// True -> include just has milestones
 | 
				
			||||||
 | 
						// False -> include just has no milestone
 | 
				
			||||||
 | 
						HasMilestones util.OptionalBool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//SearchOrderBy is used to sort the result
 | 
					//SearchOrderBy is used to sort the result
 | 
				
			||||||
@@ -171,12 +175,9 @@ const (
 | 
				
			|||||||
	SearchOrderByForksReverse          SearchOrderBy = "num_forks DESC"
 | 
						SearchOrderByForksReverse          SearchOrderBy = "num_forks DESC"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SearchRepository returns repositories based on search options,
 | 
					// SearchRepositoryCondition returns repositories based on search options,
 | 
				
			||||||
// it returns results in given range and number of total results.
 | 
					// it returns results in given range and number of total results.
 | 
				
			||||||
func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
					func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
 | 
				
			||||||
	if opts.Page <= 0 {
 | 
					 | 
				
			||||||
		opts.Page = 1
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var cond = builder.NewCond()
 | 
						var cond = builder.NewCond()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if opts.Private {
 | 
						if opts.Private {
 | 
				
			||||||
@@ -276,6 +277,29 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
				
			|||||||
		cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
 | 
							cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch opts.HasMilestones {
 | 
				
			||||||
 | 
						case util.OptionalBoolTrue:
 | 
				
			||||||
 | 
							cond = cond.And(builder.Gt{"num_milestones": 0})
 | 
				
			||||||
 | 
						case util.OptionalBoolFalse:
 | 
				
			||||||
 | 
							cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cond
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchRepository returns repositories based on search options,
 | 
				
			||||||
 | 
					// it returns results in given range and number of total results.
 | 
				
			||||||
 | 
					func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
				
			||||||
 | 
						cond := SearchRepositoryCondition(opts)
 | 
				
			||||||
 | 
						return SearchRepositoryByCondition(opts, cond)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchRepositoryByCondition search repositories by condition
 | 
				
			||||||
 | 
					func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond) (RepositoryList, int64, error) {
 | 
				
			||||||
 | 
						if opts.Page <= 0 {
 | 
				
			||||||
 | 
							opts.Page = 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(opts.OrderBy) == 0 {
 | 
						if len(opts.OrderBy) == 0 {
 | 
				
			||||||
		opts.OrderBy = SearchOrderByAlphabetically
 | 
							opts.OrderBy = SearchOrderByAlphabetically
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -296,11 +320,11 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repos := make(RepositoryList, 0, opts.PageSize)
 | 
						repos := make(RepositoryList, 0, opts.PageSize)
 | 
				
			||||||
	if err = sess.
 | 
						sess.Where(cond).OrderBy(opts.OrderBy.String())
 | 
				
			||||||
		Where(cond).
 | 
						if opts.PageSize > 0 {
 | 
				
			||||||
		OrderBy(opts.OrderBy.String()).
 | 
							sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
 | 
				
			||||||
		Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
 | 
						}
 | 
				
			||||||
		Find(&repos); err != nil {
 | 
						if err = sess.Find(&repos); err != nil {
 | 
				
			||||||
		return nil, 0, fmt.Errorf("Repo: %v", err)
 | 
							return nil, 0, fmt.Errorf("Repo: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -315,6 +339,17 @@ func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
 | 
					// accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
 | 
				
			||||||
func accessibleRepositoryCondition(userID int64) builder.Cond {
 | 
					func accessibleRepositoryCondition(userID int64) builder.Cond {
 | 
				
			||||||
 | 
						if userID <= 0 {
 | 
				
			||||||
 | 
							return builder.And(
 | 
				
			||||||
 | 
								builder.Eq{"`repository`.is_private": false},
 | 
				
			||||||
 | 
								builder.Or(
 | 
				
			||||||
 | 
									//   A. Aren't in organisations  __OR__
 | 
				
			||||||
 | 
									builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})),
 | 
				
			||||||
 | 
									//   B. Is a public organisation.
 | 
				
			||||||
 | 
									builder.In("`repository`.owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"visibility": structs.VisibleTypePublic}))),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return builder.Or(
 | 
						return builder.Or(
 | 
				
			||||||
		// 1. Be able to see all non-private repositories that either:
 | 
							// 1. Be able to see all non-private repositories that either:
 | 
				
			||||||
		builder.And(
 | 
							builder.And(
 | 
				
			||||||
@@ -349,6 +384,12 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err
 | 
				
			|||||||
	return SearchRepository(opts)
 | 
						return SearchRepository(opts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
 | 
				
			||||||
 | 
					func AccessibleRepoIDsQuery(userID int64) *builder.Builder {
 | 
				
			||||||
 | 
						// NB: Please note this code needs to still work if user is nil
 | 
				
			||||||
 | 
						return builder.Select("id").From("repository").Where(accessibleRepositoryCondition(userID))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
 | 
					// FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
 | 
				
			||||||
func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
 | 
					func FindUserAccessibleRepoIDs(userID int64) ([]int64, error) {
 | 
				
			||||||
	var accessCond builder.Cond = builder.Eq{"is_private": false}
 | 
						var accessCond builder.Cond = builder.Eq{"is_private": false}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -129,7 +129,7 @@ func addTopicByNameToRepo(e Engine, repoID int64, topicName string) (*Topic, err
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// removeTopicFromRepo remove a topic from a repo and decrements the topic repo count
 | 
					// removeTopicFromRepo remove a topic from a repo and decrements the topic repo count
 | 
				
			||||||
func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
 | 
					func removeTopicFromRepo(e Engine, repoID int64, topic *Topic) error {
 | 
				
			||||||
	topic.RepoCount--
 | 
						topic.RepoCount--
 | 
				
			||||||
	if _, err := e.ID(topic.ID).Cols("repo_count").Update(topic); err != nil {
 | 
						if _, err := e.ID(topic.ID).Cols("repo_count").Update(topic); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -145,6 +145,24 @@ func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// removeTopicsFromRepo remove all topics from the repo and decrements respective topics repo count
 | 
				
			||||||
 | 
					func removeTopicsFromRepo(e Engine, repoID int64) error {
 | 
				
			||||||
 | 
						_, err := e.Where(
 | 
				
			||||||
 | 
							builder.In("id",
 | 
				
			||||||
 | 
								builder.Select("topic_id").From("repo_topic").Where(builder.Eq{"repo_id": repoID}),
 | 
				
			||||||
 | 
							),
 | 
				
			||||||
 | 
						).Cols("repo_count").SetExpr("repo_count", "repo_count-1").Update(&Topic{})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = e.Delete(&RepoTopic{RepoID: repoID}); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FindTopicOptions represents the options when fdin topics
 | 
					// FindTopicOptions represents the options when fdin topics
 | 
				
			||||||
type FindTopicOptions struct {
 | 
					type FindTopicOptions struct {
 | 
				
			||||||
	RepoID  int64
 | 
						RepoID  int64
 | 
				
			||||||
@@ -217,7 +235,7 @@ func DeleteTopic(repoID int64, topicName string) (*Topic, error) {
 | 
				
			|||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = removeTopicFromRepo(repoID, topic, x)
 | 
						err = removeTopicFromRepo(x, repoID, topic)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return topic, err
 | 
						return topic, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -278,7 +296,7 @@ func SaveTopics(repoID int64, topicNames ...string) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, topic := range removeTopics {
 | 
						for _, topic := range removeTopics {
 | 
				
			||||||
		err := removeTopicFromRepo(repoID, topic, sess)
 | 
							err := removeTopicFromRepo(sess, repoID, topic)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func createTestEngine(fixturesDir string) error {
 | 
					func createTestEngine(fixturesDir string) error {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
 | 
						x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared&_txlock=immediate")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ import (
 | 
				
			|||||||
	"image/png"
 | 
						"image/png"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@@ -87,6 +88,9 @@ var (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// ErrUnsupportedLoginType login source is unknown error
 | 
						// ErrUnsupportedLoginType login source is unknown error
 | 
				
			||||||
	ErrUnsupportedLoginType = errors.New("Login source is unknown")
 | 
						ErrUnsupportedLoginType = errors.New("Login source is unknown")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Characters prohibited in a user name (anything except A-Za-z0-9_.-)
 | 
				
			||||||
 | 
						alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// User represents the object of individual and member of organization.
 | 
					// User represents the object of individual and member of organization.
 | 
				
			||||||
@@ -503,7 +507,7 @@ func (u *User) ValidatePassword(passwd string) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsPasswordSet checks if the password is set or left empty
 | 
					// IsPasswordSet checks if the password is set or left empty
 | 
				
			||||||
func (u *User) IsPasswordSet() bool {
 | 
					func (u *User) IsPasswordSet() bool {
 | 
				
			||||||
	return len(u.Passwd) > 0
 | 
						return !u.ValidatePassword("")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UploadAvatar saves custom avatar for user.
 | 
					// UploadAvatar saves custom avatar for user.
 | 
				
			||||||
@@ -708,9 +712,11 @@ func (u *User) DisplayName() string {
 | 
				
			|||||||
// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
 | 
					// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set,
 | 
				
			||||||
// returns username otherwise.
 | 
					// returns username otherwise.
 | 
				
			||||||
func (u *User) GetDisplayName() string {
 | 
					func (u *User) GetDisplayName() string {
 | 
				
			||||||
	trimmed := strings.TrimSpace(u.FullName)
 | 
						if setting.UI.DefaultShowFullName {
 | 
				
			||||||
	if len(trimmed) > 0 && setting.UI.DefaultShowFullName {
 | 
							trimmed := strings.TrimSpace(u.FullName)
 | 
				
			||||||
		return trimmed
 | 
							if len(trimmed) > 0 {
 | 
				
			||||||
 | 
								return trimmed
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return u.Name
 | 
						return u.Name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -819,7 +825,9 @@ var (
 | 
				
			|||||||
		"issues",
 | 
							"issues",
 | 
				
			||||||
		"js",
 | 
							"js",
 | 
				
			||||||
		"less",
 | 
							"less",
 | 
				
			||||||
 | 
							"manifest.json",
 | 
				
			||||||
		"metrics",
 | 
							"metrics",
 | 
				
			||||||
 | 
							"milestones",
 | 
				
			||||||
		"new",
 | 
							"new",
 | 
				
			||||||
		"notifications",
 | 
							"notifications",
 | 
				
			||||||
		"org",
 | 
							"org",
 | 
				
			||||||
@@ -868,6 +876,11 @@ func isUsableName(names, patterns []string, name string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsUsableUsername returns an error when a username is reserved
 | 
					// IsUsableUsername returns an error when a username is reserved
 | 
				
			||||||
func IsUsableUsername(name string) error {
 | 
					func IsUsableUsername(name string) error {
 | 
				
			||||||
 | 
						// Validate username make sure it satisfies requirement.
 | 
				
			||||||
 | 
						if alphaDashDotPattern.MatchString(name) {
 | 
				
			||||||
 | 
							// Note: usually this error is normally caught up earlier in the UI
 | 
				
			||||||
 | 
							return ErrNameCharsNotAllowed{Name: name}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return isUsableName(reservedUsernames, reservedUserPatterns, name)
 | 
						return isUsableName(reservedUsernames, reservedUserPatterns, name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -987,7 +1000,7 @@ func VerifyActiveEmailCode(code, email string) *EmailAddress {
 | 
				
			|||||||
		data := com.ToStr(user.ID) + email + user.LowerName + user.Passwd + user.Rands
 | 
							data := com.ToStr(user.ID) + email + user.LowerName + user.Passwd + user.Rands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if base.VerifyTimeLimitCode(data, minutes, prefix) {
 | 
							if base.VerifyTimeLimitCode(data, minutes, prefix) {
 | 
				
			||||||
			emailAddress := &EmailAddress{Email: email}
 | 
								emailAddress := &EmailAddress{UID: user.ID, Email: email}
 | 
				
			||||||
			if has, _ := x.Get(emailAddress); has {
 | 
								if has, _ := x.Get(emailAddress); has {
 | 
				
			||||||
				return emailAddress
 | 
									return emailAddress
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -1760,6 +1773,15 @@ func SyncExternalUsers(ctx context.Context) {
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if len(sr) == 0 {
 | 
				
			||||||
 | 
									if !s.LDAP().AllowDeactivateAll {
 | 
				
			||||||
 | 
										log.Error("LDAP search found no entries but did not report an error. Refusing to deactivate all users")
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										log.Warn("LDAP search found no entries but did not report an error. All users will be deactivated as per settings")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for _, su := range sr {
 | 
								for _, su := range sr {
 | 
				
			||||||
				select {
 | 
									select {
 | 
				
			||||||
				case <-ctx.Done():
 | 
									case <-ctx.Done():
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
// Copyright 2016 The Gogs Authors. All rights reserved.
 | 
					// Copyright 2016 The Gogs Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Copyright 2020 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -8,6 +9,12 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"xorm.io/builder"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -54,13 +61,66 @@ func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
 | 
				
			|||||||
	if !isPrimaryFound {
 | 
						if !isPrimaryFound {
 | 
				
			||||||
		emails = append(emails, &EmailAddress{
 | 
							emails = append(emails, &EmailAddress{
 | 
				
			||||||
			Email:       u.Email,
 | 
								Email:       u.Email,
 | 
				
			||||||
			IsActivated: true,
 | 
								IsActivated: u.IsActive,
 | 
				
			||||||
			IsPrimary:   true,
 | 
								IsPrimary:   true,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return emails, nil
 | 
						return emails, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetEmailAddressByID gets a user's email address by ID
 | 
				
			||||||
 | 
					func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) {
 | 
				
			||||||
 | 
						// User ID is required for security reasons
 | 
				
			||||||
 | 
						email := &EmailAddress{ID: id, UID: uid}
 | 
				
			||||||
 | 
						if has, err := x.Get(email); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						} else if !has {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return email, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isEmailActive(e Engine, email string, userID, emailID int64) (bool, error) {
 | 
				
			||||||
 | 
						if len(email) == 0 {
 | 
				
			||||||
 | 
							return true, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Can't filter by boolean field unless it's explicit
 | 
				
			||||||
 | 
						cond := builder.NewCond()
 | 
				
			||||||
 | 
						cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": emailID})
 | 
				
			||||||
 | 
						if setting.Service.RegisterEmailConfirm {
 | 
				
			||||||
 | 
							// Inactive (unvalidated) addresses don't count as active if email validation is required
 | 
				
			||||||
 | 
							cond = cond.And(builder.Eq{"is_activated": true})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						em := EmailAddress{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if has, err := e.Where(cond).Get(&em); has || err != nil {
 | 
				
			||||||
 | 
							if has {
 | 
				
			||||||
 | 
								log.Info("isEmailActive('%s',%d,%d) found duplicate in email ID %d", email, userID, emailID, em.ID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return has, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Can't filter by boolean field unless it's explicit
 | 
				
			||||||
 | 
						cond = builder.NewCond()
 | 
				
			||||||
 | 
						cond = cond.And(builder.Eq{"email": email}, builder.Neq{"id": userID})
 | 
				
			||||||
 | 
						if setting.Service.RegisterEmailConfirm {
 | 
				
			||||||
 | 
							cond = cond.And(builder.Eq{"is_active": true})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						us := User{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if has, err := e.Where(cond).Get(&us); has || err != nil {
 | 
				
			||||||
 | 
							if has {
 | 
				
			||||||
 | 
								log.Info("isEmailActive('%s',%d,%d) found duplicate in user ID %d", email, userID, emailID, us.ID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return has, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isEmailUsed(e Engine, email string) (bool, error) {
 | 
					func isEmailUsed(e Engine, email string) (bool, error) {
 | 
				
			||||||
	if len(email) == 0 {
 | 
						if len(email) == 0 {
 | 
				
			||||||
		return true, nil
 | 
							return true, nil
 | 
				
			||||||
@@ -118,31 +178,30 @@ func AddEmailAddresses(emails []*EmailAddress) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Activate activates the email address to given user.
 | 
					// Activate activates the email address to given user.
 | 
				
			||||||
func (email *EmailAddress) Activate() error {
 | 
					func (email *EmailAddress) Activate() error {
 | 
				
			||||||
	user, err := GetUserByID(email.UID)
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := email.updateActivation(sess, true); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (email *EmailAddress) updateActivation(e Engine, activate bool) error {
 | 
				
			||||||
 | 
						user, err := getUserByID(e, email.UID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if user.Rands, err = GetUserSalt(); err != nil {
 | 
						if user.Rands, err = GetUserSalt(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						email.IsActivated = activate
 | 
				
			||||||
	sess := x.NewSession()
 | 
						if _, err := e.ID(email.ID).Cols("is_activated").Update(email); err != nil {
 | 
				
			||||||
	defer sess.Close()
 | 
					 | 
				
			||||||
	if err = sess.Begin(); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return updateUserCols(e, user, "rands")
 | 
				
			||||||
	email.IsActivated = true
 | 
					 | 
				
			||||||
	if _, err := sess.
 | 
					 | 
				
			||||||
		ID(email.ID).
 | 
					 | 
				
			||||||
		Cols("is_activated").
 | 
					 | 
				
			||||||
		Update(email); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	} else if err = updateUserCols(sess, user, "rands"); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return sess.Commit()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteEmailAddress deletes an email address of given user.
 | 
					// DeleteEmailAddress deletes an email address of given user.
 | 
				
			||||||
@@ -201,7 +260,7 @@ func MakeEmailPrimary(email *EmailAddress) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Make sure the former primary email doesn't disappear.
 | 
						// Make sure the former primary email doesn't disappear.
 | 
				
			||||||
	formerPrimaryEmail := &EmailAddress{Email: user.Email}
 | 
						formerPrimaryEmail := &EmailAddress{UID: user.ID, Email: user.Email}
 | 
				
			||||||
	has, err = x.Get(formerPrimaryEmail)
 | 
						has, err = x.Get(formerPrimaryEmail)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -228,3 +287,199 @@ func MakeEmailPrimary(email *EmailAddress) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchEmailOrderBy is used to sort the results from SearchEmails()
 | 
				
			||||||
 | 
					type SearchEmailOrderBy string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s SearchEmailOrderBy) String() string {
 | 
				
			||||||
 | 
						return string(s)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Strings for sorting result
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						SearchEmailOrderByEmail        SearchEmailOrderBy = "emails.email ASC, is_primary DESC, sortid ASC"
 | 
				
			||||||
 | 
						SearchEmailOrderByEmailReverse SearchEmailOrderBy = "emails.email DESC, is_primary ASC, sortid DESC"
 | 
				
			||||||
 | 
						SearchEmailOrderByName         SearchEmailOrderBy = "`user`.lower_name ASC, is_primary DESC, sortid ASC"
 | 
				
			||||||
 | 
						SearchEmailOrderByNameReverse  SearchEmailOrderBy = "`user`.lower_name DESC, is_primary ASC, sortid DESC"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchEmailOptions are options to search e-mail addresses for the admin panel
 | 
				
			||||||
 | 
					type SearchEmailOptions struct {
 | 
				
			||||||
 | 
						Page        int
 | 
				
			||||||
 | 
						PageSize    int // Can be smaller than or equal to setting.UI.ExplorePagingNum
 | 
				
			||||||
 | 
						Keyword     string
 | 
				
			||||||
 | 
						SortType    SearchEmailOrderBy
 | 
				
			||||||
 | 
						IsPrimary   util.OptionalBool
 | 
				
			||||||
 | 
						IsActivated util.OptionalBool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchEmailResult is an e-mail address found in the user or email_address table
 | 
				
			||||||
 | 
					type SearchEmailResult struct {
 | 
				
			||||||
 | 
						UID         int64
 | 
				
			||||||
 | 
						Email       string
 | 
				
			||||||
 | 
						IsActivated bool
 | 
				
			||||||
 | 
						IsPrimary   bool
 | 
				
			||||||
 | 
						// From User
 | 
				
			||||||
 | 
						Name     string
 | 
				
			||||||
 | 
						FullName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SearchEmails takes options i.e. keyword and part of email name to search,
 | 
				
			||||||
 | 
					// it returns results in given range and number of total results.
 | 
				
			||||||
 | 
					func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) {
 | 
				
			||||||
 | 
						// Unfortunately, UNION support for SQLite in xorm is currently broken, so we must
 | 
				
			||||||
 | 
						// build the SQL ourselves.
 | 
				
			||||||
 | 
						where := make([]string, 0, 5)
 | 
				
			||||||
 | 
						args := make([]interface{}, 0, 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						emailsSQL := "(SELECT id as sortid, uid, email, is_activated, 0 as is_primary " +
 | 
				
			||||||
 | 
							"FROM email_address " +
 | 
				
			||||||
 | 
							"UNION ALL " +
 | 
				
			||||||
 | 
							"SELECT id as sortid, id AS uid, email, is_active AS is_activated, 1 as is_primary " +
 | 
				
			||||||
 | 
							"FROM `user` " +
 | 
				
			||||||
 | 
							"WHERE type = ?) AS emails"
 | 
				
			||||||
 | 
						args = append(args, UserTypeIndividual)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(opts.Keyword) > 0 {
 | 
				
			||||||
 | 
							// Note: % can be injected in the Keyword parameter, but it won't do any harm.
 | 
				
			||||||
 | 
							where = append(where, "(lower(`user`.full_name) LIKE ? OR `user`.lower_name LIKE ? OR emails.email LIKE ?)")
 | 
				
			||||||
 | 
							likeStr := "%" + strings.ToLower(opts.Keyword) + "%"
 | 
				
			||||||
 | 
							args = append(args, likeStr)
 | 
				
			||||||
 | 
							args = append(args, likeStr)
 | 
				
			||||||
 | 
							args = append(args, likeStr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case opts.IsPrimary.IsTrue():
 | 
				
			||||||
 | 
							where = append(where, "emails.is_primary = ?")
 | 
				
			||||||
 | 
							args = append(args, true)
 | 
				
			||||||
 | 
						case opts.IsPrimary.IsFalse():
 | 
				
			||||||
 | 
							where = append(where, "emails.is_primary = ?")
 | 
				
			||||||
 | 
							args = append(args, false)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch {
 | 
				
			||||||
 | 
						case opts.IsActivated.IsTrue():
 | 
				
			||||||
 | 
							where = append(where, "emails.is_activated = ?")
 | 
				
			||||||
 | 
							args = append(args, true)
 | 
				
			||||||
 | 
						case opts.IsActivated.IsFalse():
 | 
				
			||||||
 | 
							where = append(where, "emails.is_activated = ?")
 | 
				
			||||||
 | 
							args = append(args, false)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var whereStr string
 | 
				
			||||||
 | 
						if len(where) > 0 {
 | 
				
			||||||
 | 
							whereStr = "WHERE " + strings.Join(where, " AND ")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						joinSQL := "FROM " + emailsSQL + " INNER JOIN `user` ON `user`.id = emails.uid " + whereStr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						count, err := x.SQL("SELECT count(*) "+joinSQL, args...).Count()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, 0, fmt.Errorf("Count: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						orderby := opts.SortType.String()
 | 
				
			||||||
 | 
						if orderby == "" {
 | 
				
			||||||
 | 
							orderby = SearchEmailOrderByEmail.String()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						querySQL := "SELECT emails.uid, emails.email, emails.is_activated, emails.is_primary, " +
 | 
				
			||||||
 | 
							"`user`.name, `user`.full_name " + joinSQL + " ORDER BY " + orderby
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.PageSize == 0 || opts.PageSize > setting.UI.ExplorePagingNum {
 | 
				
			||||||
 | 
							opts.PageSize = setting.UI.ExplorePagingNum
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Page <= 0 {
 | 
				
			||||||
 | 
							opts.Page = 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rows, err := x.SQL(querySQL, args...).Rows(new(SearchEmailResult))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, 0, fmt.Errorf("Emails: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Page manually because xorm can't handle Limit() with raw SQL
 | 
				
			||||||
 | 
						defer rows.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						emails := make([]*SearchEmailResult, 0, opts.PageSize)
 | 
				
			||||||
 | 
						skip := (opts.Page - 1) * opts.PageSize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for rows.Next() {
 | 
				
			||||||
 | 
							var email SearchEmailResult
 | 
				
			||||||
 | 
							if err := rows.Scan(&email); err != nil {
 | 
				
			||||||
 | 
								return nil, 0, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if skip > 0 {
 | 
				
			||||||
 | 
								skip--
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							emails = append(emails, &email)
 | 
				
			||||||
 | 
							if len(emails) == opts.PageSize {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return emails, count, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ActivateUserEmail will change the activated state of an email address,
 | 
				
			||||||
 | 
					// either primary (in the user table) or secondary (in the email_address table)
 | 
				
			||||||
 | 
					func ActivateUserEmail(userID int64, email string, primary, activate bool) (err error) {
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if primary {
 | 
				
			||||||
 | 
							// Activate/deactivate a user's primary email address
 | 
				
			||||||
 | 
							user := User{ID: userID, Email: email}
 | 
				
			||||||
 | 
							if has, err := sess.Get(&user); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							} else if !has {
 | 
				
			||||||
 | 
								return fmt.Errorf("no such user: %d (%s)", userID, email)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if user.IsActive == activate {
 | 
				
			||||||
 | 
								// Already in the desired state; no action
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if activate {
 | 
				
			||||||
 | 
								if used, err := isEmailActive(sess, email, userID, 0); err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("isEmailActive(): %v", err)
 | 
				
			||||||
 | 
								} else if used {
 | 
				
			||||||
 | 
									return ErrEmailAlreadyUsed{Email: email}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							user.IsActive = activate
 | 
				
			||||||
 | 
							if user.Rands, err = GetUserSalt(); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("generate salt: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err = updateUserCols(sess, &user, "is_active", "rands"); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("updateUserCols(): %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Activate/deactivate a user's secondary email address
 | 
				
			||||||
 | 
							// First check if there's another user active with the same address
 | 
				
			||||||
 | 
							addr := EmailAddress{UID: userID, Email: email}
 | 
				
			||||||
 | 
							if has, err := sess.Get(&addr); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							} else if !has {
 | 
				
			||||||
 | 
								return fmt.Errorf("no such email: %d (%s)", userID, email)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if addr.IsActivated == activate {
 | 
				
			||||||
 | 
								// Already in the desired state; no action
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if activate {
 | 
				
			||||||
 | 
								if used, err := isEmailActive(sess, email, 0, addr.ID); err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("isEmailActive(): %v", err)
 | 
				
			||||||
 | 
								} else if used {
 | 
				
			||||||
 | 
									return ErrEmailAlreadyUsed{Email: email}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err = addr.updateActivation(sess, activate); err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("updateActivation(): %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,8 @@ package models
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -169,3 +171,65 @@ func TestActivate(t *testing.T) {
 | 
				
			|||||||
	assert.True(t, emails[2].IsActivated)
 | 
						assert.True(t, emails[2].IsActivated)
 | 
				
			||||||
	assert.True(t, emails[2].IsPrimary)
 | 
						assert.True(t, emails[2].IsPrimary)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestListEmails(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Must find all users and their emails
 | 
				
			||||||
 | 
						opts := &SearchEmailOptions{}
 | 
				
			||||||
 | 
						emails, count, err := SearchEmails(opts)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.NotEqual(t, int64(0), count)
 | 
				
			||||||
 | 
						assert.True(t, count > 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contains := func(match func(s *SearchEmailResult) bool) bool {
 | 
				
			||||||
 | 
							for _, v := range emails {
 | 
				
			||||||
 | 
								if match(v) {
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 18 }))
 | 
				
			||||||
 | 
						// 'user3' is an organization
 | 
				
			||||||
 | 
						assert.False(t, contains(func(s *SearchEmailResult) bool { return s.UID == 3 }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Must find no records
 | 
				
			||||||
 | 
						opts = &SearchEmailOptions{Keyword: "NOTFOUND"}
 | 
				
			||||||
 | 
						emails, count, err = SearchEmails(opts)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Equal(t, int64(0), count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Must find users 'user2', 'user28', etc.
 | 
				
			||||||
 | 
						opts = &SearchEmailOptions{Keyword: "user2"}
 | 
				
			||||||
 | 
						emails, count, err = SearchEmails(opts)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.NotEqual(t, int64(0), count)
 | 
				
			||||||
 | 
						assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 2 }))
 | 
				
			||||||
 | 
						assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 27 }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Must find only primary addresses (i.e. from the `user` table)
 | 
				
			||||||
 | 
						opts = &SearchEmailOptions{IsPrimary: util.OptionalBoolTrue}
 | 
				
			||||||
 | 
						emails, count, err = SearchEmails(opts)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.True(t, contains(func(s *SearchEmailResult) bool { return s.IsPrimary }))
 | 
				
			||||||
 | 
						assert.False(t, contains(func(s *SearchEmailResult) bool { return !s.IsPrimary }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Must find only inactive addresses (i.e. not validated)
 | 
				
			||||||
 | 
						opts = &SearchEmailOptions{IsActivated: util.OptionalBoolFalse}
 | 
				
			||||||
 | 
						emails, count, err = SearchEmails(opts)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.True(t, contains(func(s *SearchEmailResult) bool { return !s.IsActivated }))
 | 
				
			||||||
 | 
						assert.False(t, contains(func(s *SearchEmailResult) bool { return s.IsActivated }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Must find more than one page, but retrieve only one
 | 
				
			||||||
 | 
						opts = &SearchEmailOptions{
 | 
				
			||||||
 | 
							PageSize: 5,
 | 
				
			||||||
 | 
							Page:     1,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						emails, count, err = SearchEmails(opts)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Equal(t, 5, len(emails))
 | 
				
			||||||
 | 
						assert.True(t, count > int64(len(emails)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,3 +47,13 @@ type AdminEditUserForm struct {
 | 
				
			|||||||
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
					func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	return validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AdminDashboardForm form for admin dashboard operations
 | 
				
			||||||
 | 
					type AdminDashboardForm struct {
 | 
				
			||||||
 | 
						Op int `binding:"required"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate validates form fields
 | 
				
			||||||
 | 
					func (f *AdminDashboardForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
				
			||||||
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,7 @@ type AuthenticationForm struct {
 | 
				
			|||||||
	SearchPageSize                int
 | 
						SearchPageSize                int
 | 
				
			||||||
	Filter                        string
 | 
						Filter                        string
 | 
				
			||||||
	AdminFilter                   string
 | 
						AdminFilter                   string
 | 
				
			||||||
 | 
						AllowDeactivateAll            bool
 | 
				
			||||||
	IsActive                      bool
 | 
						IsActive                      bool
 | 
				
			||||||
	IsSyncEnabled                 bool
 | 
						IsSyncEnabled                 bool
 | 
				
			||||||
	SMTPAuth                      string
 | 
						SMTPAuth                      string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,6 +47,7 @@ type Source struct {
 | 
				
			|||||||
	Filter                string // Query filter to validate entry
 | 
						Filter                string // Query filter to validate entry
 | 
				
			||||||
	AdminFilter           string // Query filter to check if user is admin
 | 
						AdminFilter           string // Query filter to check if user is admin
 | 
				
			||||||
	Enabled               bool   // if this source is disabled
 | 
						Enabled               bool   // if this source is disabled
 | 
				
			||||||
 | 
						AllowDeactivateAll    bool   // Allow an empty search response to deactivate all users from this source
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SearchResult : user data
 | 
					// SearchResult : user data
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Auth pam auth service
 | 
					// Auth pam auth service
 | 
				
			||||||
func Auth(serviceName, userName, passwd string) error {
 | 
					func Auth(serviceName, userName, passwd string) (string, error) {
 | 
				
			||||||
	t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) {
 | 
						t, err := pam.StartFunc(serviceName, userName, func(s pam.Style, msg string) (string, error) {
 | 
				
			||||||
		switch s {
 | 
							switch s {
 | 
				
			||||||
		case pam.PromptEchoOff:
 | 
							case pam.PromptEchoOff:
 | 
				
			||||||
@@ -25,12 +25,14 @@ func Auth(serviceName, userName, passwd string) error {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = t.Authenticate(0); err != nil {
 | 
						if err = t.Authenticate(0); err != nil {
 | 
				
			||||||
		return err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						// PAM login names might suffer transformations in the PAM stack.
 | 
				
			||||||
 | 
						// We should take whatever the PAM stack returns for it.
 | 
				
			||||||
 | 
						return t.GetItem(pam.User)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,6 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Auth not supported lack of pam tag
 | 
					// Auth not supported lack of pam tag
 | 
				
			||||||
func Auth(serviceName, userName, passwd string) error {
 | 
					func Auth(serviceName, userName, passwd string) (string, error) {
 | 
				
			||||||
	return errors.New("PAM not supported")
 | 
						return "", errors.New("PAM not supported")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
					// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Copyright 2020 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -122,7 +123,7 @@ func (ctx *Context) RedirectToFirst(location ...string) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		u, err := url.Parse(loc)
 | 
							u, err := url.Parse(loc)
 | 
				
			||||||
		if err != nil || (u.Scheme != "" && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
 | 
							if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,12 +91,12 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
 | 
				
			|||||||
	// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
 | 
						// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
 | 
				
			||||||
	isAssigned, _ := models.IsUserAssignedToIssue(issue, user)
 | 
						isAssigned, _ := models.IsUserAssignedToIssue(issue, user)
 | 
				
			||||||
	return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
 | 
						return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
 | 
				
			||||||
		r.Permission.CanWrite(models.UnitTypeIssues) || issue.IsPoster(user.ID) || isAssigned)
 | 
							r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
 | 
					// CanCreateIssueDependencies returns whether or not a user can create dependencies.
 | 
				
			||||||
func (r *Repository) CanCreateIssueDependencies(user *models.User) bool {
 | 
					func (r *Repository) CanCreateIssueDependencies(user *models.User, isPull bool) bool {
 | 
				
			||||||
	return r.Permission.CanWrite(models.UnitTypeIssues) && r.Repository.IsDependenciesEnabled()
 | 
						return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCommitsCount returns cached commit count for current view
 | 
					// GetCommitsCount returns cached commit count for current view
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,8 +30,17 @@ func ToEmail(email *models.EmailAddress) *api.Email {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
 | 
					// ToBranch convert a git.Commit and git.Branch to an api.Branch
 | 
				
			||||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User) *api.Branch {
 | 
					func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User) (*api.Branch, error) {
 | 
				
			||||||
	if bp == nil {
 | 
						if bp == nil {
 | 
				
			||||||
 | 
							var hasPerm bool
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							if user != nil {
 | 
				
			||||||
 | 
								hasPerm, err = models.HasAccessUnit(user, repo, models.UnitTypeCode, models.AccessModeWrite)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return &api.Branch{
 | 
							return &api.Branch{
 | 
				
			||||||
			Name:                b.Name,
 | 
								Name:                b.Name,
 | 
				
			||||||
			Commit:              ToCommit(repo, c),
 | 
								Commit:              ToCommit(repo, c),
 | 
				
			||||||
@@ -39,20 +48,25 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
 | 
				
			|||||||
			RequiredApprovals:   0,
 | 
								RequiredApprovals:   0,
 | 
				
			||||||
			EnableStatusCheck:   false,
 | 
								EnableStatusCheck:   false,
 | 
				
			||||||
			StatusCheckContexts: []string{},
 | 
								StatusCheckContexts: []string{},
 | 
				
			||||||
			UserCanPush:         true,
 | 
								UserCanPush:         hasPerm,
 | 
				
			||||||
			UserCanMerge:        true,
 | 
								UserCanMerge:        hasPerm,
 | 
				
			||||||
		}
 | 
							}, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &api.Branch{
 | 
					
 | 
				
			||||||
 | 
						branch := &api.Branch{
 | 
				
			||||||
		Name:                b.Name,
 | 
							Name:                b.Name,
 | 
				
			||||||
		Commit:              ToCommit(repo, c),
 | 
							Commit:              ToCommit(repo, c),
 | 
				
			||||||
		Protected:           true,
 | 
							Protected:           true,
 | 
				
			||||||
		RequiredApprovals:   bp.RequiredApprovals,
 | 
							RequiredApprovals:   bp.RequiredApprovals,
 | 
				
			||||||
		EnableStatusCheck:   bp.EnableStatusCheck,
 | 
							EnableStatusCheck:   bp.EnableStatusCheck,
 | 
				
			||||||
		StatusCheckContexts: bp.StatusCheckContexts,
 | 
							StatusCheckContexts: bp.StatusCheckContexts,
 | 
				
			||||||
		UserCanPush:         bp.CanUserPush(user.ID),
 | 
					 | 
				
			||||||
		UserCanMerge:        bp.CanUserMerge(user.ID),
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if user != nil {
 | 
				
			||||||
 | 
							branch.UserCanPush = bp.CanUserPush(user.ID)
 | 
				
			||||||
 | 
							branch.UserCanMerge = bp.IsUserMergeWhitelisted(user.ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return branch, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ToTag convert a git.Tag to an api.Tag
 | 
					// ToTag convert a git.Tag to an api.Tag
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Blob represents a Git object.
 | 
					// Blob represents a Git object.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,7 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Commit represents a git commit.
 | 
					// Commit represents a git commit.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,9 +6,9 @@ package git
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/emirpasic/gods/trees/binaryheap"
 | 
						"github.com/emirpasic/gods/trees/binaryheap"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
	cgobject "gopkg.in/src-d/go-git.v4/plumbing/object/commitgraph"
 | 
						cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCommitsInfo gets information of all commits that are corresponding to these entries
 | 
					// GetCommitsInfo gets information of all commits that are corresponding to these entries
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ package git
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NotesRef is the git ref where Gitea will look for git-notes data.
 | 
					// NotesRef is the git ref where Gitea will look for git-notes data.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,8 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
 | 
						"github.com/go-git/go-git/v5/plumbing/filemode"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ParseTreeEntries parses the output of a `git ls-tree` command.
 | 
					// ParseTreeEntries parses the output of a `git ls-tree` command.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,9 @@ package git
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/filemode"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
 | 
					 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestParseTreeEntries(t *testing.T) {
 | 
					func TestParseTreeEntries(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,11 +18,11 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gitealog "code.gitea.io/gitea/modules/log"
 | 
						gitealog "code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"github.com/go-git/go-billy/v5/osfs"
 | 
				
			||||||
 | 
						gogit "github.com/go-git/go-git/v5"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/cache"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/storage/filesystem"
 | 
				
			||||||
	"github.com/unknwon/com"
 | 
						"github.com/unknwon/com"
 | 
				
			||||||
	"gopkg.in/src-d/go-billy.v4/osfs"
 | 
					 | 
				
			||||||
	gogit "gopkg.in/src-d/go-git.v4"
 | 
					 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/cache"
 | 
					 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/storage/filesystem"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Repository represents a Git repository.
 | 
					// Repository represents a Git repository.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (repo *Repository) getBlob(id SHA1) (*Blob, error) {
 | 
					func (repo *Repository) getBlob(id SHA1) (*Blob, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BranchPrefix base dir of the branch information file store on git
 | 
					// BranchPrefix base dir of the branch information file store on git
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,15 +12,20 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
	"github.com/mcuadros/go-version"
 | 
						"github.com/mcuadros/go-version"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
					 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRefCommitID returns the last commit ID string of given reference (branch or tag).
 | 
					// GetRefCommitID returns the last commit ID string of given reference (branch or tag).
 | 
				
			||||||
func (repo *Repository) GetRefCommitID(name string) (string, error) {
 | 
					func (repo *Repository) GetRefCommitID(name string) (string, error) {
 | 
				
			||||||
	ref, err := repo.gogitRepo.Reference(plumbing.ReferenceName(name), true)
 | 
						ref, err := repo.gogitRepo.Reference(plumbing.ReferenceName(name), true)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if err == plumbing.ErrReferenceNotFound {
 | 
				
			||||||
 | 
								return "", ErrNotExist{
 | 
				
			||||||
 | 
									ID: name,
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -89,9 +94,15 @@ func (repo *Repository) getCommit(id SHA1) (*Commit, error) {
 | 
				
			|||||||
	gogitCommit, err := repo.gogitRepo.CommitObject(id)
 | 
						gogitCommit, err := repo.gogitRepo.CommitObject(id)
 | 
				
			||||||
	if err == plumbing.ErrObjectNotFound {
 | 
						if err == plumbing.ErrObjectNotFound {
 | 
				
			||||||
		tagObject, err = repo.gogitRepo.TagObject(id)
 | 
							tagObject, err = repo.gogitRepo.TagObject(id)
 | 
				
			||||||
 | 
							if err == plumbing.ErrObjectNotFound {
 | 
				
			||||||
 | 
								return nil, ErrNotExist{
 | 
				
			||||||
 | 
									ID: id.String(),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			gogitCommit, err = repo.gogitRepo.CommitObject(tagObject.Target)
 | 
								gogitCommit, err = repo.gogitRepo.CommitObject(tagObject.Target)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// if we get a plumbing.ErrObjectNotFound here then the repository is broken and it should be 500
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -318,7 +329,7 @@ func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List
 | 
				
			|||||||
	var stdout []byte
 | 
						var stdout []byte
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if before == nil {
 | 
						if before == nil {
 | 
				
			||||||
		stdout, err = NewCommand("rev-list", before.ID.String()).RunInDirBytes(repo.Path)
 | 
							stdout, err = NewCommand("rev-list", last.ID.String()).RunInDirBytes(repo.Path)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		stdout, err = NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
 | 
							stdout, err = NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,8 +11,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	gitealog "code.gitea.io/gitea/modules/log"
 | 
						gitealog "code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/format/commitgraph"
 | 
						"github.com/go-git/go-git/v5/plumbing/format/commitgraph"
 | 
				
			||||||
	cgobject "gopkg.in/src-d/go-git.v4/plumbing/object/commitgraph"
 | 
						cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommitNodeIndex returns the index for walking commit graph
 | 
					// CommitNodeIndex returns the index for walking commit graph
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,8 +7,8 @@ package git
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4"
 | 
						"github.com/go-git/go-git/v5"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRefs returns all references of the repository.
 | 
					// GetRefs returns all references of the repository.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,8 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	"github.com/mcuadros/go-version"
 | 
						"github.com/mcuadros/go-version"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TagPrefix tags prefix path on the repository
 | 
					// TagPrefix tags prefix path on the repository
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EmptySHA defines empty git SHA
 | 
					// EmptySHA defines empty git SHA
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Signature represents the Author or Committer information.
 | 
					// Signature represents the Author or Committer information.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,8 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Tree represents a flat directory listing.
 | 
					// Tree represents a flat directory listing.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,8 +9,8 @@ import (
 | 
				
			|||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
 | 
						"github.com/go-git/go-git/v5/plumbing/filemode"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetTreeEntryByPath get the tree entries according the sub dir
 | 
					// GetTreeEntryByPath get the tree entries according the sub dir
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,9 +10,9 @@ import (
 | 
				
			|||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing"
 | 
						"github.com/go-git/go-git/v5/plumbing"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
 | 
						"github.com/go-git/go-git/v5/plumbing/filemode"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EntryMode the type of the object in the git tree
 | 
					// EntryMode the type of the object in the git tree
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,9 +7,9 @@ package git
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/filemode"
 | 
				
			||||||
 | 
						"github.com/go-git/go-git/v5/plumbing/object"
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/filemode"
 | 
					 | 
				
			||||||
	"gopkg.in/src-d/go-git.v4/plumbing/object"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getTestEntries() Entries {
 | 
					func getTestEntries() Entries {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,12 @@ package graceful
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
						"sync/atomic"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -99,12 +101,25 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string, serve ServeFuncti
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config.Certificates = make([]tls.Certificate, 1)
 | 
						config.Certificates = make([]tls.Certificate, 1)
 | 
				
			||||||
	var err error
 | 
					
 | 
				
			||||||
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
 | 
						certPEMBlock, err := ioutil.ReadFile(certFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, srv.network, srv.address, err)
 | 
							log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, srv.network, srv.address, err)
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						keyPEMBlock, err := ioutil.ReadFile(keyFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Error("Failed to load https key file %s for %s:%s: %v", keyFile, srv.network, srv.address, err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						config.Certificates[0], err = tls.X509KeyPair(certPEMBlock, keyPEMBlock)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Error("Failed to create certificate from cert file %s and key file %s for %s:%s: %v", certFile, keyFile, srv.network, srv.address, err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return srv.ListenAndServeTLSConfig(config, serve)
 | 
						return srv.ListenAndServeTLSConfig(config, serve)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -201,9 +216,12 @@ func (wl *wrappedListener) Accept() (net.Conn, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						closed := int32(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c = wrappedConn{
 | 
						c = wrappedConn{
 | 
				
			||||||
		Conn:   c,
 | 
							Conn:   c,
 | 
				
			||||||
		server: wl.server,
 | 
							server: wl.server,
 | 
				
			||||||
 | 
							closed: &closed,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl.server.wg.Add(1)
 | 
						wl.server.wg.Add(1)
 | 
				
			||||||
@@ -227,12 +245,12 @@ func (wl *wrappedListener) File() (*os.File, error) {
 | 
				
			|||||||
type wrappedConn struct {
 | 
					type wrappedConn struct {
 | 
				
			||||||
	net.Conn
 | 
						net.Conn
 | 
				
			||||||
	server *Server
 | 
						server *Server
 | 
				
			||||||
 | 
						closed *int32
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (w wrappedConn) Close() error {
 | 
					func (w wrappedConn) Close() error {
 | 
				
			||||||
	err := w.Conn.Close()
 | 
						if atomic.CompareAndSwapInt32(w.closed, 0, 1) {
 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		w.server.wg.Done()
 | 
							w.server.wg.Done()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return err
 | 
						return w.Conn.Close()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,7 +116,12 @@ func nonGenesisChanges(repo *models.Repository, revision string) (*repoChanges,
 | 
				
			|||||||
		if len(line) == 0 {
 | 
							if len(line) == 0 {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		filename := strings.TrimSpace(line[1:])
 | 
							fields := strings.Split(line, "\t")
 | 
				
			||||||
 | 
							if len(fields) < 2 {
 | 
				
			||||||
 | 
								log.Warn("Unparseable output for diff --name-status: `%s`)", line)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							filename := fields[1]
 | 
				
			||||||
		if len(filename) == 0 {
 | 
							if len(filename) == 0 {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		} else if filename[0] == '"' {
 | 
							} else if filename[0] == '"' {
 | 
				
			||||||
@@ -126,11 +131,31 @@ func nonGenesisChanges(repo *models.Repository, revision string) (*repoChanges,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch status := line[0]; status {
 | 
							switch status := fields[0][0]; status {
 | 
				
			||||||
		case 'M', 'A':
 | 
							case 'M', 'A':
 | 
				
			||||||
			updatedFilenames = append(updatedFilenames, filename)
 | 
								updatedFilenames = append(updatedFilenames, filename)
 | 
				
			||||||
		case 'D':
 | 
							case 'D':
 | 
				
			||||||
			changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
 | 
								changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
 | 
				
			||||||
 | 
							case 'R', 'C':
 | 
				
			||||||
 | 
								if len(fields) < 3 {
 | 
				
			||||||
 | 
									log.Warn("Unparseable output for diff --name-status: `%s`)", line)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dest := fields[2]
 | 
				
			||||||
 | 
								if len(dest) == 0 {
 | 
				
			||||||
 | 
									log.Warn("Unparseable output for diff --name-status: `%s`)", line)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if dest[0] == '"' {
 | 
				
			||||||
 | 
									dest, err = strconv.Unquote(dest)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return nil, err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if status == 'R' {
 | 
				
			||||||
 | 
									changes.RemovedFilenames = append(changes.RemovedFilenames, filename)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								updatedFilenames = append(updatedFilenames, dest)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			log.Warn("Unrecognized status: %c (line=%s)", status, line)
 | 
								log.Warn("Unrecognized status: %c (line=%s)", status, line)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,25 +143,23 @@ func InitIssueIndexer(syncReindex bool) {
 | 
				
			|||||||
		var populate bool
 | 
							var populate bool
 | 
				
			||||||
		switch setting.Indexer.IssueType {
 | 
							switch setting.Indexer.IssueType {
 | 
				
			||||||
		case "bleve":
 | 
							case "bleve":
 | 
				
			||||||
			graceful.GetManager().RunWithShutdownFns(func(_, atTerminate func(context.Context, func())) {
 | 
								issueIndexer := NewBleveIndexer(setting.Indexer.IssuePath)
 | 
				
			||||||
				issueIndexer := NewBleveIndexer(setting.Indexer.IssuePath)
 | 
								exist, err := issueIndexer.Init()
 | 
				
			||||||
				exist, err := issueIndexer.Init()
 | 
								if err != nil {
 | 
				
			||||||
				if err != nil {
 | 
									holder.cancel()
 | 
				
			||||||
					holder.cancel()
 | 
									log.Fatal("Unable to initialize Bleve Issue Indexer: %v", err)
 | 
				
			||||||
					log.Fatal("Unable to initialize Bleve Issue Indexer: %v", err)
 | 
								}
 | 
				
			||||||
 | 
								populate = !exist
 | 
				
			||||||
 | 
								holder.set(issueIndexer)
 | 
				
			||||||
 | 
								graceful.GetManager().RunAtTerminate(context.Background(), func() {
 | 
				
			||||||
 | 
									log.Debug("Closing issue indexer")
 | 
				
			||||||
 | 
									issueIndexer := holder.get()
 | 
				
			||||||
 | 
									if issueIndexer != nil {
 | 
				
			||||||
 | 
										issueIndexer.Close()
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				populate = !exist
 | 
									log.Info("PID: %d Issue Indexer closed", os.Getpid())
 | 
				
			||||||
				holder.set(issueIndexer)
 | 
					 | 
				
			||||||
				atTerminate(context.Background(), func() {
 | 
					 | 
				
			||||||
					log.Debug("Closing issue indexer")
 | 
					 | 
				
			||||||
					issueIndexer := holder.get()
 | 
					 | 
				
			||||||
					if issueIndexer != nil {
 | 
					 | 
				
			||||||
						issueIndexer.Close()
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					log.Info("PID: %d Issue Indexer closed", os.Getpid())
 | 
					 | 
				
			||||||
				})
 | 
					 | 
				
			||||||
				log.Debug("Created Bleve Indexer")
 | 
					 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
								log.Debug("Created Bleve Indexer")
 | 
				
			||||||
		case "db":
 | 
							case "db":
 | 
				
			||||||
			issueIndexer := &DBIndexer{}
 | 
								issueIndexer := &DBIndexer{}
 | 
				
			||||||
			holder.set(issueIndexer)
 | 
								holder.set(issueIndexer)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,7 +108,7 @@ func (s *linkifyParser) Parse(parent ast.Node, block text.Reader, pc parser.Cont
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		at := bytes.IndexByte(line, '@')
 | 
							at := bytes.IndexByte(line, '@')
 | 
				
			||||||
		m = []int{0, stop, at, stop - 1}
 | 
							m = []int{0, stop, at, stop - 1}
 | 
				
			||||||
		if m == nil || bytes.IndexByte(line[m[2]:m[3]], '.') < 0 {
 | 
							if bytes.IndexByte(line[m[2]:m[3]], '.') < 0 {
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		lastChar := line[m[1]-1]
 | 
							lastChar := line[m[1]-1]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,7 +52,6 @@ func (g *GiteaASTTransformer) Transform(node *ast.Document, reader text.Reader,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				lnk := string(link)
 | 
									lnk := string(link)
 | 
				
			||||||
				lnk = giteautil.URLJoin(prefix, lnk)
 | 
									lnk = giteautil.URLJoin(prefix, lnk)
 | 
				
			||||||
				lnk = strings.Replace(lnk, " ", "+", -1)
 | 
					 | 
				
			||||||
				link = []byte(lnk)
 | 
									link = []byte(lnk)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			v.Destination = link
 | 
								v.Destination = link
 | 
				
			||||||
@@ -79,6 +78,9 @@ func (g *GiteaASTTransformer) Transform(node *ast.Document, reader text.Reader,
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				link = []byte(giteautil.URLJoin(pc.Get(urlPrefixKey).(string), lnk))
 | 
									link = []byte(giteautil.URLJoin(pc.Get(urlPrefixKey).(string), lnk))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if len(link) > 0 && link[0] == '#' {
 | 
				
			||||||
 | 
									link = []byte("#user-content-" + string(link)[1:])
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			v.Destination = link
 | 
								v.Destination = link
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return ast.WalkContinue, nil
 | 
							return ast.WalkContinue, nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,8 +48,9 @@ func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte {
 | 
				
			|||||||
				common.FootnoteExtension,
 | 
									common.FootnoteExtension,
 | 
				
			||||||
				extension.NewTypographer(
 | 
									extension.NewTypographer(
 | 
				
			||||||
					extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{
 | 
										extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{
 | 
				
			||||||
						extension.EnDash: nil,
 | 
											extension.EnDash:   nil,
 | 
				
			||||||
						extension.EmDash: nil,
 | 
											extension.EmDash:   nil,
 | 
				
			||||||
 | 
											extension.Ellipsis: nil,
 | 
				
			||||||
					}),
 | 
										}),
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,6 @@ func RenderWiki(filename string, rawBytes []byte, urlPrefix string, metas map[st
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func render(parser Parser, rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
 | 
					func render(parser Parser, rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte {
 | 
				
			||||||
	urlPrefix = strings.Replace(urlPrefix, " ", "+", -1)
 | 
					 | 
				
			||||||
	result := parser.Render(rawBytes, urlPrefix, metas, isWiki)
 | 
						result := parser.Render(rawBytes, urlPrefix, metas, isWiki)
 | 
				
			||||||
	// TODO: one day the error should be returned.
 | 
						// TODO: one day the error should be returned.
 | 
				
			||||||
	result, err := PostProcess(result, urlPrefix, metas, isWiki)
 | 
						result, err := PostProcess(result, urlPrefix, metas, isWiki)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ func NewSanitizer() {
 | 
				
			|||||||
func ReplaceSanitizer() {
 | 
					func ReplaceSanitizer() {
 | 
				
			||||||
	sanitizer.policy = bluemonday.UGCPolicy()
 | 
						sanitizer.policy = bluemonday.UGCPolicy()
 | 
				
			||||||
	// We only want to allow HighlightJS specific classes for code blocks
 | 
						// We only want to allow HighlightJS specific classes for code blocks
 | 
				
			||||||
	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code")
 | 
						sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-[\w-]+$`)).OnElements("code")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Checkboxes
 | 
						// Checkboxes
 | 
				
			||||||
	sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
 | 
						sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ type Comment struct {
 | 
				
			|||||||
	PosterName  string
 | 
						PosterName  string
 | 
				
			||||||
	PosterEmail string
 | 
						PosterEmail string
 | 
				
			||||||
	Created     time.Time
 | 
						Created     time.Time
 | 
				
			||||||
 | 
						Updated     time.Time
 | 
				
			||||||
	Content     string
 | 
						Content     string
 | 
				
			||||||
	Reactions   *Reactions
 | 
						Reactions   *Reactions
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ type Issue struct {
 | 
				
			|||||||
	State       string // closed, open
 | 
						State       string // closed, open
 | 
				
			||||||
	IsLocked    bool
 | 
						IsLocked    bool
 | 
				
			||||||
	Created     time.Time
 | 
						Created     time.Time
 | 
				
			||||||
 | 
						Updated     time.Time
 | 
				
			||||||
	Closed      *time.Time
 | 
						Closed      *time.Time
 | 
				
			||||||
	Labels      []*Label
 | 
						Labels      []*Label
 | 
				
			||||||
	Reactions   *Reactions
 | 
						Reactions   *Reactions
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ type PullRequest struct {
 | 
				
			|||||||
	Milestone      string
 | 
						Milestone      string
 | 
				
			||||||
	State          string
 | 
						State          string
 | 
				
			||||||
	Created        time.Time
 | 
						Created        time.Time
 | 
				
			||||||
 | 
						Updated        time.Time
 | 
				
			||||||
	Closed         *time.Time
 | 
						Closed         *time.Time
 | 
				
			||||||
	Labels         []*Label
 | 
						Labels         []*Label
 | 
				
			||||||
	PatchURL       string
 | 
						PatchURL       string
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user