diff --git a/.drone.yml b/.drone.yml
index e766ba6134..b9d928eb21 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -15,12 +15,12 @@ trigger:
steps:
- name: deps-frontend
pull: always
- image: node:16
+ image: node:14
commands:
- make node_modules
- name: lint-frontend
- image: node:16
+ image: node:14
commands:
- make lint-frontend
depends_on: [deps-frontend]
@@ -58,7 +58,7 @@ steps:
TAGS: bindata gogit sqlite sqlite_unlock_notify
- name: checks-frontend
- image: node:16
+ image: node:14
commands:
- make checks-frontend
depends_on: [deps-frontend]
@@ -71,20 +71,20 @@ steps:
depends_on: [lint-backend]
- name: test-frontend
- image: node:16
+ image: node:14
commands:
- make test-frontend
depends_on: [lint-frontend]
- name: build-frontend
- image: node:16
+ image: node:14
commands:
- make frontend
depends_on: [test-frontend]
- name: build-backend-no-gcc
pull: always
- image: golang:1.14 # this step is kept as the lowest version of golang that we support
+ image: golang:1.16 # this step is kept as the lowest version of golang that we support
environment:
GO111MODULE: on
GOPROXY: off
@@ -404,7 +404,7 @@ steps:
- name: update
pull: default
- image: alpine:3.14
+ image: alpine:3.13
commands:
- ./build/update-locales.sh
@@ -503,7 +503,7 @@ steps:
pull: always
image: techknowlogick/xgo:go-1.16.x
commands:
- - curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
+ - curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs
- export PATH=$PATH:$GOPATH/bin
- make release
environment:
@@ -527,11 +527,11 @@ steps:
- name: release-branch
pull: always
- image: plugins/s3:1
+ image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
- endpoint: https://storage.gitea.io
+ endpoint: https://ams3.digitaloceanspaces.com
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
@@ -548,11 +548,11 @@ steps:
- push
- name: release-main
- image: plugins/s3:1
+ image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
- endpoint: https://storage.gitea.io
+ endpoint: https://ams3.digitaloceanspaces.com
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
@@ -599,7 +599,7 @@ steps:
pull: always
image: techknowlogick/xgo:go-1.16.x
commands:
- - curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
+ - curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs
- export PATH=$PATH:$GOPATH/bin
- make release
environment:
@@ -623,11 +623,11 @@ steps:
- name: release-tag
pull: always
- image: plugins/s3:1
+ image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: gitea-artifacts
- endpoint: https://storage.gitea.io
+ endpoint: https://ams3.digitaloceanspaces.com
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
@@ -817,6 +817,70 @@ steps:
exclude:
- pull_request
+---
+kind: pipeline
+name: docker-linux-amd64-release-branch
+
+platform:
+ os: linux
+ arch: amd64
+
+depends_on:
+ - testing-amd64
+ - testing-arm64
+
+trigger:
+ ref:
+ - "refs/heads/release/v*"
+ event:
+ exclude:
+ - cron
+
+steps:
+ - name: fetch-tags
+ image: docker:git
+ commands:
+ - git fetch --tags --force
+
+ - name: publish
+ pull: always
+ image: techknowlogick/drone-docker:latest
+ settings:
+ auto_tag: false
+ tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64
+ repo: gitea/gitea
+ build_args:
+ - GOPROXY=off
+ password:
+ from_secret: docker_password
+ username:
+ from_secret: docker_username
+ when:
+ event:
+ exclude:
+ - pull_request
+
+ - name: publish-rootless
+ image: techknowlogick/drone-docker:latest
+ settings:
+ dockerfile: Dockerfile.rootless
+ auto_tag: false
+ tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64-rootless
+ repo: gitea/gitea
+ build_args:
+ - GOPROXY=off
+ password:
+ from_secret: docker_password
+ username:
+ from_secret: docker_username
+ environment:
+ PLUGIN_MIRROR:
+ from_secret: plugin_mirror
+ when:
+ event:
+ exclude:
+ - pull_request
+
---
kind: pipeline
name: docker-linux-arm64-dry-run
@@ -982,6 +1046,74 @@ steps:
event:
exclude:
- pull_request
+
+---
+kind: pipeline
+name: docker-linux-arm64-release-branch
+
+platform:
+ os: linux
+ arch: arm64
+
+depends_on:
+ - testing-amd64
+ - testing-arm64
+
+trigger:
+ ref:
+ - "refs/heads/release/v*"
+ event:
+ exclude:
+ - cron
+
+steps:
+ - name: fetch-tags
+ image: docker:git
+ commands:
+ - git fetch --tags --force
+
+ - name: publish
+ pull: always
+ image: techknowlogick/drone-docker:latest
+ settings:
+ auto_tag: false
+ tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64
+ repo: gitea/gitea
+ build_args:
+ - GOPROXY=off
+ password:
+ from_secret: docker_password
+ username:
+ from_secret: docker_username
+ environment:
+ PLUGIN_MIRROR:
+ from_secret: plugin_mirror
+ when:
+ event:
+ exclude:
+ - pull_request
+
+ - name: publish-rootless
+ image: techknowlogick/drone-docker:latest
+ settings:
+ dockerfile: Dockerfile.rootless
+ auto_tag: false
+ tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64-rootless
+ repo: gitea/gitea
+ build_args:
+ - GOPROXY=off
+ password:
+ from_secret: docker_password
+ username:
+ from_secret: docker_username
+ environment:
+ PLUGIN_MIRROR:
+ from_secret: plugin_mirror
+ when:
+ event:
+ exclude:
+ - pull_request
+
---
kind: pipeline
name: docker-manifest-version
@@ -1041,6 +1173,7 @@ steps:
auto_tag: false
ignore_missing: true
spec: docker/manifest.rootless.tmpl
+ dump: true
password:
from_secret: docker_password
username:
@@ -1052,6 +1185,7 @@ steps:
auto_tag: false
ignore_missing: true
spec: docker/manifest.tmpl
+ dump: true
password:
from_secret: docker_password
username:
@@ -1060,6 +1194,7 @@ steps:
trigger:
ref:
- refs/heads/main
+ - "refs/heads/release/v*"
event:
exclude:
- cron
@@ -1067,6 +1202,8 @@ trigger:
depends_on:
- docker-linux-amd64-release
- docker-linux-arm64-release
+ - docker-linux-amd64-release-branch
+ - docker-linux-arm64-release-branch
---
kind: pipeline
diff --git a/.eslintrc b/.eslintrc
index 640956f734..71cbd5cfb6 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -2,6 +2,7 @@ root: true
reportUnusedDisableDirectives: true
ignorePatterns:
+ - /web_src/js/vendor
- /templates/base/head.tmpl
- /templates/repo/activity.tmpl
- /templates/repo/view_file.tmpl
diff --git a/.golangci.yml b/.golangci.yml
index c3dd47ec29..6e7aa750f7 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -9,7 +9,6 @@ linters:
- unused
- structcheck
- varcheck
- - golint
- dupl
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
- gofmt
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63a49e7f07..6a09e29763 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,209 @@ 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
been added to each release, please refer to the [blog](https://blog.gitea.io).
-## [1.15.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.15.0-rc1) - 2021-07-15
+
+## [1.15.11](https://github.com/go-gitea/gitea/releases/tag/v1.15.11) - 2022-01-29
+
+* SECURITY
+ * Only view milestones from current repo (#18414) (#18418)
+* BUGFIXES
+ * Fix broken when no commits and default branch is not master (#18422) (#18424)
+ * Fix commit's time (#18375) (#18409)
+ * Fix restore without topic failure (#18387) (#18401)
+ * Fix mermaid import in 1.15 (it uses ESModule now) (#18382)
+ * Update to go/text 0.3.7 (#18336)
+* MISC
+ * Upgrade EasyMDE to 2.16.1 (#18278) (#18279)
+
+## [1.15.10](https://github.com/go-gitea/gitea/releases/tag/v1.15.10) - 2022-01-14
+
+* BUGFIXES
+ * Fix inconsistent PR comment counts (#18260) (#18261)
+ * Fix release link broken (#18252) (#18253)
+ * Fix update user from site administration page bug (#18250) (#18251)
+ * Set HeadCommit when creating tags (#18116) (#18173)
+ * Use correct translation key for error messages due to max repo limits (#18135 & #18153) (#18152)
+ * Fix purple color in suggested label colors (#18241) (#18242)
+* SECURITY
+ * Bump mermaid from 8.10.1 to 8.13.8 (#18198) (#18206)
+
+## [1.15.9](https://github.com/go-gitea/gitea/releases/tag/v1.15.9) - 2021-12-30
+
+* BUGFIXES
+ * Fix wrong redirect on org labels (#18128) (#18134)
+ * Fix: unstable sort skips/duplicates issues across pages (#18094) (#18095)
+ * Revert "Fix delete u2f keys bug (#18042)" (#18107)
+ * Migrating wiki don't require token, so we should move it out of the require form (#17645) (#18104)
+ * Prevent NPE if gitea uploader fails to open url (#18080) (#18101)
+ * Reset locale on login (#17734) (#18100)
+ * Correctly handle failed migrations (#17575) (#18099)
+ * Instead of using routerCtx just escape the url before routing (#18086) (#18098)
+ * Quote references to the user table in consistency checks (#18072) (#18073)
+ * Add NotFound handler (#18062) (#18067)
+ * Ensure that git repository is closed before transfer (#18049) (#18057)
+ * Use common sessioner for API and web routes (#18114)
+* TRANSLATION
+ * Fix code search result hint on zh-CN (#18053)
+
+## [1.15.8](https://github.com/go-gitea/gitea/releases/tag/v1.15.8) - 2021-12-20
+
+* BUGFIXES
+ * Move POST /{username}/action/{action} to simply POST /{username} (#18045) (#18046)
+ * Fix delete u2f keys bug (#18040) (#18042)
+ * Reset Session ID on login (#18018) (#18041)
+ * Prevent off-by-one error on comments on newly appended lines (#18029) (#18035)
+ * Stop printing 03d after escaped characters in logs (#18030) (#18034)
+ * Reset locale on login (#18023) (#18025)
+ * Fix reset password email template (#17025) (#18022)
+ * Fix outType on gitea dump (#18000) (#18016)
+ * Ensure complexity, minlength and isPwned are checked on password setting (#18005) (#18015)
+ * Fix rename notification bug (#18011)
+ * Prevent double decoding of % in url params (#17997) (#18001)
+ * Prevent hang in git cat-file if the repository is not a valid repository (Partial #17991) (#17992)
+ * Prevent deadlock in create issue (#17970) (#17982)
+* TESTING
+ * Use non-expiring key. (#17984) (#17985)
+
+## [1.15.7](https://github.com/go-gitea/gitea/releases/tag/v1.15.7) - 2021-12-01
+
+* ENHANCEMENTS
+ * Only allow webhook to send requests to allowed hosts (#17482) (#17510)
+ * Fix login redirection links (#17451) (#17473)
+* BUGFIXES
+ * Fix database inconsistent when admin change user email (#17549) (#17840)
+ * Use correct user on releases (#17806) (#17818)
+ * Fix commit count in tag view (#17698) (#17790)
+ * Fix close issue but time watcher still running (#17643) (#17761)
+ * Fix Migrate Description (#17692) (#17727)
+ * Fix bug when project board get open issue number (#17703) (#17726)
+ * Return 400 but not 500 when request archive with wrong format (#17691) (#17700)
+ * Fix bug when read mysql database max lifetime (#17682) (#17690)
+ * Fix database deadlock when update issue labels (#17649) (#17665)
+ * Fix bug on detect issue/comment writer (#17592)
+ * Remove appSubUrl from pasted images (#17572) (#17588)
+ * Make `ParsePatch` more robust (#17573) (#17580)
+ * Fix stats upon searching issues (#17566) (#17578)
+ * Escape issue titles in comments list (#17555) (#17556)
+ * Fix zero created time bug on commit api (#17546) (#17547)
+ * Fix database keyword quote problem on migration v161 (#17522) (#17523)
+ * Fix email with + when active (#17518) (#17520)
+ * Stop double encoding blame commit messages (#17498) (#17500)
+ * Quote the table name in CountOrphanedObjects (#17487) (#17488)
+ * Run Migrate in Install rather than just SyncTables (#17475) (#17486)
+* BUILD
+ * Fix golangci-lint warnings (#17598 et al) (#17668)
+* MISC
+ * Preserve color when inverting emojis (#17797) (#17799)
+
+## [1.15.6](https://github.com/go-gitea/gitea/releases/tag/v1.15.6) - 2021-10-28
+
+* BUGFIXES
+ * Prevent panic in serv.go with Deploy Keys (#17434) (#17435)
+ * Fix CSV render error (#17406) (#17431)
+ * Read expected buffer size (#17409) (#17430)
+ * Ensure that restricted users can access repos for which they are members (#17460) (#17464)
+ * Make commit-statuses popup show correctly (#17447) (#17466)
+* TESTING
+ * Add integration tests for private.NoServCommand and private.ServCommand (#17456) (#17463)
+
+## [1.15.5](https://github.com/go-gitea/gitea/releases/tag/v1.15.5) - 2021-10-21
+
+* SECURITY
+ * Upgrade Bluemonday to v1.0.16 (#17372) (#17374)
+ * Ensure correct SSH permissions check for private and restricted users (#17370) (#17373)
+* BUGFIXES
+ * Prevent NPE in CSV diff rendering when column removed (#17018) (#17377)
+ * Offer rsa-sha2-512 and rsa-sha2-256 algorithms in internal SSH (#17281) (#17376)
+ * Don't panic if we fail to parse U2FRegistration data (#17304) (#17371)
+ * Ensure popup text is aligned left (backport for 1.15) (#17343)
+ * Ensure that git daemon export ok is created for mirrors (#17243) (#17306)
+ * Disable core.protectNTFS (#17300) (#17302)
+ * Use pointer for wrappedConn methods (#17295) (#17296)
+ * AutoRegistration is supposed to be working with disabled registration (backport) (#17292)
+ * Handle duplicate keys on GPG key ring (#17242) (#17284)
+ * Fix SVG side by side comparison link (#17375) (#17391)
+
+## [1.15.4](https://github.com/go-gitea/gitea/releases/tag/v1.15.4) - 2021-10-08
+* BUGFIXES
+ * Raw file API: don't try to interpret 40char filenames as commit SHA (#17185) (#17272)
+ * Don't allow merged PRs to be reopened (#17192) (#17271)
+ * Fix incorrect repository count on organization tab of dashboard (#17256) (#17266)
+ * Fix unwanted team review request deletion (#17257) (#17264)
+ * Fix broken Activities link in team dashboard (#17255) (#17258)
+ * API pull's head/base have correct permission(#17214) (#17245)
+ * Fix stange behavior of DownloadPullDiffOrPatch in incorect index (#17223) (#17227)
+ * Upgrade xorm to v1.2.5 (#17177) (#17188)
+ * Fix missing repo link in issue/pull assigned emails (#17183) (#17184)
+ * Fix bug of get context user (#17169) (#17172)
+ * Nicely handle missing user in collaborations (#17049) (#17166)
+ * Add Horizontal scrollbar to inner menu on Chrome (#17086) (#17164)
+ * Fix wrong i18n keys (#17150) (#17153)
+ * Fix Archive Creation: correct transaction ending (#17151)
+ * Prevent panic in Org mode HighlightCodeBlock (#17140) (#17141)
+ * Create doctor command to fix repo_units broken by dumps from 1.14.3-1.14.6 (#17136) (#17137)
+* ENHANCEMENT
+ * Check user instead of organization when creating a repo from a template via API (#16346) (#17195)
+* TRANSLATION
+ * v1.15 fix Sprintf format 'verbs' in locale files (#17187)
+
+
+## [1.15.3](https://github.com/go-gitea/gitea/releases/tag/v1.15.3) - 2021-09-19
+
+* ENHANCEMENTS
+ * Add fluid to ui container class to remove margin (#16396) (#16976)
+ * Add caller to cat-file batch calls (#17082) (#17089)
+* BUGFIXES
+ * Render full plain readme. (#17083) (#17090)
+ * Upgrade xorm to v1.2.4 (#17059)
+ * Fix bug of migrate comments which only fetch one page (#17055) (#17058)
+ * Do not show issue context popup on external issues (#17050) (#17054)
+ * Decrement Fork Num when converting from Fork (#17035) (#17046)
+ * Correctly rollback in ForkRepository (#17034) (#17045)
+ * Fix missing close in WalkGitLog (#17008) (#17009)
+ * Add prefix to SVG id/class attributes (#16997) (#17000)
+ * Fix bug of migrated repository not index (#16991) (#16996)
+ * Skip AllowedUserVisibilityModes validation on update user if it is an organisation (#16988) (#16990)
+ * Fix storage Iterate bug and Add storage doctor to delete garbage attachments (#16971) (#16977)
+ * Fix issue with issue default mail template (#16956) (#16975)
+ * Ensure that rebase conflicts are handled in updates (#16952) (#16960)
+ * Prevent panic on diff generation (#16950) (#16951)
+
+## [1.15.2](https://github.com/go-gitea/gitea/releases/tag/v1.15.2) - 2021-09-03
+
+* BUGFIXES
+ * Add unique constraint back into issue_index (#16938)
+ * Close storage objects before cleaning (#16934) (#16942)
+
+## [1.15.1](https://github.com/go-gitea/gitea/releases/tag/v1.15.1) - 2021-09-02
+
+* BUGFIXES
+ * Allow BASIC authentication access to /:owner/:repo/releases/download/* (#16916) (#16923)
+ * Prevent leave changes dialogs due to autofill fields (#16912) (#16920)
+ * Ignore review comment when ref commit is missed (#16905) (#16919)
+ * Fix wrong attachment removal (#16915) (#16917)
+ * Gitlab Migrator: dont ignore reactions of last request (#16903) (#16913)
+ * Correctly return the number of Repositories for Organizations (#16807) (#16911)
+ * Test if LFS object is accessible (#16865) (#16904)
+ * Fix git.Blob.DataAsync(): close pipe since we return a NopCloser (#16899) (#16900)
+ * Fix dump and restore respository (#16698) (#16898)
+ * Repare and Improve GetDiffRangeWithWhitespaceBehavior (#16894) (#16895)
+ * Fix wiki raw commit diff/patch view (#16891) (#16892)
+ * Ensure wiki repos are all closed (#16886) (#16888)
+ * List limited and private orgs if authenticated on API (#16866) (#16879)
+ * Simplify split diff view generation and remove JS dependency (#16775) (#16863)
+ * Ensure that the default visibility is set on the user create page (#16845) (#16862)
+ * In Render tolerate not being passed a context (#16842) (#16858)
+ * Upgrade xorm to v1.2.2 (#16663) & Add test to ensure that dumping of login sources remains correct (#16847) (#16848)
+ * Report the correct number of pushes on the feeds (#16811) (#16822)
+ * Add primary_key to issue_index (#16813) (#16820)
+ * Prevent NPE on empty commit (#16812) (#16819)
+ * Fix branch pagination error (#16805) (#16816)
+ * Add missing return to handleSettingRemoteAddrError (#16794) (#16795)
+ * Remove spurious / from issues.opened_by (#16793)
+ * Ensure that template compilation panics are sent to the logs (#16788) (#16792)
+ * Update caddyserver/certmagic (#16789) (#16790)
+
+## [1.15.0](https://github.com/go-gitea/gitea/releases/tag/v1.15.0) - 2021-08-21
* BREAKING
* Make app.ini permissions more restrictive (#16266)
@@ -19,9 +221,15 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Move (custom) assets into subpath `/assets` (#15219)
* Use level config in log section when sub log section not set level (#15176)
* Links in markdown should be absolute to the repository not the server (#15088)
+ * Upgrade to the latest version of golang-jwt (#16590) (#16606)
+ * Set minimum supported version of go to 1.16 (#16710)
* SECURITY
* Encrypt LDAP bind password in db with SECRET_KEY (#15547)
* Remove random password in Dockerfiles (#15362)
+ * Upgrade to the latest version of golang-jwt and increase minimum go to 1.15 (#16590) (#16606)
+ * Correctly create of git-daemon-export-ok files (#16508) (#16514)
+ * Don't show private user's repo in explore view (#16550) (#16554)
+ * Update node tar dependency to 6.1.6 (#16622) (#16623)
* FEATURES
* Update Go-Git to take advantage of LargeObjectThreshold (#16316)
* Support custom mime type mapping for text files (#16304)
@@ -42,7 +250,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Add LFS Migration and Mirror (#14726)
* Improve notifications for WIP draft PR's (#14663)
* Disable Stars config option (#14653)
- * Add option to provide signature for a token to verify key ownership (#14054)
+ * GPG Key Ownership verification with Signed Token (#14054)
* OAuth2 auto-register (#5123)
* API
* Return updated repository when changing repository using API (#16420)
@@ -62,6 +270,8 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Add Active and ProhibitLogin to API (#15689)
* Add Location, Website and Description to API (#15675)
* Expose resolver via API (#15167)
+ * Swagger AccessToken fixes (#16574) (#16597)
+ * Set AllowedHeaders on API CORS handler (#16524) (#16618)
* ENHANCEMENTS
* Support HTTP/2 in Let's Encrypt (#16371)
* Introduce NotifySubjectType (#16320)
@@ -187,6 +397,41 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Add NeedPostProcess for Parser interface to improve performance of csv parser and some external parser (#15153)
* Add code block highlight to orgmode back (#14222)
* Remove User.GetOrganizations() (#14032)
+ * Restore Accessibility for Dropdown (#16576) (#16617)
+ * Pass down SignedUserName down to AccessLogger context (#16605) (#16616)
+ * Fix table alignment in markdown (#16596) (#16602)
+ * Fix 500 on first wiki page (#16586) (#16598)
+ * Lock goth/gothic and Re-attempt OAuth2 registration on login if registration failed at startup (#16564) (#16570)
+ * Upgrade levelqueue to v0.4.0 (#16560) (#16561)
+ * Handle too long PR titles correctly (#16517) (#16549)
+ * Fix data race in bleve indexer (#16474) (#16509)
+ * Restore CORS on git smart http protocol (#16496) (#16506)
+ * Fix race in log (#16490) (#16505)
+ * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498)
+ * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480)
+ * Update notification table with only latest data (#16445) (#16469)
+ * Fix crash following ldap authentication update (#16447) (#16448)
+ * Fix direct creation of external users on admin page (partial #16612) (#16613)
+ * Prevent 500 on draft releases without tag (#16634) (#16636)
+ * Restore creation of git-daemon-export-ok files (#16508) (#16514)
+ * Fix data race in bleve indexer (#16474) (#16509)
+ * Restore CORS on git smart http protocol (#16496) (#16506)
+ * Fix race in log (#16490) (#16505)
+ * Fix prepareWikiFileName to respect existing unescaped files (#16487) (#16498)
+ * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16480)
+ * Update notification table with only latest data (#16445) (#16469)
+ * Fix crash following ldap authentication update (#16447) (#16448)
+ * Restore compatibility with SQLServer 2008 R2 in migrations (#16638)
+ * Fix direct creation of external users on admin page (#16613)
+ * Fix go-git implementation of GetNote when passed a non-existent commit (#16658) (#16659)
+ * Fix NPE in fuzzer (#16680) (#16682)
+ * Set issue_index when finishing migration (#16685) (#16687)
+ * Skip patch download when no patch file exists (#16356) (#16681)
+ * Ensure empty lines are copiable and final new line too (#16678) (#16692)
+ * Fix wrong user in OpenID response (#16736) (#16741)
+ * Do not use thin scrollbars on Firefox (#16738) (#16745)
+ * Recreate Tables should Recreate indexes on MySQL (#16718) (#16739)
+ * Keep attachments on tasklist update (#16750) (#16757)
* TESTING
* Bump `postgres` and `mysql` versions (#15710)
* Add tests for clone from wiki (#15513)
@@ -197,7 +442,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Fix mirror_lfs source string in en-US locale (#15369)
* BUILD
* Upgrade xorm to v1.1.1 (#16339)
- * Alpine 3.14 released (#16170)
* Disable legal comments in esbuild (#15929)
* Switch to Node 16 to build fronted (#15804)
* Use esbuild to minify CSS (#15756)
@@ -216,6 +460,28 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
* Remove utf8 option from installation page (#16126)
* Use Wants= over Requires= in systemd file (#15897)
+## [1.14.6](https://github.com/go-gitea/gitea/releases/tag/v1.14.6) - 2021-08-04
+
+* SECURITY
+ * Bump github.com/markbates/goth from v1.67.1 to v1.68.0 (#16538) (#16540)
+ * Switch to maintained JWT lib (#16532) (#16535)
+ * Upgrade to latest version of golang-jwt (as forked for 1.14) (#16590) (#16607)
+* BUGFIXES
+ * Add basic edit ldap auth test & actually fix #16252 (#16465) (#16495)
+ * Make cancel from CatFileBatch and CatFileBatchCheck wait for the command to end (#16479) (#16481)
+
+## [1.14.5](https://github.com/go-gitea/gitea/releases/tag/v1.14.5) - 2021-07-16
+
+* SECURITY
+ * Hide mirror passwords on repo settings page (#16022) (#16355)
+ * Update bluemonday to v1.0.15 (#16379) (#16380)
+* BUGFIXES
+ * Retry rename on lock induced failures (#16435) (#16439)
+ * Validate issue index before querying DB (#16406) (#16410)
+ * Fix crash following ldap authentication update (#16447) (#16449)
+* ENHANCEMENTS
+ * Redirect on bad CSRF instead of presenting bad page (#14937) (#16378)
+
## [1.14.4](https://github.com/go-gitea/gitea/releases/tag/v1.14.4) - 2021-07-06
* BUGFIXES
diff --git a/Dockerfile b/Dockerfile
index 9960e5b1e3..bd9ba51678 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
###################################
#Build stage
-FROM golang:1.16-alpine3.14 AS build-env
+FROM techknowlogick/go:1.16-alpine3.13 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
@@ -25,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
# Begin env-to-ini build
RUN go build contrib/environment-to-ini/environment-to-ini.go
-FROM alpine:3.14
+FROM alpine:3.13
LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000
diff --git a/Dockerfile.rootless b/Dockerfile.rootless
index d3ba15f109..950d3e2bb0 100644
--- a/Dockerfile.rootless
+++ b/Dockerfile.rootless
@@ -1,7 +1,7 @@
###################################
#Build stage
-FROM golang:1.16-alpine3.14 AS build-env
+FROM techknowlogick/go:1.16-alpine3.13 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
@@ -25,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
# Begin env-to-ini build
RUN go build contrib/environment-to-ini/environment-to-ini.go
-FROM alpine:3.14
+FROM alpine:3.13
LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000
diff --git a/Makefile b/Makefile
index c07dfbf475..bf3d93d3c8 100644
--- a/Makefile
+++ b/Makefile
@@ -25,7 +25,7 @@ HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
COMMA := ,
XGO_VERSION := go-1.16.x
-MIN_GO_VERSION := 001014000
+MIN_GO_VERSION := 001016000
MIN_NODE_VERSION := 012017000
DOCKER_IMAGE ?= gitea/gitea
@@ -200,7 +200,7 @@ help:
go-check:
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
- echo "Gitea requires Go 1.14 or greater to build. You can get it at https://golang.org/dl/"; \
+ echo "Gitea requires Go 1.16 or greater to build. You can get it at https://golang.org/dl/"; \
exit 1; \
fi
@@ -699,6 +699,7 @@ fomantic:
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
+ cp -f web_src/js/vendor/dropdown.js $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/definitions/modules
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
.PHONY: webpack
diff --git a/README.md b/README.md
index 8d8cf09f44..b2a147471e 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,6 @@
-
-
-
diff --git a/build.go b/build.go
index b843465dca..aa56141340 100644
--- a/build.go
+++ b/build.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-//+build vendor
+//go:build vendor
+// +build vendor
package main
diff --git a/build/generate-bindata.go b/build/generate-bindata.go
index fa1669fcf9..efd172f779 100644
--- a/build/generate-bindata.go
+++ b/build/generate-bindata.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
diff --git a/build/generate-emoji.go b/build/generate-emoji.go
index 53c5285b23..f8e6498840 100644
--- a/build/generate-emoji.go
+++ b/build/generate-emoji.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
diff --git a/build/generate-gitignores.go b/build/generate-gitignores.go
index 846bb07636..d0b972e803 100644
--- a/build/generate-gitignores.go
+++ b/build/generate-gitignores.go
@@ -1,3 +1,4 @@
+//go:build ignore
// +build ignore
package main
diff --git a/build/generate-licenses.go b/build/generate-licenses.go
index 9dd13adf9a..4009a0351d 100644
--- a/build/generate-licenses.go
+++ b/build/generate-licenses.go
@@ -1,3 +1,4 @@
+//go:build ignore
// +build ignore
package main
diff --git a/build/generate-svg.js b/build/generate-svg.js
index 72c3be3cbd..cde3f31106 100755
--- a/build/generate-svg.js
+++ b/build/generate-svg.js
@@ -29,6 +29,7 @@ async function processFile(file, {prefix, fullName} = {}) {
plugins: extendDefaultPlugins([
'removeXMLNS',
'removeDimensions',
+ {name: 'prefixIds', params: {prefix: () => name}},
{
name: 'addClassesToSVGElement',
params: {classNames: ['svg', name]},
diff --git a/build/gocovmerge.go b/build/gocovmerge.go
index 65d6f2cd6c..b38cf5ea3d 100644
--- a/build/gocovmerge.go
+++ b/build/gocovmerge.go
@@ -6,6 +6,7 @@
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
// merges them into one profile
+//go:build ignore
// +build ignore
package main
diff --git a/cmd/admin.go b/cmd/admin.go
index f58a1f9960..674c5792c4 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -335,6 +335,10 @@ func runChangePassword(c *cli.Context) error {
if err := initDB(); err != nil {
return err
}
+ if len(c.String("password")) < setting.MinPasswordLength {
+ return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
+ }
+
if !pwd.IsComplexEnough(c.String("password")) {
return errors.New("Password does not meet complexity requirements")
}
diff --git a/cmd/docs.go b/cmd/docs.go
index 52233c7ac8..073c574973 100644
--- a/cmd/docs.go
+++ b/cmd/docs.go
@@ -43,7 +43,11 @@ func runDocs(ctx *cli.Context) error {
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
// It affects markdown output (even though the issue is referring to man pages)
// https://github.com/urfave/cli/issues/1040
- docs = docs[strings.Index(docs, "#"):]
+ firstHashtagIndex := strings.Index(docs, "#")
+
+ if firstHashtagIndex > 0 {
+ docs = docs[firstHashtagIndex:]
+ }
}
out := os.Stdout
diff --git a/cmd/doctor.go b/cmd/doctor.go
index 0152aebe39..2e79275434 100644
--- a/cmd/doctor.go
+++ b/cmd/doctor.go
@@ -124,7 +124,6 @@ func runRecreateTable(ctx *cli.Context) error {
}
func runDoctor(ctx *cli.Context) error {
-
// Silence the default loggers
log.DelNamedLogger("console")
log.DelNamedLogger(log.DEFAULT)
diff --git a/cmd/dump.go b/cmd/dump.go
index 629993b6c8..0f60872826 100644
--- a/cmd/dump.go
+++ b/cmd/dump.go
@@ -87,7 +87,7 @@ func (o outputType) String() string {
}
var outputTypeEnum = &outputType{
- Enum: []string{"zip", "tar", "tar.gz", "tar.xz", "tar.bz2"},
+ Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
Default: "zip",
}
@@ -153,12 +153,16 @@ func fatal(format string, args ...interface{}) {
func runDump(ctx *cli.Context) error {
var file *os.File
fileName := ctx.String("file")
+ outType := ctx.String("type")
if fileName == "-" {
file = os.Stdout
err := log.DelLogger("console")
if err != nil {
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
}
+ } else {
+ fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
+ fileName += "." + outType
}
setting.NewContext()
// make sure we are logging to the console no matter what the configuration tells us do to
@@ -197,7 +201,6 @@ func runDump(ctx *cli.Context) error {
}
verbose := ctx.Bool("verbose")
- outType := ctx.String("type")
var iface interface{}
if fileName == "-" {
iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
diff --git a/cmd/embedded.go b/cmd/embedded.go
index 528f32402e..2aeaba4786 100644
--- a/cmd/embedded.go
+++ b/cmd/embedded.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build bindata
// +build bindata
package cmd
diff --git a/cmd/embedded_stub.go b/cmd/embedded_stub.go
index 1f9af7b86b..0e9e3e6ec3 100644
--- a/cmd/embedded_stub.go
+++ b/cmd/embedded_stub.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !bindata
// +build !bindata
package cmd
diff --git a/cmd/serv.go b/cmd/serv.go
index 97ae901d27..3b180fc626 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -23,7 +23,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/lfs"
- "github.com/dgrijalva/jwt-go"
+ "github.com/golang-jwt/jwt"
jsoniter "github.com/json-iterator/go"
"github.com/kballard/go-shellquote"
"github.com/urfave/cli"
diff --git a/cmd/web.go b/cmd/web.go
index 6953e7c64f..8d9387e06f 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -86,6 +86,11 @@ func runWeb(ctx *cli.Context) error {
_ = log.DelLogger("console")
log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "fatal", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
}
+ defer func() {
+ if panicked := recover(); panicked != nil {
+ log.Fatal("PANIC: %v\n%s", panicked, string(log.Stack(2)))
+ }
+ }()
managerCtx, cancel := context.WithCancel(context.Background())
graceful.InitManager(managerCtx)
@@ -189,6 +194,10 @@ func listen(m http.Handler, handleRedirector bool) error {
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
}
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
+ // This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy.
+ // A user may fix the configuration mistake when he sees this log.
+ // And this is also very helpful to maintainers to provide help to users to resolve their configuration problems.
+ log.Info("AppURL(ROOT_URL): %s", setting.AppURL)
if setting.LFS.StartServer {
log.Info("LFS server enabled")
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 251ec7a80e..0dfae105f9 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -576,6 +576,8 @@ PATH =
;;
;; (Go-Git only) Don't cache objects greater than this in memory. (Set to 0 to disable.)
;LARGE_OBJECT_THRESHOLD = 1048576
+;; Set to true to forcibly set core.protectNTFS=false
+;DISABLE_CORE_PROTECT_NTFS=false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1386,6 +1388,13 @@ PATH =
;; Deliver timeout in seconds
;DELIVER_TIMEOUT = 5
;;
+;; Webhook can only call allowed hosts for security reasons. Comma separated list, eg: external, 192.168.1.0/24, *.mydomain.com
+;; Built-in: loopback (for localhost), private (for LAN/intranet), external (for public hosts on internet), * (for all hosts)
+;; CIDR list: 1.2.3.0/8, 2001:db8::/32
+;; Wildcard hosts: *.mydomain.com, 192.168.100.*
+;; Default to * for 1.15.x, external for 1.16 and later
+;ALLOWED_HOST_LIST = *
+;;
;; Allow insecure certification
;SKIP_TLS_VERIFY = false
;;
diff --git a/docker/manifest.rootless.tmpl b/docker/manifest.rootless.tmpl
index 1d14041ff2..9559416470 100644
--- a/docker/manifest.rootless.tmpl
+++ b/docker/manifest.rootless.tmpl
@@ -1,4 +1,4 @@
-image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}dev{{/if}}-rootless
+image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-rootless
{{#if build.tags}}
tags:
{{#each build.tags}}
@@ -8,12 +8,12 @@ tags:
{{/if}}
manifests:
-
- image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}dev{{/if}}-linux-amd64-rootless
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-amd64-rootless
platform:
architecture: amd64
os: linux
-
- image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}dev{{/if}}-linux-arm64-rootless
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-arm64-rootless
platform:
architecture: arm64
os: linux
diff --git a/docker/manifest.tmpl b/docker/manifest.tmpl
index 43a57f7f27..8ca3d5be1d 100644
--- a/docker/manifest.tmpl
+++ b/docker/manifest.tmpl
@@ -1,20 +1,19 @@
-image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}dev{{/if}}
+image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
- - "latest"
{{/if}}
manifests:
-
- image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{else}}dev-{{/if}}linux-amd64
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-amd64
platform:
architecture: amd64
os: linux
-
- image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{else}}dev-{{/if}}linux-arm64
+ image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-arm64
platform:
architecture: arm64
os: linux
- variant: v8
\ No newline at end of file
+ variant: v8
diff --git a/docs/config.yaml b/docs/config.yaml
index 451984e09d..ec243e20d4 100644
--- a/docs/config.yaml
+++ b/docs/config.yaml
@@ -18,9 +18,9 @@ params:
description: Git with a cup of tea
author: The Gitea Authors
website: https://docs.gitea.io
- version: 1.14.4
- minGoVersion: 1.14
- goVersion: 1.16
+ version: 1.15.8
+ minGoVersion: 1.16
+ goVersion: 1.17
minNodeVersion: 12.17
outputs:
diff --git a/docs/content/doc/advanced/adding-legal-pages.en-us.md b/docs/content/doc/advanced/adding-legal-pages.en-us.md
index b13337a4ab..8535a6e865 100644
--- a/docs/content/doc/advanced/adding-legal-pages.en-us.md
+++ b/docs/content/doc/advanced/adding-legal-pages.en-us.md
@@ -32,7 +32,7 @@ You absolutely must not place a general ToS or privacy statement that implies th
Create or append to `/path/to/custom/templates/custom/extra_links_footer.tmpl`:
```go
-Privacy Policy
+Privacy Policy
```
Restart Gitea to see the changes.
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 611a7a887a..a611ab0cd3 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -545,6 +545,14 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
- `QUEUE_LENGTH`: **1000**: Hook task queue length. Use caution when editing this value.
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
+- `ALLOWED_HOST_LIST`: `*`: Default to `*` for 1.15.x, `external` for 1.16 and later. Webhook can only call allowed hosts for security reasons. Comma separated list.
+ - Built-in networks:
+ - `loopback`: 127.0.0.0/8 for IPv4 and ::1/128 for IPv6, localhost is included.
+ - `private`: RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and RFC 4193 (FC00::/7). Also called LAN/Intranet.
+ - `external`: A valid non-private unicast IP, you can access all hosts on public internet.
+ - `*`: All hosts are allowed.
+ - CIDR list: `1.2.3.0/8` for IPv4 and `2001:db8::/32` for IPv6
+ - Wildcard hosts: `*.mydomain.com`, `192.168.100.*`
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
- `PROXY_URL`: ****: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
@@ -839,6 +847,7 @@ NB: You must have `DISABLE_ROUTER_LOG` set to `false` for this option to take ef
- `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.
- `LARGE_OBJECT_THRESHOLD`: **1048576**: (Go-Git only), don't cache objects greater than this in memory. (Set to 0 to disable.)
+- `DISABLE_CORE_PROTECT_NTFS`: **false** Set to true to forcibly set `core.protectNTFS` to false.
## Git - Timeout settings (`git.timeout`)
- `DEFAUlT`: **360**: Git operations default timeout seconds.
- `MIGRATE`: **600**: Migrate external repositories timeout seconds.
diff --git a/docs/content/doc/advanced/customizing-gitea.en-us.md b/docs/content/doc/advanced/customizing-gitea.en-us.md
index cbb749412f..8ea7a8d129 100644
--- a/docs/content/doc/advanced/customizing-gitea.en-us.md
+++ b/docs/content/doc/advanced/customizing-gitea.en-us.md
@@ -102,7 +102,7 @@ For instance, let's say you are in Germany and must add the famously legally-req
just place it under your "$GITEA_CUSTOM/public/" directory (for instance `$GITEA_CUSTOM/public/impressum.html`) and put a link to it in either `$GITEA_CUSTOM/templates/custom/extra_links.tmpl` or `$GITEA_CUSTOM/templates/custom/extra_links_footer.tmpl`.
To match the current style, the link should have the class name "item", and you can use `{{AppSubUrl}}` to get the base URL:
-`Impressum`
+`Impressum`
For more information, see [Adding Legal Pages](https://docs.gitea.io/en-us/adding-legal-pages).
@@ -174,13 +174,13 @@ You can display STL file directly in Gitea by adding:
if ($('.view-raw>a[href$=".stl" i]').length) {
$("body").append(
- ''
+ ''
);
Promise.all([
- lS("/Madeleine.js/src/lib/stats.js"),
- lS("/Madeleine.js/src/lib/detector.js"),
- lS("/Madeleine.js/src/lib/three.min.js"),
- lS("/Madeleine.js/src/Madeleine.js"),
+ lS("/assets/Madeleine.js/src/lib/stats.js"),
+ lS("/assets/Madeleine.js/src/lib/detector.js"),
+ lS("/assets/Madeleine.js/src/lib/three.min.js"),
+ lS("/assets/Madeleine.js/src/Madeleine.js"),
]).then(function () {
$(".view-raw")
.attr("id", "view-raw")
@@ -188,7 +188,7 @@ You can display STL file directly in Gitea by adding:
new Madeleine({
target: "view-raw",
data: $('.view-raw>a[href$=".stl" i]').attr("href"),
- path: "/Madeleine.js/src",
+ path: "/assets/Madeleine.js/src",
});
$('.view-raw>a[href$=".stl"]').remove();
});
diff --git a/docs/content/doc/advanced/customizing-gitea.zh-cn.md b/docs/content/doc/advanced/customizing-gitea.zh-cn.md
index 4640f878ad..bb55f35e17 100644
--- a/docs/content/doc/advanced/customizing-gitea.zh-cn.md
+++ b/docs/content/doc/advanced/customizing-gitea.zh-cn.md
@@ -61,7 +61,7 @@ Gitea 引用 `custom` 目录中的自定义配置文件来覆盖配置、模板
"custom/public/"目录下(比如 `custom/public/impressum.html`)并且将它与 `custom/templates/custom/extra_links.tmpl` 链接起来即可。
这个链接应当使用一个名为“item”的 class 来匹配当前样式,您可以使用 `{{AppSubUrl}}` 来获取 base URL:
-`Impressum`
+`Impressum`
同理,您可以将页签添加到 `extra_tabs.tmpl` 中,使用同样的方式来添加页签。它的具体样式需要与
`templates/repo/header.tmpl` 中已有的其他选项卡的样式匹配
diff --git a/docs/content/doc/advanced/external-renderers.en-us.md b/docs/content/doc/advanced/external-renderers.en-us.md
index c0109b8014..e5de7e8efd 100644
--- a/docs/content/doc/advanced/external-renderers.en-us.md
+++ b/docs/content/doc/advanced/external-renderers.en-us.md
@@ -164,5 +164,5 @@ And so you could write some CSS:
Add your stylesheet to your custom directory e.g `custom/public/css/my-style-XXXXX.css` and import it using a custom header file `custom/templates/custom/header.tmpl`:
```html
-
+
```
diff --git a/go.mod b/go.mod
index 5032acce99..ca1c64d119 100644
--- a/go.mod
+++ b/go.mod
@@ -9,8 +9,8 @@ require (
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e
- gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee
- gitea.com/lunny/levelqueue v0.3.0
+ gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
+ gitea.com/lunny/levelqueue v0.4.1
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/NYTimes/gziphandler v1.1.1
github.com/ProtonMail/go-crypto v0.0.0-20210705153151-cc34b1f6908b // indirect
@@ -22,13 +22,12 @@ require (
github.com/blevesearch/bleve/v2 v2.0.6
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
- github.com/caddyserver/certmagic v0.14.0
+ github.com/caddyserver/certmagic v0.14.1
github.com/chi-middleware/proxy v1.1.1
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
github.com/couchbase/gomemcached v0.1.2 // indirect
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
github.com/denisenkom/go-mssqldb v0.10.0
- github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/djherbis/buffer v1.2.0
github.com/djherbis/nio/v3 v3.0.1
github.com/dustin/go-humanize v1.0.0
@@ -51,6 +50,7 @@ require (
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
+ github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-github/v32 v32.1.0
github.com/google/go-querystring v1.1.0 // indirect
@@ -75,12 +75,12 @@ require (
github.com/lafriks/xormstore v1.4.0
github.com/lib/pq v1.10.2
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
- github.com/markbates/goth v1.67.1
+ github.com/markbates/goth v1.68.0
github.com/mattn/go-isatty v0.0.13
github.com/mattn/go-runewidth v0.0.13 // indirect
- github.com/mattn/go-sqlite3 v1.14.7
+ github.com/mattn/go-sqlite3 v1.14.8
github.com/mholt/archiver/v3 v3.5.0
- github.com/microcosm-cc/bluemonday v1.0.15
+ github.com/microcosm-cc/bluemonday v1.0.16
github.com/miekg/dns v1.1.43 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.12
@@ -125,10 +125,10 @@ require (
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.18.1 // indirect
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
- golang.org/x/net v0.0.0-20210614182718-04defd469f4e
+ golang.org/x/net v0.0.0-20211020060615-d418f374d309
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
- golang.org/x/text v0.3.6
+ golang.org/x/text v0.3.7
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
golang.org/x/tools v0.1.0
google.golang.org/protobuf v1.27.1 // indirect
@@ -139,7 +139,9 @@ require (
mvdan.cc/xurls/v2 v2.2.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.9
- xorm.io/xorm v1.1.2
+ xorm.io/xorm v1.2.5
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
+
+replace github.com/golang-jwt/jwt v3.2.1+incompatible => github.com/golang-jwt/jwt v3.2.2+incompatible
diff --git a/go.sum b/go.sum
index fc6e2422c8..075dc6aa37 100644
--- a/go.sum
+++ b/go.sum
@@ -47,10 +47,10 @@ gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e h1:zgPGaf3kXP0cVm9J0l8
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e h1:YjaQU6XFicdhPN+MlGolcXO8seYY2+EY5g7vZPB17CQ=
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e/go.mod h1:nfA7JaGv3hbGQ1ktdhAsZhdS84qKffI8NMlHr+Opsog=
-gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee h1:9U6HuKUBt/cGK6T/64dEuz0r7Yp97WAAEJvXHDlY3ws=
-gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0=
-gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I=
-gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
+gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q=
+gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck=
+gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw=
+gitea.com/lunny/levelqueue v0.4.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
@@ -60,6 +60,9 @@ github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzS
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
+github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
@@ -84,8 +87,12 @@ github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06
github.com/RoaringBitmap/roaring v0.7.3/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I=
github.com/RoaringBitmap/roaring v0.9.1 h1:5PRizBmoN/PfV17nPNQou4dHQ7NcJi8FO/bihdYyCEM=
github.com/RoaringBitmap/roaring v0.9.1/go.mod h1:h1B7iIUOmnAeb5ytYMvnHJwxMc6LUrwBnzXWRuqTQUc=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
@@ -114,12 +121,15 @@ github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxB
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
@@ -127,8 +137,11 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:o
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go v1.38.17/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
@@ -184,8 +197,10 @@ github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
-github.com/caddyserver/certmagic v0.14.0 h1:XW1o32s7smIYEJSc6g+N8YXljpjRo5ZE2zi3CIYTs74=
-github.com/caddyserver/certmagic v0.14.0/go.mod h1:oRQOZmUVKwlpgNidslysHt05osM9uMrJ4YMk+Ot4P4Q=
+github.com/caddyserver/certmagic v0.14.1 h1:8RIFS/LbGne/I7Op56Kkm2annnei7io9VW/IWDttE9U=
+github.com/caddyserver/certmagic v0.14.1/go.mod h1:oRQOZmUVKwlpgNidslysHt05osM9uMrJ4YMk+Ot4P4Q=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -196,12 +211,15 @@ github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdi
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -209,8 +227,10 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
@@ -238,10 +258,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
-github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8=
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@@ -260,15 +278,20 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/editorconfig/editorconfig-core-go/v2 v2.4.2 h1:1lkDpSoAaFLrgYTVJ/eNCV+lkDSv/j9Wm0jcvDfVVEo=
github.com/editorconfig/editorconfig-core-go/v2 v2.4.2/go.mod h1:IXeWRVO4LZRoNunhHh/oP6BQvTs94nB2pNvbw32l8tQ=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -283,6 +306,8 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -300,8 +325,9 @@ github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
-github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-chi/chi/v5 v5.0.4 h1:5e494iHzsYBiyXQAHHuI4tyJS9M3V84OuX3ufIIGHFo=
+github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-enry/go-enry/v2 v2.7.1 h1:WCqtfyteIz61GYk9lRVy8HblvIv4cP9GIiwm/6txCbU=
@@ -322,6 +348,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
@@ -428,6 +455,7 @@ github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w
github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
github.com/go-redis/redis/v8 v8.11.0 h1:O1Td0mQ8UFChQ3N9zFQqo6kTU2cJ+/it88gDB+zg0wo=
github.com/go-redis/redis/v8 v8.11.0/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
@@ -466,9 +494,14 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
+github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k=
+github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 h1:gBeyun7mySAKWg7Fb0GOcv0upX9bdaZScs8QcRo8mEY=
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
@@ -476,9 +509,12 @@ github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQ
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -567,6 +603,7 @@ github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
@@ -577,12 +614,17 @@ github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
@@ -614,11 +656,13 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/issue9/assert v1.4.1 h1:gUtOpMTeaE4JTe9kACma5foOHBvVt1p5XTFrULDwdXI=
github.com/issue9/assert v1.4.1/go.mod h1:Yktk83hAVl1SPSYtd9kjhBizuiBIqUQyj+D5SE2yjVY=
github.com/issue9/identicon v1.2.0 h1:ek+UcTTyMW/G0iNbLOAlrPC13eSzXTWhbJSs8PHhHGQ=
@@ -631,12 +675,18 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
-github.com/jackc/pgconn v1.5.0 h1:oFSOilzIZkyg787M1fEmyMfOUUvwj0daqYMfaWwNL4o=
+github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
+github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
+github.com/jackc/pgconn v1.9.0 h1:gqibKSTJup/ahCsNKyMZAniPuZEfIqfXFc8FOWVYR+Q=
+github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
-github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
+github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd h1:eDErF6V/JPJON/B7s68BxwHgfmyOntHJQ8IOaz0x4R8=
+github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
@@ -645,25 +695,40 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
-github.com/jackc/pgproto3/v2 v2.0.1 h1:Rdjp4NFjwHnEslx2b66FfCI2S0LhO4itac3hXz6WX9M=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU=
+github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
+github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
-github.com/jackc/pgtype v1.3.0 h1:l8JvKrby3RI7Kg3bYEeU9TA4vqC38QDpFCfcrC7KuN0=
+github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik=
+github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
+github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
+github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
+github.com/jackc/pgtype v1.8.0 h1:iFVCcVhYlw0PulYCVoguRGm0SE9guIcPcccnLzHj8bA=
+github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
-github.com/jackc/pgx/v4 v4.6.0 h1:Fh0O9GdlG4gYpjpwOqjdEodJUQM9jzN3Hdv7PN0xmm0=
+github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg=
+github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
+github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
+github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
+github.com/jackc/pgx/v4 v4.12.0 h1:xiP3TdnkwyslWNp77yE5XAPfxAsU9RMFDe0c1SwN8h4=
+github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
@@ -671,6 +736,7 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
@@ -680,6 +746,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -740,15 +808,19 @@ github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/Y
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
@@ -762,8 +834,8 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
-github.com/markbates/goth v1.67.1 h1:gU5B0pzHVyhnJPwGynfFnkfvaQ39C1Sy+ewdl+bhAOw=
-github.com/markbates/goth v1.67.1/go.mod h1:EyLFHGU5ySr2GXRDyJH5nu2dA7parbC8QwIYW/rGcWg=
+github.com/markbates/goth v1.68.0 h1:90sKvjRAKHcl9V2uC9x/PJXeD78cFPiBsyP1xVhoQfA=
+github.com/markbates/goth v1.68.0/go.mod h1:V2VcDMzDiMHW+YmqYl7i0cMiAUeCkAe4QE6jRKBhXZw=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
@@ -781,22 +853,24 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
+github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk=
github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
-github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY=
-github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30=
+github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc=
+github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
@@ -840,6 +914,13 @@ github.com/msteinert/pam v0.0.0-20201130170657-e61372126161 h1:XQ1+fYPzaWZCVdu1x
github.com/msteinert/pam v0.0.0-20201130170657-e61372126161/go.mod h1:np1wUFZ6tyoke22qDJZY40URn9Ae51gX7ljIWXN5TJs=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -849,8 +930,11 @@ github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/oliamb/cutter v0.2.2 h1:Lfwkya0HHNU1YLnGv2hTkzHfasrSMkgv4Dn+5rmlk3k=
@@ -871,7 +955,17 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -880,7 +974,11 @@ github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAv
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.0 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0=
github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
+github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.0.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.8 h1:ieHkV+i2BRzngO4Wd/3HGowuZStgq6QkPsD1eolNAO4=
github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
@@ -888,6 +986,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -896,31 +995,40 @@ github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac/go.mod h1:hoL
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasoft/websspi v1.0.0 h1:5nDgdM5xSur9s+B5w2xQ5kxf5nUGqgFgU4W0aDLZ8Mw=
github.com/quasoft/websspi v1.0.0/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@@ -943,14 +1051,17 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
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/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
+github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -979,6 +1090,7 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
@@ -992,6 +1104,7 @@ github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSW
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -1004,6 +1117,9 @@ github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02n
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM=
github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -1021,6 +1137,7 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
@@ -1040,6 +1157,8 @@ github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54I
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae/go.mod h1:1fdkY6xxl6ExVs2QFv7R0F5IRZHKA8RahhB9fMC9RvM=
github.com/unrolled/render v1.4.0 h1:p73obhpsXuE3paXOtcuXTBKgBJpLCfmABnsUiO35x+Q=
github.com/unrolled/render v1.4.0/go.mod h1:cK4RSTTVdND5j9EYEc0LAMOvdG11JeiKjyjfyZRvV2w=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
@@ -1074,9 +1193,11 @@ github.com/yuin/goldmark-meta v1.0.0/go.mod h1:zsNNOrZ4nLuyHAJeLQEZcQat8dm70SmB2
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.jolheiser.com/hcaptcha v0.0.4 h1:RrDERcr/Tz/kWyJenjVtI+V09RtLinXxlAemiwN5F+I=
go.jolheiser.com/hcaptcha v0.0.4/go.mod h1:aw32WQOxnQZ6E06C0LypCf+sxNxPACyOnq+ZGnrIYho=
go.jolheiser.com/pwn v0.0.3 h1:MQowb3QvCL5r5NmHmCPxw93SdjfgJ0q6rAwYn4i1Hjg=
@@ -1090,6 +1211,8 @@ go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4S
go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
go.mongodb.org/mongo-driver v1.5.1 h1:9nOVLGDfOaZ9R0tBumx/BcuqkbFpyTCU2r/Po7A2azI=
go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1100,6 +1223,7 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.8.0 h1:CUhrE4N1rqSE6FM9ecihEjRkLQu8cDfgDyoOs83mEY4=
@@ -1107,6 +1231,7 @@ go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
@@ -1114,6 +1239,7 @@ go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95a
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4=
@@ -1132,13 +1258,16 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/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-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
@@ -1191,6 +1320,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1235,8 +1365,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI=
+golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1270,6 +1401,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1299,6 +1431,7 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1352,14 +1485,17 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs=
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1397,6 +1533,7 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@@ -1434,6 +1571,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1455,6 +1593,7 @@ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1468,6 +1607,7 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
@@ -1499,10 +1639,15 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -1539,8 +1684,10 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
@@ -1570,6 +1717,7 @@ gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1577,31 +1725,34 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0=
-modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
-modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA=
-modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY=
+lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
+lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
+modernc.org/cc/v3 v3.33.6 h1:r63dgSzVzRxUpAJFPQWHy1QeZeY1ydNENUDaBx1GqYc=
+modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/ccgo/v3 v3.9.5 h1:dEuUSf8WN51rDkprFuAqjfchKEzN0WttP/Py3enBwjk=
+modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
-modernc.org/libc v1.8.0 h1:Pp4uv9g0csgBMpGPABKtkieF6O5MGhfGo6ZiOdlYfR8=
-modernc.org/libc v1.8.0/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
+modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
+modernc.org/libc v1.9.11 h1:QUxZMs48Ahg2F7SN41aERvMfGLY2HU/ADnB9DC4Yts8=
+modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
-modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
+modernc.org/mathutil v1.4.0 h1:GCjoRaBew8ECCKINQA2nYjzvufFW9YiEuuB+rQ9bn2E=
+modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84 h1:rgEUzE849tFlHSoeCrKyS9cZAljC+DY7MdMHKq6R6sY=
-modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU=
-modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc=
-modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
-modernc.org/tcl v1.5.0 h1:euZSUNfE0Fd4W8VqXI1Ly1v7fqDJoBuAV88Ea+SnaSs=
-modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc=
+modernc.org/sqlite v1.11.2 h1:ShWQpeD3ag/bmx6TqidBlIWonWmQaSQKls3aenCbt+w=
+modernc.org/sqlite v1.11.2/go.mod h1:+mhs/P1ONd+6G7hcAs6irwDi/bjTQ7nLW6LHRBsEa3A=
+modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
+modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
+modernc.org/tcl v1.5.5 h1:N03RwthgTR/l/eQvz3UjfYnvVVj1G2sZqzFGfoD4HE4=
+modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
-modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc=
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A=
@@ -1609,12 +1760,13 @@ mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
-xorm.io/builder v0.3.8/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc=
xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.0.6/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
-xorm.io/xorm v1.1.2 h1:bje+1KZvK3m5AHtZNfUDlKEEyuw/IRHT+an0CLIG5TU=
-xorm.io/xorm v1.1.2/go.mod h1:Cb0DKYTHbyECMaSfgRnIZp5aiUgQozxcJJ0vzcLGJSg=
+xorm.io/xorm v1.2.5 h1:tqN7OhN8P9xi52qBb76I8m5maAJMz/SSbgK2RGPCPbo=
+xorm.io/xorm v1.2.5/go.mod h1:fTG8tSjk6O1BYxwuohZUK+S1glnRycsCF05L1qQyEU0=
diff --git a/integrations/api_private_serv_test.go b/integrations/api_private_serv_test.go
new file mode 100644
index 0000000000..8d814271cd
--- /dev/null
+++ b/integrations/api_private_serv_test.go
@@ -0,0 +1,154 @@
+// Copyright 2021 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 (
+ "context"
+ "net/url"
+ "testing"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/private"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAPIPrivateNoServ(t *testing.T) {
+ onGiteaRun(t, func(*testing.T, *url.URL) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ key, user, err := private.ServNoCommand(ctx, 1)
+ assert.NoError(t, err)
+ assert.Equal(t, int64(2), user.ID)
+ assert.Equal(t, "user2", user.Name)
+ assert.Equal(t, int64(1), key.ID)
+ assert.Equal(t, "user2@localhost", key.Name)
+
+ deployKey, err := models.AddDeployKey(1, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", false)
+ assert.NoError(t, err)
+
+ key, user, err = private.ServNoCommand(ctx, deployKey.KeyID)
+ assert.NoError(t, err)
+ assert.Empty(t, user)
+ assert.Equal(t, deployKey.KeyID, key.ID)
+ assert.Equal(t, "test-deploy", key.Name)
+ })
+}
+
+func TestAPIPrivateServ(t *testing.T) {
+ onGiteaRun(t, func(*testing.T, *url.URL) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ // Can push to a repo we own
+ results, err := private.ServCommand(ctx, 1, "user2", "repo1", models.AccessModeWrite, "git-upload-pack", "")
+ assert.NoError(t, err)
+ assert.False(t, results.IsWiki)
+ assert.False(t, results.IsDeployKey)
+ assert.Equal(t, int64(1), results.KeyID)
+ assert.Equal(t, "user2@localhost", results.KeyName)
+ assert.Equal(t, "user2", results.UserName)
+ assert.Equal(t, int64(2), results.UserID)
+ assert.Equal(t, "user2", results.OwnerName)
+ assert.Equal(t, "repo1", results.RepoName)
+ assert.Equal(t, int64(1), results.RepoID)
+
+ // Cannot push to a private repo we're not associated with
+ results, err = private.ServCommand(ctx, 1, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
+ assert.Error(t, err)
+ assert.Empty(t, results)
+
+ // Cannot pull from a private repo we're not associated with
+ results, err = private.ServCommand(ctx, 1, "user15", "big_test_private_1", models.AccessModeRead, "git-upload-pack", "")
+ assert.Error(t, err)
+ assert.Empty(t, results)
+
+ // Can pull from a public repo we're not associated with
+ results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", models.AccessModeRead, "git-upload-pack", "")
+ assert.NoError(t, err)
+ assert.False(t, results.IsWiki)
+ assert.False(t, results.IsDeployKey)
+ assert.Equal(t, int64(1), results.KeyID)
+ assert.Equal(t, "user2@localhost", results.KeyName)
+ assert.Equal(t, "user2", results.UserName)
+ assert.Equal(t, int64(2), results.UserID)
+ assert.Equal(t, "user15", results.OwnerName)
+ assert.Equal(t, "big_test_public_1", results.RepoName)
+ assert.Equal(t, int64(17), results.RepoID)
+
+ // Cannot push to a public repo we're not associated with
+ results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", models.AccessModeWrite, "git-upload-pack", "")
+ assert.Error(t, err)
+ assert.Empty(t, results)
+
+ // Add reading deploy key
+ deployKey, err := models.AddDeployKey(19, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", true)
+ assert.NoError(t, err)
+
+ // Can pull from repo we're a deploy key for
+ results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeRead, "git-upload-pack", "")
+ assert.NoError(t, err)
+ assert.False(t, results.IsWiki)
+ assert.True(t, results.IsDeployKey)
+ assert.Equal(t, deployKey.KeyID, results.KeyID)
+ assert.Equal(t, "test-deploy", results.KeyName)
+ assert.Equal(t, "user15", results.UserName)
+ assert.Equal(t, int64(15), results.UserID)
+ assert.Equal(t, "user15", results.OwnerName)
+ assert.Equal(t, "big_test_private_1", results.RepoName)
+ assert.Equal(t, int64(19), results.RepoID)
+
+ // Cannot push to a private repo with reading key
+ results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
+ assert.Error(t, err)
+ assert.Empty(t, results)
+
+ // Cannot pull from a private repo we're not associated with
+ results, err = private.ServCommand(ctx, deployKey.ID, "user15", "big_test_private_2", models.AccessModeRead, "git-upload-pack", "")
+ assert.Error(t, err)
+ assert.Empty(t, results)
+
+ // Cannot pull from a public repo we're not associated with
+ results, err = private.ServCommand(ctx, deployKey.ID, "user15", "big_test_public_1", models.AccessModeRead, "git-upload-pack", "")
+ assert.Error(t, err)
+ assert.Empty(t, results)
+
+ // Add writing deploy key
+ deployKey, err = models.AddDeployKey(20, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", false)
+ assert.NoError(t, err)
+
+ // Cannot push to a private repo with reading key
+ results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
+ assert.Error(t, err)
+ assert.Empty(t, results)
+
+ // Can pull from repo we're a writing deploy key for
+ results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", models.AccessModeRead, "git-upload-pack", "")
+ assert.NoError(t, err)
+ assert.False(t, results.IsWiki)
+ assert.True(t, results.IsDeployKey)
+ assert.Equal(t, deployKey.KeyID, results.KeyID)
+ assert.Equal(t, "test-deploy", results.KeyName)
+ assert.Equal(t, "user15", results.UserName)
+ assert.Equal(t, int64(15), results.UserID)
+ assert.Equal(t, "user15", results.OwnerName)
+ assert.Equal(t, "big_test_private_2", results.RepoName)
+ assert.Equal(t, int64(20), results.RepoID)
+
+ // Can push to repo we're a writing deploy key for
+ results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", models.AccessModeWrite, "git-upload-pack", "")
+ assert.NoError(t, err)
+ assert.False(t, results.IsWiki)
+ assert.True(t, results.IsDeployKey)
+ assert.Equal(t, deployKey.KeyID, results.KeyID)
+ assert.Equal(t, "test-deploy", results.KeyName)
+ assert.Equal(t, "user15", results.UserName)
+ assert.Equal(t, int64(15), results.UserID)
+ assert.Equal(t, "user15", results.OwnerName)
+ assert.Equal(t, "big_test_private_2", results.RepoName)
+ assert.Equal(t, int64(20), results.RepoID)
+
+ })
+
+}
diff --git a/integrations/api_repo_archive_test.go b/integrations/api_repo_archive_test.go
new file mode 100644
index 0000000000..4828c4aae1
--- /dev/null
+++ b/integrations/api_repo_archive_test.go
@@ -0,0 +1,44 @@
+// Copyright 2021 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"
+ "io"
+ "net/http"
+ "net/url"
+ "testing"
+
+ "code.gitea.io/gitea/models"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAPIDownloadArchive(t *testing.T) {
+ defer prepareTestEnv(t)()
+
+ repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
+ user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+ session := loginUser(t, user2.LowerName)
+ token := getTokenForLoggedInUser(t, session)
+
+ link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.zip", user2.Name, repo.Name))
+ link.RawQuery = url.Values{"token": {token}}.Encode()
+ resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
+ bs, err := io.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 320, len(bs))
+
+ link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.tar.gz", user2.Name, repo.Name))
+ link.RawQuery = url.Values{"token": {token}}.Encode()
+ resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
+ bs, err = io.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 266, len(bs))
+
+ link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master", user2.Name, repo.Name))
+ link.RawQuery = url.Values{"token": {token}}.Encode()
+ MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusBadRequest)
+}
diff --git a/integrations/api_repo_lfs_test.go b/integrations/api_repo_lfs_test.go
index 9e1e2b0418..49350c428a 100644
--- a/integrations/api_repo_lfs_test.go
+++ b/integrations/api_repo_lfs_test.go
@@ -254,6 +254,10 @@ func TestAPILFSBatch(t *testing.T) {
assert.NoError(t, err)
assert.True(t, exist)
+ repo2 := createLFSTestRepository(t, "batch2")
+ content := []byte("dummy0")
+ storeObjectInRepo(t, repo2.ID, &content)
+
meta, err := repo.GetLFSMetaObjectByOid(p.Oid)
assert.Nil(t, meta)
assert.Equal(t, models.ErrLFSObjectNotExist, err)
@@ -359,13 +363,19 @@ func TestAPILFSUpload(t *testing.T) {
assert.Nil(t, meta)
assert.Equal(t, models.ErrLFSObjectNotExist, err)
- req := newRequest(t, p, "")
+ t.Run("InvalidAccess", func(t *testing.T) {
+ req := newRequest(t, p, "invalid")
+ session.MakeRequest(t, req, http.StatusUnprocessableEntity)
+ })
- session.MakeRequest(t, req, http.StatusOK)
+ t.Run("ValidAccess", func(t *testing.T) {
+ req := newRequest(t, p, "dummy5")
- meta, err = repo.GetLFSMetaObjectByOid(p.Oid)
- assert.NoError(t, err)
- assert.NotNil(t, meta)
+ session.MakeRequest(t, req, http.StatusOK)
+ meta, err = repo.GetLFSMetaObjectByOid(p.Oid)
+ assert.NoError(t, err)
+ assert.NotNil(t, meta)
+ })
})
t.Run("MetaAlreadyExists", func(t *testing.T) {
diff --git a/integrations/api_user_heatmap_test.go b/integrations/api_user_heatmap_test.go
index a0f0552a17..019cf7dcac 100644
--- a/integrations/api_user_heatmap_test.go
+++ b/integrations/api_user_heatmap_test.go
@@ -8,8 +8,10 @@ import (
"fmt"
"net/http"
"testing"
+ "time"
"code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
@@ -20,6 +22,10 @@ func TestUserHeatmap(t *testing.T) {
normalUsername := "user2"
session := loginUser(t, adminUsername)
+ var fakeNow = time.Date(2011, 10, 20, 0, 0, 0, 0, time.Local)
+ timeutil.Set(fakeNow)
+ defer timeutil.Unset()
+
urlStr := fmt.Sprintf("/api/v1/users/%s/heatmap", normalUsername)
req := NewRequest(t, "GET", urlStr)
resp := session.MakeRequest(t, req, http.StatusOK)
diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go
index 4d82c092e7..59f5195123 100644
--- a/integrations/auth_ldap_test.go
+++ b/integrations/auth_ldap_test.go
@@ -144,6 +144,60 @@ func TestLDAPUserSignin(t *testing.T) {
assert.Equal(t, u.Email, htmlDoc.Find(`label[for="email"]`).Siblings().First().Text())
}
+func TestLDAPAuthChange(t *testing.T) {
+ defer prepareTestEnv(t)()
+ addAuthSourceLDAP(t, "")
+
+ session := loginUser(t, "user1")
+ req := NewRequest(t, "GET", "/admin/auths")
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ doc := NewHTMLParser(t, resp.Body)
+ href, exists := doc.Find("table.table td a").Attr("href")
+ if !exists {
+ assert.True(t, exists, "No authentication source found")
+ return
+ }
+
+ req = NewRequest(t, "GET", href)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ doc = NewHTMLParser(t, resp.Body)
+ csrf := doc.GetCSRF()
+ host, _ := doc.Find(`input[name="host"]`).Attr("value")
+ assert.Equal(t, host, getLDAPServerHost())
+ binddn, _ := doc.Find(`input[name="bind_dn"]`).Attr("value")
+ assert.Equal(t, binddn, "uid=gitea,ou=service,dc=planetexpress,dc=com")
+
+ req = NewRequestWithValues(t, "POST", href, map[string]string{
+ "_csrf": csrf,
+ "type": "2",
+ "name": "ldap",
+ "host": getLDAPServerHost(),
+ "port": "389",
+ "bind_dn": "uid=gitea,ou=service,dc=planetexpress,dc=com",
+ "bind_password": "password",
+ "user_base": "ou=people,dc=planetexpress,dc=com",
+ "filter": "(&(objectClass=inetOrgPerson)(memberOf=cn=git,ou=people,dc=planetexpress,dc=com)(uid=%s))",
+ "admin_filter": "(memberOf=cn=admin_staff,ou=people,dc=planetexpress,dc=com)",
+ "restricted_filter": "(uid=leela)",
+ "attribute_username": "uid",
+ "attribute_name": "givenName",
+ "attribute_surname": "sn",
+ "attribute_mail": "mail",
+ "attribute_ssh_public_key": "",
+ "is_sync_enabled": "on",
+ "is_active": "on",
+ })
+ session.MakeRequest(t, req, http.StatusFound)
+
+ req = NewRequest(t, "GET", href)
+ resp = session.MakeRequest(t, req, http.StatusOK)
+ doc = NewHTMLParser(t, resp.Body)
+ host, _ = doc.Find(`input[name="host"]`).Attr("value")
+ assert.Equal(t, host, getLDAPServerHost())
+ binddn, _ = doc.Find(`input[name="bind_dn"]`).Attr("value")
+ assert.Equal(t, binddn, "uid=gitea,ou=service,dc=planetexpress,dc=com")
+}
+
func TestLDAPUserSync(t *testing.T) {
if skipLDAPTests() {
t.Skip()
diff --git a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/0d/ca5bd9b5d7ef937710e056f575e86c0184ba85 b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/0d/ca5bd9b5d7ef937710e056f575e86c0184ba85
new file mode 100644
index 0000000000..a46c1925ec
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/0d/ca5bd9b5d7ef937710e056f575e86c0184ba85 differ
diff --git a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c
new file mode 100644
index 0000000000..062641bee7
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/objects/89/43a1d5f93c00439d5ffc0f8e36f5d60abae46c differ
diff --git a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master
index 1b1d96a1f0..38984b12b7 100644
--- a/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master
+++ b/integrations/gitea-repositories-meta/user2/repo1.wiki.git/refs/heads/master
@@ -1 +1 @@
-423313fbd38093bb10d0c8387db9105409c6f196
+0dca5bd9b5d7ef937710e056f575e86c0184ba85
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/4c/61dd0a799e0830e77edfe6c74f7c349bc8e62a b/integrations/gitea-repositories-meta/user2/utf8.git/objects/4c/61dd0a799e0830e77edfe6c74f7c349bc8e62a
new file mode 100644
index 0000000000..17b3104773
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/4c/61dd0a799e0830e77edfe6c74f7c349bc8e62a differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/50/4d9fe743979d4e9785a25a363c7007293f0838 b/integrations/gitea-repositories-meta/user2/utf8.git/objects/50/4d9fe743979d4e9785a25a363c7007293f0838
new file mode 100644
index 0000000000..25794ae805
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/50/4d9fe743979d4e9785a25a363c7007293f0838 differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/59/e2c41e8f5140bb0182acebec17c8ad9831cc62 b/integrations/gitea-repositories-meta/user2/utf8.git/objects/59/e2c41e8f5140bb0182acebec17c8ad9831cc62
new file mode 100644
index 0000000000..736a24227c
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/59/e2c41e8f5140bb0182acebec17c8ad9831cc62 differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/64/89894ad11093fdc49c0ed857d80682344a7264 b/integrations/gitea-repositories-meta/user2/utf8.git/objects/64/89894ad11093fdc49c0ed857d80682344a7264
new file mode 100644
index 0000000000..87e198aa9c
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/64/89894ad11093fdc49c0ed857d80682344a7264 differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/84/7c6d93c6860dd377651245711b7fbcd34a18d4 b/integrations/gitea-repositories-meta/user2/utf8.git/objects/84/7c6d93c6860dd377651245711b7fbcd34a18d4
new file mode 100644
index 0000000000..ffea321c19
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/84/7c6d93c6860dd377651245711b7fbcd34a18d4 differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/9b/9cc8f558d1c4f815592496fa24308ba2a9c824 b/integrations/gitea-repositories-meta/user2/utf8.git/objects/9b/9cc8f558d1c4f815592496fa24308ba2a9c824
new file mode 100644
index 0000000000..8f033d5ae7
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/9b/9cc8f558d1c4f815592496fa24308ba2a9c824 differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/a4/f1bb3f2f8c6a0e840e935812ef4903ce515dad b/integrations/gitea-repositories-meta/user2/utf8.git/objects/a4/f1bb3f2f8c6a0e840e935812ef4903ce515dad
new file mode 100644
index 0000000000..9655a74c83
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/a4/f1bb3f2f8c6a0e840e935812ef4903ce515dad differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/c7/85b65bf16928b58567cb23669125c0ccd25a4f b/integrations/gitea-repositories-meta/user2/utf8.git/objects/c7/85b65bf16928b58567cb23669125c0ccd25a4f
new file mode 100644
index 0000000000..2cc606b7f2
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/c7/85b65bf16928b58567cb23669125c0ccd25a4f differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/objects/e9/63733b8a355cf860c465b4af7b236a6ef08783 b/integrations/gitea-repositories-meta/user2/utf8.git/objects/e9/63733b8a355cf860c465b4af7b236a6ef08783
new file mode 100644
index 0000000000..8d16f34e59
Binary files /dev/null and b/integrations/gitea-repositories-meta/user2/utf8.git/objects/e9/63733b8a355cf860c465b4af7b236a6ef08783 differ
diff --git a/integrations/gitea-repositories-meta/user2/utf8.git/refs/heads/Plus+Is+Not+Space b/integrations/gitea-repositories-meta/user2/utf8.git/refs/heads/Plus+Is+Not+Space
index 00dd05db8c..c2850d4c4d 100644
--- a/integrations/gitea-repositories-meta/user2/utf8.git/refs/heads/Plus+Is+Not+Space
+++ b/integrations/gitea-repositories-meta/user2/utf8.git/refs/heads/Plus+Is+Not+Space
@@ -1 +1 @@
-3a810dbf6b96afaa8c5f69a8b6ec1dabfca7368b
+59e2c41e8f5140bb0182acebec17c8ad9831cc62
diff --git a/integrations/integration_test.go b/integrations/integration_test.go
index 8a008ac621..3b70605c09 100644
--- a/integrations/integration_test.go
+++ b/integrations/integration_test.go
@@ -251,6 +251,26 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
+ ownerDirs, err := os.ReadDir(setting.RepoRootPath)
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, ownerDir := range ownerDirs {
+ if !ownerDir.Type().IsDir() {
+ continue
+ }
+ repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, repoDir := range repoDirs {
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
+ }
+ }
+
return deferFn
}
@@ -529,4 +549,23 @@ func resetFixtures(t *testing.T) {
assert.NoError(t, models.LoadFixtures())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
+ ownerDirs, err := os.ReadDir(setting.RepoRootPath)
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, ownerDir := range ownerDirs {
+ if !ownerDir.Type().IsDir() {
+ continue
+ }
+ repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, repoDir := range repoDirs {
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
+ }
+ }
}
diff --git a/integrations/links_test.go b/integrations/links_test.go
index 03229e10e1..c461f81b97 100644
--- a/integrations/links_test.go
+++ b/integrations/links_test.go
@@ -33,6 +33,7 @@ func TestLinksNoLogin(t *testing.T) {
"/user/forgot_password",
"/api/swagger",
"/user2/repo1",
+ "/user2/repo1/",
"/user2/repo1/projects",
"/user2/repo1/projects/1",
"/assets/img/404.png",
diff --git a/integrations/migration-test/migration_test.go b/integrations/migration-test/migration_test.go
index 209ff5a058..971d52b1bf 100644
--- a/integrations/migration-test/migration_test.go
+++ b/integrations/migration-test/migration_test.go
@@ -61,6 +61,25 @@ func initMigrationTest(t *testing.T) func() {
assert.True(t, len(setting.RepoRootPath) != 0)
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
+ ownerDirs, err := os.ReadDir(setting.RepoRootPath)
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, ownerDir := range ownerDirs {
+ if !ownerDir.Type().IsDir() {
+ continue
+ }
+ repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, repoDir := range repoDirs {
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
+ }
+ }
git.CheckLFSVersion()
setting.InitDBConfig()
diff --git a/integrations/nonascii_branches_test.go b/integrations/nonascii_branches_test.go
index 22d71e6ee2..1aab16fa3e 100644
--- a/integrations/nonascii_branches_test.go
+++ b/integrations/nonascii_branches_test.go
@@ -6,6 +6,7 @@ package integrations
import (
"net/http"
+ "net/url"
"path"
"testing"
@@ -83,7 +84,7 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "Plus+Is+Not+Space/Файл.md",
- to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
+ to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
status: http.StatusOK,
},
{
@@ -114,7 +115,7 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "タグ/ファイル.md",
- to: "tag/%e3%82%bf%e3%82%b0/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
+ to: "tag/%e3%82%bf%e3%82%b0/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
status: http.StatusOK,
},
// Files
@@ -125,12 +126,12 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "Файл.md",
- to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
+ to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
status: http.StatusOK,
},
{
from: "ファイル.md",
- to: "branch/Plus+Is+Not+Space/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
+ to: "branch/Plus+Is+Not+Space/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
status: http.StatusNotFound, // it's not on default branch
},
// Same but url-encoded (few tests)
@@ -146,7 +147,7 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "%D0%A4%D0%B0%D0%B9%D0%BB.md",
- to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
+ to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
status: http.StatusOK,
},
{
@@ -159,6 +160,41 @@ func TestNonasciiBranches(t *testing.T) {
to: "tag/%d0%81/%e4%ba%ba",
status: http.StatusOK,
},
+ {
+ from: "Plus+Is+Not+Space/%25%252525mightnotplaywell",
+ to: "branch/Plus+Is+Not+Space/%25%252525mightnotplaywell",
+ status: http.StatusOK,
+ },
+ {
+ from: "Plus+Is+Not+Space/%25253Fisnotaquestion%25253F",
+ to: "branch/Plus+Is+Not+Space/%25253Fisnotaquestion%25253F",
+ status: http.StatusOK,
+ },
+ {
+ from: "Plus+Is+Not+Space/" + url.PathEscape("%3Fis?and#afile"),
+ to: "branch/Plus+Is+Not+Space/" + url.PathEscape("%3Fis?and#afile"),
+ status: http.StatusOK,
+ },
+ {
+ from: "Plus+Is+Not+Space/10%25.md",
+ to: "branch/Plus+Is+Not+Space/10%25.md",
+ status: http.StatusOK,
+ },
+ {
+ from: "Plus+Is+Not+Space/" + url.PathEscape("This+file%20has 1space"),
+ to: "branch/Plus+Is+Not+Space/" + url.PathEscape("This+file%20has 1space"),
+ status: http.StatusOK,
+ },
+ {
+ from: "Plus+Is+Not+Space/" + url.PathEscape("This+file%2520has 2 spaces"),
+ to: "branch/Plus+Is+Not+Space/" + url.PathEscape("This+file%2520has 2 spaces"),
+ status: http.StatusOK,
+ },
+ {
+ from: "Plus+Is+Not+Space/" + url.PathEscape("£15&$6.txt"),
+ to: "branch/Plus+Is+Not+Space/" + url.PathEscape("£15&$6.txt"),
+ status: http.StatusOK,
+ },
}
defer prepareTestEnv(t)()
diff --git a/integrations/org_test.go b/integrations/org_test.go
index ee61aae6f5..ac234de650 100644
--- a/integrations/org_test.go
+++ b/integrations/org_test.go
@@ -5,10 +5,12 @@
package integrations
import (
+ "fmt"
"net/http"
"strings"
"testing"
+ api "code.gitea.io/gitea/modules/structs"
"github.com/stretchr/testify/assert"
)
@@ -110,3 +112,64 @@ func TestPrivateOrg(t *testing.T) {
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
session.MakeRequest(t, req, http.StatusOK)
}
+
+func TestOrgRestrictedUser(t *testing.T) {
+ defer prepareTestEnv(t)()
+
+ // privated_org is a private org who has id 23
+ orgName := "privated_org"
+
+ // public_repo_on_private_org is a public repo on privated_org
+ repoName := "public_repo_on_private_org"
+
+ // user29 is a restricted user who is not a member of the organization
+ restrictedUser := "user29"
+
+ // #17003 reports a bug whereby adding a restricted user to a read-only team doesn't work
+
+ // assert restrictedUser cannot see the org or the public repo
+ restrictedSession := loginUser(t, restrictedUser)
+ req := NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
+ restrictedSession.MakeRequest(t, req, http.StatusNotFound)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
+ restrictedSession.MakeRequest(t, req, http.StatusNotFound)
+
+ // Therefore create a read-only team
+ adminSession := loginUser(t, "user1")
+ token := getTokenForLoggedInUser(t, adminSession)
+
+ teamToCreate := &api.CreateTeamOption{
+ Name: "codereader",
+ Description: "Code Reader",
+ IncludesAllRepositories: true,
+ Permission: "read",
+ Units: []string{"repo.code"},
+ }
+
+ req = NewRequestWithJSON(t, "POST",
+ fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", orgName, token), teamToCreate)
+
+ var apiTeam api.Team
+
+ resp := adminSession.MakeRequest(t, req, http.StatusCreated)
+ DecodeJSON(t, resp, &apiTeam)
+ checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
+ teamToCreate.Permission, teamToCreate.Units)
+ checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
+ teamToCreate.Permission, teamToCreate.Units)
+ //teamID := apiTeam.ID
+
+ // Now we need to add the restricted user to the team
+ req = NewRequest(t, "PUT",
+ fmt.Sprintf("/api/v1/teams/%d/members/%s?token=%s", apiTeam.ID, restrictedUser, token))
+ _ = adminSession.MakeRequest(t, req, http.StatusNoContent)
+
+ // Now we need to check if the restrictedUser can access the repo
+ req = NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
+ restrictedSession.MakeRequest(t, req, http.StatusOK)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
+ restrictedSession.MakeRequest(t, req, http.StatusOK)
+
+}
diff --git a/integrations/private-testing.key b/integrations/private-testing.key
index 293256a16c..b3874eab80 100644
--- a/integrations/private-testing.key
+++ b/integrations/private-testing.key
@@ -1,128 +1,81 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
-lQVYBF3190cBDADpfh1CvNtLl8N6lJQ03jymeE8h2ocvT61stAj31eefiOvZPDyx
-n0kRztBcrQ1OITBEslYqKiYHVfVptscIWf+5bARoHnLQIgpJEio00ggqO3AP8lIZ
-uiy9/ARDVgyPl3WgMza/J3Z7W0sBJTtN/6W35i+eNEY4Q0mScmNIVc75oo5ey/pL
-JSF0qumiy94o38dy6Vk2dPEf5LyxJWoA4dLvj49LL/QGPqnsAXAQXr1zULaRuwf2
-a8oKebr3m9wVkZcaH86duUmkU822k6OZSSxWCxZFFGVkC/VaA0uUjIL4a6SQqOOr
-PPOttmCLXJrtvpFdRwJ4PNwr9r2nPDDLwoajLJ8zo5jRumzwvkK9vALkY0BPuVoC
-qFdGY+2SGYKa4FTE7Mri/5j0NhWplhbcdGRe5doGDxJHbIN2UO21XjDlTBcscKep
-mWPVE2cdJrspYaZ9O0L6vhMVwGyyk+Qxmf6NbDw0q63AtqVe9qwbr2O3Irxseftw
-uWuSuLXzp+SMh/0AEQEAAQAL/2ExopuDwuNSHsh5bcIeGnAPV51Xentqtt2viaYk
-0AB8PfTVGsyzafa0OM7DKG0z6oRGGhD+L4tRMFGbiHlFAWqdeK4gsplJ+i8VlSUc
-otJ1oH262IsmEPban6mp+ZuSKCASAYGLu0m5JF0rMucSeli1RHAeAXbtJ4SDAin7
-sib/EDWMwjkikS0f8hZWt7kbAcqnMQA2qKKmlBdHZDtOxX/8KeFZ6kHpNtFrfcsK
-rOECIaVDDhr5HobCyl3E7tW5nrlrvSUkLVFl0IjcypqfzDlZp04PMdswhkdfBhu+
-0iY4K+d4uMPMzcpF1+mcn8C+7XK7jOqZysQa42bqgFHWEqljjJiUCuXfHbxnZWls
-0R2j9FLgTqtPQ33f3zMjhOyvdiy1DmfzU9MSu/I0VqCJnq6AwlW5rBQaKwAQuHMB
-UJ7bjMx/z41z41v0IFpxHnwSa+tkl49tV+y8zVtajfwXxJNy8j/ElX0ywfM5sDHa
-RAVwI7DSwMk5azp3F15DnA6XbwYA8O0b5AIeCo8edmIdKgY3vAi20j/lsTgsTUkY
-GTQ4BdMohr9gpZWHZZmQ1TeZokm4Auex7UgPblflufepkADassXixMmSNUsggGI+
-sR9qydNCw+qzgaJjchpwT5TdLJNHRbE+6VuGXJftcjdfXiKYZltEQBX8U4w7hui8
-D6dpzJK5mE1QebrFnJ7IKpAe+hWTc1+g9iHH3rInPMIzQW72WqSKndKIrRy1PZS5
-WM5MJzgWQaDzZSOQhrKA4yLIyzsrBgD4GfFLWh+sQ02sob5xmpDblfQUYVRy5TAx
-4WOLSflJqqyarrk7s1D7obqsSoAEdJk521dpE/0ciI5xT41fQKMXH1Qm9tu9uW5d
-1Y3oDxQXFJFa34gi5J9UbUBBIJRU0KyFcB1mGVF+fKbAKGPFR2lMCmkeqAYjVohM
-PG+tluArQrQYCwkZroR460TqvSadmPUekEjYsIzwlaOkJhGf7r40G5Djgyb2/LoC
-JY28zH7P9MXxIc7WAWuMJniUOqvslXcGAOkfZ1KVI61AIAvkEoRUpKwNSofs2PDQ
-1K5Q9DN0NK5UNAAr+Wn91mw/MBXqxdheq9wjmcsvx8OAhvw7O89QMuTviCTUQjSl
-Wzel6gpoZhpOgVb2RTxV7yVrp2fgYKkeUr7hiGhSxw78guF2jLgfBgb1ef+XKIMk
-5anUqKcsHHiouBQbcUCDyKBcVeIUKjAuh9ADpqn1v1oVshugnjpx32Oq1AW6Mn9e
-SmxBoR7YIvsy79P2IonjixEAjSp1chkGpNQTtBhnaXRlYSA8Z2l0ZWFAZmFrZS5s
-b2NhbD6JAdQEEwEKAD4WIQQ4G/p4KVUOUEVu5g5R68KXFICqDwUCXfX3RwIbAwUJ
-A8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRBR68KXFICqD/8/C/4wYdr1
-Q6fnXiAdBZPQDOHUjCSjH1d6tbVYaCz0ku8rK1uU0oTToKRSTXH9K59erHl//MEX
-Rte6loI22y3GFA8wAWzFKGwb6wDUr0SkH21espsp+plKUI/gHV8JyfWs0pLmy/C0
-tOr9XhdnQepFjpDAquWszSO0B70G32Tl82Tzbrho6+ePvU+2X0fYj1F8q/2bmegB
-lL1CcdVuivBqYglj6tzlurPXFq1QenJdssZNn0fizGiGfTY/7kgrvKHc4KN03i9d
-PUrPMQw7J59KSFNdkE3KYdedmEeWBVmrbfC8QBEO1zcTJN9wwV8fVv4qOhKN8yIO
-QLuhBZTeChtP3i2FCPHQqbeD2f0SG+yBbWu/OyfSC2YHcGyjbNV8D9upNg5eIJ34
-Sm5i0tGUYEdq9QQROacXn2/MhyJuJYbFrTcsHLsSiwygUXvHOqi0G0gEjWl67yMd
-9YIq1sZlNs3WY7ASrV+QcB7B9WKJAyh5YWz/G4MlThU91YUfltAb3QmNFeadBVgE
-XfX3RwEMALH7pae22J/BmhFFBCjMld166hTknXjOR459csv5ppky91Yl82VFFg+J
-G6hbkGzvCQ5XDJu8xgWeXowe2sXkyDwhTRaB6MEnA4KW5PUazL7KlDGsR5oPvBlE
-dSQDGzTV/RPcszSNdcN9MRNbfAf0ZFV6D9R3CIlNZAm6HwML7lZ0JmCiLORz3TbF
-4kg1KDZIQAhY7Y7AuMdoXfnpUqFLba2ZxZBvdcrMcuYz8GkmFsYdi1/JuXEK3//B
-Mo7Pg78zsq7UolUcT2p3qKb7hB3CEtwa3xffwzgAcFSKYrCE/5/IYjHhS97uKWor
-8dh59wUCuPCmAiuIz3aD84rxZIHgBGPy03TEWCrCBCVxAdH/2Ezpn3DpuZyCuanJ
-0WGSzrPBw+twA8bk9BATvFVQ/7Bs9deSsMAOI1uj9lTy1R9LU/KHEr8BEz61+Bgk
-+m4ev6OQAVDY6QpRtf+zfB2xO95Wu4l1pIFuz7OJaZLCm2ApeAKsCCUDdTSJn5e7
-0i1E4SIVgwARAQABAAv/WHaZqbiqBw21RCwnmxfEzWbQfj37PxZYXqxfqJ6XfcHl
-Sb5nMcia5HHje1S3fk15FNWTgLzdN+G1YLPdTUsfczOiGzPKumZnyjqx5lnBtnr+
-GYpltF9pwK1UA+g/V42c0oh50f8Vr2rEP7jS9ykzzYBz6ciYR5ZdyK/nxh3iArqM
-cK9q3MnyA81rYTR6njBfE0cQHEoSDZsESrj7xwu0ofqyRc4AoCHqYh0iu0ChRSte
-IOgk8djT6Uzfkjf2ZcyNiD2/iFzAXaI8CpoiiRJDn/qIhtSFqjb284wwbmTUE+Nw
-LjeMbpKQiWqnsw2GKhlXVvTLjrCb8TIKjbLtFH2HlEaIjL332GcVqkVy2TMtjZRi
-lhy/uSY2kzkBkoGJXp5isJFk3ZcOHHsG4VQ+08vq++GoqQE8U1t8zMfAbBFoFlpP
-nkRjZs0MwY9u6C0IiXRDrYrMIW12LjsRBiebGHUhzv10/4T0XZ8FKTewxcszcuMf
-lpbIotF1ItGqCXqgufnhBgDBhi8BErxO59ksWLAHdozDyiXwQ5ua6WesGrIRvw23
-Z8a5wxTByXmd1fMgJ509hpXbxUC94TGObJoUo23YE3TpqlTLs5NqeFzzU+OjWSb0
-Wo1hpFlzyatuynpz2aXbKbjw5dgyeIxj9t+NhGo2SW6v+RHtYAWJRFBFOPVOLwTy
-an733pA3MSUT0oEh+aggDkXEJLBum0P3Onnma7wR7Xj2Nk1SGLCMgVmKbtGlvyrj
-yc5FZhzuvOfeuLSYoa0KE4sGAOtxED2jDkV2HS67bxrUzMLvAAhOnRmunA7Qk4F7
-B2uMYa03O7vnUJAINpmZVu/ubWz1/JRV6M3/1lQTH+2B9kZ5v6kHrczCsoSP2dXD
-7CQnxSm6zngdgwkoo+9pgFztGUZM071SjRW+r1IwE/XBZNwFya5PM02/Akb0ejuB
-6K2ClnIFf7gflndUZ0mhZn48I88b6mzEG4X4uUZG+4vW8EZEInl+nMA9f3S6YT0U
-ZG4JC8JMKsmoYLye/BuedHxk6QX6AnMFBjK7cnfBnViJkmXhDLxmcCjwjUUBraRI
-QbyzHzY2Jq1VyhTJ1HZxE+vj26MzFFzjpe84r1Ggrcowx53RHstBBYBA5OjRy+cN
-vDzqqWz4cDKU/XlwJhRnG+PcY3c47obpvjjagcwG7xU4df15fDetKajnIloA5r22
-hbmVmTAqljyWLnvSNYrvf5QDqqg6tBuHITUiZhYgECpIoeEj9hU8MZSvQOscK0kx
-Vn8SqUjxDcNazQM8NoxNB10wfJw63hCJAbwEGAEKACYWIQQ4G/p4KVUOUEVu5g5R
-68KXFICqDwUCXfX3RwIbDAUJA8JnAAAKCRBR68KXFICqD54+DAC4VZpKrU6Oo04z
-/gJeC+3fNon6W9Pdxx7KimDOttkpCiss8JydO6aSB97xrWdvMBTui333qGo2exE/
-XFA4RF7K4lAKUWbwaR1brLQfGVYOltmMb986/LeE3OsmMt4vbxUnGvHVX+QXDWAr
-p6q4DZvMgQQhbWp+rMjXtRr10iQnSlM5CYhyawdiiahFqgoo8395l/2JA2YGhUgU
-nARUPZ9SqaUmRm+KGsSyoYnvN9apiDk5KVQoyfrmweNN7DCIIcoh/B9Ax8nmouKz
-yBB2fjCM/bJNtN/AsgYbZIScuYK/xqTkwNtbe5WdCyD/QJOHTsPJzx59hgSVo6gf
-Fe8VBnxHtrY8gPSUU3gkhYLvLzyVX+YLNzRcffobd8gJbfumwFJUkz91oGvYz7xg
-XN2qmsgBNCbTIzWZMpRDMAbY+n2QFImGf+EJZlMdj6gOrIYq8N4+nMW1FwJivsOb
-muqySyjZnD2AYjEA6OYPXfCVhaB5fTfhQXbIrZbgsEh4ob/eIdOdBVgEXta5egEM
-AMYlmZ47NqBMBeaN0o/ahYMe8eIMaroWkufMfC9VRBSMAkpbDl34oNp0cflmnMYo
-AFAl8ucRMFTiUnjiWpo27q14tjSyDVsn/CqwbnrgJgCFNV/MGsYsToEkb4JwDIRC
-bky+1BvqvI8RMlO3MlwzrlIaMrlQfx5NtUb9TyO7S4xZTz864+Ty5p3HhRwbdZMe
-Ko8sfXFhCcCHFXosI0mX83EyzsrXlbkGRawId7jvrdOAUg/cYP8f/XmV6z1NHHH9
-cvz+3oLOGuVxUdG0KuS/jigHrLWdRuKM3xfEeesp870yZU3AbyFdoHnGXROJePTl
-FV8j2P5Ahf/yuVhjdyJSKdZC2h6+HtLG9RiGgLviLLYhtlZG2H6pYyKY5Ud3php+
-qw1aYL1xtdxrHYkQlAa0vLY/mwpuPfMke9I+rtnrwlLRMCstdiN34ybZ4sRD+gL1
-w5VIZ/aM6/Gsczd3s/T8psIi09TKPfEU2gWLMGvlDsgz+aSDdVP7XYQpNglaEPet
-PwARAQABAAv8CHg6+hnV2pblTwGTlTU7V8DO3gwMfn/QhQ/8ju66G5a7J6p/ZreQ
-nfCJnqYq4AgoW0SuqVSBbbTENF6YjixNmiSlb9iHMZ+ilms24xG0Y3lOMBYYCY3Y
-nTSNf6nXyconz31TW7jLmTdG9hpykKEKO9WFgt5UpgWe+2CAgtUoBDZyaLrVBZ2h
-te99WmziDbPQZeZPm7UQ0aX0iRBclxy4+dxjcnrcmi1mdQAM/glgs2sHbEjN7JnV
-dTOvUSN7/8ixj6I719Wx6MN6jE+BNd0ytZOun6tcDl0vamfT5fBpqbQoJMib2ggo
-+FGg9VFnzEMLqyI47LfOKUjCIhwVsxS4q9HXa2FtpO8UfRMPjDKgDZQzRTRJScrP
-s1NJ9HiM/eCHS1YjRmgroo60HygxkoLVCHp+Rz/hi0tG/ptv4q6mdnm8Mwb5JJtV
-48EvmZoNTWl9xOez1wmQn6caVHipc0qDqn/veoe8N5wdc+3hoMEXbSXqU+kx2KUa
-cVxCCVoUeURhBgDUGWtx34j1y17zE92BYhtVJTCU89dDe4wOEqGPyCGvRtgTmZ+1
-KwWr66pij91MV9mlY+7Ue2QHUSmgav2EFGIjVes956p4/F/CJ6qaYoekirMSnmX5
-jhRt4p6RW7m4omha3LAQ+gN4Fqa4acZUywENBvv1x3v+IWbjGJGn3eBnRrP3o9P+
-QUAtyMifiRm0ZN8J767o+bzUVmscXrkh7Qml47lQfDToyRI1UZZQmP2izpwHcwbZ
-NtfkgRUdeEq4GJUGAO8o4Oebbt0ALZ54E2LHhk8xi4ofKkFBDCkUFjcqS3bJJNck
-rkhfqEkMLETNhPbiC4TRNiunI5PXOinwNPkKI8P/hfp4S49WdIvnARazCoxjZNtl
-0Cbo+F1wtOH9FZaaWzNlU2lCQ2JJ3MCpLHz+nEmdYWOIWGQu2/s7smLODVEFbYKR
-50VWVRL7mB83v1XdfMFvExdQ7i5MOX4hFvmwi/WJIKClJfhNwTrHp6Jrm9jA66RL
-+dNyPKfwcFcYrqt1gwYAruZzP7QgTYVL+cmvGtCaHY4KoR8hanbpqR4YbzzyEXwS
-ll2FUCaVSokuRAdH3+/CHF9bqog3Zvn6HYcCS/A/rHVGIU9a+7s5IbRe0Ysc2FAN
-Nm9AsC5YnuyoAjW3cJGaZLYxp2WOZcMEXZeLPFYrNz22R1nRoxnUIPRpsKICXcK0
-aC4rSMk479jc/8WprWx4d45EVG+6Gsh1AT8LVhDL9yHFrh50ss2jCe1Fnftet6DI
-V5zHcxBx4sCs91aPxxe12UiJA2wEGAEKACAWIQQ4G/p4KVUOUEVu5g5R68KXFICq
-DwUCXta5egIbAgHACRBR68KXFICqD8D0IAQZAQoAHRYhBKAm5ShdO9gmF/o8jan0
-RkmWoKbKBQJe1rl6AAoJEKn0RkmWoKbKacUL/3YYKmiVvcr5LYFzMdwdahkla+6m
-hEEkL0l3dJNuU97Ou71tA1ieF0fjbVRSWjXKsntKwhyPoXjaZEZwMmv7iZ8BXV+b
-oO/EG5sg2/6iukJFXZqGnQwMdLVo1jPoXDteZU1qYiCoxLHhGhHL7ivtD1ygEi6w
-/cMbbOEB5Le1vOWIwqazs8dDcAYyy1PKthRl0ygvh8CpqPwy+AK3uLm0TVwetQAp
-taux0bDYWCb5Aft1r1nlV44gU4RiC131TDo+TKd754+UuI+UHk1D+LjTmZxRX2S6
-fXgoMXzrWmthGPdqvVOgKWm7Ef18hmaBECvPnp/tUJeDVVe02KrYQi8Bf2kxveSd
-8T0N/ExcydU9HgzTL8MuyPI+yp086elQzKJu6vb9tpgxCcglQZrUNT9Uy82pzTRY
-z9MmhnCDI2SD5L/CW5PsNpPTPy7s3f9DOV0G5Vka4LTSBOCK64NvAGBmRf8rFjJU
-lPtRPhC7h6uHdUIx3Q550Xogvq5sQm8UBCsbG8OJDADT3FJSIulR9Sh96OsES3sc
-H09juN4KcbpS03MAeUFwXqw3jBMhDoGKlsjX17Jf31qh/nI/XjigS3XWyj1BLSMG
-rJfH0NyYoGDCnff37tf+8lD9km9TlnV4Qjd9ujYbDRsefhaSjLVcy/gqdxZEuNBC
-BWmGwsmLI3nyZ4KDtNsa5JUHUNNZLBN20hvmE41Eszmz4Yg9Ho9DxKiFKvzUULMc
-bnMHaVHseHHq6+NVUnN1SAcOA0ygjnEid8D57RtdBCD90LXjLB7vlR+HaSMZYOnr
-DtseivHvqqy4+rxhwV2S3avnls9vRwE4bV6GCiqhoBnWIZRrARLZc2OTBIya82vS
-BIS1eyhjif1mE7Lqhs6aPD+eqQK2mBtQ/sidN8P/IfKfVF5siXfFbuGZLz5nRIho
-Yp1z7oO3OZ09lpUk0G1h+ouIFF6goDP48M/AKtbvs9OWk3QKxnOUZD8sRncq95x6
-m4q1MVb+aJyxwBqDRGaFY+3TVArB1b+kG1JsAvV5dag=
-=511T
+lQVYBGG44vABDAC7VVdrVcU2CzI4P1vm0HtsgRCj9TsCpxjESleIheG/jrLjpVaF
+YrlVKQ0+q6HXOMcbjJnsm+N6hgZNqwaKTNC6+LJZMXHlPG8wUGrHgHyUZ03urYB6
+vjlJ70RUBu1+dB5yJcTOk7kMLx8/is9FlAEEY/G98aviv2m3My6B5SJ2BErjREIw
+eRnWFm+JDcga9nRi8ra/DMac45iQ4IcQcj0NDlCn3aY88nGa6o1+07h7wYwI3t8S
++pfITuJgWf2cYK49v9QVsBMR8XHuS8UDGFuJ1Y4KK5zMHWKhah/6isyWPSgiC0wo
+V7LZDJp/tN8IoQf2fchRQN+x0PBeVXdt3KGXqvsfk7hnwGDKjGMp4nTxL8PFhpG8
+KJP0tTA063bbnrGjVYHaulTBTSKS8R3Zk2utA8JUgTU6tkNFoh8rNLgh2xtw/Ci3
+kvKzTdikWxBfspYgrWloMyCTZwOHssARyarXgtysEI1hNpvgpJo0WZOMurYuFDIB
+kEqgnqe1b1B7ItcAEQEAAQAL/iNebgZkZ7sX6w/mmn3eL+dhCNjD5LPQA6OP2635
+hRFLKmhDn63IYXB8MzV5ZzGA1UrUxX0AQ7cu1cLVPwNelGwwp0+iv7vFqMKI9Fgd
+YKgORw8AsAi8oIlehNqOgkmFN/haPCm6h04PGYnANfkPhA+lpQ81MTw64oVFwwqg
+TdzVW6RED3EidCfRDZblRLoefQPvimRQz7DwYa48zhNjVjaAVOcUuJ26MovKrBNd
+eu/Wr48/MQPez0hw6FnDs9fSAtB/cLmSlSL3yBkDB4RHTne6amvemX5SyQqOSKLJ
+F+YM33yIN3NQNQtJUkjNkBWuIe+s8pxFuKTHNyulCe/ES0ivtnqaCJ/J/PPzn/3t
+2S5f1K26jqJEnu4SfCxG3xTbSMu9DIcDP6BkU6WK9dQCPyfWZ3r3QkgZjHt02HP9
+Gbzh2tSxBO3b4ujysdSB2l78I0s3XLWae6FPNNKG+zmlCV8mUEa+OFVjS60GrX83
+NQVfoyjNdSQkLlg3+bo5DFma+QYAwr/HXi06iC8dh23HkPkYedIOml70SPAQqvVj
+xYtZRRSXo98P+QtA2kX0G3/9f606n2qqA9JXc3m4euvE94oSp708M5xAkSfdsc6B
+QIDNrR5ty+f+WdhZAsW4Gu/XbQ5ndkRReTtc3UtzIrC0zg8egCoE0yMfCJWPS2nF
+QTdlsl+cXDSQj7UMfCP9cKSsTzdEAF/P5ALI7Y+W4va/gy/0czJne+ZNMxPWE+Gs
+00KJCbSfgktnYhVt/XdWKuRZ8ylZBgD2QHts6MHkfno/OUK3wYDB7zLMIBdLltlg
+wvp7CXh8hIxzNqxaAjGus1XAg+/7QbSey/t88CR9XQsekd/L8NIYaFOxSpVAe03V
+RaW2/EXtmKIHKoWBTQJLJle3mp+iUiVjzdmTyUAqhFaCBYVMBlSvBuC99jXnu3U3
+UcUelLDvP2ufMdeXhVU1Anfg45wqvyfPIAhpgYMmyprGpfkd2Sf2W1ThaTec0kI1
+cT7AtkrqijCGDgo9ohl8ojmRhRCl968F/imENQATANdkhbYJ0k1+Ubm690xYNN7u
+d+wnQzS9P/UPpMrC4H2esz9g+Nls7X6/jeGB6K0bpOYAUR1VlRfuXREJcy9bK9Q8
+gzfBC4XWELA726fc9YeJqWH4fI9SFx0AjVVx6VFwSiDcoYbX26CLZN+jY6Gx8kx6
+PrOf4tPCU+8EP5f/tYn/dwN9oQPoyM7bYyN/zcrupLhHON7ryFr++Kpiw0feBGbg
+kEP+0HWJ2cX1MvcqTurx344RVlmnEBesDuFstBhnaXRlYSA8Z2l0ZWFAZmFrZS5s
+b2NhbD6JAc4EEwEKADgWIQT3rIVBIbYw8mUW1p+Z3Yqpy9FcAQUCYbji8AIbAwUL
+CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCZ3Yqpy9FcAY6AC/9GUc0vGAmZ1N7P
+ThOxy3SvoIWJzycEu6DKdp4FlucKW9Rm66vCwPDg7XcQxZQTIWNPIGB3kln0yRdx
+zRtGLKIDPo2qW8kPrLN3GXToKX2mBb76duaShW34W1rUVY613olmtwLT+QqgRX+H
+x0rNNJloOh3kawwaMoYZy4B2vq7AZ5ybIsT4ROKgKPzAlajI4+jI+qKA5GSyP7Jq
+Tu254BCeg0v51p0VWIbGdgPyVkZkLtrlxN7s8UGDoTUAJgB/K3SOGNtQFSxnJba5
+q0YBxDUScd65b1+YCUHY+3FdC4/5168y4Zic9bBxeVu3jBwSVDvrELsWzIDNgHmP
+eyl/Nv+CTZDDKOtzpS823k7gC129rcxMk0mkIzAt/wG7N4zf0vpt02LZ/Ei/azqK
+xq782Fmc3un+pgQWJrlU2ZT7yHi6aJAfxfDpQZwz8qXGgdaFsumNylEWy9o80pJG
+8RYgM+phZL4INYIiHoWUuz2v+qK9jmhxtTLOpKDXxtGrz6aFWJadBVgEYbji8AEM
+AMDFivCjl7vGACeST4iboZw817uAJFOTOk3uOnXuAx5NLq/DbL3Cyhjictwxhxot
+U1MdAZOSOHlWPBJTiib1145rDTJCH6gwQNVaqn/V0i/Dc2Isua4YF0efztzwD2aH
+NX4RCDp74bQ08YTsAlCWHk7blg3NCU/y4maaxdJ26PsNrIiY0l5SC3oNiEAp2aWP
+Yf+plmQwqk+Z3laB5fkVz8Vca8TZle11/NZVVwrpq8rubPUYHC2KmabFLihcMCGv
+eTt3LCB7tDzohDmX/0vuqTD09YTv5gmIzU/tx4+qH4tVfCK/DKTxsxafY4KZY4kM
+hqrhuGWq8EAu4RUG6AzbSDJZnO1UAfzC9j/8upr3qxOXx/xhWKzixGrRXo9eK5eR
+1pqEj+XGH+f9bQiF/pEIojcUp45S0ZBaSBPj2W7TZbbHzqXYNzmXa3IVdz+9l7MB
+cRfIe67wt4h66/fmATe47KvHNRfKpyhFD2utdOSd61tKXo/bu/5LBath0mxMBPHd
+4QARAQABAAv5Aacf824U/LiW+JU4poVJFofEr22gQhwwIt9rnmZm80ak+L+o9MaR
+CN4WLzJN2X5b1B8FTAXerexR8bPy1QsvaN/yRMT23wW3j0IVVf5tbIM/6m6o5+fP
+zp7S5/zh8OvbXE7v6Qp2C19sgQqB/ugOmff9hSBF18A6II2Wq8uLtgKua5xof1kI
+5/1qNpH1SltcndPPKjbq8D7zk6kjoZCw5PJk1ShVcKwIjzDmS729qezZ6nm6sh7v
+BX70JUdHErQzBtcb+Y39nRC/7aQ/X5s73Iy9OsnAzzTSTtw1RgxgAYXxQKhQN5xP
+rzUdZqCSFicjLAPvY4PxQmIL+DS7tb/rrWUJAfr/9LcrzoOC5LaYFTuykq231ORs
+4oRfHmJqYAiMYQ7iXMtFVspxQWq/8qrBPmmEkS2oAnmd8Ld5hbd7sFBsS5GCW9a3
+UyQQ9WQECyvpgFOR9m746/bFjKMgG+aBHyKvndniF3XWjHWrzrbk5vAViMb+9Al+
+7MxSqZ/oNrvdBgDT6hTMwyNBvQwJ/Lev0S3XPDJmxg+Y8QIrNbBrXjA70yVeLFgr
+emDnfdAwuhmZ5vKRe2YcIyMIOagRIDUEWs8EyCvM2e+bF+I0meQvWT536Cm2TouI
+jCUIip4HRTwe7NAR50OMACtji8sbcmfnIfFMfGUS3dPpNGURhCEHxWB6hlvbkbkV
+CToTlMS/agY0sV4O4kWqWiaKgZRefJSiVfj6RDKs43SbNxhJu+DslU7PPlfv6SFJ
+nX9LWE6daLrpuF0GAOjf+kjqpFFgF50h3B7lCsSfxIKW587z93rkmccGKvZj4Qeq
+ahjekO6kxapYJhtjY9BOQdU0rzEPhh8bF39GE/iCfXVdIh1suqp3uQv9birgkWJN
+CROrHvk5NmlBBb4BDid0hY8hM3lEi+6rK2lhs4krpoHin/h852AI+YBzeAVYSqor
+fqEzCiPlX7f1EI3I6kPnGrgeIWcznOO0yXkM/QuKCDWZlaLDxu7Rc5lBnsmiChrT
+3HwOiyOFfU1Rib/TVQYAng1PxHZfIfC77cblAiv3SXjFtSDIfyueER3Ii11DyEfB
+zco+qbpqYiDEI7yLZFuyExEpT2GbHTTEn28aEZzZBv/aFRnVFPTMiyquFE7QKuLc
+aEpEYZE3qSiAUDAckfDblM1SHZAVP6CaStkoUigtYBND2F316MTNGGLtcJ4y9s1r
+soqvCJ/cx0lR359kljqCHyv+iMqeBttwTGjFbiNJ5as4ATA988FlR6PnB0cr+Lg2
+8X3xiRcAaxlLFcUOifpa3m6JAbYEGAEKACAWIQT3rIVBIbYw8mUW1p+Z3Yqpy9Fc
+AQUCYbji8AIbDAAKCRCZ3Yqpy9FcAT/pDACilZ8zPUs+MwwI0BI6dMWxmhusHwTx
+kdwbxt2TuCQE3DEftCTCaxO5f8hQ6CL9pxYw5mn/6p8ELUpindFxgzpBjUQZyynb
++ZA7LOK5gKw25vGTRcMFiWZOBnMEAifyywmG6XCPtio8i3/In95ix/Adi17tzdpy
+EfFfWTeDocTNPhIPhg9REteZ71eBW3qEbY2iCeG3XSpKhkj6obY7BL8xLT9iaezh
+C6Upzb3gvjEInoaMR2yra9fVugW32lCFgXr6UZ5osBqVNjXGcwBqxg5IAkt4R5v1
+vdt5h69cagkbdS0qSRbS56GctmxVnbWyuAuKON55BDri5BhO3V4GmIXXUW12dQhl
+1/P9+xMjHm424QlGL7jgEzOMR5CJFdDQ+osabA2iZAUEQ7Ut8SgREfCduqKqzJ7z
+Uvb3feuoW45VNBqv7op8hH1S8okFaCTuznrAPqGXxee0I3oTX1lbBW+IySoisWMC
+ZMtt+nu5oJo/m1bvhWiYLhW6WX8TcmRKD3s=
+=V9rS
-----END PGP PRIVATE KEY BLOCK-----
diff --git a/integrations/signout_test.go b/integrations/signout_test.go
index c31c913070..b54e7ee9ee 100644
--- a/integrations/signout_test.go
+++ b/integrations/signout_test.go
@@ -18,7 +18,7 @@ func TestSignOut(t *testing.T) {
session.MakeRequest(t, req, http.StatusFound)
// try to view a private repo, should fail
- req = NewRequest(t, "GET", "/user2/repo2/")
+ req = NewRequest(t, "GET", "/user2/repo2")
session.MakeRequest(t, req, http.StatusNotFound)
// invalidate cached cookies for user2, for subsequent tests
diff --git a/models/access.go b/models/access.go
index d35b900cfd..264dbc6f60 100644
--- a/models/access.go
+++ b/models/access.go
@@ -225,6 +225,9 @@ func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int6
return fmt.Errorf("getCollaborations: %v", err)
}
for _, c := range collaborators {
+ if c.User.IsGhost() {
+ continue
+ }
updateUserAccess(accessMap, c.User, c.Collaboration.Mode)
}
return nil
diff --git a/models/attachment.go b/models/attachment.go
index e12609f810..4e0ccba5a0 100644
--- a/models/attachment.go
+++ b/models/attachment.go
@@ -144,6 +144,11 @@ func GetAttachmentByUUID(uuid string) (*Attachment, error) {
return getAttachmentByUUID(x, uuid)
}
+// ExistAttachmentsByUUID returns true if attachment is exist by given UUID
+func ExistAttachmentsByUUID(uuid string) (bool, error) {
+ return x.Where("`uuid`=?", uuid).Exist(new(Attachment))
+}
+
// GetAttachmentByReleaseIDFileName returns attachment by given releaseId and fileName.
func GetAttachmentByReleaseIDFileName(releaseID int64, fileName string) (*Attachment, error) {
return getAttachmentByReleaseIDFileName(x, releaseID, fileName)
diff --git a/models/commit_status.go b/models/commit_status.go
index 1105c3b173..4193ad0d08 100644
--- a/models/commit_status.go
+++ b/models/commit_status.go
@@ -160,7 +160,7 @@ func getLatestCommitStatus(e Engine, repoID int64, sha string, listOptions ListO
if len(ids) == 0 {
return statuses, nil
}
- return statuses, x.In("id", ids).Find(&statuses)
+ return statuses, e.In("id", ids).Find(&statuses)
}
// FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts
diff --git a/models/consistency.go b/models/consistency.go
index f037b05157..896c07ab8d 100644
--- a/models/consistency.go
+++ b/models/consistency.go
@@ -302,7 +302,7 @@ func DeleteOrphanedIssues() error {
// CountOrphanedObjects count subjects with have no existing refobject anymore
func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) {
return x.Table("`"+subject+"`").
- Join("LEFT", refobject, joinCond).
+ Join("LEFT", "`"+refobject+"`", joinCond).
Where(builder.IsNull{"`" + refobject + "`.id"}).
Count("id")
}
diff --git a/models/context.go b/models/context.go
index 1221ab7ded..6481a8209c 100644
--- a/models/context.go
+++ b/models/context.go
@@ -45,19 +45,16 @@ func WithContext(f func(ctx DBContext) error) error {
// WithTx represents executing database operations on a transaction
func WithTx(f func(ctx DBContext) error) error {
sess := x.NewSession()
+ defer sess.Close()
if err := sess.Begin(); err != nil {
- sess.Close()
return err
}
if err := f(DBContext{sess}); err != nil {
- sess.Close()
return err
}
- err := sess.Commit()
- sess.Close()
- return err
+ return sess.Commit()
}
// Iterate iterates the databases and doing something
diff --git a/models/fixtures/oauth2_grant.yml b/models/fixtures/oauth2_grant.yml
index 105e3f22db..e52a2bce95 100644
--- a/models/fixtures/oauth2_grant.yml
+++ b/models/fixtures/oauth2_grant.yml
@@ -5,3 +5,19 @@
scope: "openid profile"
created_unix: 1546869730
updated_unix: 1546869730
+
+- id: 2
+ user_id: 3
+ application_id: 1
+ counter: 1
+ scope: "openid"
+ created_unix: 1546869730
+ updated_unix: 1546869730
+
+- id: 3
+ user_id: 5
+ application_id: 1
+ counter: 1
+ scope: "openid profile email"
+ created_unix: 1546869730
+ updated_unix: 1546869730
\ No newline at end of file
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index 492040316c..51ef840060 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -568,7 +568,7 @@
-
id: 40
owner_id: 23
- owner_name: limited_org
+ owner_name: privated_org
lower_name: public_repo_on_private_org
name: public_repo_on_private_org
is_private: false
@@ -581,7 +581,7 @@
-
id: 41
owner_id: 23
- owner_name: limited_org
+ owner_name: privated_org
lower_name: private_repo_on_private_org
name: private_repo_on_private_org
is_private: true
diff --git a/models/gpg_key_add.go b/models/gpg_key_add.go
index 1e589e7fee..baf1ded023 100644
--- a/models/gpg_key_add.go
+++ b/models/gpg_key_add.go
@@ -99,6 +99,46 @@ func AddGPGKey(ownerID int64, content, token, signature string) ([]*GPGKey, erro
verified = true
}
+ if len(ekeys) > 1 {
+ id2key := map[string]*openpgp.Entity{}
+ newEKeys := make([]*openpgp.Entity, 0, len(ekeys))
+ for _, ekey := range ekeys {
+ id := ekey.PrimaryKey.KeyIdString()
+ if original, has := id2key[id]; has {
+ // Coalesce this with the other one
+ for _, subkey := range ekey.Subkeys {
+ if subkey.PublicKey == nil {
+ continue
+ }
+ found := false
+
+ for _, originalSubkey := range original.Subkeys {
+ if originalSubkey.PublicKey == nil {
+ continue
+ }
+ if originalSubkey.PublicKey.KeyId == subkey.PublicKey.KeyId {
+ found = true
+ break
+ }
+ }
+ if !found {
+ original.Subkeys = append(original.Subkeys, subkey)
+ }
+ }
+ for name, identity := range ekey.Identities {
+ if _, has := original.Identities[name]; has {
+ continue
+ }
+ original.Identities[name] = identity
+ }
+ continue
+ }
+ id2key[id] = ekey
+ newEKeys = append(newEKeys, ekey)
+ }
+ ekeys = newEKeys
+ }
+
for _, ekey := range ekeys {
// Key ID cannot be duplicated.
has, err := sess.Where("key_id=?", ekey.PrimaryKey.KeyIdString()).
diff --git a/models/index.go b/models/index.go
index 18db13c490..750d333829 100644
--- a/models/index.go
+++ b/models/index.go
@@ -14,7 +14,7 @@ import (
// ResourceIndex represents a resource index which could be used as issue/release and others
// We can create different tables i.e. issue_index, release_index and etc.
type ResourceIndex struct {
- GroupID int64 `xorm:"unique"`
+ GroupID int64 `xorm:"pk unique"`
MaxIndex int64 `xorm:"index"`
}
diff --git a/models/issue.go b/models/issue.go
index 225dfee20f..4e8b41f8ce 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -982,6 +982,31 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
return opts.Issue.addCrossReferences(e, doer, false)
}
+// RecalculateIssueIndexForRepo create issue_index for repo if not exist and
+// update it based on highest index of existing issues assigned to a repo
+func RecalculateIssueIndexForRepo(repoID int64) error {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ if err := upsertResourceIndex(sess, "issue_index", repoID); err != nil {
+ return err
+ }
+
+ var max int64
+ if _, err := sess.Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
+ return err
+ }
+
+ if _, err := sess.Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil {
+ return err
+ }
+
+ return sess.Commit()
+}
+
// NewIssue creates new issue with labels for repository.
func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
idx, err := GetNextResourceIndex("issue_index", repo.ID)
@@ -1120,17 +1145,17 @@ type IssuesOptions struct {
func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64) {
switch sortType {
case "oldest":
- sess.Asc("issue.created_unix")
+ sess.Asc("issue.created_unix").Asc("issue.id")
case "recentupdate":
- sess.Desc("issue.updated_unix")
+ sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id")
case "leastupdate":
- sess.Asc("issue.updated_unix")
+ sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id")
case "mostcomment":
- sess.Desc("issue.num_comments")
+ sess.Desc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id")
case "leastcomment":
- sess.Asc("issue.num_comments")
+ sess.Asc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id")
case "priority":
- sess.Desc("issue.priority")
+ sess.Desc("issue.priority").Desc("issue.created_unix").Desc("issue.id")
case "nearduedate":
// 253370764800 is 01/01/9999 @ 12:00am (UTC)
sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id").
@@ -1138,17 +1163,25 @@ func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64
"WHEN issue.deadline_unix = 0 AND (milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL) THEN 253370764800 " +
"WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " +
"WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " +
- "ELSE issue.deadline_unix END ASC")
+ "ELSE issue.deadline_unix END ASC").
+ Desc("issue.created_unix").
+ Desc("issue.id")
case "farduedate":
sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id").
OrderBy("CASE " +
"WHEN milestone.deadline_unix IS NULL THEN issue.deadline_unix " +
"WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " +
- "ELSE issue.deadline_unix END DESC")
+ "ELSE issue.deadline_unix END DESC").
+ Desc("issue.created_unix").
+ Desc("issue.id")
case "priorityrepo":
- sess.OrderBy("CASE WHEN issue.repo_id = " + strconv.FormatInt(priorityRepoID, 10) + " THEN 1 ELSE 2 END, issue.created_unix DESC")
+ sess.OrderBy("CASE " +
+ "WHEN issue.repo_id = " + strconv.FormatInt(priorityRepoID, 10) + " THEN 1 " +
+ "ELSE 2 END ASC").
+ Desc("issue.created_unix").
+ Desc("issue.id")
default:
- sess.Desc("issue.created_unix")
+ sess.Desc("issue.created_unix").Desc("issue.id")
}
}
@@ -1492,12 +1525,12 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) {
stats := &IssueStats{}
- countSession := func(opts *IssueStatsOptions) *xorm.Session {
+ countSession := func(opts *IssueStatsOptions, issueIDs []int64) *xorm.Session {
sess := x.
Where("issue.repo_id = ?", opts.RepoID)
- if len(opts.IssueIDs) > 0 {
- sess.In("issue.id", opts.IssueIDs)
+ if len(issueIDs) > 0 {
+ sess.In("issue.id", issueIDs)
}
if len(opts.Labels) > 0 && opts.Labels != "0" {
@@ -1547,13 +1580,13 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
}
var err error
- stats.OpenCount, err = countSession(opts).
+ stats.OpenCount, err = countSession(opts, issueIDs).
And("issue.is_closed = ?", false).
Count(new(Issue))
if err != nil {
return stats, err
}
- stats.ClosedCount, err = countSession(opts).
+ stats.ClosedCount, err = countSession(opts, issueIDs).
And("issue.is_closed = ?", true).
Count(new(Issue))
return stats, err
diff --git a/models/issue_comment.go b/models/issue_comment.go
index 4c5b77ff8e..cd24b655d1 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -762,13 +762,12 @@ func updateCommentInfos(e *xorm.Session, opts *CreateCommentOptions, comment *Co
}
}
fallthrough
- case CommentTypeReview:
- fallthrough
case CommentTypeComment:
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
return err
}
-
+ fallthrough
+ case CommentTypeReview:
// Check attachments
attachments, err := getAttachmentsByUUIDs(e, opts.Attachments)
if err != nil {
diff --git a/models/issue_milestone.go b/models/issue_milestone.go
index 5e934cde0a..81fa2baba1 100644
--- a/models/issue_milestone.go
+++ b/models/issue_milestone.go
@@ -127,22 +127,6 @@ func GetMilestoneByRepoIDANDName(repoID int64, name string) (*Milestone, error)
return &mile, nil
}
-// GetMilestoneByID returns the milestone via id .
-func GetMilestoneByID(id int64) (*Milestone, error) {
- return getMilestoneByID(x, id)
-}
-
-func getMilestoneByID(e Engine, id int64) (*Milestone, error) {
- var m Milestone
- has, err := e.ID(id).Get(&m)
- if err != nil {
- return nil, err
- } else if !has {
- return nil, ErrMilestoneNotExist{ID: id, RepoID: 0}
- }
- return &m, nil
-}
-
// UpdateMilestone updates information of given milestone.
func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
sess := x.NewSession()
diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go
index b72dcaf60c..4b53b48111 100644
--- a/models/issue_stopwatch.go
+++ b/models/issue_stopwatch.go
@@ -9,8 +9,30 @@ import (
"time"
"code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/xorm"
)
+// ErrIssueStopwatchNotExist represents an error that stopwatch is not exist
+type ErrIssueStopwatchNotExist struct {
+ UserID int64
+ IssueID int64
+}
+
+func (err ErrIssueStopwatchNotExist) Error() string {
+ return fmt.Sprintf("issue stopwatch doesn't exist[uid: %d, issue_id: %d", err.UserID, err.IssueID)
+}
+
+// ErrIssueStopwatchAlreadyExist represents an error that stopwatch is already exist
+type ErrIssueStopwatchAlreadyExist struct {
+ UserID int64
+ IssueID int64
+}
+
+func (err ErrIssueStopwatchAlreadyExist) Error() string {
+ return fmt.Sprintf("issue stopwatch already exists[uid: %d, issue_id: %d", err.UserID, err.IssueID)
+}
+
// Stopwatch represents a stopwatch for time tracking.
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
@@ -61,107 +83,185 @@ func StopwatchExists(userID, issueID int64) bool {
// HasUserStopwatch returns true if the user has a stopwatch
func HasUserStopwatch(userID int64) (exists bool, sw *Stopwatch, err error) {
+ return hasUserStopwatch(x, userID)
+}
+
+func hasUserStopwatch(e Engine, userID int64) (exists bool, sw *Stopwatch, err error) {
sw = new(Stopwatch)
- exists, err = x.
+ exists, err = e.
Where("user_id = ?", userID).
Get(sw)
return
}
-// CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline.
-func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
- sw, exists, err := getStopwatch(x, user.ID, issue.ID)
+// FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore
+func FinishIssueStopwatchIfPossible(user *User, issue *Issue) error {
+ _, exists, err := getStopwatch(x, user.ID, issue.ID)
if err != nil {
return err
}
- if err := issue.loadRepo(x); err != nil {
+ if !exists {
+ return nil
+ }
+ return FinishIssueStopwatch(user, issue)
+}
+
+// CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline.
+func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
+ _, exists, err := getStopwatch(x, user.ID, issue.ID)
+ if err != nil {
+ return err
+ }
+ if exists {
+ return FinishIssueStopwatch(user, issue)
+ }
+ return CreateIssueStopwatch(user, issue)
+}
+
+// FinishIssueStopwatch if stopwatch exist then finish it otherwise return an error
+func FinishIssueStopwatch(user *User, issue *Issue) error {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+ if err := finishIssueStopwatch(sess, user, issue); err != nil {
+ return err
+ }
+ return sess.Commit()
+}
+
+func finishIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error {
+ sw, exists, err := getStopwatch(e, user.ID, issue.ID)
+ if err != nil {
+ return err
+ }
+ if !exists {
+ return ErrIssueStopwatchNotExist{
+ UserID: user.ID,
+ IssueID: issue.ID,
+ }
+ }
+
+ // Create tracked time out of the time difference between start date and actual date
+ timediff := time.Now().Unix() - int64(sw.CreatedUnix)
+
+ // Create TrackedTime
+ tt := &TrackedTime{
+ Created: time.Now(),
+ IssueID: issue.ID,
+ UserID: user.ID,
+ Time: timediff,
+ }
+
+ if _, err := e.Insert(tt); err != nil {
return err
}
+ if err := issue.loadRepo(e); err != nil {
+ return err
+ }
+ if _, err := createComment(e, &CreateCommentOptions{
+ Doer: user,
+ Issue: issue,
+ Repo: issue.Repo,
+ Content: SecToTime(timediff),
+ Type: CommentTypeStopTracking,
+ TimeID: tt.ID,
+ }); err != nil {
+ return err
+ }
+ _, err = e.Delete(sw)
+ return err
+}
+
+// CreateIssueStopwatch creates a stopwatch if not exist, otherwise return an error
+func CreateIssueStopwatch(user *User, issue *Issue) error {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+ if err := createIssueStopwatch(sess, user, issue); err != nil {
+ return err
+ }
+ return sess.Commit()
+}
+
+func createIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error {
+ if err := issue.loadRepo(e); err != nil {
+ return err
+ }
+
+ // if another stopwatch is running: stop it
+ exists, sw, err := hasUserStopwatch(e, user.ID)
+ if err != nil {
+ return err
+ }
if exists {
- // Create tracked time out of the time difference between start date and actual date
- timediff := time.Now().Unix() - int64(sw.CreatedUnix)
-
- // Create TrackedTime
- tt := &TrackedTime{
- Created: time.Now(),
- IssueID: issue.ID,
- UserID: user.ID,
- Time: timediff,
- }
-
- if _, err := x.Insert(tt); err != nil {
- return err
- }
-
- if _, err := CreateComment(&CreateCommentOptions{
- Doer: user,
- Issue: issue,
- Repo: issue.Repo,
- Content: SecToTime(timediff),
- Type: CommentTypeStopTracking,
- TimeID: tt.ID,
- }); err != nil {
- return err
- }
- if _, err := x.Delete(sw); err != nil {
- return err
- }
- } else {
- // if another stopwatch is running: stop it
- exists, sw, err := HasUserStopwatch(user.ID)
+ issue, err := getIssueByID(e, sw.IssueID)
if err != nil {
return err
}
- if exists {
- issue, err := getIssueByID(x, sw.IssueID)
- if err != nil {
- return err
- }
- if err := CreateOrStopIssueStopwatch(user, issue); err != nil {
- return err
- }
- }
-
- // Create stopwatch
- sw = &Stopwatch{
- UserID: user.ID,
- IssueID: issue.ID,
- }
-
- if _, err := x.Insert(sw); err != nil {
+ if err := finishIssueStopwatch(e, user, issue); err != nil {
return err
}
+ }
- if _, err := CreateComment(&CreateCommentOptions{
- Doer: user,
- Issue: issue,
- Repo: issue.Repo,
- Type: CommentTypeStartTracking,
- }); err != nil {
- return err
- }
+ // Create stopwatch
+ sw = &Stopwatch{
+ UserID: user.ID,
+ IssueID: issue.ID,
+ }
+
+ if _, err := e.Insert(sw); err != nil {
+ return err
+ }
+
+ if err := issue.loadRepo(e); err != nil {
+ return err
+ }
+
+ if _, err := createComment(e, &CreateCommentOptions{
+ Doer: user,
+ Issue: issue,
+ Repo: issue.Repo,
+ Type: CommentTypeStartTracking,
+ }); err != nil {
+ return err
}
return nil
}
// CancelStopwatch removes the given stopwatch and logs it into issue's timeline.
func CancelStopwatch(user *User, issue *Issue) error {
- sw, exists, err := getStopwatch(x, user.ID, issue.ID)
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+ if err := cancelStopwatch(sess, user, issue); err != nil {
+ return err
+ }
+ return sess.Commit()
+}
+
+func cancelStopwatch(e *xorm.Session, user *User, issue *Issue) error {
+ sw, exists, err := getStopwatch(e, user.ID, issue.ID)
if err != nil {
return err
}
if exists {
- if _, err := x.Delete(sw); err != nil {
+ if _, err := e.Delete(sw); err != nil {
return err
}
- if err := issue.loadRepo(x); err != nil {
+ if err := issue.loadRepo(e); err != nil {
return err
}
- if _, err := CreateComment(&CreateCommentOptions{
+ if _, err := createComment(e, &CreateCommentOptions{
Doer: user,
Issue: issue,
Repo: issue.Repo,
diff --git a/models/issue_test.go b/models/issue_test.go
index f2c9b7a68f..ec41c44ed3 100644
--- a/models/issue_test.go
+++ b/models/issue_test.go
@@ -5,7 +5,9 @@
package models
import (
+ "fmt"
"sort"
+ "sync"
"testing"
"time"
@@ -417,3 +419,43 @@ func TestIssue_ResolveMentions(t *testing.T) {
// Private repo, whole team
testSuccess("user17", "big_test_private_4", "user15", []string{"user17/owners"}, []int64{18})
}
+
+func TestCorrectIssueStats(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ // Because the condition is to have chunked database look-ups,
+ // We have to more issues than `maxQueryParameters`, we will insert.
+ // maxQueryParameters + 10 issues into the testDatabase.
+ // Each new issues will have a constant description "Bugs are nasty"
+ // Which will be used later on.
+
+ issueAmount := maxQueryParameters + 10
+
+ var wg sync.WaitGroup
+ for i := 0; i < issueAmount; i++ {
+ wg.Add(1)
+ go func(i int) {
+ testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
+ wg.Done()
+ }(i)
+ }
+ wg.Wait()
+
+ // Now we will get all issueID's that match the "Bugs are nasty" query.
+ total, ids, err := SearchIssueIDsByKeyword("Bugs are nasty", []int64{1}, issueAmount, 0)
+
+ // Just to be sure.
+ assert.NoError(t, err)
+ assert.EqualValues(t, issueAmount, total)
+
+ // Now we will call the GetIssueStats with these IDs and if working,
+ // get the correct stats back.
+ issueStats, err := GetIssueStats(&IssueStatsOptions{
+ RepoID: 1,
+ IssueIDs: ids,
+ })
+
+ // Now check the values.
+ assert.NoError(t, err)
+ assert.EqualValues(t, issueStats.OpenCount, issueAmount)
+}
diff --git a/models/login_source.go b/models/login_source.go
index f9bd496b3a..5897743269 100644
--- a/models/login_source.go
+++ b/models/login_source.go
@@ -7,6 +7,7 @@ package models
import (
"crypto/tls"
+ "encoding/binary"
"errors"
"fmt"
"net/smtp"
@@ -70,13 +71,32 @@ var (
_ convert.Conversion = &SSPIConfig{}
)
-// jsonUnmarshalIgnoreErroneousBOM - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
-// possible that a Blob may gain an unwanted prefix of 0xff 0xfe.
-func jsonUnmarshalIgnoreErroneousBOM(bs []byte, v interface{}) error {
+// JSONUnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's
+// possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe.
+func JSONUnmarshalHandleDoubleEncode(bs []byte, v interface{}) error {
json := jsoniter.ConfigCompatibleWithStandardLibrary
- err := json.Unmarshal(bs, &v)
+ err := json.Unmarshal(bs, v)
+ if err != nil {
+ ok := true
+ rs := []byte{}
+ temp := make([]byte, 2)
+ for _, rn := range string(bs) {
+ if rn > 0xffff {
+ ok = false
+ break
+ }
+ binary.LittleEndian.PutUint16(temp, uint16(rn))
+ rs = append(rs, temp...)
+ }
+ if ok {
+ if len(rs) > 1 && rs[0] == 0xff && rs[1] == 0xfe {
+ rs = rs[2:]
+ }
+ err = json.Unmarshal(rs, v)
+ }
+ }
if err != nil && len(bs) > 2 && bs[0] == 0xff && bs[1] == 0xfe {
- err = json.Unmarshal(bs[2:], &v)
+ err = json.Unmarshal(bs[2:], v)
}
return err
}
@@ -88,7 +108,7 @@ type LDAPConfig struct {
// FromDB fills up a LDAPConfig from serialized format.
func (cfg *LDAPConfig) FromDB(bs []byte) error {
- err := jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
+ err := JSONUnmarshalHandleDoubleEncode(bs, &cfg)
if err != nil {
return err
}
@@ -129,7 +149,7 @@ type SMTPConfig struct {
// FromDB fills up an SMTPConfig from serialized format.
func (cfg *SMTPConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports an SMTPConfig to a serialized format.
@@ -146,7 +166,7 @@ type PAMConfig struct {
// FromDB fills up a PAMConfig from serialized format.
func (cfg *PAMConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports a PAMConfig to a serialized format.
@@ -167,7 +187,7 @@ type OAuth2Config struct {
// FromDB fills up an OAuth2Config from serialized format.
func (cfg *OAuth2Config) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports an SMTPConfig to a serialized format.
@@ -187,7 +207,7 @@ type SSPIConfig struct {
// FromDB fills up an SSPIConfig from serialized format.
func (cfg *SSPIConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, cfg)
}
// ToDB exports an SSPIConfig to a serialized format.
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 7a4193199c..dd251a5bcd 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -7,6 +7,7 @@ package migrations
import (
"context"
+ "errors"
"fmt"
"os"
"reflect"
@@ -590,11 +591,26 @@ func recreateTable(sess *xorm.Session, bean interface{}) error {
return err
}
+ if err := sess.Table(tempTableName).DropIndexes(bean); err != nil {
+ log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err)
+ return err
+ }
+
// SQLite and MySQL will move all the constraints from the temporary table to the new table
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil {
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
return err
}
+
+ if err := sess.Table(tableName).CreateIndexes(bean); err != nil {
+ log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err)
+ return err
+ }
+
+ if err := sess.Table(tableName).CreateUniques(bean); err != nil {
+ log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
+ return err
+ }
case setting.Database.UsePostgreSQL:
var originalSequences []string
type sequenceData struct {
@@ -747,8 +763,14 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
}
tableSQL := string(res[0]["sql"])
+ // Get the string offset for column definitions: `CREATE TABLE ( column-definitions... )`
+ columnDefinitionsIndex := strings.Index(tableSQL, "(")
+ if columnDefinitionsIndex < 0 {
+ return errors.New("couldn't find column definitions")
+ }
+
// Separate out the column definitions
- tableSQL = tableSQL[strings.Index(tableSQL, "("):]
+ tableSQL = tableSQL[columnDefinitionsIndex:]
// Remove the required columnNames
for _, name := range columnNames {
@@ -836,7 +858,7 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
}
cols += "`" + strings.ToLower(col) + "`"
}
- sql := fmt.Sprintf("SELECT Name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('%[1]s') AND PARENT_COLUMN_ID IN (SELECT column_id FROM sys.columns WHERE lower(NAME) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
+ sql := fmt.Sprintf("SELECT Name FROM sys.default_constraints WHERE parent_object_id = OBJECT_ID('%[1]s') AND parent_column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
tableName, strings.ReplaceAll(cols, "`", "'"))
constraints := make([]string, 0)
if err := sess.SQL(sql).Find(&constraints); err != nil {
@@ -847,17 +869,14 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
return fmt.Errorf("Drop table `%s` default constraint `%s`: %v", tableName, constraint, err)
}
}
- sql = fmt.Sprintf("SELECT DISTINCT Name FROM SYS.INDEXES INNER JOIN SYS.INDEX_COLUMNS ON INDEXES.INDEX_ID = INDEX_COLUMNS.INDEX_ID AND INDEXES.OBJECT_ID = INDEX_COLUMNS.OBJECT_ID WHERE INDEXES.OBJECT_ID = OBJECT_ID('%[1]s') AND INDEX_COLUMNS.COLUMN_ID IN (SELECT column_id FROM sys.columns WHERE lower(NAME) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
+ sql = fmt.Sprintf("SELECT DISTINCT Name FROM sys.indexes INNER JOIN sys.index_columns ON indexes.index_id = index_columns.index_id AND indexes.object_id = index_columns.object_id WHERE indexes.object_id = OBJECT_ID('%[1]s') AND index_columns.column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))",
tableName, strings.ReplaceAll(cols, "`", "'"))
constraints = make([]string, 0)
if err := sess.SQL(sql).Find(&constraints); err != nil {
return fmt.Errorf("Find constraints: %v", err)
}
for _, constraint := range constraints {
- if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT IF EXISTS `%s`", tableName, constraint)); err != nil {
- return fmt.Errorf("Drop table `%s` index constraint `%s`: %v", tableName, constraint, err)
- }
- if _, err := sess.Exec(fmt.Sprintf("DROP INDEX IF EXISTS `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil {
+ if _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil {
return fmt.Errorf("Drop index `%[2]s` on `%[1]s`: %v", tableName, constraint, err)
}
}
diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go
index 26066580d8..faab36697d 100644
--- a/models/migrations/migrations_test.go
+++ b/models/migrations/migrations_test.go
@@ -205,6 +205,25 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
setting.RepoRootPath))
+ ownerDirs, err := os.ReadDir(setting.RepoRootPath)
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, ownerDir := range ownerDirs {
+ if !ownerDir.Type().IsDir() {
+ continue
+ }
+ repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
+ if err != nil {
+ assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
+ }
+ for _, repoDir := range repoDirs {
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
+ }
+ }
if err := deleteDB(); err != nil {
t.Errorf("unable to reset database: %v", err)
diff --git a/models/migrations/v161.go b/models/migrations/v161.go
index 4ca9f01218..283c44464c 100644
--- a/models/migrations/v161.go
+++ b/models/migrations/v161.go
@@ -5,6 +5,8 @@
package migrations
import (
+ "context"
+
"xorm.io/xorm"
)
@@ -40,8 +42,17 @@ func convertTaskTypeToString(x *xorm.Engine) error {
return err
}
+ // to keep the migration could be rerun
+ exist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "hook_task", "type")
+ if err != nil {
+ return err
+ }
+ if !exist {
+ return nil
+ }
+
for i, s := range hookTaskTypes {
- if _, err := x.Exec("UPDATE hook_task set typ = ? where type=?", s, i); err != nil {
+ if _, err := x.Exec("UPDATE hook_task set typ = ? where `type`=?", s, i); err != nil {
return err
}
}
diff --git a/models/migrations/v182.go b/models/migrations/v182.go
index dd9a04f27e..29c2d2654a 100644
--- a/models/migrations/v182.go
+++ b/models/migrations/v182.go
@@ -10,8 +10,8 @@ import (
func addIssueResourceIndexTable(x *xorm.Engine) error {
type ResourceIndex struct {
- GroupID int64 `xorm:"index unique(s)"`
- MaxIndex int64 `xorm:"index unique(s)"`
+ GroupID int64 `xorm:"pk"`
+ MaxIndex int64 `xorm:"index"`
}
sess := x.NewSession()
diff --git a/models/migrations/v182_test.go b/models/migrations/v182_test.go
index 6f418f7794..9fb371e078 100644
--- a/models/migrations/v182_test.go
+++ b/models/migrations/v182_test.go
@@ -33,8 +33,8 @@ func Test_addIssueResourceIndexTable(t *testing.T) {
}
type ResourceIndex struct {
- GroupID int64 `xorm:"index unique(s)"`
- MaxIndex int64 `xorm:"index unique(s)"`
+ GroupID int64 `xorm:"pk"`
+ MaxIndex int64 `xorm:"index"`
}
var start = 0
diff --git a/models/migrations/v184.go b/models/migrations/v184.go
index b7be342b87..97bc72d5d9 100644
--- a/models/migrations/v184.go
+++ b/models/migrations/v184.go
@@ -5,6 +5,7 @@
package migrations
import (
+ "context"
"fmt"
"code.gitea.io/gitea/modules/setting"
@@ -19,6 +20,22 @@ func renameTaskErrorsToMessage(x *xorm.Engine) error {
Status int `xorm:"index"`
}
+ // This migration maybe rerun so that we should check if it has been run
+ messageExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "task", "message")
+ if err != nil {
+ return err
+ }
+
+ if messageExist {
+ errorsExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "task", "errors")
+ if err != nil {
+ return err
+ }
+ if !errorsExist {
+ return nil
+ }
+ }
+
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
@@ -29,6 +46,13 @@ func renameTaskErrorsToMessage(x *xorm.Engine) error {
return fmt.Errorf("error on Sync2: %v", err)
}
+ if messageExist {
+ // if both errors and message exist, drop message at first
+ if err := dropTableColumns(sess, "task", "message"); err != nil {
+ return err
+ }
+ }
+
switch {
case setting.Database.UseMySQL:
if _, err := sess.Exec("ALTER TABLE `task` CHANGE errors message text"); err != nil {
diff --git a/models/models.go b/models/models.go
index 610933d327..3b1fe91633 100644
--- a/models/models.go
+++ b/models/models.go
@@ -33,7 +33,7 @@ type Engine interface {
Table(tableNameOrBean interface{}) *xorm.Session
Count(...interface{}) (int64, error)
Decr(column string, arg ...interface{}) *xorm.Session
- Delete(interface{}) (int64, error)
+ Delete(...interface{}) (int64, error)
Exec(...interface{}) (sql.Result, error)
Find(interface{}, ...interface{}) error
Get(interface{}) (bool, error)
@@ -179,16 +179,35 @@ func syncTables() error {
return x.StoreEngine("InnoDB").Sync2(tables...)
}
-// NewTestEngine sets a new test xorm.Engine
-func NewTestEngine() (err error) {
+// NewInstallTestEngine creates a new xorm.Engine for testing during install
+//
+// This function will cause the basic database schema to be created
+func NewInstallTestEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
x, err = GetNewEngine()
if err != nil {
- return fmt.Errorf("Connect to database: %v", err)
+ return fmt.Errorf("failed to connect to database: %w", err)
}
x.SetMapper(names.GonicMapper{})
x.SetLogger(NewXORMLogger(!setting.IsProd()))
x.ShowSQL(!setting.IsProd())
+
+ x.SetDefaultContext(ctx)
+
+ if err = x.Ping(); err != nil {
+ return err
+ }
+
+ // We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
+ // If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
+ //
+ // Installation should only be being re-run if users want to recover an old database.
+ // However, we should think carefully about should we support re-install on an installed instance,
+ // as there may be other problems due to secret reinitialization.
+ if err = migrateFunc(x); err != nil {
+ return fmt.Errorf("migrate: %v", err)
+ }
+
return syncTables()
}
diff --git a/models/models_test.go b/models/models_test.go
index 9793394e0b..626856df7d 100644
--- a/models/models_test.go
+++ b/models/models_test.go
@@ -8,9 +8,12 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
"testing"
+ "code.gitea.io/gitea/modules/auth/oauth2"
"code.gitea.io/gitea/modules/setting"
+ "xorm.io/xorm/schemas"
"github.com/stretchr/testify/assert"
)
@@ -32,3 +35,26 @@ func TestDumpDatabase(t *testing.T) {
assert.NoError(t, DumpDatabase(filepath.Join(dir, dbType+".sql"), dbType))
}
}
+
+func TestDumpLoginSource(t *testing.T) {
+ assert.NoError(t, PrepareTestDatabase())
+
+ loginSourceSchema, err := x.TableInfo(new(LoginSource))
+ assert.NoError(t, err)
+
+ CreateLoginSource(&LoginSource{
+ Type: LoginOAuth2,
+ Name: "TestSource",
+ IsActived: false,
+ Cfg: &OAuth2Config{
+ Provider: "TestSourceProvider",
+ CustomURLMapping: &oauth2.CustomURLMapping{},
+ },
+ })
+
+ sb := new(strings.Builder)
+
+ x.DumpTables([]*schemas.Table{loginSourceSchema}, sb)
+
+ assert.Contains(t, sb.String(), `"Provider":"TestSourceProvider"`)
+}
diff --git a/models/oauth2.go b/models/oauth2.go
index 46da60e02d..8693726a70 100644
--- a/models/oauth2.go
+++ b/models/oauth2.go
@@ -155,11 +155,6 @@ func initOAuth2LoginSources() error {
err := oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret, oAuth2Config.OpenIDConnectAutoDiscoveryURL, oAuth2Config.CustomURLMapping)
if err != nil {
log.Critical("Unable to register source: %s due to Error: %v. This source will be disabled.", source.Name, err)
- source.IsActived = false
- if err = UpdateSource(source); err != nil {
- log.Critical("Unable to update source %s to disable it. Error: %v", err)
- return err
- }
}
}
return nil
diff --git a/models/oauth2_application.go b/models/oauth2_application.go
index 5a924763be..f0e51b18d4 100644
--- a/models/oauth2_application.go
+++ b/models/oauth2_application.go
@@ -17,7 +17,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
- "github.com/dgrijalva/jwt-go"
+ "github.com/golang-jwt/jwt"
uuid "github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
"xorm.io/xorm"
diff --git a/models/org.go b/models/org.go
index 58fb26b1bb..f670ad2ae1 100644
--- a/models/org.go
+++ b/models/org.go
@@ -425,23 +425,67 @@ func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) {
return getOrgsByUserID(sess, userID, showAll)
}
-// queryUserOrgIDs returns a condition to return user's organization id
-func queryUserOrgIDs(uid int64) *builder.Builder {
- return builder.Select("team.org_id").
- From("team_user").InnerJoin("team", "team.id = team_user.team_id").
- Where(builder.Eq{"team_user.uid": uid})
-}
-
// MinimalOrg represents a simple orgnization with only needed columns
type MinimalOrg = User
// GetUserOrgsList returns one user's all orgs list
-func GetUserOrgsList(uid int64) ([]*MinimalOrg, error) {
- var orgs = make([]*MinimalOrg, 0, 20)
- return orgs, x.Select("id, name, full_name, visibility, avatar, avatar_email, use_custom_avatar").
+func GetUserOrgsList(user *User) ([]*MinimalOrg, error) {
+ sess := x.NewSession()
+ defer sess.Close()
+
+ schema, err := x.TableInfo(new(User))
+ if err != nil {
+ return nil, err
+ }
+
+ outputCols := []string{
+ "id",
+ "name",
+ "full_name",
+ "visibility",
+ "avatar",
+ "avatar_email",
+ "use_custom_avatar",
+ }
+
+ groupByCols := &strings.Builder{}
+ for _, col := range outputCols {
+ fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
+ }
+ groupByStr := groupByCols.String()
+ groupByStr = groupByStr[0 : len(groupByStr)-1]
+
+ sess.Select(groupByStr+", count(distinct repo_id) as org_count").
Table("user").
- In("id", queryUserOrgIDs(uid)).
- Find(&orgs)
+ Join("INNER", "team", "`team`.org_id = `user`.id").
+ Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
+ Join("LEFT", builder.
+ Select("id as repo_id, owner_id as repo_owner_id").
+ From("repository").
+ Where(accessibleRepositoryCondition(user)), "`repository`.repo_owner_id = `team`.org_id").
+ Where("`team_user`.uid = ?", user.ID).
+ GroupBy(groupByStr)
+
+ type OrgCount struct {
+ User `xorm:"extends"`
+ OrgCount int
+ }
+
+ orgCounts := make([]*OrgCount, 0, 10)
+
+ if err := sess.
+ Asc("`user`.name").
+ Find(&orgCounts); err != nil {
+ return nil, err
+ }
+
+ orgs := make([]*MinimalOrg, len(orgCounts))
+ for i, orgCount := range orgCounts {
+ orgCount.User.NumRepos = orgCount.OrgCount
+ orgs[i] = &orgCount.User
+ }
+
+ return orgs, nil
}
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
diff --git a/models/project_issue.go b/models/project_issue.go
index e35307158d..b5b3a6492c 100644
--- a/models/project_issue.go
+++ b/models/project_issue.go
@@ -115,7 +115,9 @@ func (p *Project) NumClosedIssues() int {
func (p *Project) NumOpenIssues() int {
c, err := x.Table("project_issue").
Join("INNER", "issue", "project_issue.issue_id=issue.id").
- Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).Count("issue.id")
+ Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).
+ Cols("issue_id").
+ Count()
if err != nil {
return 0
}
diff --git a/models/pull.go b/models/pull.go
index 3717878f42..c737688410 100644
--- a/models/pull.go
+++ b/models/pull.go
@@ -502,6 +502,9 @@ func GetLatestPullRequestByHeadInfo(repoID int64, branch string) (*PullRequest,
// GetPullRequestByIndex returns a pull request by the given index
func GetPullRequestByIndex(repoID, index int64) (*PullRequest, error) {
+ if index < 1 {
+ return nil, ErrPullRequestNotExist{}
+ }
pr := &PullRequest{
BaseRepoID: repoID,
Index: index,
diff --git a/models/pull_test.go b/models/pull_test.go
index 5eaeb60e67..055d5a5538 100644
--- a/models/pull_test.go
+++ b/models/pull_test.go
@@ -133,6 +133,10 @@ func TestGetPullRequestByIndex(t *testing.T) {
_, err = GetPullRequestByIndex(9223372036854775807, 9223372036854775807)
assert.Error(t, err)
assert.True(t, IsErrPullRequestNotExist(err))
+
+ _, err = GetPullRequestByIndex(1, 0)
+ assert.Error(t, err)
+ assert.True(t, IsErrPullRequestNotExist(err))
}
func TestGetPullRequestByID(t *testing.T) {
diff --git a/models/repo.go b/models/repo.go
index d6abc1b5e3..d56728ef36 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1125,7 +1125,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, overwriteO
// Give access to all members in teams with access to all repositories.
if u.IsOrganization() {
- if err := u.GetTeams(&SearchTeamOptions{}); err != nil {
+ if err := u.getTeams(ctx.e); err != nil {
return fmt.Errorf("GetTeams: %v", err)
}
for _, t := range u.Teams {
@@ -1165,6 +1165,46 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, overwriteO
return nil
}
+// CheckDaemonExportOK creates/removes git-daemon-export-ok for git-daemon...
+func (repo *Repository) CheckDaemonExportOK() error {
+ return repo.checkDaemonExportOK(x)
+}
+
+// CheckDaemonExportOKCtx creates/removes git-daemon-export-ok for git-daemon...
+func (repo *Repository) CheckDaemonExportOKCtx(ctx DBContext) error {
+ return repo.checkDaemonExportOK(ctx.e)
+}
+
+func (repo *Repository) checkDaemonExportOK(e Engine) error {
+ if err := repo.getOwner(e); err != nil {
+ return err
+ }
+
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
+
+ isExist, err := util.IsExist(daemonExportFile)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
+ return err
+ }
+
+ isPublic := !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePublic
+ if !isPublic && isExist {
+ if err = util.Remove(daemonExportFile); err != nil {
+ log.Error("Failed to remove %s: %v", daemonExportFile, err)
+ }
+ } else if isPublic && !isExist {
+ if f, err := os.Create(daemonExportFile); err != nil {
+ log.Error("Failed to create %s: %v", daemonExportFile, err)
+ } else {
+ f.Close()
+ }
+ }
+
+ return nil
+}
+
func countRepositories(userID int64, private bool) int64 {
sess := x.Where("id > 0")
@@ -1207,6 +1247,12 @@ func IncrementRepoForkNum(ctx DBContext, repoID int64) error {
return err
}
+// DecrementRepoForkNum decrement repository fork number
+func DecrementRepoForkNum(ctx DBContext, repoID int64) error {
+ _, err := ctx.e.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repoID)
+ return err
+}
+
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err error) {
oldRepoName := repo.Name
@@ -1308,23 +1354,9 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
}
// Create/Remove git-daemon-export-ok for git-daemon...
- daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
- isExist, err := util.IsExist(daemonExportFile)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
+ if err := repo.checkDaemonExportOK(e); err != nil {
return err
}
- if repo.IsPrivate && isExist {
- if err = util.Remove(daemonExportFile); err != nil {
- log.Error("Failed to remove %s: %v", daemonExportFile, err)
- }
- } else if !repo.IsPrivate && !isExist {
- if f, err := os.Create(daemonExportFile); err != nil {
- log.Error("Failed to create %s: %v", daemonExportFile, err)
- } else {
- f.Close()
- }
- }
forkRepos, err := getRepositoriesByForkID(e, repo.ID)
if err != nil {
@@ -1479,6 +1511,11 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
releaseAttachments = append(releaseAttachments, attachments[i].RelativePath())
}
+ if _, err = sess.In("release_id", builder.Select("id").From("`release`").Where(builder.Eq{"`release`.repo_id": repoID})).
+ Delete(&Attachment{}); err != nil {
+ return err
+ }
+
if _, err := sess.Exec("UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)", repo.ID); err != nil {
return err
}
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index b9488f5e2e..5dbba21242 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -8,6 +8,7 @@ package models
import (
"fmt"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
@@ -83,16 +84,21 @@ func (repo *Repository) getCollaborators(e Engine, listOptions ListOptions) ([]*
return nil, fmt.Errorf("getCollaborations: %v", err)
}
- collaborators := make([]*Collaborator, len(collaborations))
- for i, c := range collaborations {
+ collaborators := make([]*Collaborator, 0, len(collaborations))
+ for _, c := range collaborations {
user, err := getUserByID(e, c.UserID)
if err != nil {
- return nil, err
+ if IsErrUserNotExist(err) {
+ log.Warn("Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist", c.UserID, repo)
+ user = NewGhostUser()
+ } else {
+ return nil, err
+ }
}
- collaborators[i] = &Collaborator{
+ collaborators = append(collaborators, &Collaborator{
User: user,
Collaboration: c,
- }
+ })
}
return collaborators, nil
}
diff --git a/models/repo_issue.go b/models/repo_issue.go
index 433d0e39bd..cd27d4fb8f 100644
--- a/models/repo_issue.go
+++ b/models/repo_issue.go
@@ -21,13 +21,18 @@ func (repo *Repository) CanEnableTimetracker() bool {
// IsTimetrackerEnabled returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs.
func (repo *Repository) IsTimetrackerEnabled() bool {
+ return repo.isTimetrackerEnabled(x)
+}
+
+// IsTimetrackerEnabled returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs.
+func (repo *Repository) isTimetrackerEnabled(e Engine) bool {
if !setting.Service.EnableTimetracking {
return false
}
var u *RepoUnit
var err error
- if u, err = repo.GetUnit(UnitTypeIssues); err != nil {
+ if u, err = repo.getUnit(e, UnitTypeIssues); err != nil {
return setting.Service.DefaultEnableTimetracking
}
return u.IssuesConfig().EnableTimetracker
diff --git a/models/repo_list.go b/models/repo_list.go
index b988ceffac..772bd20be3 100644
--- a/models/repo_list.go
+++ b/models/repo_list.go
@@ -217,16 +217,14 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
cond = cond.And(accessibleRepositoryCondition(opts.Actor))
}
} else {
- // Not looking at private organisations
+ // Not looking at private organisations and users
// We should be able to see all non-private repositories that
// isn't in a private or limited organisation.
cond = cond.And(
builder.Eq{"is_private": false},
builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(
- builder.And(
- builder.Eq{"type": UserTypeOrganization},
- builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
- ))))
+ builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
+ )))
}
if opts.IsPrivate != util.OptionalBoolNone {
diff --git a/models/repo_transfer.go b/models/repo_transfer.go
index d7ef0a8ca6..37fd166a6d 100644
--- a/models/repo_transfer.go
+++ b/models/repo_transfer.go
@@ -269,6 +269,14 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
// Dummy object.
collaboration := &Collaboration{RepoID: repo.ID}
for _, c := range collaborators {
+ if c.IsGhost() {
+ collaboration.ID = c.Collaboration.ID
+ if _, err := sess.Delete(collaboration); err != nil {
+ return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
+ }
+ collaboration.ID = 0
+ }
+
if c.ID != newOwner.ID {
isMember, err := isOrganizationMember(sess, newOwner.ID, c.ID)
if err != nil {
@@ -281,6 +289,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
if _, err := sess.Delete(collaboration); err != nil {
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
}
+ collaboration.UserID = 0
}
// Remove old team-repository relations.
diff --git a/models/repo_unit.go b/models/repo_unit.go
index a12e056a7d..143ba1a42d 100644
--- a/models/repo_unit.go
+++ b/models/repo_unit.go
@@ -28,7 +28,7 @@ type UnitConfig struct{}
// FromDB fills up a UnitConfig from serialized format.
func (cfg *UnitConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a UnitConfig to a serialized format.
@@ -44,7 +44,7 @@ type ExternalWikiConfig struct {
// FromDB fills up a ExternalWikiConfig from serialized format.
func (cfg *ExternalWikiConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a ExternalWikiConfig to a serialized format.
@@ -62,7 +62,7 @@ type ExternalTrackerConfig struct {
// FromDB fills up a ExternalTrackerConfig from serialized format.
func (cfg *ExternalTrackerConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a ExternalTrackerConfig to a serialized format.
@@ -80,7 +80,7 @@ type IssuesConfig struct {
// FromDB fills up a IssuesConfig from serialized format.
func (cfg *IssuesConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a IssuesConfig to a serialized format.
@@ -104,7 +104,7 @@ type PullRequestsConfig struct {
// FromDB fills up a PullRequestsConfig from serialized format.
func (cfg *PullRequestsConfig) FromDB(bs []byte) error {
- return jsonUnmarshalIgnoreErroneousBOM(bs, &cfg)
+ return JSONUnmarshalHandleDoubleEncode(bs, &cfg)
}
// ToDB exports a PullRequestsConfig to a serialized format.
@@ -219,3 +219,9 @@ func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
return units, nil
}
+
+// UpdateRepoUnit updates the provided repo unit
+func UpdateRepoUnit(unit *RepoUnit) error {
+ _, err := x.ID(unit.ID).Update(unit)
+ return err
+}
diff --git a/models/review.go b/models/review.go
index acb54d970f..165e67922b 100644
--- a/models/review.go
+++ b/models/review.go
@@ -434,7 +434,7 @@ func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content, comm
// try to remove team review request if need
if issue.Repo.Owner.IsOrganization() && (reviewType == ReviewTypeApprove || reviewType == ReviewTypeReject) {
teamReviewRequests := make([]*Review, 0, 10)
- if err := sess.SQL("SELECT * FROM review WHERE reviewer_team_id > 0 AND type = ?", ReviewTypeRequest).Find(&teamReviewRequests); err != nil {
+ if err := sess.SQL("SELECT * FROM review WHERE issue_id = ? AND reviewer_team_id > 0 AND type = ?", issue.ID, ReviewTypeRequest).Find(&teamReviewRequests); err != nil {
return nil, nil, err
}
diff --git a/models/u2f.go b/models/u2f.go
index 28341906fa..c588783209 100644
--- a/models/u2f.go
+++ b/models/u2f.go
@@ -52,7 +52,7 @@ func (list U2FRegistrationList) ToRegistrations() []u2f.Registration {
for _, reg := range list {
r, err := reg.Parse()
if err != nil {
- log.Fatal("parsing u2f registration: %v", err)
+ log.Error("parsing u2f registration: %v", err)
continue
}
regs = append(regs, *r)
diff --git a/models/u2f_test.go b/models/u2f_test.go
index 7a38334cf8..5770217c39 100644
--- a/models/u2f_test.go
+++ b/models/u2f_test.go
@@ -5,6 +5,7 @@
package models
import (
+ "encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
@@ -27,6 +28,7 @@ func TestGetU2FRegistrationsByUID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())
res, err := GetU2FRegistrationsByUID(1)
+
assert.NoError(t, err)
assert.Len(t, res, 1)
assert.Equal(t, "U2F Key", res[0].Name)
@@ -71,3 +73,27 @@ func TestDeleteRegistration(t *testing.T) {
assert.NoError(t, DeleteRegistration(reg))
AssertNotExistsBean(t, &U2FRegistration{ID: 1})
}
+
+const validU2FRegistrationResponseHex = "0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c253082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871"
+
+func TestToRegistrations_SkipInvalidItemsWithoutCrashing(t *testing.T) {
+ regKeyRaw, _ := hex.DecodeString(validU2FRegistrationResponseHex)
+ regs := U2FRegistrationList{
+ &U2FRegistration{ID: 1},
+ &U2FRegistration{ID: 2, Name: "U2F Key", UserID: 2, Counter: 0, Raw: regKeyRaw, CreatedUnix: 946684800, UpdatedUnix: 946684800},
+ }
+
+ actual := regs.ToRegistrations()
+ assert.Len(t, actual, 1)
+}
+
+func TestToRegistrations(t *testing.T) {
+ regKeyRaw, _ := hex.DecodeString(validU2FRegistrationResponseHex)
+ regs := U2FRegistrationList{
+ &U2FRegistration{ID: 1, Name: "U2F Key", UserID: 1, Counter: 0, Raw: regKeyRaw, CreatedUnix: 946684800, UpdatedUnix: 946684800},
+ &U2FRegistration{ID: 2, Name: "U2F Key", UserID: 2, Counter: 0, Raw: regKeyRaw, CreatedUnix: 946684800, UpdatedUnix: 946684800},
+ }
+
+ actual := regs.ToRegistrations()
+ assert.Len(t, actual, 2)
+}
diff --git a/models/unit_tests.go b/models/unit_tests.go
index f8d6819333..03c9a2022a 100644
--- a/models/unit_tests.go
+++ b/models/unit_tests.go
@@ -87,6 +87,26 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
fatalTestError("util.CopyDir: %v\n", err)
}
+ ownerDirs, err := os.ReadDir(setting.RepoRootPath)
+ if err != nil {
+ fatalTestError("unable to read the new repo root: %v\n", err)
+ }
+ for _, ownerDir := range ownerDirs {
+ if !ownerDir.Type().IsDir() {
+ continue
+ }
+ repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
+ if err != nil {
+ fatalTestError("unable to read the new repo root: %v\n", err)
+ }
+ for _, repoDir := range repoDirs {
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
+ }
+ }
+
exitStatus := m.Run()
if err = util.RemoveAll(setting.RepoRootPath); err != nil {
fatalTestError("util.RemoveAll: %v\n", err)
@@ -128,6 +148,23 @@ func PrepareTestEnv(t testing.TB) {
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
assert.NoError(t, util.CopyDir(metaPath, setting.RepoRootPath))
+
+ ownerDirs, err := os.ReadDir(setting.RepoRootPath)
+ assert.NoError(t, err)
+ for _, ownerDir := range ownerDirs {
+ if !ownerDir.Type().IsDir() {
+ continue
+ }
+ repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
+ assert.NoError(t, err)
+ for _, repoDir := range repoDirs {
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
+ _ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
+ }
+ }
+
base.SetupGiteaRoot() // Makes sure GITEA_ROOT is set
}
diff --git a/models/user.go b/models/user.go
index f606da53d6..cb3691fb96 100644
--- a/models/user.go
+++ b/models/user.go
@@ -77,9 +77,6 @@ var (
// ErrEmailNotActivated e-mail address has not been activated error
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
- // ErrUserNameIllegal user name contains illegal characters error
- ErrUserNameIllegal = errors.New("User name contains illegal characters")
-
// ErrLoginSourceNotActived login source is not actived error
ErrLoginSourceNotActived = errors.New("Login source is not actived")
@@ -296,7 +293,7 @@ func (u *User) CanImportLocal() bool {
// DashboardLink returns the user dashboard page link.
func (u *User) DashboardLink() string {
if u.IsOrganization() {
- return u.OrganisationLink() + "/dashboard/"
+ return u.OrganisationLink() + "/dashboard"
}
return setting.AppSubURL + "/"
}
@@ -1062,9 +1059,9 @@ func checkDupEmail(e Engine, u *User) error {
return nil
}
-// validateUser check if user is valide to insert / update into database
+// validateUser check if user is valid to insert / update into database
func validateUser(u *User) error {
- if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) {
+ if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) && !u.IsOrganization() {
return fmt.Errorf("visibility Mode not allowed: %s", u.Visibility.String())
}
@@ -1072,18 +1069,46 @@ func validateUser(u *User) error {
return ValidateEmail(u.Email)
}
-func updateUser(e Engine, u *User) error {
+func updateUser(e Engine, u *User, changePrimaryEmail bool) error {
if err := validateUser(u); err != nil {
return err
}
+ if changePrimaryEmail {
+ var emailAddress EmailAddress
+ has, err := e.Where("lower_email=?", strings.ToLower(u.Email)).Get(&emailAddress)
+ if err != nil {
+ return err
+ }
+ if !has {
+ // 1. Update old primary email
+ if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
+ IsPrimary: false,
+ }); err != nil {
+ return err
+ }
+
+ emailAddress.Email = u.Email
+ emailAddress.UID = u.ID
+ emailAddress.IsActivated = true
+ emailAddress.IsPrimary = true
+ if _, err := e.Insert(&emailAddress); err != nil {
+ return err
+ }
+ } else if _, err := e.ID(emailAddress.ID).Cols("is_primary").Update(&EmailAddress{
+ IsPrimary: true,
+ }); err != nil {
+ return err
+ }
+ }
+
_, err := e.ID(u.ID).AllCols().Update(u)
return err
}
// UpdateUser updates user's information.
-func UpdateUser(u *User) error {
- return updateUser(x, u)
+func UpdateUser(u *User, changePrimaryEmail bool) error {
+ return updateUser(x, u, changePrimaryEmail)
}
// UpdateUserCols update user according special columns
@@ -1112,7 +1137,7 @@ func UpdateUserSetting(u *User) (err error) {
return err
}
}
- if err = updateUser(sess, u); err != nil {
+ if err = updateUser(sess, u, false); err != nil {
return err
}
return sess.Commit()
diff --git a/models/user_heatmap_test.go b/models/user_heatmap_test.go
index b2aaea6499..be44833789 100644
--- a/models/user_heatmap_test.go
+++ b/models/user_heatmap_test.go
@@ -7,6 +7,9 @@ package models
import (
"fmt"
"testing"
+ "time"
+
+ "code.gitea.io/gitea/modules/timeutil"
jsoniter "github.com/json-iterator/go"
"github.com/stretchr/testify/assert"
@@ -37,6 +40,10 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
// Prepare
assert.NoError(t, PrepareTestDatabase())
+ // Mock time
+ timeutil.Set(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC))
+ defer timeutil.Unset()
+
for i, tc := range testCases {
user := AssertExistsAndLoadBean(t, &User{ID: tc.userID}).(*User)
diff --git a/models/user_test.go b/models/user_test.go
index 34c465c586..af1c1e5ee6 100644
--- a/models/user_test.go
+++ b/models/user_test.go
@@ -475,17 +475,17 @@ func TestUpdateUser(t *testing.T) {
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
user.KeepActivityPrivate = true
- assert.NoError(t, UpdateUser(user))
+ assert.NoError(t, UpdateUser(user, false))
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
assert.True(t, user.KeepActivityPrivate)
setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false}
user.KeepActivityPrivate = false
user.Visibility = structs.VisibleTypePrivate
- assert.Error(t, UpdateUser(user))
+ assert.Error(t, UpdateUser(user, false))
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
assert.True(t, user.KeepActivityPrivate)
user.Email = "no mail@mail.org"
- assert.Error(t, UpdateUser(user))
+ assert.Error(t, UpdateUser(user, true))
}
diff --git a/modules/auth/oauth2/jwtsigningkey.go b/modules/auth/oauth2/jwtsigningkey.go
index 75e62a7c43..b8f1e40e8c 100644
--- a/modules/auth/oauth2/jwtsigningkey.go
+++ b/modules/auth/oauth2/jwtsigningkey.go
@@ -25,7 +25,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
- "github.com/dgrijalva/jwt-go"
+ "github.com/golang-jwt/jwt"
ini "gopkg.in/ini.v1"
)
diff --git a/modules/auth/oauth2/oauth2.go b/modules/auth/oauth2/oauth2.go
index 5d152e0a55..df49b1c4a3 100644
--- a/modules/auth/oauth2/oauth2.go
+++ b/modules/auth/oauth2/oauth2.go
@@ -7,6 +7,7 @@ package oauth2
import (
"net/http"
"net/url"
+ "sync"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -34,6 +35,7 @@ import (
var (
sessionUsersStoreKey = "gitea-oauth2-sessions"
providerHeaderKey = "gitea-oauth2-provider"
+ gothRWMutex = sync.RWMutex{}
)
// CustomURLMapping describes the urls values to use when customizing OAuth2 provider URLs
@@ -60,6 +62,10 @@ func Init(x *xorm.Engine) error {
// Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk
store.MaxLength(setting.OAuth2.MaxTokenLength)
+
+ gothRWMutex.Lock()
+ defer gothRWMutex.Unlock()
+
gothic.Store = store
gothic.SetState = func(req *http.Request) string {
@@ -82,6 +88,9 @@ func Auth(provider string, request *http.Request, response http.ResponseWriter)
// normally the gothic library will write some custom stuff to the response instead of our own nice error page
//gothic.BeginAuthHandler(response, request)
+ gothRWMutex.RLock()
+ defer gothRWMutex.RUnlock()
+
url, err := gothic.GetAuthURL(response, request)
if err == nil {
http.Redirect(response, request, url, http.StatusTemporaryRedirect)
@@ -95,6 +104,9 @@ func ProviderCallback(provider string, request *http.Request, response http.Resp
// not sure if goth is thread safe (?) when using multiple providers
request.Header.Set(providerHeaderKey, provider)
+ gothRWMutex.RLock()
+ defer gothRWMutex.RUnlock()
+
user, err := gothic.CompleteUserAuth(response, request)
if err != nil {
return user, err
@@ -108,6 +120,9 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID
provider, err := createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL, customURLMapping)
if err == nil && provider != nil {
+ gothRWMutex.Lock()
+ defer gothRWMutex.Unlock()
+
goth.UseProviders(provider)
}
@@ -116,11 +131,17 @@ func RegisterProvider(providerName, providerType, clientID, clientSecret, openID
// RemoveProvider removes the given OAuth2 provider from the goth lib
func RemoveProvider(providerName string) {
+ gothRWMutex.Lock()
+ defer gothRWMutex.Unlock()
+
delete(goth.GetProviders(), providerName)
}
// ClearProviders clears all OAuth2 providers from the goth lib
func ClearProviders() {
+ gothRWMutex.Lock()
+ defer gothRWMutex.Unlock()
+
goth.ClearProviders()
}
diff --git a/modules/auth/pam/pam.go b/modules/auth/pam/pam.go
index f21602c6b5..6906a9da89 100644
--- a/modules/auth/pam/pam.go
+++ b/modules/auth/pam/pam.go
@@ -1,3 +1,4 @@
+//go:build pam
// +build pam
// Copyright 2014 The Gogs Authors. All rights reserved.
diff --git a/modules/auth/pam/pam_stub.go b/modules/auth/pam/pam_stub.go
index 02d8da3c57..815ccf2b0e 100644
--- a/modules/auth/pam/pam_stub.go
+++ b/modules/auth/pam/pam_stub.go
@@ -1,9 +1,10 @@
-// +build !pam
-
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !pam
+// +build !pam
+
package pam
import (
diff --git a/modules/auth/pam/pam_test.go b/modules/auth/pam/pam_test.go
index fa16ff0fe7..d6d78a748b 100644
--- a/modules/auth/pam/pam_test.go
+++ b/modules/auth/pam/pam_test.go
@@ -1,3 +1,4 @@
+//go:build pam
// +build pam
// Copyright 2021 The Gitea Authors. All rights reserved.
diff --git a/modules/charset/charset.go b/modules/charset/charset.go
index 3000864c2e..55e183ebfc 100644
--- a/modules/charset/charset.go
+++ b/modules/charset/charset.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"github.com/gogs/chardet"
"golang.org/x/net/html/charset"
@@ -26,9 +27,9 @@ var UTF8BOM = []byte{'\xef', '\xbb', '\xbf'}
// ToUTF8WithFallbackReader detects the encoding of content and coverts to UTF-8 reader if possible
func ToUTF8WithFallbackReader(rd io.Reader) io.Reader {
var buf = make([]byte, 2048)
- n, err := rd.Read(buf)
+ n, err := util.ReadAtMost(rd, buf)
if err != nil {
- return rd
+ return io.MultiReader(bytes.NewReader(RemoveBOMIfPresent(buf[:n])), rd)
}
charsetLabel, err := DetectEncoding(buf[:n])
diff --git a/modules/context/access_log.go b/modules/context/access_log.go
index 97bb32f4c5..1a10c4763a 100644
--- a/modules/context/access_log.go
+++ b/modules/context/access_log.go
@@ -6,6 +6,7 @@ package context
import (
"bytes"
+ "context"
"html/template"
"net/http"
"time"
@@ -22,6 +23,8 @@ type routerLoggerOptions struct {
Ctx map[string]interface{}
}
+var signedUserNameStringPointerKey interface{} = "signedUserNameStringPointerKey"
+
// AccessLogger returns a middleware to log access logger
func AccessLogger() func(http.Handler) http.Handler {
logger := log.GetLogger("access")
@@ -29,11 +32,10 @@ func AccessLogger() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
start := time.Now()
- next.ServeHTTP(w, req)
identity := "-"
- if val := SignedUserName(req); val != "" {
- identity = val
- }
+ r := req.WithContext(context.WithValue(req.Context(), signedUserNameStringPointerKey, &identity))
+
+ next.ServeHTTP(w, r)
rw := w.(ResponseWriter)
buf := bytes.NewBuffer([]byte{})
diff --git a/modules/context/api.go b/modules/context/api.go
index 5068246745..9b1e27f93c 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -223,6 +223,9 @@ func APIAuth(authMethod auth.Auth) func(*APIContext) {
// Get user from session if logged in.
ctx.User = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
if ctx.User != nil {
+ if ctx.Locale.Language() != ctx.User.Language {
+ ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
+ }
ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == new(auth.Basic).Name()
ctx.IsSigned = true
ctx.Data["IsSigned"] = ctx.IsSigned
@@ -275,6 +278,17 @@ func APIContexter() func(http.Handler) http.Handler {
ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken())
next.ServeHTTP(ctx.Resp, ctx.Req)
+
+ // Handle adding signedUserName to the context for the AccessLogger
+ usernameInterface := ctx.Data["SignedUserName"]
+ identityPtrInterface := ctx.Req.Context().Value(signedUserNameStringPointerKey)
+ if usernameInterface != nil && identityPtrInterface != nil {
+ username := usernameInterface.(string)
+ identityPtr := identityPtrInterface.(*string)
+ if identityPtr != nil && username != "" {
+ *identityPtr = username
+ }
+ }
})
}
}
diff --git a/modules/context/context.go b/modules/context/context.go
index 64f8b12084..651fc42b7e 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -587,6 +587,17 @@ func GetContext(req *http.Request) *Context {
return req.Context().Value(contextKey).(*Context)
}
+// GetContextUser returns context user
+func GetContextUser(req *http.Request) *models.User {
+ if apiContext, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
+ return apiContext.User
+ }
+ if ctx, ok := req.Context().Value(contextKey).(*Context); ok {
+ return ctx.User
+ }
+ return nil
+}
+
// SignedUserName returns signed user's name via context
func SignedUserName(req *http.Request) string {
if middleware.IsInternalPath(req) {
@@ -631,6 +642,9 @@ func Auth(authMethod auth.Auth) func(*Context) {
return func(ctx *Context) {
ctx.User = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
if ctx.User != nil {
+ if ctx.Locale.Language() != ctx.User.Language {
+ ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
+ }
ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == new(auth.Basic).Name()
ctx.IsSigned = true
ctx.Data["IsSigned"] = ctx.IsSigned
@@ -658,6 +672,7 @@ func Contexter() func(next http.Handler) http.Handler {
var locale = middleware.Locale(resp, req)
var startTime = time.Now()
var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/")
+
var ctx = Context{
Resp: NewResponse(resp),
Cache: mc.GetCache(),
@@ -774,6 +789,17 @@ func Contexter() func(next http.Handler) http.Handler {
}
next.ServeHTTP(ctx.Resp, ctx.Req)
+
+ // Handle adding signedUserName to the context for the AccessLogger
+ usernameInterface := ctx.Data["SignedUserName"]
+ identityPtrInterface := ctx.Req.Context().Value(signedUserNameStringPointerKey)
+ if usernameInterface != nil && identityPtrInterface != nil {
+ username := usernameInterface.(string)
+ identityPtr := identityPtrInterface.(*string)
+ if identityPtr != nil && username != "" {
+ *identityPtr = username
+ }
+ }
})
}
}
diff --git a/modules/context/repo.go b/modules/context/repo.go
index ea8323bdfc..0f78ff429b 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -58,6 +58,7 @@ type Repository struct {
Commit *git.Commit
Tag *git.Tag
GitRepo *git.Repository
+ RefName string
BranchName string
TagName string
TreePath string
@@ -190,9 +191,9 @@ func (r *Repository) BranchNameSubURL() string {
case r.IsViewBranch:
return "branch/" + r.BranchName
case r.IsViewTag:
- return "tag/" + r.BranchName
+ return "tag/" + r.TagName
case r.IsViewCommit:
- return "commit/" + r.BranchName
+ return "commit/" + r.CommitID
}
log.Error("Unknown view type for repo: %v", r)
return ""
@@ -345,7 +346,7 @@ func repoAssignment(ctx *Context, repo *models.Repository) {
}
// Check access.
- if ctx.Repo.Permission.AccessMode == models.AccessModeNone {
+ if !ctx.Repo.Permission.HasAccess() {
if ctx.Query("go-get") == "1" {
EarlyResponseForGoGetMeta(ctx)
return
@@ -562,8 +563,6 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
ctx.Data["Branches"] = brs
ctx.Data["BranchesCount"] = len(brs)
- ctx.Data["TagName"] = ctx.Repo.TagName
-
// If not branch selected, try default one.
// If default branch doesn't exists, fall back to some other branch.
if len(ctx.Repo.BranchName) == 0 {
@@ -572,9 +571,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
} else if len(brs) > 0 {
ctx.Repo.BranchName = brs[0]
}
+ ctx.Repo.RefName = ctx.Repo.BranchName
}
ctx.Data["BranchName"] = ctx.Repo.BranchName
- ctx.Data["CommitID"] = ctx.Repo.CommitID
// People who have push access or have forked repository can propose a new pull request.
canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID))
@@ -695,7 +694,7 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
}
// For legacy and API support only full commit sha
parts := strings.Split(path, "/")
- if len(parts) > 0 && len(parts[0]) == 40 {
+ if len(parts) > 1 && len(parts[0]) == 40 {
ctx.Repo.TreePath = strings.Join(parts[1:], "/")
return parts[0]
}
@@ -759,7 +758,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
// Get default branch.
if len(ctx.Params("*")) == 0 {
refName = ctx.Repo.Repository.DefaultBranch
- ctx.Repo.BranchName = refName
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
if err != nil {
@@ -773,6 +771,8 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
}
refName = brs[0]
}
+ ctx.Repo.RefName = refName
+ ctx.Repo.BranchName = refName
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
if err != nil {
ctx.ServerError("GetBranchCommit", err)
@@ -783,9 +783,10 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
} else {
refName = getRefName(ctx, refType)
- ctx.Repo.BranchName = refName
+ ctx.Repo.RefName = refName
if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
ctx.Repo.IsViewBranch = true
+ ctx.Repo.BranchName = refName
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
if err != nil {
@@ -796,6 +797,8 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
ctx.Repo.IsViewTag = true
+ ctx.Repo.TagName = refName
+
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
if err != nil {
ctx.ServerError("GetTagCommit", err)
@@ -830,13 +833,14 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
setting.AppSubURL,
strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
ctx.Repo.BranchNameSubURL(),
- ctx.Repo.TreePath))
+ util.PathEscapeSegments(ctx.Repo.TreePath)))
return
}
}
ctx.Data["BranchName"] = ctx.Repo.BranchName
ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
+ ctx.Data["TagName"] = ctx.Repo.TagName
ctx.Data["CommitID"] = ctx.Repo.CommitID
ctx.Data["TreePath"] = ctx.Repo.TreePath
ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
diff --git a/modules/convert/git_commit.go b/modules/convert/git_commit.go
index fd4f12ecfa..9f43bb82f8 100644
--- a/modules/convert/git_commit.go
+++ b/modules/convert/git_commit.go
@@ -147,8 +147,9 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
return &api.Commit{
CommitMeta: &api.CommitMeta{
- URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
- SHA: commit.ID.String(),
+ URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
+ SHA: commit.ID.String(),
+ Created: commit.Committer.When,
},
HTMLURL: repo.HTMLURL() + "/commit/" + commit.ID.String(),
RepoCommit: &api.RepoCommit{
@@ -169,8 +170,9 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
},
Message: commit.Message(),
Tree: &api.CommitMeta{
- URL: repo.APIURL() + "/git/trees/" + commit.ID.String(),
- SHA: commit.ID.String(),
+ URL: repo.APIURL() + "/git/trees/" + commit.ID.String(),
+ SHA: commit.ID.String(),
+ Created: commit.Committer.When,
},
},
Author: apiAuthor,
diff --git a/modules/convert/pull.go b/modules/convert/pull.go
index 8bdf17a049..ad70eb5ca1 100644
--- a/modules/convert/pull.go
+++ b/modules/convert/pull.go
@@ -17,7 +17,7 @@ import (
// ToAPIPullRequest assumes following fields have been assigned with valid values:
// Required - Issue
// Optional - Merger
-func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
+func ToAPIPullRequest(pr *models.PullRequest, doer *models.User) *api.PullRequest {
var (
baseBranch *git.Branch
headBranch *git.Branch
@@ -41,6 +41,12 @@ func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
return nil
}
+ perm, err := models.GetUserRepoPermission(pr.BaseRepo, doer)
+ if err != nil {
+ log.Error("GetUserRepoPermission[%d]: %v", pr.BaseRepoID, err)
+ perm.AccessMode = models.AccessModeNone
+ }
+
apiPullRequest := &api.PullRequest{
ID: pr.ID,
URL: pr.Issue.HTMLURL(),
@@ -68,7 +74,7 @@ func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
Name: pr.BaseBranch,
Ref: pr.BaseBranch,
RepoID: pr.BaseRepoID,
- Repository: ToRepo(pr.BaseRepo, models.AccessModeNone),
+ Repository: ToRepo(pr.BaseRepo, perm.AccessMode),
},
Head: &api.PRBranchInfo{
Name: pr.HeadBranch,
@@ -96,8 +102,14 @@ func ToAPIPullRequest(pr *models.PullRequest) *api.PullRequest {
}
if pr.HeadRepo != nil {
+ perm, err := models.GetUserRepoPermission(pr.HeadRepo, doer)
+ if err != nil {
+ log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err)
+ perm.AccessMode = models.AccessModeNone
+ }
+
apiPullRequest.Head.RepoID = pr.HeadRepo.ID
- apiPullRequest.Head.Repository = ToRepo(pr.HeadRepo, models.AccessModeNone)
+ apiPullRequest.Head.Repository = ToRepo(pr.HeadRepo, perm.AccessMode)
headGitRepo, err := git.OpenRepository(pr.HeadRepo.RepoPath())
if err != nil {
diff --git a/modules/convert/pull_test.go b/modules/convert/pull_test.go
index adf42d8ca4..a88135780c 100644
--- a/modules/convert/pull_test.go
+++ b/modules/convert/pull_test.go
@@ -20,14 +20,14 @@ func TestPullRequest_APIFormat(t *testing.T) {
pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest)
assert.NoError(t, pr.LoadAttributes())
assert.NoError(t, pr.LoadIssue())
- apiPullRequest := ToAPIPullRequest(pr)
+ apiPullRequest := ToAPIPullRequest(pr, nil)
assert.NotNil(t, apiPullRequest)
assert.EqualValues(t, &structs.PRBranchInfo{
Name: "branch1",
Ref: "refs/pull/2/head",
Sha: "4a357436d925b5c974181ff12a994538ddc5a269",
RepoID: 1,
- Repository: ToRepo(headRepo, models.AccessModeNone),
+ Repository: ToRepo(headRepo, models.AccessModeRead),
}, apiPullRequest.Head)
//withOut HeadRepo
@@ -37,7 +37,7 @@ func TestPullRequest_APIFormat(t *testing.T) {
// simulate fork deletion
pr.HeadRepo = nil
pr.HeadRepoID = 100000
- apiPullRequest = ToAPIPullRequest(pr)
+ apiPullRequest = ToAPIPullRequest(pr, nil)
assert.NotNil(t, apiPullRequest)
assert.Nil(t, apiPullRequest.Head.Repository)
assert.EqualValues(t, -1, apiPullRequest.Head.RepoID)
diff --git a/modules/csv/csv.go b/modules/csv/csv.go
index bf433f77d2..6164ecb90f 100644
--- a/modules/csv/csv.go
+++ b/modules/csv/csv.go
@@ -28,32 +28,24 @@ func CreateReader(input io.Reader, delimiter rune) *stdcsv.Reader {
}
// CreateReaderAndGuessDelimiter tries to guess the field delimiter from the content and creates a csv.Reader.
+// Reads at most 10k bytes.
func CreateReaderAndGuessDelimiter(rd io.Reader) (*stdcsv.Reader, error) {
var data = make([]byte, 1e4)
- size, err := rd.Read(data)
+ size, err := util.ReadAtMost(rd, data)
if err != nil {
return nil, err
}
- delimiter := guessDelimiter(data[:size])
-
- var newInput io.Reader
- if size < 1e4 {
- newInput = bytes.NewReader(data[:size])
- } else {
- newInput = io.MultiReader(bytes.NewReader(data), rd)
- }
-
- return CreateReader(newInput, delimiter), nil
+ return CreateReader(
+ io.MultiReader(bytes.NewReader(data[:size]), rd),
+ guessDelimiter(data[:size]),
+ ), nil
}
// guessDelimiter scores the input CSV data against delimiters, and returns the best match.
-// Reads at most 10k bytes & 10 lines.
func guessDelimiter(data []byte) rune {
maxLines := 10
- maxBytes := util.Min(len(data), 1e4)
- text := string(data[:maxBytes])
- text = quoteRegexp.ReplaceAllLiteralString(text, "")
+ text := quoteRegexp.ReplaceAllLiteralString(string(data), "")
lines := strings.SplitN(text, "\n", maxLines+1)
lines = lines[:util.Min(maxLines, len(lines))]
diff --git a/modules/doctor/dbconsistency.go b/modules/doctor/dbconsistency.go
index 23e8331e77..4f6db760fa 100644
--- a/modules/doctor/dbconsistency.go
+++ b/modules/doctor/dbconsistency.go
@@ -13,6 +13,64 @@ import (
"code.gitea.io/gitea/modules/setting"
)
+type consistencyCheck struct {
+ Name string
+ Counter func() (int64, error)
+ Fixer func() (int64, error)
+ FixedMessage string
+}
+
+func (c *consistencyCheck) Run(logger log.Logger, autofix bool) error {
+ count, err := c.Counter()
+ if err != nil {
+ logger.Critical("Error: %v whilst counting %s", err, c.Name)
+ return err
+ }
+ if count > 0 {
+ if autofix {
+ var fixed int64
+ if fixed, err = c.Fixer(); err != nil {
+ logger.Critical("Error: %v whilst fixing %s", err, c.Name)
+ return err
+ }
+
+ prompt := "Deleted"
+ if c.FixedMessage != "" {
+ prompt = c.FixedMessage
+ }
+
+ if fixed < 0 {
+ logger.Info(prompt+" %d %s", count, c.Name)
+ } else {
+ logger.Info(prompt+" %d/%d %s", fixed, count, c.Name)
+ }
+ } else {
+ logger.Warn("Found %d %s", count, c.Name)
+ }
+ }
+ return nil
+}
+
+func asFixer(fn func() error) func() (int64, error) {
+ return func() (int64, error) {
+ err := fn()
+ return -1, err
+ }
+}
+
+func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCheck {
+ return consistencyCheck{
+ Name: name,
+ Counter: func() (int64, error) {
+ return models.CountOrphanedObjects(subject, refobject, joincond)
+ },
+ Fixer: func() (int64, error) {
+ err := models.DeleteOrphanedObjects(subject, refobject, joincond)
+ return -1, err
+ },
+ }
+}
+
func checkDBConsistency(logger log.Logger, autofix bool) error {
// make sure DB version is uptodate
if err := models.NewEngine(context.Background(), migrations.EnsureUpToDate); err != nil {
@@ -20,246 +78,103 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
return err
}
- // find labels without existing repo or org
- count, err := models.CountOrphanedLabels()
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned labels", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedLabels(); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned labels", err)
- return err
- }
- logger.Info("%d labels without existing repository/organisation deleted", count)
- } else {
- logger.Warn("%d labels without existing repository/organisation", count)
- }
- }
-
- // find IssueLabels without existing label
- count, err = models.CountOrphanedIssueLabels()
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned issue_labels", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedIssueLabels(); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned issue_labels", err)
- return err
- }
- logger.Info("%d issue_labels without existing label deleted", count)
- } else {
- logger.Warn("%d issue_labels without existing label", count)
- }
- }
-
- // find issues without existing repository
- count, err = models.CountOrphanedIssues()
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned issues", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedIssues(); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned issues", err)
- return err
- }
- logger.Info("%d issues without existing repository deleted", count)
- } else {
- logger.Warn("%d issues without existing repository", count)
- }
- }
-
- // find pulls without existing issues
- count, err = models.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned objects", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id"); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned objects", err)
- return err
- }
- logger.Info("%d pull requests without existing issue deleted", count)
- } else {
- logger.Warn("%d pull requests without existing issue", count)
- }
- }
-
- // find tracked times without existing issues/pulls
- count, err = models.CountOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id")
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned objects", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id"); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned objects", err)
- return err
- }
- logger.Info("%d tracked times without existing issue deleted", count)
- } else {
- logger.Warn("%d tracked times without existing issue", count)
- }
- }
-
- // find null archived repositories
- count, err = models.CountNullArchivedRepository()
- if err != nil {
- logger.Critical("Error: %v whilst counting null archived repositories", err)
- return err
- }
- if count > 0 {
- if autofix {
- updatedCount, err := models.FixNullArchivedRepository()
- if err != nil {
- logger.Critical("Error: %v whilst fixing null archived repositories", err)
- return err
- }
- logger.Info("%d repositories with null is_archived updated", updatedCount)
- } else {
- logger.Warn("%d repositories with null is_archived", count)
- }
- }
-
- // find label comments with empty labels
- count, err = models.CountCommentTypeLabelWithEmptyLabel()
- if err != nil {
- logger.Critical("Error: %v whilst counting label comments with empty labels", err)
- return err
- }
- if count > 0 {
- if autofix {
- updatedCount, err := models.FixCommentTypeLabelWithEmptyLabel()
- if err != nil {
- logger.Critical("Error: %v whilst removing label comments with empty labels", err)
- return err
- }
- logger.Info("%d label comments with empty labels removed", updatedCount)
- } else {
- logger.Warn("%d label comments with empty labels", count)
- }
- }
-
- // find label comments with labels from outside the repository
- count, err = models.CountCommentTypeLabelWithOutsideLabels()
- if err != nil {
- logger.Critical("Error: %v whilst counting label comments with outside labels", err)
- return err
- }
- if count > 0 {
- if autofix {
- updatedCount, err := models.FixCommentTypeLabelWithOutsideLabels()
- if err != nil {
- logger.Critical("Error: %v whilst removing label comments with outside labels", err)
- return err
- }
- log.Info("%d label comments with outside labels removed", updatedCount)
- } else {
- log.Warn("%d label comments with outside labels", count)
- }
- }
-
- // find issue_label with labels from outside the repository
- count, err = models.CountIssueLabelWithOutsideLabels()
- if err != nil {
- logger.Critical("Error: %v whilst counting issue_labels from outside the repository or organisation", err)
- return err
- }
- if count > 0 {
- if autofix {
- updatedCount, err := models.FixIssueLabelWithOutsideLabels()
- if err != nil {
- logger.Critical("Error: %v whilst removing issue_labels from outside the repository or organisation", err)
- return err
- }
- logger.Info("%d issue_labels from outside the repository or organisation removed", updatedCount)
- } else {
- logger.Warn("%d issue_labels from outside the repository or organisation", count)
- }
+ consistencyChecks := []consistencyCheck{
+ {
+ // find labels without existing repo or org
+ Name: "Orphaned Labels without existing repository or organisation",
+ Counter: models.CountOrphanedLabels,
+ Fixer: asFixer(models.DeleteOrphanedLabels),
+ },
+ {
+ // find IssueLabels without existing label
+ Name: "Orphaned Issue Labels without existing label",
+ Counter: models.CountOrphanedIssueLabels,
+ Fixer: asFixer(models.DeleteOrphanedIssueLabels),
+ },
+ {
+ // find issues without existing repository
+ Name: "Orphaned Issues without existing repository",
+ Counter: models.CountOrphanedIssues,
+ Fixer: asFixer(models.DeleteOrphanedIssues),
+ },
+ // find releases without existing repository
+ genericOrphanCheck("Orphaned Releases without existing repository",
+ "release", "repository", "release.repo_id=repository.id"),
+ // find pulls without existing issues
+ genericOrphanCheck("Orphaned PullRequests without existing issue",
+ "pull_request", "issue", "pull_request.issue_id=issue.id"),
+ // find tracked times without existing issues/pulls
+ genericOrphanCheck("Orphaned TrackedTimes without existing issue",
+ "tracked_time", "issue", "tracked_time.issue_id=issue.id"),
+ // find null archived repositories
+ {
+ Name: "Repositories with is_archived IS NULL",
+ Counter: models.CountNullArchivedRepository,
+ Fixer: models.FixNullArchivedRepository,
+ FixedMessage: "Fixed",
+ },
+ // find label comments with empty labels
+ {
+ Name: "Label comments with empty labels",
+ Counter: models.CountCommentTypeLabelWithEmptyLabel,
+ Fixer: models.FixCommentTypeLabelWithEmptyLabel,
+ FixedMessage: "Fixed",
+ },
+ // find label comments with labels from outside the repository
+ {
+ Name: "Label comments with labels from outside the repository",
+ Counter: models.CountCommentTypeLabelWithOutsideLabels,
+ Fixer: models.FixCommentTypeLabelWithOutsideLabels,
+ FixedMessage: "Removed",
+ },
+ // find issue_label with labels from outside the repository
+ {
+ Name: "IssueLabels with Labels from outside the repository",
+ Counter: models.CountIssueLabelWithOutsideLabels,
+ Fixer: models.FixIssueLabelWithOutsideLabels,
+ FixedMessage: "Removed",
+ },
}
// TODO: function to recalc all counters
if setting.Database.UsePostgreSQL {
- count, err = models.CountBadSequences()
- if err != nil {
- logger.Critical("Error: %v whilst checking sequence values", err)
+ consistencyChecks = append(consistencyChecks, consistencyCheck{
+ Name: "Sequence values",
+ Counter: models.CountBadSequences,
+ Fixer: asFixer(models.FixBadSequences),
+ FixedMessage: "Updated",
+ })
+ }
+
+ consistencyChecks = append(consistencyChecks,
+ // find protected branches without existing repository
+ genericOrphanCheck("Protected Branches without existing repository",
+ "protected_branch", "repository", "protected_branch.repo_id=repository.id"),
+ // find deleted branches without existing repository
+ genericOrphanCheck("Deleted Branches without existing repository",
+ "deleted_branch", "repository", "deleted_branch.repo_id=repository.id"),
+ // find LFS locks without existing repository
+ genericOrphanCheck("LFS locks without existing repository",
+ "lfs_lock", "repository", "lfs_lock.repo_id=repository.id"),
+ // find collaborations without users
+ genericOrphanCheck("Collaborations without existing user",
+ "collaboration", "user", "collaboration.user_id=`user`.id"),
+ // find collaborations without repository
+ genericOrphanCheck("Collaborations without existing repository",
+ "collaboration", "repository", "collaboration.repo_id=repository.id"),
+ // find access without users
+ genericOrphanCheck("Access entries without existing user",
+ "access", "user", "access.user_id=`user`.id"),
+ // find access without repository
+ genericOrphanCheck("Access entries without existing repository",
+ "access", "repository", "access.repo_id=repository.id"),
+ )
+
+ for _, c := range consistencyChecks {
+ if err := c.Run(logger, autofix); err != nil {
return err
}
- if count > 0 {
- if autofix {
- err := models.FixBadSequences()
- if err != nil {
- logger.Critical("Error: %v whilst attempting to fix sequences", err)
- return err
- }
- logger.Info("%d sequences updated", count)
- } else {
- logger.Warn("%d sequences with incorrect values", count)
- }
- }
- }
-
- // find protected branches without existing repository
- count, err = models.CountOrphanedObjects("protected_branch", "repository", "protected_branch.repo_id=repository.id")
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned objects", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedObjects("protected_branch", "repository", "protected_branch.repo_id=repository.id"); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned objects", err)
- return err
- }
- logger.Info("%d protected branches without existing repository deleted", count)
- } else {
- logger.Warn("%d protected branches without existing repository", count)
- }
- }
-
- // find deleted branches without existing repository
- count, err = models.CountOrphanedObjects("deleted_branch", "repository", "deleted_branch.repo_id=repository.id")
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned objects", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedObjects("deleted_branch", "repository", "deleted_branch.repo_id=repository.id"); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned objects", err)
- return err
- }
- logger.Info("%d deleted branches without existing repository deleted", count)
- } else {
- logger.Warn("%d deleted branches without existing repository", count)
- }
- }
-
- // find LFS locks without existing repository
- count, err = models.CountOrphanedObjects("lfs_lock", "repository", "lfs_lock.repo_id=repository.id")
- if err != nil {
- logger.Critical("Error: %v whilst counting orphaned objects", err)
- return err
- }
- if count > 0 {
- if autofix {
- if err = models.DeleteOrphanedObjects("lfs_lock", "repository", "lfs_lock.repo_id=repository.id"); err != nil {
- logger.Critical("Error: %v whilst deleting orphaned objects", err)
- return err
- }
- logger.Info("%d LFS locks without existing repository deleted", count)
- } else {
- logger.Warn("%d LFS locks without existing repository", count)
- }
}
return nil
diff --git a/modules/doctor/fix16961.go b/modules/doctor/fix16961.go
new file mode 100644
index 0000000000..07cfc4633a
--- /dev/null
+++ b/modules/doctor/fix16961.go
@@ -0,0 +1,317 @@
+// Copyright 2021 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 doctor
+
+import (
+ "bytes"
+ "fmt"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/timeutil"
+ "xorm.io/builder"
+)
+
+// #16831 revealed that the dump command that was broken in 1.14.3-1.14.6 and 1.15.0 (#15885).
+// This led to repo_unit and login_source cfg not being converted to JSON in the dump
+// Unfortunately although it was hoped that there were only a few users affected it
+// appears that many users are affected.
+
+// We therefore need to provide a doctor command to fix this repeated issue #16961
+
+func parseBool16961(bs []byte) (bool, error) {
+ if bytes.EqualFold(bs, []byte("%!s(bool=false)")) {
+ return false, nil
+ }
+
+ if bytes.EqualFold(bs, []byte("%!s(bool=true)")) {
+ return true, nil
+ }
+
+ return false, fmt.Errorf("unexpected bool format: %s", string(bs))
+}
+
+func fixUnitConfig16961(bs []byte, cfg *models.UnitConfig) (fixed bool, err error) {
+ err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
+ if err == nil {
+ return
+ }
+
+ // Handle #16961
+ if string(bs) != "&{}" && len(bs) != 0 {
+ return
+ }
+
+ return true, nil
+}
+
+func fixExternalWikiConfig16961(bs []byte, cfg *models.ExternalWikiConfig) (fixed bool, err error) {
+ err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
+ if err == nil {
+ return
+ }
+
+ if len(bs) < 3 {
+ return
+ }
+ if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
+ return
+ }
+ cfg.ExternalWikiURL = string(bs[2 : len(bs)-1])
+ return true, nil
+}
+
+func fixExternalTrackerConfig16961(bs []byte, cfg *models.ExternalTrackerConfig) (fixed bool, err error) {
+ err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
+ if err == nil {
+ return
+ }
+ // Handle #16961
+ if len(bs) < 3 {
+ return
+ }
+
+ if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
+ return
+ }
+
+ parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
+ if len(parts) != 3 {
+ return
+ }
+
+ cfg.ExternalTrackerURL = string(bytes.Join(parts[:len(parts)-2], []byte{' '}))
+ cfg.ExternalTrackerFormat = string(parts[len(parts)-2])
+ cfg.ExternalTrackerStyle = string(parts[len(parts)-1])
+ return true, nil
+}
+
+func fixPullRequestsConfig16961(bs []byte, cfg *models.PullRequestsConfig) (fixed bool, err error) {
+ err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
+ if err == nil {
+ return
+ }
+
+ // Handle #16961
+ if len(bs) < 3 {
+ return
+ }
+
+ if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
+ return
+ }
+
+ // PullRequestsConfig was the following in 1.14
+ // type PullRequestsConfig struct {
+ // IgnoreWhitespaceConflicts bool
+ // AllowMerge bool
+ // AllowRebase bool
+ // AllowRebaseMerge bool
+ // AllowSquash bool
+ // AllowManualMerge bool
+ // AutodetectManualMerge bool
+ // }
+ //
+ // 1.15 added in addition:
+ // DefaultDeleteBranchAfterMerge bool
+ // DefaultMergeStyle MergeStyle
+ parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
+ if len(parts) < 7 {
+ return
+ }
+
+ var parseErr error
+ cfg.IgnoreWhitespaceConflicts, parseErr = parseBool16961(parts[0])
+ if parseErr != nil {
+ return
+ }
+ cfg.AllowMerge, parseErr = parseBool16961(parts[1])
+ if parseErr != nil {
+ return
+ }
+ cfg.AllowRebase, parseErr = parseBool16961(parts[2])
+ if parseErr != nil {
+ return
+ }
+ cfg.AllowRebaseMerge, parseErr = parseBool16961(parts[3])
+ if parseErr != nil {
+ return
+ }
+ cfg.AllowSquash, parseErr = parseBool16961(parts[4])
+ if parseErr != nil {
+ return
+ }
+ cfg.AllowManualMerge, parseErr = parseBool16961(parts[5])
+ if parseErr != nil {
+ return
+ }
+ cfg.AutodetectManualMerge, parseErr = parseBool16961(parts[6])
+ if parseErr != nil {
+ return
+ }
+
+ // 1.14 unit
+ if len(parts) == 7 {
+ return true, nil
+ }
+
+ if len(parts) < 9 {
+ return
+ }
+
+ cfg.DefaultDeleteBranchAfterMerge, parseErr = parseBool16961(parts[7])
+ if parseErr != nil {
+ return
+ }
+
+ cfg.DefaultMergeStyle = models.MergeStyle(string(bytes.Join(parts[8:], []byte{' '})))
+ return true, nil
+}
+
+func fixIssuesConfig16961(bs []byte, cfg *models.IssuesConfig) (fixed bool, err error) {
+ err = models.JSONUnmarshalHandleDoubleEncode(bs, &cfg)
+ if err == nil {
+ return
+ }
+
+ // Handle #16961
+ if len(bs) < 3 {
+ return
+ }
+
+ if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' {
+ return
+ }
+
+ parts := bytes.Split(bs[2:len(bs)-1], []byte{' '})
+ if len(parts) != 3 {
+ return
+ }
+ var parseErr error
+ cfg.EnableTimetracker, parseErr = parseBool16961(parts[0])
+ if parseErr != nil {
+ return
+ }
+ cfg.AllowOnlyContributorsToTrackTime, parseErr = parseBool16961(parts[1])
+ if parseErr != nil {
+ return
+ }
+ cfg.EnableDependencies, parseErr = parseBool16961(parts[2])
+ if parseErr != nil {
+ return
+ }
+ return true, nil
+}
+
+func fixBrokenRepoUnit16961(repoUnit *models.RepoUnit, bs []byte) (fixed bool, err error) {
+ // Shortcut empty or null values
+ if len(bs) == 0 {
+ return false, nil
+ }
+
+ switch models.UnitType(repoUnit.Type) {
+ case models.UnitTypeCode, models.UnitTypeReleases, models.UnitTypeWiki, models.UnitTypeProjects:
+ cfg := &models.UnitConfig{}
+ repoUnit.Config = cfg
+ if fixed, err := fixUnitConfig16961(bs, cfg); !fixed {
+ return false, err
+ }
+ case models.UnitTypeExternalWiki:
+ cfg := &models.ExternalWikiConfig{}
+ repoUnit.Config = cfg
+
+ if fixed, err := fixExternalWikiConfig16961(bs, cfg); !fixed {
+ return false, err
+ }
+ case models.UnitTypeExternalTracker:
+ cfg := &models.ExternalTrackerConfig{}
+ repoUnit.Config = cfg
+ if fixed, err := fixExternalTrackerConfig16961(bs, cfg); !fixed {
+ return false, err
+ }
+ case models.UnitTypePullRequests:
+ cfg := &models.PullRequestsConfig{}
+ repoUnit.Config = cfg
+
+ if fixed, err := fixPullRequestsConfig16961(bs, cfg); !fixed {
+ return false, err
+ }
+ case models.UnitTypeIssues:
+ cfg := &models.IssuesConfig{}
+ repoUnit.Config = cfg
+ if fixed, err := fixIssuesConfig16961(bs, cfg); !fixed {
+ return false, err
+ }
+ default:
+ panic(fmt.Sprintf("unrecognized repo unit type: %v", repoUnit.Type))
+ }
+ return true, nil
+}
+
+func fixBrokenRepoUnits16961(logger log.Logger, autofix bool) error {
+ // RepoUnit describes all units of a repository
+ type RepoUnit struct {
+ ID int64
+ RepoID int64
+ Type models.UnitType
+ Config []byte
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
+ }
+
+ count := 0
+
+ err := models.Iterate(
+ models.DefaultDBContext(),
+ new(RepoUnit),
+ builder.Gt{
+ "id": 0,
+ },
+ func(idx int, bean interface{}) error {
+ unit := bean.(*RepoUnit)
+
+ bs := unit.Config
+ repoUnit := &models.RepoUnit{
+ ID: unit.ID,
+ RepoID: unit.RepoID,
+ Type: unit.Type,
+ CreatedUnix: unit.CreatedUnix,
+ }
+
+ if fixed, err := fixBrokenRepoUnit16961(repoUnit, bs); !fixed {
+ return err
+ }
+
+ count++
+ if !autofix {
+ return nil
+ }
+
+ return models.UpdateRepoUnit(repoUnit)
+ },
+ )
+
+ if err != nil {
+ logger.Critical("Unable to iterate acrosss repounits to fix the broken units: Error %v", err)
+ return err
+ }
+
+ if !autofix {
+ logger.Warn("Found %d broken repo_units", count)
+ return nil
+ }
+ logger.Info("Fixed %d broken repo_units", count)
+
+ return nil
+}
+
+func init() {
+ Register(&Check{
+ Title: "Check for incorrectly dumped repo_units (See #16961)",
+ Name: "fix-broken-repo-units",
+ IsDefault: false,
+ Run: fixBrokenRepoUnits16961,
+ Priority: 7,
+ })
+}
diff --git a/modules/doctor/fix16961_test.go b/modules/doctor/fix16961_test.go
new file mode 100644
index 0000000000..017f585335
--- /dev/null
+++ b/modules/doctor/fix16961_test.go
@@ -0,0 +1,271 @@
+// Copyright 2021 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 doctor
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models"
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_fixUnitConfig_16961(t *testing.T) {
+ tests := []struct {
+ name string
+ bs string
+ wantFixed bool
+ wantErr bool
+ }{
+ {
+ name: "empty",
+ bs: "",
+ wantFixed: true,
+ wantErr: false,
+ },
+ {
+ name: "normal: {}",
+ bs: "{}",
+ wantFixed: false,
+ wantErr: false,
+ },
+ {
+ name: "broken but fixable: &{}",
+ bs: "&{}",
+ wantFixed: true,
+ wantErr: false,
+ },
+ {
+ name: "broken but unfixable: &{asdasd}",
+ bs: "&{asdasd}",
+ wantFixed: false,
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ gotFixed, err := fixUnitConfig16961([]byte(tt.bs), &models.UnitConfig{})
+ if (err != nil) != tt.wantErr {
+ t.Errorf("fixUnitConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if gotFixed != tt.wantFixed {
+ t.Errorf("fixUnitConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
+ }
+ })
+ }
+}
+
+func Test_fixExternalWikiConfig_16961(t *testing.T) {
+ tests := []struct {
+ name string
+ bs string
+ expected string
+ wantFixed bool
+ wantErr bool
+ }{
+ {
+ name: "normal: {\"ExternalWikiURL\":\"http://someurl\"}",
+ bs: "{\"ExternalWikiURL\":\"http://someurl\"}",
+ expected: "http://someurl",
+ wantFixed: false,
+ wantErr: false,
+ },
+ {
+ name: "broken: &{http://someurl}",
+ bs: "&{http://someurl}",
+ expected: "http://someurl",
+ wantFixed: true,
+ wantErr: false,
+ },
+ {
+ name: "broken but unfixable: http://someurl",
+ bs: "http://someurl",
+ wantFixed: false,
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cfg := &models.ExternalWikiConfig{}
+ gotFixed, err := fixExternalWikiConfig16961([]byte(tt.bs), cfg)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("fixExternalWikiConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if gotFixed != tt.wantFixed {
+ t.Errorf("fixExternalWikiConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
+ }
+ if cfg.ExternalWikiURL != tt.expected {
+ t.Errorf("fixExternalWikiConfig_16961().ExternalWikiURL = %v, want %v", cfg.ExternalWikiURL, tt.expected)
+ }
+ })
+ }
+}
+
+func Test_fixExternalTrackerConfig_16961(t *testing.T) {
+ tests := []struct {
+ name string
+ bs string
+ expected models.ExternalTrackerConfig
+ wantFixed bool
+ wantErr bool
+ }{
+ {
+ name: "normal",
+ bs: `{"ExternalTrackerURL":"a","ExternalTrackerFormat":"b","ExternalTrackerStyle":"c"}`,
+ expected: models.ExternalTrackerConfig{
+ ExternalTrackerURL: "a",
+ ExternalTrackerFormat: "b",
+ ExternalTrackerStyle: "c",
+ },
+ wantFixed: false,
+ wantErr: false,
+ },
+ {
+ name: "broken",
+ bs: "&{a b c}",
+ expected: models.ExternalTrackerConfig{
+ ExternalTrackerURL: "a",
+ ExternalTrackerFormat: "b",
+ ExternalTrackerStyle: "c",
+ },
+ wantFixed: true,
+ wantErr: false,
+ },
+ {
+ name: "broken - too many fields",
+ bs: "&{a b c d}",
+ wantFixed: false,
+ wantErr: true,
+ },
+ {
+ name: "broken - wrong format",
+ bs: "a b c d}",
+ wantFixed: false,
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cfg := &models.ExternalTrackerConfig{}
+ gotFixed, err := fixExternalTrackerConfig16961([]byte(tt.bs), cfg)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("fixExternalTrackerConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if gotFixed != tt.wantFixed {
+ t.Errorf("fixExternalTrackerConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
+ }
+ if cfg.ExternalTrackerFormat != tt.expected.ExternalTrackerFormat {
+ t.Errorf("fixExternalTrackerConfig_16961().ExternalTrackerFormat = %v, want %v", tt.expected.ExternalTrackerFormat, cfg.ExternalTrackerFormat)
+ }
+ if cfg.ExternalTrackerStyle != tt.expected.ExternalTrackerStyle {
+ t.Errorf("fixExternalTrackerConfig_16961().ExternalTrackerStyle = %v, want %v", tt.expected.ExternalTrackerStyle, cfg.ExternalTrackerStyle)
+ }
+ if cfg.ExternalTrackerURL != tt.expected.ExternalTrackerURL {
+ t.Errorf("fixExternalTrackerConfig_16961().ExternalTrackerURL = %v, want %v", tt.expected.ExternalTrackerURL, cfg.ExternalTrackerURL)
+ }
+ })
+ }
+}
+
+func Test_fixPullRequestsConfig_16961(t *testing.T) {
+ tests := []struct {
+ name string
+ bs string
+ expected models.PullRequestsConfig
+ wantFixed bool
+ wantErr bool
+ }{
+ {
+ name: "normal",
+ bs: `{"IgnoreWhitespaceConflicts":false,"AllowMerge":false,"AllowRebase":false,"AllowRebaseMerge":false,"AllowSquash":false,"AllowManualMerge":false,"AutodetectManualMerge":false,"DefaultDeleteBranchAfterMerge":false,"DefaultMergeStyle":""}`,
+ },
+ {
+ name: "broken - 1.14",
+ bs: `&{%!s(bool=false) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=false) %!s(bool=false)}`,
+ expected: models.PullRequestsConfig{
+ IgnoreWhitespaceConflicts: false,
+ AllowMerge: true,
+ AllowRebase: true,
+ AllowRebaseMerge: true,
+ AllowSquash: true,
+ AllowManualMerge: false,
+ AutodetectManualMerge: false,
+ },
+ wantFixed: true,
+ },
+ {
+ name: "broken - 1.15",
+ bs: `&{%!s(bool=false) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=true) %!s(bool=false) %!s(bool=false) %!s(bool=false) merge}`,
+ expected: models.PullRequestsConfig{
+ AllowMerge: true,
+ AllowRebase: true,
+ AllowRebaseMerge: true,
+ AllowSquash: true,
+ DefaultMergeStyle: models.MergeStyleMerge,
+ },
+ wantFixed: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cfg := &models.PullRequestsConfig{}
+ gotFixed, err := fixPullRequestsConfig16961([]byte(tt.bs), cfg)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("fixPullRequestsConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if gotFixed != tt.wantFixed {
+ t.Errorf("fixPullRequestsConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
+ }
+ assert.EqualValues(t, &tt.expected, cfg)
+ })
+ }
+}
+
+func Test_fixIssuesConfig_16961(t *testing.T) {
+ tests := []struct {
+ name string
+ bs string
+ expected models.IssuesConfig
+ wantFixed bool
+ wantErr bool
+ }{
+ {
+ name: "normal",
+ bs: `{"EnableTimetracker":true,"AllowOnlyContributorsToTrackTime":true,"EnableDependencies":true}`,
+ expected: models.IssuesConfig{
+ EnableTimetracker: true,
+ AllowOnlyContributorsToTrackTime: true,
+ EnableDependencies: true,
+ },
+ },
+ {
+ name: "broken",
+ bs: `&{%!s(bool=true) %!s(bool=true) %!s(bool=true)}`,
+ expected: models.IssuesConfig{
+ EnableTimetracker: true,
+ AllowOnlyContributorsToTrackTime: true,
+ EnableDependencies: true,
+ },
+ wantFixed: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cfg := &models.IssuesConfig{}
+ gotFixed, err := fixIssuesConfig16961([]byte(tt.bs), cfg)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("fixIssuesConfig_16961() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if gotFixed != tt.wantFixed {
+ t.Errorf("fixIssuesConfig_16961() = %v, want %v", gotFixed, tt.wantFixed)
+ }
+ assert.EqualValues(t, &tt.expected, cfg)
+ })
+ }
+}
diff --git a/modules/doctor/misc.go b/modules/doctor/misc.go
index 0ca1e841ee..47fee8f7fd 100644
--- a/modules/doctor/misc.go
+++ b/modules/doctor/misc.go
@@ -6,7 +6,9 @@ package doctor
import (
"fmt"
+ "os"
"os/exec"
+ "path"
"strings"
"code.gitea.io/gitea/models"
@@ -14,6 +16,9 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
+ lru "github.com/hashicorp/golang-lru"
"xorm.io/builder"
)
@@ -75,6 +80,7 @@ func checkUserStarNum(logger log.Logger, autofix bool) error {
func checkEnablePushOptions(logger log.Logger, autofix bool) error {
numRepos := 0
numNeedUpdate := 0
+
if err := iterateRepositories(func(repo *models.Repository) error {
numRepos++
r, err := git.OpenRepository(repo.RepoPath())
@@ -114,6 +120,66 @@ func checkEnablePushOptions(logger log.Logger, autofix bool) error {
return nil
}
+func checkDaemonExport(logger log.Logger, autofix bool) error {
+ numRepos := 0
+ numNeedUpdate := 0
+ cache, err := lru.New(512)
+ if err != nil {
+ logger.Critical("Unable to create cache: %v", err)
+ return err
+ }
+ if err := iterateRepositories(func(repo *models.Repository) error {
+ numRepos++
+
+ if owner, has := cache.Get(repo.OwnerID); has {
+ repo.Owner = owner.(*models.User)
+ } else {
+ if err := repo.GetOwner(); err != nil {
+ return err
+ }
+ cache.Add(repo.OwnerID, repo.Owner)
+ }
+
+ // Create/Remove git-daemon-export-ok for git-daemon...
+ daemonExportFile := path.Join(repo.RepoPath(), `git-daemon-export-ok`)
+ isExist, err := util.IsExist(daemonExportFile)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", daemonExportFile, err)
+ return err
+ }
+ isPublic := !repo.IsPrivate && repo.Owner.Visibility == structs.VisibleTypePublic
+
+ if isPublic != isExist {
+ numNeedUpdate++
+ if autofix {
+ if !isPublic && isExist {
+ if err = util.Remove(daemonExportFile); err != nil {
+ log.Error("Failed to remove %s: %v", daemonExportFile, err)
+ }
+ } else if isPublic && !isExist {
+ if f, err := os.Create(daemonExportFile); err != nil {
+ log.Error("Failed to create %s: %v", daemonExportFile, err)
+ } else {
+ f.Close()
+ }
+ }
+ }
+ }
+ return nil
+ }); err != nil {
+ logger.Critical("Unable to checkDaemonExport: %v", err)
+ return err
+ }
+
+ if autofix {
+ logger.Info("Updated git-daemon-export-ok files for %d of %d repositories.", numNeedUpdate, numRepos)
+ } else {
+ logger.Info("Checked %d repositories, %d need updates.", numRepos, numNeedUpdate)
+ }
+
+ return nil
+}
+
func init() {
Register(&Check{
Title: "Check if SCRIPT_TYPE is available",
@@ -143,4 +209,11 @@ func init() {
Run: checkEnablePushOptions,
Priority: 7,
})
+ Register(&Check{
+ Title: "Check git-daemon-export-ok files",
+ Name: "check-git-daemon-export-ok",
+ IsDefault: false,
+ Run: checkDaemonExport,
+ Priority: 8,
+ })
}
diff --git a/modules/doctor/storage.go b/modules/doctor/storage.go
new file mode 100644
index 0000000000..cb06d66204
--- /dev/null
+++ b/modules/doctor/storage.go
@@ -0,0 +1,76 @@
+// Copyright 2021 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 doctor
+
+import (
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/storage"
+)
+
+func checkAttachmentStorageFiles(logger log.Logger, autofix bool) error {
+ var total, garbageNum int
+ var deletePaths []string
+ if err := storage.Attachments.IterateObjects(func(p string, obj storage.Object) error {
+ defer obj.Close()
+
+ total++
+ stat, err := obj.Stat()
+ if err != nil {
+ return err
+ }
+ exist, err := models.ExistAttachmentsByUUID(stat.Name())
+ if err != nil {
+ return err
+ }
+ if !exist {
+ garbageNum++
+ if autofix {
+ deletePaths = append(deletePaths, p)
+ }
+ }
+ return nil
+ }); err != nil {
+ logger.Error("storage.Attachments.IterateObjects failed: %v", err)
+ return err
+ }
+
+ if garbageNum > 0 {
+ if autofix {
+ var deletedNum int
+ for _, p := range deletePaths {
+ if err := storage.Attachments.Delete(p); err != nil {
+ log.Error("Delete attachment %s failed: %v", p, err)
+ } else {
+ deletedNum++
+ }
+ }
+ logger.Info("%d missed information attachment detected, %d deleted.", garbageNum, deletedNum)
+ } else {
+ logger.Warn("Checked %d attachment, %d missed information.", total, garbageNum)
+ }
+ }
+ return nil
+}
+
+func checkStorageFiles(logger log.Logger, autofix bool) error {
+ if err := storage.Init(); err != nil {
+ logger.Error("storage.Init failed: %v", err)
+ return err
+ }
+ return checkAttachmentStorageFiles(logger, autofix)
+}
+
+func init() {
+ Register(&Check{
+ Title: "Check if there is garbage storage files",
+ Name: "storages",
+ IsDefault: false,
+ Run: checkStorageFiles,
+ AbortIfFailed: false,
+ SkipDatabaseInitialization: false,
+ Priority: 1,
+ })
+}
diff --git a/modules/generate/generate.go b/modules/generate/generate.go
index 4ed2a503b0..ab05a9cb83 100644
--- a/modules/generate/generate.go
+++ b/modules/generate/generate.go
@@ -12,7 +12,8 @@ import (
"time"
"code.gitea.io/gitea/modules/util"
- "github.com/dgrijalva/jwt-go"
+
+ "github.com/golang-jwt/jwt"
)
// NewInternalToken generate a new value intended to be used by INTERNAL_TOKEN.
diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go
index bdf82bde89..7a2ebcfc57 100644
--- a/modules/git/batch_reader.go
+++ b/modules/git/batch_reader.go
@@ -7,8 +7,11 @@ package git
import (
"bufio"
"bytes"
+ "context"
+ "fmt"
"io"
"math"
+ "runtime"
"strconv"
"strings"
@@ -24,20 +27,43 @@ type WriteCloserError interface {
CloseWithError(err error) error
}
+// EnsureValidGitRepository runs git rev-parse in the repository path - thus ensuring that the repository is a valid repository.
+// Run before opening git cat-file.
+// This is needed otherwise the git cat-file will hang for invalid repositories.
+func EnsureValidGitRepository(ctx context.Context, repoPath string) error {
+ stderr := strings.Builder{}
+ err := NewCommandContext(ctx, "rev-parse").
+ SetDescription(fmt.Sprintf("%s rev-parse [repo_path: %s]", GitExecutable, repoPath)).
+ RunInDirFullPipeline(repoPath, nil, &stderr, nil)
+ if err != nil {
+ return ConcatenateError(err, (&stderr).String())
+ }
+ return nil
+}
+
// CatFileBatchCheck opens git cat-file --batch-check in the provided repo and returns a stdin pipe, a stdout reader and cancel function
func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
batchStdinReader, batchStdinWriter := io.Pipe()
batchStdoutReader, batchStdoutWriter := io.Pipe()
+ ctx, ctxCancel := context.WithCancel(DefaultContext)
+ closed := make(chan struct{})
cancel := func() {
_ = batchStdinReader.Close()
_ = batchStdinWriter.Close()
_ = batchStdoutReader.Close()
_ = batchStdoutWriter.Close()
+ ctxCancel()
+ <-closed
}
+ _, filename, line, _ := runtime.Caller(2)
+ filename = strings.TrimPrefix(filename, callerPrefix)
+
go func() {
stderr := strings.Builder{}
- err := NewCommand("cat-file", "--batch-check").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
+ err := NewCommandContext(ctx, "cat-file", "--batch-check").
+ SetDescription(fmt.Sprintf("%s cat-file --batch-check [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
+ RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
if err != nil {
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
@@ -45,6 +71,7 @@ func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()
_ = batchStdoutWriter.Close()
_ = batchStdinReader.Close()
}
+ close(closed)
}()
// For simplicities sake we'll use a buffered reader to read from the cat-file --batch-check
@@ -59,16 +86,25 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
// so let's create a batch stdin and stdout
batchStdinReader, batchStdinWriter := io.Pipe()
batchStdoutReader, batchStdoutWriter := nio.Pipe(buffer.New(32 * 1024))
+ ctx, ctxCancel := context.WithCancel(DefaultContext)
+ closed := make(chan struct{})
cancel := func() {
_ = batchStdinReader.Close()
_ = batchStdinWriter.Close()
_ = batchStdoutReader.Close()
_ = batchStdoutWriter.Close()
+ ctxCancel()
+ <-closed
}
+ _, filename, line, _ := runtime.Caller(2)
+ filename = strings.TrimPrefix(filename, callerPrefix)
+
go func() {
stderr := strings.Builder{}
- err := NewCommand("cat-file", "--batch").RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
+ err := NewCommandContext(ctx, "cat-file", "--batch").
+ SetDescription(fmt.Sprintf("%s cat-file --batch [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
+ RunInDirFullPipeline(repoPath, batchStdoutWriter, &stderr, batchStdinReader)
if err != nil {
_ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
_ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String()))
@@ -76,6 +112,7 @@ func CatFileBatch(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
_ = batchStdoutWriter.Close()
_ = batchStdinReader.Close()
}
+ close(closed)
}()
// For simplicities sake we'll us a buffered reader to read from the cat-file --batch
@@ -281,3 +318,10 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn
sha = shaBuf
return
}
+
+var callerPrefix string
+
+func init() {
+ _, filename, _, _ := runtime.Caller(0)
+ callerPrefix = strings.TrimSuffix(filename, "modules/git/batch_reader.go")
+}
diff --git a/modules/git/blob.go b/modules/git/blob.go
index 5831bc3735..39b28cbe26 100644
--- a/modules/git/blob.go
+++ b/modules/git/blob.go
@@ -12,6 +12,7 @@ import (
"io/ioutil"
"code.gitea.io/gitea/modules/typesniffer"
+ "code.gitea.io/gitea/modules/util"
)
// This file contains common functions between the gogit and !gogit variants for git Blobs
@@ -29,7 +30,7 @@ func (b *Blob) GetBlobContent() (string, error) {
}
defer dataRc.Close()
buf := make([]byte, 1024)
- n, _ := dataRc.Read(buf)
+ n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
return string(buf), nil
}
diff --git a/modules/git/blob_gogit.go b/modules/git/blob_gogit.go
index 7a82eb5c37..ef7a90c3f4 100644
--- a/modules/git/blob_gogit.go
+++ b/modules/git/blob_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/blob_nogogit.go b/modules/git/blob_nogogit.go
index 5b42920ebe..3391bc3931 100644
--- a/modules/git/blob_nogogit.go
+++ b/modules/git/blob_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
@@ -46,8 +47,8 @@ func (b *Blob) DataAsync() (io.ReadCloser, error) {
if size < 4096 {
bs, err := ioutil.ReadAll(io.LimitReader(rd, size))
+ defer cancel()
if err != nil {
- cancel()
return nil, err
}
_, err = rd.Discard(1)
@@ -105,12 +106,12 @@ func (b *blobReader) Read(p []byte) (n int, err error) {
// Close implements io.Closer
func (b *blobReader) Close() error {
+ defer b.cancel()
if b.n > 0 {
for b.n > math.MaxInt32 {
n, err := b.rd.Discard(math.MaxInt32)
b.n -= int64(n)
if err != nil {
- b.cancel()
return err
}
b.n -= math.MaxInt32
@@ -118,14 +119,12 @@ func (b *blobReader) Close() error {
n, err := b.rd.Discard(int(b.n))
b.n -= int64(n)
if err != nil {
- b.cancel()
return err
}
}
if b.n == 0 {
_, err := b.rd.Discard(1)
b.n--
- b.cancel()
return err
}
return nil
diff --git a/modules/git/command_test.go b/modules/git/command_test.go
index 00801ae31f..58d616a038 100644
--- a/modules/git/command_test.go
+++ b/modules/git/command_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build race
// +build race
package git
diff --git a/modules/git/commit_convert_gogit.go b/modules/git/commit_convert_gogit.go
index be2b948b36..b328b3c0ed 100644
--- a/modules/git/commit_convert_gogit.go
+++ b/modules/git/commit_convert_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go
index a8006dcef2..8b82f3f66c 100644
--- a/modules/git/commit_info_gogit.go
+++ b/modules/git/commit_info_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go
index 060ecba261..f57355d16e 100644
--- a/modules/git/commit_info_nogogit.go
+++ b/modules/git/commit_info_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/diff.go b/modules/git/diff.go
index 20f25c1bee..3fd57c9ad6 100644
--- a/modules/git/diff.go
+++ b/modules/git/diff.go
@@ -218,6 +218,8 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
} else {
otherLine++
}
+ case '\\':
+ // FIXME: handle `\ No newline at end of file`
default:
currentLine++
otherLine++
diff --git a/modules/git/diff_test.go b/modules/git/diff_test.go
index 363ff0b970..de71248508 100644
--- a/modules/git/diff_test.go
+++ b/modules/git/diff_test.go
@@ -42,6 +42,57 @@ index d8e4c92..19dc8ad 100644
/
`
+var issue17875Diff = `diff --git a/Geschäftsordnung.md b/Geschäftsordnung.md
+index d46c152..a7d2d55 100644
+--- a/Geschäftsordnung.md
++++ b/Geschäftsordnung.md
+@@ -1,5 +1,5 @@
+ ---
+-date: "23.01.2021"
++date: "30.11.2021"
+ ...
+ ` + `
+ # Geschäftsordnung
+@@ -16,4 +16,22 @@ Diese Geschäftsordnung regelt alle Prozesse des Vereins, solange diese nicht du
+ ` + `
+ ## § 3 Datenschutzverantwortlichkeit
+ ` + `
+-1. Der Verein bestellt eine datenschutzverantwortliche Person mit den Aufgaben nach Artikel 39 DSGVO.
+\ No newline at end of file
++1. Der Verein bestellt eine datenschutzverantwortliche Person mit den Aufgaben nach Artikel 39 DSGVO.
++
++## §4 Umgang mit der SARS-Cov-2-Pandemie
++
++1. Der Vorstand hat die Befugnis, in Rücksprache mit den Vereinsmitgliedern, verschiedene Hygienemaßnahmen für Präsenzveranstaltungen zu beschließen.
++
++2. Die Einführung, Änderung und Abschaffung dieser Maßnahmen sind nur zum Zweck der Eindämmung der SARS-Cov-2-Pandemie zulässig.
++
++3. Die Einführung, Änderung und Abschaffung von Maßnahmen nach Abs. 2 bedarf einer wissenschaftlichen Grundlage.
++
++4. Die Maßnahmen nach Abs. 2 setzen sich aus den folgenden Bausteinen inklusive einer ihrer Ausprägungen zusammen.
++
++ 1. Maskenpflicht: Keine; Maskenpflicht, außer am Platz, oder wo Abstände nicht eingehalten werden können; Maskenpflicht, wenn Abstände nicht eingehalten werden können; Maskenpflicht
++
++ 2. Geimpft-, Genesen- oder Testnachweis: Kein Nachweis notwendig; Nachweis, dass Person geimpft, genesen oder tagesaktuell getestet ist (3G); Nachweis, dass Person geimpft oder genesen ist (2G); Nachweis, dass Person geimpft bzw. genesen und tagesaktuell getestet ist (2G+)
++
++ 3. Online-Veranstaltung: Keine, parallele Online-Veranstaltung, ausschließlich Online-Veranstaltung
++
++5. Bei Präsenzveranstungen gelten außerdem die Hygienevorschriften des Veranstaltungsorts. Bei Regelkollision greift die restriktivere Regel.
+\ No newline at end of file`
+
+func TestCutDiffAroundLineIssue17875(t *testing.T) {
+ result, err := CutDiffAroundLine(strings.NewReader(issue17875Diff), 23, false, 3)
+ assert.NoError(t, err)
+ expected := `diff --git a/Geschäftsordnung.md b/Geschäftsordnung.md
+--- a/Geschäftsordnung.md
++++ b/Geschäftsordnung.md
+@@ -20,0 +21,3 @@
++## §4 Umgang mit der SARS-Cov-2-Pandemie
++
++1. Der Vorstand hat die Befugnis, in Rücksprache mit den Vereinsmitgliedern, verschiedene Hygienemaßnahmen für Präsenzveranstaltungen zu beschließen.`
+ assert.Equal(t, expected, result)
+}
+
func TestCutDiffAroundLine(t *testing.T) {
result, err := CutDiffAroundLine(strings.NewReader(exampleDiff), 4, false, 3)
assert.NoError(t, err)
diff --git a/modules/git/git.go b/modules/git/git.go
index ef6ec0c2bf..d8f4cdd239 100644
--- a/modules/git/git.go
+++ b/modules/git/git.go
@@ -188,6 +188,12 @@ func Init(ctx context.Context) error {
return err
}
}
+ if setting.Git.DisableCoreProtectNTFS {
+ if err := checkAndSetConfig("core.protectntfs", "false", true); err != nil {
+ return err
+ }
+ GlobalCommandArgs = append(GlobalCommandArgs, "-c", "core.protectntfs=false")
+ }
return nil
}
diff --git a/modules/git/last_commit_cache_gogit.go b/modules/git/last_commit_cache_gogit.go
index b8e0db46a9..fb09af6f2a 100644
--- a/modules/git/last_commit_cache_gogit.go
+++ b/modules/git/last_commit_cache_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/last_commit_cache_nogogit.go b/modules/git/last_commit_cache_nogogit.go
index faf6e23fa8..f71e7350a1 100644
--- a/modules/git/last_commit_cache_nogogit.go
+++ b/modules/git/last_commit_cache_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go
index 803d614d61..7d83ac7270 100644
--- a/modules/git/log_name_status.go
+++ b/modules/git/log_name_status.go
@@ -18,11 +18,16 @@ import (
)
// LogNameStatusRepo opens git log --raw in the provided repo and returns a stdin pipe, a stdout reader and cancel function
-func LogNameStatusRepo(repository, head, treepath string, paths ...string) (*bufio.Reader, func()) {
+func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, paths ...string) (*bufio.Reader, func()) {
// We often want to feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
// so let's create a batch stdin and stdout
stdoutReader, stdoutWriter := nio.Pipe(buffer.New(32 * 1024))
+
+ // Lets also create a context so that we can absolutely ensure that the command should die when we're done
+ ctx, ctxCancel := context.WithCancel(ctx)
+
cancel := func() {
+ ctxCancel()
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}
@@ -50,7 +55,7 @@ func LogNameStatusRepo(repository, head, treepath string, paths ...string) (*buf
go func() {
stderr := strings.Builder{}
- err := NewCommand(args...).RunInDirFullPipeline(repository, stdoutWriter, &stderr, nil)
+ err := NewCommandContext(ctx, args...).RunInDirFullPipeline(repository, stdoutWriter, &stderr, nil)
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
} else {
@@ -75,8 +80,8 @@ type LogNameStatusRepoParser struct {
}
// NewLogNameStatusRepoParser returns a new parser for a git log raw output
-func NewLogNameStatusRepoParser(repository, head, treepath string, paths ...string) *LogNameStatusRepoParser {
- rd, cancel := LogNameStatusRepo(repository, head, treepath, paths...)
+func NewLogNameStatusRepoParser(ctx context.Context, repository, head, treepath string, paths ...string) *LogNameStatusRepoParser {
+ rd, cancel := LogNameStatusRepo(ctx, repository, head, treepath, paths...)
return &LogNameStatusRepoParser{
treepath: treepath,
paths: paths,
@@ -167,6 +172,9 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int
return nil, err
}
}
+ if len(g.next) == 0 {
+ return &ret, nil
+ }
if g.next[0] == '\x00' {
g.buffull = false
g.next, err = g.rd.ReadSlice('\x00')
@@ -308,8 +316,11 @@ func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath st
}
}
- g := NewLogNameStatusRepoParser(repo.Path, head.ID.String(), treepath, paths...)
- defer g.Close()
+ g := NewLogNameStatusRepoParser(ctx, repo.Path, head.ID.String(), treepath, paths...)
+ // don't use defer g.Close() here as g may change its value - instead wrap in a func
+ defer func() {
+ g.Close()
+ }()
results := make([]string, len(paths))
remaining := len(paths)
@@ -328,6 +339,7 @@ heaploop:
for {
select {
case <-ctx.Done():
+ g.Close()
return nil, ctx.Err()
default:
}
@@ -377,7 +389,7 @@ heaploop:
remainingPaths = append(remainingPaths, pth)
}
}
- g = NewLogNameStatusRepoParser(repo.Path, lastEmptyParent, treepath, remainingPaths...)
+ g = NewLogNameStatusRepoParser(ctx, repo.Path, lastEmptyParent, treepath, remainingPaths...)
parentRemaining = map[string]bool{}
nextRestart = (remaining * 3) / 4
continue heaploop
diff --git a/modules/git/notes_gogit.go b/modules/git/notes_gogit.go
index 534a5d5171..654a52f5f4 100644
--- a/modules/git/notes_gogit.go
+++ b/modules/git/notes_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
@@ -36,6 +37,9 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note)
remainingCommitID = remainingCommitID[2:]
}
if err != nil {
+ if err == object.ErrDirectoryNotFound {
+ return ErrNotExist{ID: remainingCommitID, RelPath: path}
+ }
return err
}
}
diff --git a/modules/git/notes_nogogit.go b/modules/git/notes_nogogit.go
index 267087a86f..f75ab33edd 100644
--- a/modules/git/notes_nogogit.go
+++ b/modules/git/notes_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/notes_test.go b/modules/git/notes_test.go
index f66a191e6a..fec46e5960 100644
--- a/modules/git/notes_test.go
+++ b/modules/git/notes_test.go
@@ -39,3 +39,15 @@ func TestGetNestedNotes(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, []byte("Note 1"), note.Message)
}
+
+func TestGetNonExistentNotes(t *testing.T) {
+ bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
+ bareRepo1, err := OpenRepository(bareRepo1Path)
+ assert.NoError(t, err)
+ defer bareRepo1.Close()
+
+ note := Note{}
+ err = GetNote(context.Background(), bareRepo1, "non_existent_sha", ¬e)
+ assert.Error(t, err)
+ assert.IsType(t, ErrNotExist{}, err)
+}
diff --git a/modules/git/parse_gogit.go b/modules/git/parse_gogit.go
index a50ebec3dd..c42e32929e 100644
--- a/modules/git/parse_gogit.go
+++ b/modules/git/parse_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/parse_gogit_test.go b/modules/git/parse_gogit_test.go
index c6374133c0..c27f5172d5 100644
--- a/modules/git/parse_gogit_test.go
+++ b/modules/git/parse_gogit_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/parse_nogogit.go b/modules/git/parse_nogogit.go
index 667111ec4a..dd5554b5dd 100644
--- a/modules/git/parse_nogogit.go
+++ b/modules/git/parse_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go
index 502c38d4e8..5f58237de8 100644
--- a/modules/git/parse_nogogit_test.go
+++ b/modules/git/parse_nogogit_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/pipeline/lfs.go b/modules/git/pipeline/lfs.go
index d47b7d91ea..46a48b710c 100644
--- a/modules/git/pipeline/lfs.go
+++ b/modules/git/pipeline/lfs.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package pipeline
diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go
index d3696fcda2..a441e37b60 100644
--- a/modules/git/pipeline/lfs_nogogit.go
+++ b/modules/git/pipeline/lfs_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package pipeline
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 43f329f448..53a8666d8b 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -75,16 +75,16 @@ func InitRepository(repoPath string, bare bool) error {
// IsEmpty Check if repository is empty.
func (repo *Repository) IsEmpty() (bool, error) {
- var errbuf strings.Builder
- if err := NewCommand("log", "-1").RunInDirPipeline(repo.Path, nil, &errbuf); err != nil {
- if strings.Contains(errbuf.String(), "fatal: bad default revision 'HEAD'") ||
- strings.Contains(errbuf.String(), "fatal: your current branch 'master' does not have any commits yet") {
- return true, nil
- }
+ var errbuf, output strings.Builder
+ if err := NewCommand("rev-list", "--all", "--count", "--max-count=1").RunInDirPipeline(repo.Path, &output, &errbuf); err != nil {
return true, fmt.Errorf("check empty: %v - %s", err, errbuf.String())
}
- return false, nil
+ c, err := strconv.Atoi(strings.TrimSpace(output.String()))
+ if err != nil {
+ return true, fmt.Errorf("check empty: convert %s to count failed: %v", output.String(), err)
+ }
+ return c == 0, nil
}
// CloneRepoOptions options when clone a repository
diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go
index 6186824c0b..afa5383b11 100644
--- a/modules/git/repo_base_gogit.go
+++ b/modules/git/repo_base_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go
index 1675967d18..4e5b1db112 100644
--- a/modules/git/repo_base_nogogit.go
+++ b/modules/git/repo_base_nogogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
@@ -42,6 +43,11 @@ func OpenRepository(repoPath string) (*Repository, error) {
return nil, errors.New("no such file or directory")
}
+ // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
+ if err := EnsureValidGitRepository(DefaultContext, repoPath); err != nil {
+ return nil, err
+ }
+
repo := &Repository{
Path: repoPath,
tagCache: newObjectCache(),
diff --git a/modules/git/repo_blob_gogit.go b/modules/git/repo_blob_gogit.go
index 485c233ff8..b11e9f58fe 100644
--- a/modules/git/repo_blob_gogit.go
+++ b/modules/git/repo_blob_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_blob_nogogit.go b/modules/git/repo_blob_nogogit.go
index afb08d29cb..775b3835dd 100644
--- a/modules/git/repo_blob_nogogit.go
+++ b/modules/git/repo_blob_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/repo_branch_gogit.go b/modules/git/repo_branch_gogit.go
index e8386b2dbd..6bf14b3999 100644
--- a/modules/git/repo_branch_gogit.go
+++ b/modules/git/repo_branch_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go
index 7d10b8ba0f..666ca81c1e 100644
--- a/modules/git/repo_branch_nogogit.go
+++ b/modules/git/repo_branch_nogogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go
index 2f9b1c4206..175b6e6446 100644
--- a/modules/git/repo_commit_gogit.go
+++ b/modules/git/repo_commit_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go
index afd5166f1d..b109bedee5 100644
--- a/modules/git/repo_commit_nogogit.go
+++ b/modules/git/repo_commit_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
@@ -37,7 +38,10 @@ func (repo *Repository) ResolveReference(name string) (string, error) {
func (repo *Repository) GetRefCommitID(name string) (string, error) {
wr, rd, cancel := repo.CatFileBatchCheck()
defer cancel()
- _, _ = wr.Write([]byte(name + "\n"))
+ _, err := wr.Write([]byte(name + "\n"))
+ if err != nil {
+ return "", err
+ }
shaBs, _, _, err := ReadBatchLine(rd)
if IsErrNotExist(err) {
return "", ErrNotExist{name, ""}
diff --git a/modules/git/repo_commitgraph_gogit.go b/modules/git/repo_commitgraph_gogit.go
index 6773109451..84a2edb664 100644
--- a/modules/git/repo_commitgraph_gogit.go
+++ b/modules/git/repo_commitgraph_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_language_stats_gogit.go b/modules/git/repo_language_stats_gogit.go
index 20a7b061f2..0a4cfbbc7b 100644
--- a/modules/git/repo_language_stats_gogit.go
+++ b/modules/git/repo_language_stats_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go
index 1684f21d16..7425e2dbb1 100644
--- a/modules/git/repo_language_stats_nogogit.go
+++ b/modules/git/repo_language_stats_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/repo_ref_gogit.go b/modules/git/repo_ref_gogit.go
index 2e83e6c462..9f0e11366f 100644
--- a/modules/git/repo_ref_gogit.go
+++ b/modules/git/repo_ref_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_ref_nogogit.go b/modules/git/repo_ref_nogogit.go
index 540961592b..ec0c5ec4ca 100644
--- a/modules/git/repo_ref_nogogit.go
+++ b/modules/git/repo_ref_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/repo_tag_gogit.go b/modules/git/repo_tag_gogit.go
index 3ac097c9a8..3022fe96f7 100644
--- a/modules/git/repo_tag_gogit.go
+++ b/modules/git/repo_tag_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go
index a9e122aeaa..0170f0cc76 100644
--- a/modules/git/repo_tag_nogogit.go
+++ b/modules/git/repo_tag_nogogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/repo_tree_gogit.go b/modules/git/repo_tree_gogit.go
index d878f5e7a7..2ddffcf79b 100644
--- a/modules/git/repo_tree_gogit.go
+++ b/modules/git/repo_tree_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/repo_tree_nogogit.go b/modules/git/repo_tree_nogogit.go
index 967f8aea3f..9d4268b13a 100644
--- a/modules/git/repo_tree_nogogit.go
+++ b/modules/git/repo_tree_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/sha1_gogit.go b/modules/git/sha1_gogit.go
index 5953af58bf..30290f14b7 100644
--- a/modules/git/sha1_gogit.go
+++ b/modules/git/sha1_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/sha1_nogogit.go b/modules/git/sha1_nogogit.go
index 09b5baacd5..53665fc921 100644
--- a/modules/git/sha1_nogogit.go
+++ b/modules/git/sha1_nogogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/signature_gogit.go b/modules/git/signature_gogit.go
index 804c0074d3..903a48133f 100644
--- a/modules/git/signature_gogit.go
+++ b/modules/git/signature_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/signature_nogogit.go b/modules/git/signature_nogogit.go
index 753d87b605..c6fe8e6d1a 100644
--- a/modules/git/signature_nogogit.go
+++ b/modules/git/signature_nogogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/submodule.go b/modules/git/submodule.go
index 231827f1e9..ee61f61179 100644
--- a/modules/git/submodule.go
+++ b/modules/git/submodule.go
@@ -52,9 +52,7 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
urlPrefixHostname = prefixURL.Host
}
- if strings.HasSuffix(urlPrefix, "/") {
- urlPrefix = urlPrefix[:len(urlPrefix)-1]
- }
+ urlPrefix = strings.TrimSuffix(urlPrefix, "/")
// FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules
// Relative url prefix check (according to git submodule documentation)
diff --git a/modules/git/tree.go b/modules/git/tree.go
index 059f0a8287..3671f421e9 100644
--- a/modules/git/tree.go
+++ b/modules/git/tree.go
@@ -6,6 +6,7 @@
package git
import (
+ "bytes"
"strings"
)
@@ -45,3 +46,23 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) {
}
return g, nil
}
+
+// LsTree checks if the given filenames are in the tree
+func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
+ cmd := NewCommand("ls-tree", "-z", "--name-only", "--", ref)
+ for _, arg := range filenames {
+ if arg != "" {
+ cmd.AddArguments(arg)
+ }
+ }
+ res, err := cmd.RunInDirBytes(repo.Path)
+ if err != nil {
+ return nil, err
+ }
+ filelist := make([]string, 0, len(filenames))
+ for _, line := range bytes.Split(res, []byte{'\000'}) {
+ filelist = append(filelist, string(line))
+ }
+
+ return filelist, err
+}
diff --git a/modules/git/tree_blob_gogit.go b/modules/git/tree_blob_gogit.go
index 93ebc8a367..a8d619cd18 100644
--- a/modules/git/tree_blob_gogit.go
+++ b/modules/git/tree_blob_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/tree_blob_nogogit.go b/modules/git/tree_blob_nogogit.go
index fdd8d79c8b..df23ff01b4 100644
--- a/modules/git/tree_blob_nogogit.go
+++ b/modules/git/tree_blob_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/tree_entry_gogit.go b/modules/git/tree_entry_gogit.go
index 219251a77e..20e767eea1 100644
--- a/modules/git/tree_entry_gogit.go
+++ b/modules/git/tree_entry_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/tree_entry_nogogit.go b/modules/git/tree_entry_nogogit.go
index 41356ceba2..288ec4db6e 100644
--- a/modules/git/tree_entry_nogogit.go
+++ b/modules/git/tree_entry_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/git/tree_entry_test.go b/modules/git/tree_entry_test.go
index 3382de4102..402c345887 100644
--- a/modules/git/tree_entry_test.go
+++ b/modules/git/tree_entry_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/tree_gogit.go b/modules/git/tree_gogit.go
index 79132c5548..bc02088366 100644
--- a/modules/git/tree_gogit.go
+++ b/modules/git/tree_gogit.go
@@ -3,6 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package git
diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go
index 9661d8faea..3d3fd26ece 100644
--- a/modules/git/tree_nogogit.go
+++ b/modules/git/tree_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package git
diff --git a/modules/gitgraph/graph_models.go b/modules/gitgraph/graph_models.go
index ec47f0ad84..5ed2642328 100644
--- a/modules/gitgraph/graph_models.go
+++ b/modules/gitgraph/graph_models.go
@@ -217,11 +217,9 @@ func newRefsFromRefNames(refNames []byte) []git.Reference {
continue
}
refName := string(refNameBytes)
- if strings.HasPrefix(refName, "tag: ") {
- refName = strings.TrimPrefix(refName, "tag: ")
- } else if strings.HasPrefix(refName, "HEAD -> ") {
- refName = strings.TrimPrefix(refName, "HEAD -> ")
- }
+ refName = strings.TrimPrefix(refName, "tag: ")
+ refName = strings.TrimPrefix(refName, "HEAD -> ")
+
refs = append(refs, git.Reference{
Name: refName,
})
diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go
index 20d9b3905c..fcbb16a3bb 100644
--- a/modules/graceful/manager_unix.go
+++ b/modules/graceful/manager_unix.go
@@ -1,9 +1,10 @@
-// +build !windows
-
// Copyright 2019 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.
+//go:build !windows
+// +build !windows
+
package graceful
import (
diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go
index 51f29778ba..e5f5541ed3 100644
--- a/modules/graceful/manager_windows.go
+++ b/modules/graceful/manager_windows.go
@@ -1,10 +1,11 @@
-// +build windows
-
// Copyright 2019 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.
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+//go:build windows
+// +build windows
+
package graceful
import (
diff --git a/modules/graceful/net_unix.go b/modules/graceful/net_unix.go
index 2dc714955e..6ffa8150cc 100644
--- a/modules/graceful/net_unix.go
+++ b/modules/graceful/net_unix.go
@@ -1,10 +1,11 @@
-// +build !windows
-
// Copyright 2019 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.
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+//go:build !windows
+// +build !windows
+
package graceful
import (
diff --git a/modules/graceful/net_windows.go b/modules/graceful/net_windows.go
index 3fc1433491..35b7a9d1fe 100644
--- a/modules/graceful/net_windows.go
+++ b/modules/graceful/net_windows.go
@@ -1,10 +1,11 @@
-// +build windows
-
// Copyright 2019 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.
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+//go:build windows
+// +build windows
+
package graceful
import "net"
diff --git a/modules/graceful/restart_unix.go b/modules/graceful/restart_unix.go
index 9a94e5fa67..392ed60cb3 100644
--- a/modules/graceful/restart_unix.go
+++ b/modules/graceful/restart_unix.go
@@ -1,10 +1,11 @@
-// +build !windows
-
// Copyright 2019 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.
// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+//go:build !windows
+// +build !windows
+
package graceful
import (
diff --git a/modules/graceful/server.go b/modules/graceful/server.go
index 6b7d4a1a97..e98d3aa86b 100644
--- a/modules/graceful/server.go
+++ b/modules/graceful/server.go
@@ -229,7 +229,7 @@ func (wl *wrappedListener) Accept() (net.Conn, error) {
closed := int32(0)
- c = wrappedConn{
+ c = &wrappedConn{
Conn: c,
server: wl.server,
closed: &closed,
@@ -264,7 +264,7 @@ type wrappedConn struct {
perWritePerKbTimeout time.Duration
}
-func (w wrappedConn) Write(p []byte) (n int, err error) {
+func (w *wrappedConn) Write(p []byte) (n int, err error) {
if w.perWriteTimeout > 0 {
minTimeout := time.Duration(len(p)/1024) * w.perWritePerKbTimeout
minDeadline := time.Now().Add(minTimeout).Add(w.perWriteTimeout)
@@ -278,7 +278,7 @@ func (w wrappedConn) Write(p []byte) (n int, err error) {
return w.Conn.Write(p)
}
-func (w wrappedConn) Close() error {
+func (w *wrappedConn) Close() error {
if atomic.CompareAndSwapInt32(w.closed, 0, 1) {
defer func() {
if err := recover(); err != nil {
diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go
index 568035fbb7..c6778de81d 100644
--- a/modules/highlight/highlight.go
+++ b/modules/highlight/highlight.go
@@ -66,17 +66,6 @@ func Code(fileName, code string) string {
if len(code) > sizeLimit {
return code
}
- formatter := html.New(html.WithClasses(true),
- html.WithLineNumbers(false),
- html.PreventSurroundingPre(true),
- )
- if formatter == nil {
- log.Error("Couldn't create chroma formatter")
- return code
- }
-
- htmlbuf := bytes.Buffer{}
- htmlw := bufio.NewWriter(&htmlbuf)
var lexer chroma.Lexer
if val, ok := highlightMapping[filepath.Ext(fileName)]; ok {
@@ -97,6 +86,18 @@ func Code(fileName, code string) string {
}
cache.Add(fileName, lexer)
}
+ return CodeFromLexer(lexer, code)
+}
+
+// CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes
+func CodeFromLexer(lexer chroma.Lexer, code string) string {
+ formatter := html.New(html.WithClasses(true),
+ html.WithLineNumbers(false),
+ html.PreventSurroundingPre(true),
+ )
+
+ htmlbuf := bytes.Buffer{}
+ htmlw := bufio.NewWriter(&htmlbuf)
iterator, err := lexer.Tokenise(nil, string(code))
if err != nil {
@@ -166,6 +167,11 @@ func File(numLines int, fileName string, code []byte) map[int]string {
}
htmlw.Flush()
+ finalNewLine := false
+ if len(code) > 0 {
+ finalNewLine = code[len(code)-1] == '\n'
+ }
+
m := make(map[int]string, numLines)
for k, v := range strings.SplitN(htmlbuf.String(), "\n", numLines) {
line := k + 1
@@ -173,9 +179,17 @@ func File(numLines int, fileName string, code []byte) map[int]string {
//need to keep lines that are only \n so copy/paste works properly in browser
if content == "" {
content = "\n"
+ } else if content == `` {
+ content += "\n"
}
+ content = strings.TrimSuffix(content, ``)
+ content = strings.TrimPrefix(content, ``)
m[line] = content
}
+ if finalNewLine {
+ m[numLines+1] = "\n"
+ }
+
return m
}
diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go
new file mode 100644
index 0000000000..7c5afaa52c
--- /dev/null
+++ b/modules/highlight/highlight_test.go
@@ -0,0 +1,103 @@
+// Copyright 2021 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 highlight
+
+import (
+ "reflect"
+ "testing"
+
+ "code.gitea.io/gitea/modules/setting"
+ "gopkg.in/ini.v1"
+)
+
+func TestFile(t *testing.T) {
+ setting.Cfg = ini.Empty()
+ tests := []struct {
+ name string
+ numLines int
+ fileName string
+ code string
+ want map[int]string
+ }{
+ {
+ name: ".drone.yml",
+ numLines: 12,
+ fileName: ".drone.yml",
+ code: `kind: pipeline
+name: default
+
+steps:
+- name: test
+ image: golang:1.13
+ environment:
+ GOPROXY: https://goproxy.cn
+ commands:
+ - go get -u
+ - go build -v
+ - go test -v -race -coverprofile=coverage.txt -covermode=atomic
+`,
+ want: map[int]string{
+ 1: `kind: pipeline`,
+ 2: `name: default`,
+ 3: `
+`,
+ 4: `steps:`,
+ 5: `- name: test`,
+ 6: ` image: golang:1.13`,
+ 7: ` environment:`,
+ 8: ` GOPROXY: https://goproxy.cn`,
+ 9: ` commands:`,
+ 10: ` - go get -u`,
+ 11: ` - go build -v`,
+ 12: ` - go test -v -race -coverprofile=coverage.txt -covermode=atomic
+`,
+ 13: `
+`,
+ },
+ },
+ {
+ name: ".drone.yml - trailing space",
+ numLines: 13,
+ fileName: ".drone.yml",
+ code: `kind: pipeline
+name: default ` + `
+
+steps:
+- name: test
+ image: golang:1.13
+ environment:
+ GOPROXY: https://goproxy.cn
+ commands:
+ - go get -u
+ - go build -v
+ - go test -v -race -coverprofile=coverage.txt -covermode=atomic
+ `,
+ want: map[int]string{
+ 1: `kind: pipeline`,
+ 2: `name: default `,
+ 3: `
+`,
+ 4: `steps:`,
+ 5: `- name: test`,
+ 6: ` image: golang:1.13`,
+ 7: ` environment:`,
+ 8: ` GOPROXY: https://goproxy.cn`,
+ 9: ` commands:`,
+ 10: ` - go get -u`,
+ 11: ` - go build -v`,
+ 12: ` - go test -v -race -coverprofile=coverage.txt -covermode=atomic`,
+ 13: ` `,
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := File(tt.numLines, tt.fileName, []byte(tt.code)); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("File() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/modules/hostmatcher/hostmatcher.go b/modules/hostmatcher/hostmatcher.go
new file mode 100644
index 0000000000..f8a787c575
--- /dev/null
+++ b/modules/hostmatcher/hostmatcher.go
@@ -0,0 +1,94 @@
+// Copyright 2021 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 hostmatcher
+
+import (
+ "net"
+ "path/filepath"
+ "strings"
+
+ "code.gitea.io/gitea/modules/util"
+)
+
+// HostMatchList is used to check if a host or IP is in a list.
+// If you only need to do wildcard matching, consider to use modules/matchlist
+type HostMatchList struct {
+ hosts []string
+ ipNets []*net.IPNet
+}
+
+// MatchBuiltinAll all hosts are matched
+const MatchBuiltinAll = "*"
+
+// MatchBuiltinExternal A valid non-private unicast IP, all hosts on public internet are matched
+const MatchBuiltinExternal = "external"
+
+// MatchBuiltinPrivate RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and RFC 4193 (FC00::/7). Also called LAN/Intranet.
+const MatchBuiltinPrivate = "private"
+
+// MatchBuiltinLoopback 127.0.0.0/8 for IPv4 and ::1/128 for IPv6, localhost is included.
+const MatchBuiltinLoopback = "loopback"
+
+// ParseHostMatchList parses the host list HostMatchList
+func ParseHostMatchList(hostList string) *HostMatchList {
+ hl := &HostMatchList{}
+ for _, s := range strings.Split(hostList, ",") {
+ s = strings.ToLower(strings.TrimSpace(s))
+ if s == "" {
+ continue
+ }
+ _, ipNet, err := net.ParseCIDR(s)
+ if err == nil {
+ hl.ipNets = append(hl.ipNets, ipNet)
+ } else {
+ hl.hosts = append(hl.hosts, s)
+ }
+ }
+ return hl
+}
+
+// MatchesHostOrIP checks if the host or IP matches an allow/deny(block) list
+func (hl *HostMatchList) MatchesHostOrIP(host string, ip net.IP) bool {
+ var matched bool
+ host = strings.ToLower(host)
+ ipStr := ip.String()
+loop:
+ for _, hostInList := range hl.hosts {
+ switch hostInList {
+ case "":
+ continue
+ case MatchBuiltinAll:
+ matched = true
+ break loop
+ case MatchBuiltinExternal:
+ if matched = ip.IsGlobalUnicast() && !util.IsIPPrivate(ip); matched {
+ break loop
+ }
+ case MatchBuiltinPrivate:
+ if matched = util.IsIPPrivate(ip); matched {
+ break loop
+ }
+ case MatchBuiltinLoopback:
+ if matched = ip.IsLoopback(); matched {
+ break loop
+ }
+ default:
+ if matched, _ = filepath.Match(hostInList, host); matched {
+ break loop
+ }
+ if matched, _ = filepath.Match(hostInList, ipStr); matched {
+ break loop
+ }
+ }
+ }
+ if !matched {
+ for _, ipNet := range hl.ipNets {
+ if matched = ipNet.Contains(ip); matched {
+ break
+ }
+ }
+ }
+ return matched
+}
diff --git a/modules/hostmatcher/hostmatcher_test.go b/modules/hostmatcher/hostmatcher_test.go
new file mode 100644
index 0000000000..8eaafbdbc8
--- /dev/null
+++ b/modules/hostmatcher/hostmatcher_test.go
@@ -0,0 +1,119 @@
+// Copyright 2021 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 hostmatcher
+
+import (
+ "net"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestHostOrIPMatchesList(t *testing.T) {
+ type tc struct {
+ host string
+ ip net.IP
+ expected bool
+ }
+
+ // for IPv6: "::1" is loopback, "fd00::/8" is private
+
+ hl := ParseHostMatchList("private, External, *.myDomain.com, 169.254.1.0/24")
+ cases := []tc{
+ {"", net.IPv4zero, false},
+ {"", net.IPv6zero, false},
+
+ {"", net.ParseIP("127.0.0.1"), false},
+ {"", net.ParseIP("::1"), false},
+
+ {"", net.ParseIP("10.0.1.1"), true},
+ {"", net.ParseIP("192.168.1.1"), true},
+ {"", net.ParseIP("fd00::1"), true},
+
+ {"", net.ParseIP("8.8.8.8"), true},
+ {"", net.ParseIP("1001::1"), true},
+
+ {"mydomain.com", net.IPv4zero, false},
+ {"sub.mydomain.com", net.IPv4zero, true},
+
+ {"", net.ParseIP("169.254.1.1"), true},
+ {"", net.ParseIP("169.254.2.2"), false},
+ }
+ for _, c := range cases {
+ assert.Equalf(t, c.expected, hl.MatchesHostOrIP(c.host, c.ip), "case %s(%v)", c.host, c.ip)
+ }
+
+ hl = ParseHostMatchList("loopback")
+ cases = []tc{
+ {"", net.IPv4zero, false},
+ {"", net.ParseIP("127.0.0.1"), true},
+ {"", net.ParseIP("10.0.1.1"), false},
+ {"", net.ParseIP("192.168.1.1"), false},
+ {"", net.ParseIP("8.8.8.8"), false},
+
+ {"", net.ParseIP("::1"), true},
+ {"", net.ParseIP("fd00::1"), false},
+ {"", net.ParseIP("1000::1"), false},
+
+ {"mydomain.com", net.IPv4zero, false},
+ }
+ for _, c := range cases {
+ assert.Equalf(t, c.expected, hl.MatchesHostOrIP(c.host, c.ip), "case %s(%v)", c.host, c.ip)
+ }
+
+ hl = ParseHostMatchList("private")
+ cases = []tc{
+ {"", net.IPv4zero, false},
+ {"", net.ParseIP("127.0.0.1"), false},
+ {"", net.ParseIP("10.0.1.1"), true},
+ {"", net.ParseIP("192.168.1.1"), true},
+ {"", net.ParseIP("8.8.8.8"), false},
+
+ {"", net.ParseIP("::1"), false},
+ {"", net.ParseIP("fd00::1"), true},
+ {"", net.ParseIP("1000::1"), false},
+
+ {"mydomain.com", net.IPv4zero, false},
+ }
+ for _, c := range cases {
+ assert.Equalf(t, c.expected, hl.MatchesHostOrIP(c.host, c.ip), "case %s(%v)", c.host, c.ip)
+ }
+
+ hl = ParseHostMatchList("external")
+ cases = []tc{
+ {"", net.IPv4zero, false},
+ {"", net.ParseIP("127.0.0.1"), false},
+ {"", net.ParseIP("10.0.1.1"), false},
+ {"", net.ParseIP("192.168.1.1"), false},
+ {"", net.ParseIP("8.8.8.8"), true},
+
+ {"", net.ParseIP("::1"), false},
+ {"", net.ParseIP("fd00::1"), false},
+ {"", net.ParseIP("1000::1"), true},
+
+ {"mydomain.com", net.IPv4zero, false},
+ }
+ for _, c := range cases {
+ assert.Equalf(t, c.expected, hl.MatchesHostOrIP(c.host, c.ip), "case %s(%v)", c.host, c.ip)
+ }
+
+ hl = ParseHostMatchList("*")
+ cases = []tc{
+ {"", net.IPv4zero, true},
+ {"", net.ParseIP("127.0.0.1"), true},
+ {"", net.ParseIP("10.0.1.1"), true},
+ {"", net.ParseIP("192.168.1.1"), true},
+ {"", net.ParseIP("8.8.8.8"), true},
+
+ {"", net.ParseIP("::1"), true},
+ {"", net.ParseIP("fd00::1"), true},
+ {"", net.ParseIP("1000::1"), true},
+
+ {"mydomain.com", net.IPv4zero, true},
+ }
+ for _, c := range cases {
+ assert.Equalf(t, c.expected, hl.MatchesHostOrIP(c.host, c.ip), "case %s(%v)", c.host, c.ip)
+ }
+}
diff --git a/modules/indexer/bleve/batch.go b/modules/indexer/bleve/batch.go
new file mode 100644
index 0000000000..79994e6e5b
--- /dev/null
+++ b/modules/indexer/bleve/batch.go
@@ -0,0 +1,59 @@
+// Copyright 2021 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 bleve
+
+import (
+ "github.com/blevesearch/bleve/v2"
+)
+
+// FlushingBatch is a batch of operations that automatically flushes to the
+// underlying index once it reaches a certain size.
+type FlushingBatch struct {
+ maxBatchSize int
+ batch *bleve.Batch
+ index bleve.Index
+}
+
+// NewFlushingBatch creates a new flushing batch for the specified index. Once
+// the number of operations in the batch reaches the specified limit, the batch
+// automatically flushes its operations to the index.
+func NewFlushingBatch(index bleve.Index, maxBatchSize int) *FlushingBatch {
+ return &FlushingBatch{
+ maxBatchSize: maxBatchSize,
+ batch: index.NewBatch(),
+ index: index,
+ }
+}
+
+// Index add a new index to batch
+func (b *FlushingBatch) Index(id string, data interface{}) error {
+ if err := b.batch.Index(id, data); err != nil {
+ return err
+ }
+ return b.flushIfFull()
+}
+
+// Delete add a delete index to batch
+func (b *FlushingBatch) Delete(id string) error {
+ b.batch.Delete(id)
+ return b.flushIfFull()
+}
+
+func (b *FlushingBatch) flushIfFull() error {
+ if b.batch.Size() < b.maxBatchSize {
+ return nil
+ }
+ return b.Flush()
+}
+
+// Flush submit the batch and create a new one
+func (b *FlushingBatch) Flush() error {
+ err := b.index.Batch(b.batch)
+ if err != nil {
+ return err
+ }
+ b.batch = b.index.NewBatch()
+ return nil
+}
diff --git a/modules/indexer/code/bleve.go b/modules/indexer/code/bleve.go
index 600789a284..55e9a7cb06 100644
--- a/modules/indexer/code/bleve.go
+++ b/modules/indexer/code/bleve.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
+ gitea_bleve "code.gitea.io/gitea/modules/indexer/bleve"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
@@ -173,10 +174,15 @@ func NewBleveIndexer(indexDir string) (*BleveIndexer, bool, error) {
indexDir: indexDir,
}
created, err := indexer.init()
+ if err != nil {
+ indexer.Close()
+ return nil, false, err
+ }
return indexer, created, err
}
-func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *bufio.Reader, commitSha string, update fileUpdate, repo *models.Repository, batch rupture.FlushingBatch) error {
+func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *bufio.Reader, commitSha string,
+ update fileUpdate, repo *models.Repository, batch *gitea_bleve.FlushingBatch) error {
// Ignore vendored files in code search
if setting.Indexer.ExcludeVendored && analyze.IsVendor(update.Filename) {
return nil
@@ -229,7 +235,7 @@ func (b *BleveIndexer) addUpdate(batchWriter git.WriteCloserError, batchReader *
})
}
-func (b *BleveIndexer) addDelete(filename string, repo *models.Repository, batch rupture.FlushingBatch) error {
+func (b *BleveIndexer) addDelete(filename string, repo *models.Repository, batch *gitea_bleve.FlushingBatch) error {
id := filenameIndexerID(repo.ID, filename)
return batch.Delete(id)
}
@@ -267,9 +273,15 @@ func (b *BleveIndexer) Close() {
// Index indexes the data
func (b *BleveIndexer) Index(repo *models.Repository, sha string, changes *repoChanges) error {
- batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
+ batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
if len(changes.Updates) > 0 {
+ // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
+ if err := git.EnsureValidGitRepository(git.DefaultContext, repo.RepoPath()); err != nil {
+ log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
+ return err
+ }
+
batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath())
defer cancel()
@@ -296,7 +308,7 @@ func (b *BleveIndexer) Delete(repoID int64) error {
if err != nil {
return err
}
- batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
+ batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
for _, hit := range result.Hits {
if err = batch.Delete(hit.ID); err != nil {
return err
diff --git a/modules/indexer/code/elastic_search.go b/modules/indexer/code/elastic_search.go
index 569917f151..822e45807b 100644
--- a/modules/indexer/code/elastic_search.go
+++ b/modules/indexer/code/elastic_search.go
@@ -83,7 +83,10 @@ func NewElasticSearchIndexer(url, indexerName string) (*ElasticSearchIndexer, bo
indexerAliasName: indexerName,
}
exists, err := indexer.init()
-
+ if err != nil {
+ indexer.Close()
+ return nil, false, err
+ }
return indexer, !exists, err
}
@@ -245,6 +248,11 @@ func (b *ElasticSearchIndexer) addDelete(filename string, repo *models.Repositor
func (b *ElasticSearchIndexer) Index(repo *models.Repository, sha string, changes *repoChanges) error {
reqs := make([]elastic.BulkableRequest, 0)
if len(changes.Updates) > 0 {
+ // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
+ if err := git.EnsureValidGitRepository(git.DefaultContext, repo.RepoPath()); err != nil {
+ log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
+ return err
+ }
batchWriter, batchReader, cancel := git.CatFileBatch(repo.RepoPath())
defer cancel()
diff --git a/modules/indexer/code/indexer.go b/modules/indexer/code/indexer.go
index 67fa43eda8..f81d9abb94 100644
--- a/modules/indexer/code/indexer.go
+++ b/modules/indexer/code/indexer.go
@@ -188,9 +188,6 @@ func Init() {
rIndexer, populate, err = NewBleveIndexer(setting.Indexer.RepoPath)
if err != nil {
- if rIndexer != nil {
- rIndexer.Close()
- }
cancel()
indexer.Close()
close(waitChannel)
@@ -208,9 +205,6 @@ func Init() {
rIndexer, populate, err = NewElasticSearchIndexer(setting.Indexer.RepoConnStr, setting.Indexer.RepoIndexerName)
if err != nil {
- if rIndexer != nil {
- rIndexer.Close()
- }
cancel()
indexer.Close()
close(waitChannel)
diff --git a/modules/indexer/issues/bleve.go b/modules/indexer/issues/bleve.go
index b1385eb676..db12874e84 100644
--- a/modules/indexer/issues/bleve.go
+++ b/modules/indexer/issues/bleve.go
@@ -9,8 +9,10 @@ import (
"os"
"strconv"
+ gitea_bleve "code.gitea.io/gitea/modules/indexer/bleve"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
+
"github.com/blevesearch/bleve/v2"
"github.com/blevesearch/bleve/v2/analysis/analyzer/custom"
"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
@@ -197,7 +199,7 @@ func (b *BleveIndexer) Close() {
// Index will save the index data
func (b *BleveIndexer) Index(issues []*IndexerData) error {
- batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
+ batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
for _, issue := range issues {
if err := batch.Index(indexerID(issue.ID), struct {
RepoID int64
@@ -218,7 +220,7 @@ func (b *BleveIndexer) Index(issues []*IndexerData) error {
// Delete deletes indexes by ids
func (b *BleveIndexer) Delete(ids ...int64) error {
- batch := rupture.NewFlushingBatch(b.indexer, maxBatchSize)
+ batch := gitea_bleve.NewFlushingBatch(b.indexer, maxBatchSize)
for _, id := range ids {
if err := batch.Delete(indexerID(id)); err != nil {
return err
diff --git a/modules/indexer/stats/queue.go b/modules/indexer/stats/queue.go
index 8309cfcd3b..fde3f2ff01 100644
--- a/modules/indexer/stats/queue.go
+++ b/modules/indexer/stats/queue.go
@@ -27,7 +27,7 @@ func handle(data ...queue.Data) {
}
func initStatsQueue() error {
- statsQueue = queue.CreateUniqueQueue("repo_stats_update", handle, int64(0)).(queue.UniqueQueue)
+ statsQueue = queue.CreateUniqueQueue("repo_stats_update", handle, int64(0))
if statsQueue == nil {
return fmt.Errorf("Unable to create repo_stats_update Queue")
}
diff --git a/modules/lfs/endpoint.go b/modules/lfs/endpoint.go
index add16ce9f1..943966ed15 100644
--- a/modules/lfs/endpoint.go
+++ b/modules/lfs/endpoint.go
@@ -29,9 +29,7 @@ func endpointFromCloneURL(rawurl string) *url.URL {
return ep
}
- if strings.HasSuffix(ep.Path, "/") {
- ep.Path = ep.Path[:len(ep.Path)-1]
- }
+ ep.Path = strings.TrimSuffix(ep.Path, "/")
if ep.Scheme == "file" {
return ep
diff --git a/modules/lfs/pointer_scanner_gogit.go b/modules/lfs/pointer_scanner_gogit.go
index abd882990c..7e8b812f46 100644
--- a/modules/lfs/pointer_scanner_gogit.go
+++ b/modules/lfs/pointer_scanner_gogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build gogit
// +build gogit
package lfs
diff --git a/modules/lfs/pointer_scanner_nogogit.go b/modules/lfs/pointer_scanner_nogogit.go
index b5654d5de7..d8076b9021 100644
--- a/modules/lfs/pointer_scanner_nogogit.go
+++ b/modules/lfs/pointer_scanner_nogogit.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !gogit
// +build !gogit
package lfs
diff --git a/modules/log/colors.go b/modules/log/colors.go
index 5d56fd7390..ad3120ee6c 100644
--- a/modules/log/colors.go
+++ b/modules/log/colors.go
@@ -259,7 +259,7 @@ normalLoop:
}
// Process naughty character
- if _, err := fmt.Fprintf(c.w, `\%#o03d`, bytes[i]); err != nil {
+ if _, err := fmt.Fprintf(c.w, `\%#03o`, bytes[i]); err != nil {
return totalWritten, err
}
i++
diff --git a/modules/log/event.go b/modules/log/event.go
index 6975bf749d..00a66c306a 100644
--- a/modules/log/event.go
+++ b/modules/log/event.go
@@ -143,7 +143,7 @@ type MultiChannelledLog struct {
name string
bufferLength int64
queue chan *Event
- mutex sync.Mutex
+ rwmutex sync.RWMutex
loggers map[string]EventLogger
flush chan bool
close chan bool
@@ -173,10 +173,10 @@ func NewMultiChannelledLog(name string, bufferLength int64) *MultiChannelledLog
// AddLogger adds a logger to this MultiChannelledLog
func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
- m.mutex.Lock()
+ m.rwmutex.Lock()
name := logger.GetName()
if _, has := m.loggers[name]; has {
- m.mutex.Unlock()
+ m.rwmutex.Unlock()
return ErrDuplicateName{name}
}
m.loggers[name] = logger
@@ -186,7 +186,7 @@ func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
if logger.GetStacktraceLevel() < m.stacktraceLevel {
m.stacktraceLevel = logger.GetStacktraceLevel()
}
- m.mutex.Unlock()
+ m.rwmutex.Unlock()
go m.Start()
return nil
}
@@ -195,15 +195,15 @@ func (m *MultiChannelledLog) AddLogger(logger EventLogger) error {
// NB: If you delete the last sublogger this logger will simply drop
// log events
func (m *MultiChannelledLog) DelLogger(name string) bool {
- m.mutex.Lock()
+ m.rwmutex.Lock()
logger, has := m.loggers[name]
if !has {
- m.mutex.Unlock()
+ m.rwmutex.Unlock()
return false
}
delete(m.loggers, name)
m.internalResetLevel()
- m.mutex.Unlock()
+ m.rwmutex.Unlock()
logger.Flush()
logger.Close()
return true
@@ -211,15 +211,15 @@ func (m *MultiChannelledLog) DelLogger(name string) bool {
// GetEventLogger returns a sub logger from this MultiChannelledLog
func (m *MultiChannelledLog) GetEventLogger(name string) EventLogger {
- m.mutex.Lock()
- defer m.mutex.Unlock()
+ m.rwmutex.RLock()
+ defer m.rwmutex.RUnlock()
return m.loggers[name]
}
// GetEventLoggerNames returns a list of names
func (m *MultiChannelledLog) GetEventLoggerNames() []string {
- m.mutex.Lock()
- defer m.mutex.Unlock()
+ m.rwmutex.RLock()
+ defer m.rwmutex.RUnlock()
var keys []string
for k := range m.loggers {
keys = append(keys, k)
@@ -228,12 +228,12 @@ func (m *MultiChannelledLog) GetEventLoggerNames() []string {
}
func (m *MultiChannelledLog) closeLoggers() {
- m.mutex.Lock()
+ m.rwmutex.Lock()
for _, logger := range m.loggers {
logger.Flush()
logger.Close()
}
- m.mutex.Unlock()
+ m.rwmutex.Unlock()
m.closed <- true
}
@@ -249,8 +249,8 @@ func (m *MultiChannelledLog) Resume() {
// ReleaseReopen causes this logger to tell its subloggers to release and reopen
func (m *MultiChannelledLog) ReleaseReopen() error {
- m.mutex.Lock()
- defer m.mutex.Unlock()
+ m.rwmutex.Lock()
+ defer m.rwmutex.Unlock()
var accumulatedErr error
for _, logger := range m.loggers {
if err := logger.ReleaseReopen(); err != nil {
@@ -266,13 +266,13 @@ func (m *MultiChannelledLog) ReleaseReopen() error {
// Start processing the MultiChannelledLog
func (m *MultiChannelledLog) Start() {
- m.mutex.Lock()
+ m.rwmutex.Lock()
if m.started {
- m.mutex.Unlock()
+ m.rwmutex.Unlock()
return
}
m.started = true
- m.mutex.Unlock()
+ m.rwmutex.Unlock()
paused := false
for {
if paused {
@@ -286,11 +286,11 @@ func (m *MultiChannelledLog) Start() {
m.closeLoggers()
return
}
- m.mutex.Lock()
+ m.rwmutex.RLock()
for _, logger := range m.loggers {
logger.Flush()
}
- m.mutex.Unlock()
+ m.rwmutex.RUnlock()
case <-m.close:
m.closeLoggers()
return
@@ -307,24 +307,24 @@ func (m *MultiChannelledLog) Start() {
m.closeLoggers()
return
}
- m.mutex.Lock()
+ m.rwmutex.RLock()
for _, logger := range m.loggers {
err := logger.LogEvent(event)
if err != nil {
fmt.Println(err)
}
}
- m.mutex.Unlock()
+ m.rwmutex.RUnlock()
case _, ok := <-m.flush:
if !ok {
m.closeLoggers()
return
}
- m.mutex.Lock()
+ m.rwmutex.RLock()
for _, logger := range m.loggers {
logger.Flush()
}
- m.mutex.Unlock()
+ m.rwmutex.RUnlock()
case <-m.close:
m.closeLoggers()
return
@@ -359,11 +359,15 @@ func (m *MultiChannelledLog) Flush() {
// GetLevel gets the level of this MultiChannelledLog
func (m *MultiChannelledLog) GetLevel() Level {
+ m.rwmutex.RLock()
+ defer m.rwmutex.RUnlock()
return m.level
}
// GetStacktraceLevel gets the level of this MultiChannelledLog
func (m *MultiChannelledLog) GetStacktraceLevel() Level {
+ m.rwmutex.RLock()
+ defer m.rwmutex.RUnlock()
return m.stacktraceLevel
}
@@ -384,8 +388,8 @@ func (m *MultiChannelledLog) internalResetLevel() Level {
// ResetLevel will reset the level of this MultiChannelledLog
func (m *MultiChannelledLog) ResetLevel() Level {
- m.mutex.Lock()
- defer m.mutex.Unlock()
+ m.rwmutex.Lock()
+ defer m.rwmutex.Unlock()
return m.internalResetLevel()
}
diff --git a/modules/markup/common/footnote.go b/modules/markup/common/footnote.go
index 9baf8a4998..a4be013250 100644
--- a/modules/markup/common/footnote.go
+++ b/modules/markup/common/footnote.go
@@ -126,7 +126,7 @@ type Footnote struct {
func (n *Footnote) Dump(source []byte, level int) {
m := map[string]string{}
m["Index"] = fmt.Sprintf("%v", n.Index)
- m["Ref"] = fmt.Sprintf("%s", n.Ref)
+ m["Ref"] = string(n.Ref)
m["Name"] = fmt.Sprintf("%v", n.Name)
ast.DumpHelper(n, source, level, m, nil)
}
diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go
index 52139f5a49..f7be06dbe9 100644
--- a/modules/markup/external/external.go
+++ b/modules/markup/external/external.go
@@ -14,6 +14,7 @@ import (
"runtime"
"strings"
+ "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/process"
@@ -99,7 +100,12 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
}
if ctx == nil || ctx.Ctx == nil {
- return fmt.Errorf("RenderContext did not provide context")
+ if ctx == nil {
+ log.Warn("RenderContext not provided defaulting to empty ctx")
+ ctx = &markup.RenderContext{}
+ }
+ log.Warn("RenderContext did not provide context, defaulting to Shutdown context")
+ ctx.Ctx = graceful.GetManager().ShutdownContext()
}
processCtx, cancel := context.WithCancel(ctx.Ctx)
diff --git a/modules/markup/html.go b/modules/markup/html.go
index 6d0b4fbea2..1d5e9ccf24 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -92,7 +92,7 @@ func isLinkStr(link string) bool {
func getIssueFullPattern() *regexp.Regexp {
if issueFullPattern == nil {
issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
- `\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
+ `\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`)
}
return issueFullPattern
}
@@ -778,7 +778,7 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
// extract repo and org name from matched link like
// http://localhost:3000/gituser/myrepo/issues/1
- linkParts := strings.Split(path.Clean(link), "/")
+ linkParts := strings.Split(link, "/")
matchOrg := linkParts[len(linkParts)-4]
matchRepo := linkParts[len(linkParts)-3]
@@ -827,7 +827,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
if exttrack && !ref.IsPull {
ctx.Metas["index"] = ref.Issue
- link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue")
+ link = createLink(com.Expand(ctx.Metas["format"], ctx.Metas), reftext, "ref-issue ref-external-issue")
} else {
// Path determines the type of link that will be rendered. It's unknown at this point whether
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index 330750a47a..f9ef90744b 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -96,12 +96,14 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
// numeric: render inputs with valid mentions
test := func(s, expectedFmt, marker string, indices ...int) {
var path, prefix string
+ isExternal := false
if marker == "!" {
path = "pulls"
prefix = "http://localhost:3000/someUser/someRepo/pulls/"
} else {
path = "issues"
prefix = "https://someurl.com/someUser/someRepo/"
+ isExternal = true
}
links := make([]interface{}, len(indices))
@@ -111,8 +113,13 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
expectedNil := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{Metas: localMetas})
+ class := "ref-issue"
+ if isExternal {
+ class += " ref-external-issue"
+ }
+
for i, index := range indices {
- links[i] = numericIssueLink(prefix, "ref-issue", index, marker)
+ links[i] = numericIssueLink(prefix, class, index, marker)
}
expectedNum := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{Metas: numericMetas})
@@ -178,7 +185,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
test := func(s, expectedFmt string, names ...string) {
links := make([]interface{}, len(names))
for i, name := range names {
- links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue", name)
+ links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name)
}
expected := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expected, &RenderContext{Metas: alphanumericMetas})
@@ -258,6 +265,10 @@ func TestRender_FullIssueURLs(t *testing.T) {
`person/repo#4`)
test("http://localhost:3000/gogits/gogs/issues/4",
`#4`)
+ test("http://localhost:3000/gogits/gogs/issues/4 test",
+ `#4 test`)
+ test("http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123 test",
+ `#4 test`)
}
func TestRegExp_sha1CurrentPattern(t *testing.T) {
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index dff9102bed..3eb2df00a9 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -5,6 +5,7 @@
package markup_test
import (
+ "io"
"strings"
"testing"
@@ -526,3 +527,18 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
assert.NoError(b, err)
}
}
+
+func TestFuzz(t *testing.T) {
+ s := "t/l/issues/8#/../../a"
+ renderContext := RenderContext{
+ URLPrefix: "https://example.com/go-gitea/gitea",
+ Metas: map[string]string{
+ "user": "go-gitea",
+ "repo": "gitea",
+ },
+ }
+
+ err := PostProcess(&renderContext, strings.NewReader(s), io.Discard)
+
+ assert.NoError(t, err)
+}
diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go
index cac2a180fa..ab026dd1b8 100644
--- a/modules/markup/markdown/markdown.go
+++ b/modules/markup/markdown/markdown.go
@@ -87,7 +87,9 @@ func newParserContext(ctx *markup.RenderContext) parser.Context {
func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
once.Do(func() {
converter = goldmark.New(
- goldmark.WithExtensions(extension.Table,
+ goldmark.WithExtensions(
+ extension.NewTable(
+ extension.WithTableCellAlignMethod(extension.TableCellAlignAttribute)),
extension.Strikethrough,
extension.TaskList,
extension.DefinitionList,
diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go
index 7e9f1f45c5..b035e04a1f 100644
--- a/modules/markup/orgmode/orgmode.go
+++ b/modules/markup/orgmode/orgmode.go
@@ -12,6 +12,7 @@ import (
"strings"
"code.gitea.io/gitea/modules/highlight"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -51,6 +52,12 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
htmlWriter := org.NewHTMLWriter()
htmlWriter.HighlightCodeBlock = func(source, lang string, inline bool) string {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Error("Panic in HighlightCodeBlock: %v\n%s", err, log.Stack(2))
+ panic(err)
+ }
+ }()
var w strings.Builder
if _, err := w.WriteString(`
`); err != nil {
return ""
@@ -80,7 +87,7 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
}
lexer = chroma.Coalesce(lexer)
- if _, err := w.WriteString(highlight.Code(lexer.Config().Filenames[0], source)); err != nil {
+ if _, err := w.WriteString(highlight.CodeFromLexer(lexer, source)); err != nil {
return ""
}
}
diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go
index da89326e9e..81d0d66a76 100644
--- a/modules/markup/orgmode/orgmode_test.go
+++ b/modules/markup/orgmode/orgmode_test.go
@@ -57,3 +57,29 @@ func TestRender_Images(t *testing.T) {
test("[[file:"+url+"]]",
"
")
}
+
+func TestRender_Source(t *testing.T) {
+ setting.AppURL = AppURL
+ setting.AppSubURL = AppSubURL
+
+ test := func(input, expected string) {
+ buffer, err := RenderString(&markup.RenderContext{
+ URLPrefix: setting.AppSubURL,
+ }, input)
+ assert.NoError(t, err)
+ assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
+ }
+
+ test(`#+begin_src go
+// HelloWorld prints "Hello World"
+func HelloWorld() {
+ fmt.Println("Hello World")
+}
+#+end_src
+`, `
+
// HelloWorld prints "Hello World"
+func HelloWorld() {
+ fmt.Println("Hello World")
+}
+
`)
+}
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 9342d65de5..c8f9de33b5 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -66,7 +66,7 @@ func createDefaultPolicy() *bluemonday.Policy {
}
// Allow classes for anchors
- policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue( ref-external-issue)?`)).OnElements("a")
// Allow classes for task lists
policy.AllowAttrs("class").Matching(regexp.MustCompile(`task-list-item`)).OnElements("li")
diff --git a/modules/migrations/dump.go b/modules/migrations/dump.go
index 6c4cf174d4..6b995c0dea 100644
--- a/modules/migrations/dump.go
+++ b/modules/migrations/dump.go
@@ -11,6 +11,7 @@ import (
"net/http"
"net/url"
"os"
+ "path"
"path/filepath"
"strconv"
"strings"
@@ -481,7 +482,9 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
if err != nil {
log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
} else {
- headBranch := filepath.Join(g.gitPath(), "refs", "heads", pr.Head.OwnerName, pr.Head.Ref)
+ // a new branch name with will be created to as new head branch
+ ref := path.Join(pr.Head.OwnerName, pr.Head.Ref)
+ headBranch := filepath.Join(g.gitPath(), "refs", "heads", ref)
if err := os.MkdirAll(filepath.Dir(headBranch), os.ModePerm); err != nil {
return err
}
@@ -494,10 +497,14 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
if err != nil {
return err
}
+ pr.Head.Ref = ref
}
}
}
}
+ // whatever it's a forked repo PR, we have to change head info as the same as the base info
+ pr.Head.OwnerName = pr.Base.OwnerName
+ pr.Head.RepoName = pr.Base.RepoName
}
var err error
diff --git a/modules/migrations/gitea_uploader.go b/modules/migrations/gitea_uploader.go
index 2b18098b7f..296e10ee5e 100644
--- a/modules/migrations/gitea_uploader.go
+++ b/modules/migrations/gitea_uploader.go
@@ -109,6 +109,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
return err
}
r.DefaultBranch = repo.DefaultBranch
+ r.Description = repo.Description
r, err = repository.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{
RepoName: g.repoName,
@@ -254,7 +255,7 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
if !release.Draft {
commit, err := g.gitRepo.GetTagCommit(rel.TagName)
if err != nil {
- return fmt.Errorf("GetCommit: %v", err)
+ return fmt.Errorf("GetTagCommit[%v]: %v", rel.TagName, err)
}
rel.NumCommits, err = commit.CommitsCount()
if err != nil {
@@ -555,6 +556,9 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
// download patch file
err := func() error {
+ if pr.PatchURL == "" {
+ return nil
+ }
// pr.PatchURL maybe a local file
ret, err := uri.Open(pr.PatchURL)
if err != nil {
@@ -805,7 +809,8 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
}
headCommitID, err := g.gitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
- return fmt.Errorf("GetRefCommitID[%s]: %v", pr.GetGitRefName(), err)
+ log.Warn("GetRefCommitID[%s]: %v, the review comment will be ignored", pr.GetGitRefName(), err)
+ continue
}
var patch string
@@ -871,6 +876,11 @@ func (g *GiteaLocalUploader) Finish() error {
return ErrRepoNotCreated
}
+ // update issue_index
+ if err := models.RecalculateIssueIndexForRepo(g.repo.ID); err != nil {
+ return err
+ }
+
g.repo.Status = models.RepositoryReady
return models.UpdateRepositoryCols(g.repo, "status")
}
diff --git a/modules/migrations/github.go b/modules/migrations/github.go
index 7d4c492c24..b09627619b 100644
--- a/modules/migrations/github.go
+++ b/modules/migrations/github.go
@@ -541,6 +541,9 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
created = "created"
asc = "asc"
)
+ if perPage > g.maxPerPage {
+ perPage = g.maxPerPage
+ }
opt := &github.IssueListCommentsOptions{
Sort: &created,
Direction: &asc,
@@ -555,7 +558,9 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
if err != nil {
return nil, false, fmt.Errorf("error while listing repos: %v", err)
}
- log.Trace("Request get comments %d/%d, but in fact get %d", perPage, page, len(comments))
+ var isEnd = resp.NextPage == 0
+
+ log.Trace("Request get comments %d/%d, but in fact get %d, next page is %d", perPage, page, len(comments), resp.NextPage)
g.rate = &resp.Rate
for _, comment := range comments {
var email string
@@ -600,7 +605,7 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment,
})
}
- return allComments, len(allComments) < perPage, nil
+ return allComments, isEnd, nil
}
// GetPullRequests returns pull requests according page and perPage
diff --git a/modules/migrations/gitlab.go b/modules/migrations/gitlab.go
index fe763f9900..08672dd118 100644
--- a/modules/migrations/gitlab.go
+++ b/modules/migrations/gitlab.go
@@ -396,12 +396,15 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
if err != nil {
return nil, false, fmt.Errorf("error while listing issue awards: %v", err)
}
- if len(awards) < perPage {
- break
- }
+
for i := range awards {
reactions = append(reactions, g.awardToReaction(awards[i]))
}
+
+ if len(awards) < perPage {
+ break
+ }
+
awardPage++
}
@@ -558,12 +561,15 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
if err != nil {
return nil, false, fmt.Errorf("error while listing merge requests awards: %v", err)
}
- if len(awards) < perPage {
- break
- }
+
for i := range awards {
reactions = append(reactions, g.awardToReaction(awards[i]))
}
+
+ if len(awards) < perPage {
+ break
+ }
+
awardPage++
}
diff --git a/modules/migrations/migrate.go b/modules/migrations/migrate.go
index 0a507d9c33..3d9b03d176 100644
--- a/modules/migrations/migrate.go
+++ b/modules/migrations/migrate.go
@@ -89,7 +89,7 @@ func IsMigrateURLAllowed(remoteURL string, doer *models.User) error {
return &models.ErrInvalidCloneAddr{Host: u.Host, NotResolvedIP: true}
}
for _, addr := range addrList {
- if isIPPrivate(addr) || !addr.IsGlobalUnicast() {
+ if util.IsIPPrivate(addr) || !addr.IsGlobalUnicast() {
return &models.ErrInvalidCloneAddr{Host: u.Host, PrivateNet: addr.String(), IsPermissionDenied: true}
}
}
@@ -486,16 +486,3 @@ func Init() error {
return nil
}
-
-// isIPPrivate reports whether ip is a private address, according to
-// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).
-// from https://github.com/golang/go/pull/42793
-// TODO remove if https://github.com/golang/go/issues/29146 got resolved
-func isIPPrivate(ip net.IP) bool {
- if ip4 := ip.To4(); ip4 != nil {
- return ip4[0] == 10 ||
- (ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
- (ip4[0] == 192 && ip4[1] == 168)
- }
- return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
-}
diff --git a/modules/migrations/restore.go b/modules/migrations/restore.go
index 6177f80cbb..ce1433bef3 100644
--- a/modules/migrations/restore.go
+++ b/modules/migrations/restore.go
@@ -98,6 +98,9 @@ func (r *RepositoryRestorer) GetTopics() ([]string, error) {
bs, err := ioutil.ReadFile(p)
if err != nil {
+ if os.IsNotExist(err) {
+ return nil, nil
+ }
return nil, err
}
diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go
index 776bee6a0c..2d0f6810da 100644
--- a/modules/notification/action/action.go
+++ b/modules/notification/action/action.go
@@ -148,8 +148,6 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions
}
func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
- log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)
-
if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID,
ActUser: doer,
diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go
index acdb91efe3..465455da15 100644
--- a/modules/notification/webhook/webhook.go
+++ b/modules/notification/webhook/webhook.go
@@ -51,7 +51,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model
err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelCleared,
Index: issue.Index,
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -145,7 +145,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo
issue.PullRequest.Issue = issue
apiPullRequest := &api.PullRequestPayload{
Index: issue.Index,
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
}
@@ -197,7 +197,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *model
From: oldTitle,
},
},
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -212,7 +212,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *model
},
Issue: convert.ToAPIIssue(issue),
Repository: convert.ToRepo(issue.Repo, mode),
- Sender: convert.ToUser(issue.Poster, nil),
+ Sender: convert.ToUser(doer, nil),
})
}
@@ -232,7 +232,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *mode
// Merge pull request calls issue.changeStatus so we need to handle separately.
apiPullRequest := &api.PullRequestPayload{
Index: issue.Index,
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
}
@@ -301,7 +301,7 @@ func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest, mention
if err := webhook_services.PrepareWebhooks(pull.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueOpened,
Index: pull.Issue.Index,
- PullRequest: convert.ToAPIPullRequest(pull),
+ PullRequest: convert.ToAPIPullRequest(pull, nil),
Repository: convert.ToRepo(pull.Issue.Repo, mode),
Sender: convert.ToUser(pull.Issue.Poster, nil),
}); err != nil {
@@ -322,7 +322,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
From: oldContent,
},
},
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -500,7 +500,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode
err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelUpdated,
Index: issue.Index,
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, models.AccessModeNone),
Sender: convert.ToUser(doer, nil),
})
@@ -542,7 +542,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
err = webhook_services.PrepareWebhooks(issue.Repo, models.HookEventPullRequestMilestone, &api.PullRequestPayload{
Action: hookAction,
Index: issue.Index,
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -609,7 +609,7 @@ func (*webhookNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mod
// Merge pull request calls issue.changeStatus so we need to handle separately.
apiPullRequest := &api.PullRequestPayload{
Index: pr.Issue.Index,
- PullRequest: convert.ToAPIPullRequest(pr),
+ PullRequest: convert.ToAPIPullRequest(pr, nil),
Repository: convert.ToRepo(pr.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
Action: api.HookIssueClosed,
@@ -642,7 +642,7 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User,
From: oldBranch,
},
},
- PullRequest: convert.ToAPIPullRequest(issue.PullRequest),
+ PullRequest: convert.ToAPIPullRequest(issue.PullRequest, nil),
Repository: convert.ToRepo(issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
})
@@ -681,7 +681,7 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
if err := webhook_services.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{
Action: api.HookIssueReviewed,
Index: review.Issue.Index,
- PullRequest: convert.ToAPIPullRequest(pr),
+ PullRequest: convert.ToAPIPullRequest(pr, nil),
Repository: convert.ToRepo(review.Issue.Repo, mode),
Sender: convert.ToUser(review.Reviewer, nil),
Review: &api.ReviewPayload{
@@ -736,7 +736,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *m
if err := webhook_services.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequestSync, &api.PullRequestPayload{
Action: api.HookIssueSynchronized,
Index: pr.Issue.Index,
- PullRequest: convert.ToAPIPullRequest(pr),
+ PullRequest: convert.ToAPIPullRequest(pr, nil),
Repository: convert.ToRepo(pr.Issue.Repo, models.AccessModeNone),
Sender: convert.ToUser(doer, nil),
}); err != nil {
@@ -766,12 +766,12 @@ func sendReleaseHook(doer *models.User, rel *models.Release, action api.HookRele
return
}
- mode, _ := models.AccessLevel(rel.Publisher, rel.Repo)
+ mode, _ := models.AccessLevel(doer, rel.Repo)
if err := webhook_services.PrepareWebhooks(rel.Repo, models.HookEventRelease, &api.ReleasePayload{
Action: action,
Release: convert.ToRelease(rel),
Repository: convert.ToRepo(rel.Repo, mode),
- Sender: convert.ToUser(rel.Publisher, nil),
+ Sender: convert.ToUser(doer, nil),
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
diff --git a/modules/options/dynamic.go b/modules/options/dynamic.go
index ffb89df882..13fa5d6aa7 100644
--- a/modules/options/dynamic.go
+++ b/modules/options/dynamic.go
@@ -1,9 +1,10 @@
-// +build !bindata
-
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !bindata
+// +build !bindata
+
package options
import (
diff --git a/modules/options/options_bindata.go b/modules/options/options_bindata.go
index 262bd0de3e..64da772c6f 100644
--- a/modules/options/options_bindata.go
+++ b/modules/options/options_bindata.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-//+build bindata
+//go:build bindata
+// +build bindata
package options
diff --git a/modules/options/static.go b/modules/options/static.go
index 5f4ffdda78..e6a4454b7d 100644
--- a/modules/options/static.go
+++ b/modules/options/static.go
@@ -1,3 +1,4 @@
+//go:build bindata
// +build bindata
// Copyright 2016 The Gitea Authors. All rights reserved.
diff --git a/modules/private/restore_repo.go b/modules/private/restore_repo.go
index 66b60d8d12..af9e423e8e 100644
--- a/modules/private/restore_repo.go
+++ b/modules/private/restore_repo.go
@@ -55,6 +55,7 @@ func RestoreRepo(ctx context.Context, repoDir, ownerName, repoName string, units
if err := json.Unmarshal(body, &ret); err != nil {
return http.StatusInternalServerError, fmt.Sprintf("Response body Unmarshal error: %v", err.Error())
}
+ return http.StatusInternalServerError, ret.Err
}
return http.StatusOK, fmt.Sprintf("Restore repo %s/%s successfully", ownerName, repoName)
diff --git a/modules/public/dynamic.go b/modules/public/dynamic.go
index 0bfe38bc3f..955c01e510 100644
--- a/modules/public/dynamic.go
+++ b/modules/public/dynamic.go
@@ -1,9 +1,10 @@
-// +build !bindata
-
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !bindata
+// +build !bindata
+
package public
import (
diff --git a/modules/public/public_bindata.go b/modules/public/public_bindata.go
index 05648aea80..eb10d96426 100644
--- a/modules/public/public_bindata.go
+++ b/modules/public/public_bindata.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-//+build bindata
+//go:build bindata
+// +build bindata
package public
diff --git a/modules/public/static.go b/modules/public/static.go
index 827dc2a1e0..1e5853961f 100644
--- a/modules/public/static.go
+++ b/modules/public/static.go
@@ -1,3 +1,4 @@
+//go:build bindata
// +build bindata
// Copyright 2016 The Gitea Authors. All rights reserved.
diff --git a/modules/queue/queue_disk_channel_test.go b/modules/queue/queue_disk_channel_test.go
index 561f98ca90..c20fe1e0a9 100644
--- a/modules/queue/queue_disk_channel_test.go
+++ b/modules/queue/queue_disk_channel_test.go
@@ -15,7 +15,6 @@ import (
func TestPersistableChannelQueue(t *testing.T) {
handleChan := make(chan *testData)
handle := func(data ...Data) {
- assert.True(t, len(data) == 2)
for _, datum := range data {
testDatum := datum.(*testData)
handleChan <- testDatum
diff --git a/modules/repofiles/diff_test.go b/modules/repofiles/diff_test.go
index d40f8a50fe..da50284150 100644
--- a/modules/repofiles/diff_test.go
+++ b/modules/repofiles/diff_test.go
@@ -83,6 +83,7 @@ func TestGetDiffPreview(t *testing.T) {
{
LeftIdx: 3,
RightIdx: 0,
+ Match: 4,
Type: 3,
Content: "-Description for repo1",
Comments: nil,
@@ -90,6 +91,7 @@ func TestGetDiffPreview(t *testing.T) {
{
LeftIdx: 0,
RightIdx: 3,
+ Match: 3,
Type: 2,
Content: "+Description for repo1",
Comments: nil,
@@ -97,6 +99,7 @@ func TestGetDiffPreview(t *testing.T) {
{
LeftIdx: 0,
RightIdx: 4,
+ Match: -1,
Type: 2,
Content: "+this is a new line",
Comments: nil,
diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go
index ad984c465a..5b45479f3f 100644
--- a/modules/repofiles/update.go
+++ b/modules/repofiles/update.go
@@ -19,6 +19,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
stdcharset "golang.org/x/net/html/charset"
"golang.org/x/text/transform"
@@ -61,7 +62,7 @@ func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string
}
defer reader.Close()
buf := make([]byte, 1024)
- n, err := reader.Read(buf)
+ n, err := util.ReadAtMost(reader, buf)
if err != nil {
// return default
return "UTF-8", false
@@ -84,7 +85,7 @@ func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string
}
defer dataRc.Close()
buf = make([]byte, 1024)
- n, err = dataRc.Read(buf)
+ n, err = util.ReadAtMost(dataRc, buf)
if err != nil {
// return default
return "UTF-8", false
diff --git a/modules/repository/adopt.go b/modules/repository/adopt.go
index 321e6ab767..fe23bdf5e7 100644
--- a/modules/repository/adopt.go
+++ b/modules/repository/adopt.go
@@ -66,6 +66,9 @@ func AdoptRepository(doer, u *models.User, opts models.CreateRepoOptions) (*mode
if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
}
+ if err := repo.CheckDaemonExportOKCtx(ctx); err != nil {
+ return fmt.Errorf("checkDaemonExportOK: %v", err)
+ }
// Initialize Issue Labels if selected
if len(opts.IssueLabels) > 0 {
@@ -149,7 +152,7 @@ func ListUnadoptedRepositories(query string, opts *models.ListOptions) ([]string
count := 0
// We're going to iterate by pagesize.
- root := filepath.Join(setting.RepoRootPath)
+ root := filepath.Clean(setting.RepoRootPath)
if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
diff --git a/modules/repository/commits.go b/modules/repository/commits.go
index eaaf3b8b19..8eaa640d56 100644
--- a/modules/repository/commits.go
+++ b/modules/repository/commits.go
@@ -31,6 +31,7 @@ type PushCommits struct {
Commits []*PushCommit
HeadCommit *PushCommit
CompareURL string
+ Len int
avatars map[string]string
emailUsers map[string]*models.User
@@ -182,5 +183,12 @@ func ListToPushCommits(l *list.List) *PushCommits {
commit := CommitToPushCommit(e.Value.(*git.Commit))
commits = append(commits, commit)
}
- return &PushCommits{commits, nil, "", make(map[string]string), make(map[string]*models.User)}
+ return &PushCommits{
+ Commits: commits,
+ HeadCommit: nil,
+ CompareURL: "",
+ Len: len(commits),
+ avatars: make(map[string]string),
+ emailUsers: make(map[string]*models.User),
+ }
}
diff --git a/modules/repository/create.go b/modules/repository/create.go
index 5eac03836e..8065543d83 100644
--- a/modules/repository/create.go
+++ b/modules/repository/create.go
@@ -103,6 +103,10 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (*mod
}
}
+ if err := repo.CheckDaemonExportOKCtx(ctx); err != nil {
+ return fmt.Errorf("checkDaemonExportOK: %v", err)
+ }
+
if stdout, err := git.NewCommand("update-server-info").
SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)).
RunInDir(repoPath); err != nil {
diff --git a/modules/repository/fork.go b/modules/repository/fork.go
index f8cb74bcb4..4cff4c24f8 100644
--- a/modules/repository/fork.go
+++ b/modules/repository/fork.go
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
)
// ForkRepository forks a repository
@@ -45,62 +46,87 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
oldRepoPath := oldRepo.RepoPath()
+ needsRollback := false
+ rollbackFn := func() {
+ if !needsRollback {
+ return
+ }
+
+ repoPath := models.RepoPath(owner.Name, repo.Name)
+
+ if exists, _ := util.IsExist(repoPath); !exists {
+ return
+ }
+
+ // As the transaction will be failed and hence database changes will be destroyed we only need
+ // to delete the related repository on the filesystem
+ if errDelete := util.RemoveAll(repoPath); errDelete != nil {
+ log.Error("Failed to remove fork repo")
+ }
+ }
+
+ needsRollbackInPanic := true
+ defer func() {
+ panicErr := recover()
+ if panicErr == nil {
+ return
+ }
+
+ if needsRollbackInPanic {
+ rollbackFn()
+ }
+ panic(panicErr)
+ }()
+
err = models.WithTx(func(ctx models.DBContext) error {
if err = models.CreateRepository(ctx, doer, owner, repo, false); err != nil {
return err
}
- rollbackRemoveFn := func() {
- if repo.ID == 0 {
- return
- }
- if errDelete := models.DeleteRepository(doer, owner.ID, repo.ID); errDelete != nil {
- log.Error("Rollback deleteRepository: %v", errDelete)
- }
- }
-
if err = models.IncrementRepoForkNum(ctx, oldRepo.ID); err != nil {
- rollbackRemoveFn()
return err
}
// copy lfs files failure should not be ignored
- if err := models.CopyLFS(ctx, repo, oldRepo); err != nil {
- rollbackRemoveFn()
+ if err = models.CopyLFS(ctx, repo, oldRepo); err != nil {
return err
}
+ needsRollback = true
+
repoPath := models.RepoPath(owner.Name, repo.Name)
- if stdout, err := git.NewCommand(
- "clone", "--bare", oldRepoPath, repoPath).
+ if stdout, err := git.NewCommand("clone", "--bare", oldRepoPath, repoPath).
SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", oldRepo.FullName(), repo.FullName())).
RunInDirTimeout(10*time.Minute, ""); err != nil {
log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, oldRepo, stdout, err)
- rollbackRemoveFn()
return fmt.Errorf("git clone: %v", err)
}
+ if err := repo.CheckDaemonExportOKCtx(ctx); err != nil {
+ return fmt.Errorf("checkDaemonExportOK: %v", err)
+ }
+
if stdout, err := git.NewCommand("update-server-info").
SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())).
RunInDir(repoPath); err != nil {
log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
- rollbackRemoveFn()
return fmt.Errorf("git update-server-info: %v", err)
}
if err = createDelegateHooks(repoPath); err != nil {
- rollbackRemoveFn()
return fmt.Errorf("createDelegateHooks: %v", err)
}
return nil
})
+ needsRollbackInPanic = false
if err != nil {
+ rollbackFn()
return nil, err
}
// even if below operations failed, it could be ignored. And they will be retried
ctx := models.DefaultDBContext()
- if err = repo.UpdateSize(ctx); err != nil {
+ if err := repo.UpdateSize(ctx); err != nil {
log.Error("Failed to update size for repository: %v", err)
}
if err := models.CopyLanguageStat(oldRepo, repo); err != nil {
@@ -109,3 +135,34 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
return repo, nil
}
+
+// ConvertForkToNormalRepository convert the provided repo from a forked repo to normal repo
+func ConvertForkToNormalRepository(repo *models.Repository) error {
+ err := models.WithTx(func(ctx models.DBContext) error {
+ repo, err := models.GetRepositoryByIDCtx(ctx, repo.ID)
+ if err != nil {
+ return err
+ }
+
+ if !repo.IsFork {
+ return nil
+ }
+
+ if err := models.DecrementRepoForkNum(ctx, repo.ForkID); err != nil {
+ log.Error("Unable to decrement repo fork num for old root repo %d of repository %-v whilst converting from fork. Error: %v", repo.ForkID, repo, err)
+ return err
+ }
+
+ repo.IsFork = false
+ repo.ForkID = 0
+
+ if err := models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
+ log.Error("Unable to update repository %-v whilst converting from fork. Error: %v", repo, err)
+ return err
+ }
+
+ return nil
+ })
+
+ return err
+}
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index 1ba457fb3a..a7adce4d02 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -275,5 +275,16 @@ func GenerateRepository(ctx models.DBContext, doer, owner *models.User, template
return generateRepo, err
}
+ if err = generateRepo.CheckDaemonExportOKCtx(ctx); err != nil {
+ return generateRepo, fmt.Errorf("checkDaemonExportOK: %v", err)
+ }
+
+ if stdout, err := git.NewCommand("update-server-info").
+ SetDescription(fmt.Sprintf("GenerateRepository(git update-server-info): %s", repoPath)).
+ RunInDir(repoPath); err != nil {
+ log.Error("GenerateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", generateRepo, stdout, err)
+ return generateRepo, fmt.Errorf("error in GenerateRepository(git update-server-info): %v", err)
+ }
+
return generateRepo, nil
}
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index 08531c04ed..45308ad162 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -95,6 +95,21 @@ func MigrateRepositoryGitData(ctx context.Context, u *models.User, repo *models.
}
}
+ if repo.OwnerID == u.ID {
+ repo.Owner = u
+ }
+
+ if err = repo.CheckDaemonExportOK(); err != nil {
+ return repo, fmt.Errorf("checkDaemonExportOK: %v", err)
+ }
+
+ if stdout, err := git.NewCommandContext(ctx, "update-server-info").
+ SetDescription(fmt.Sprintf("MigrateRepositoryGitData(git update-server-info): %s", repoPath)).
+ RunInDir(repoPath); err != nil {
+ log.Error("MigrateRepositoryGitData(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
+ return repo, fmt.Errorf("error in MigrateRepositoryGitData(git update-server-info): %v", err)
+ }
+
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
return repo, fmt.Errorf("OpenRepository: %v", err)
diff --git a/modules/session/store.go b/modules/session/store.go
index 529187d3be..8c5d7d82eb 100644
--- a/modules/session/store.go
+++ b/modules/session/store.go
@@ -4,9 +4,21 @@
package session
+import (
+ "net/http"
+
+ "gitea.com/go-chi/session"
+)
+
// Store represents a session store
type Store interface {
Get(interface{}) interface{}
Set(interface{}, interface{}) error
Delete(interface{}) error
}
+
+// RegenerateSession regenerates the underlying session and returns the new store
+func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
+ s, err := session.RegenerateSession(resp, req)
+ return s, err
+}
diff --git a/modules/setting/database.go b/modules/setting/database.go
index 3d13ee10c8..02b5e43f4a 100644
--- a/modules/setting/database.go
+++ b/modules/setting/database.go
@@ -91,9 +91,9 @@ func InitDBConfig() {
Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2)
if Database.UseMySQL {
- Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second)
+ Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(3 * time.Second)
} else {
- Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(0)
+ Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(0)
}
Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0)
diff --git a/modules/setting/database_sqlite.go b/modules/setting/database_sqlite.go
index 623326ddc2..798292fec8 100644
--- a/modules/setting/database_sqlite.go
+++ b/modules/setting/database_sqlite.go
@@ -1,3 +1,4 @@
+//go:build sqlite
// +build sqlite
// Copyright 2014 The Gogs Authors. All rights reserved.
diff --git a/modules/setting/git.go b/modules/setting/git.go
index aa838a8d64..aaa65ed81c 100644
--- a/modules/setting/git.go
+++ b/modules/setting/git.go
@@ -26,6 +26,7 @@ var (
EnableAutoGitWireProtocol bool
PullRequestPushMessage bool
LargeObjectThreshold int64
+ DisableCoreProtectNTFS bool
Timeout struct {
Default int
Migrate int
diff --git a/modules/setting/log.go b/modules/setting/log.go
index 0fb108c93d..b91f43aa73 100644
--- a/modules/setting/log.go
+++ b/modules/setting/log.go
@@ -121,7 +121,7 @@ func getLogLevel(section *ini.Section, key string, defaultValue log.Level) log.L
}
func getStacktraceLogLevel(section *ini.Section, key string, defaultValue string) string {
- value := section.Key(key).MustString("none")
+ value := section.Key(key).MustString(defaultValue)
return log.FromString(value).String()
}
diff --git a/modules/setting/webhook.go b/modules/setting/webhook.go
index 4a0c593c8d..e25117c473 100644
--- a/modules/setting/webhook.go
+++ b/modules/setting/webhook.go
@@ -7,20 +7,22 @@ package setting
import (
"net/url"
+ "code.gitea.io/gitea/modules/hostmatcher"
"code.gitea.io/gitea/modules/log"
)
var (
// Webhook settings
Webhook = struct {
- QueueLength int
- DeliverTimeout int
- SkipTLSVerify bool
- Types []string
- PagingNum int
- ProxyURL string
- ProxyURLFixed *url.URL
- ProxyHosts []string
+ QueueLength int
+ DeliverTimeout int
+ SkipTLSVerify bool
+ AllowedHostList *hostmatcher.HostMatchList
+ Types []string
+ PagingNum int
+ ProxyURL string
+ ProxyURLFixed *url.URL
+ ProxyHosts []string
}{
QueueLength: 1000,
DeliverTimeout: 5,
@@ -36,6 +38,7 @@ func newWebhookService() {
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
+ Webhook.AllowedHostList = hostmatcher.ParseHostMatchList(sec.Key("ALLOWED_HOST_LIST").MustString(hostmatcher.MatchBuiltinAll))
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix"}
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index efe9525345..909ed32c5e 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -316,10 +316,66 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs
}
}
+ // Workaround slightly broken behaviour in x/crypto/ssh/handshake.go:458-463
+ //
+ // Fundamentally the issue here is that HostKeyAlgos make the incorrect assumption
+ // that the PublicKey().Type() matches the signature algorithm.
+ //
+ // Therefore we need to add duplicates for the RSA with different signing algorithms.
+ signers := make([]ssh.Signer, 0, len(srv.HostSigners))
+ for _, signer := range srv.HostSigners {
+ if signer.PublicKey().Type() == "ssh-rsa" {
+ signers = append(signers,
+ &wrapSigner{
+ Signer: signer,
+ algorithm: gossh.SigAlgoRSASHA2512,
+ },
+ &wrapSigner{
+ Signer: signer,
+ algorithm: gossh.SigAlgoRSASHA2256,
+ },
+ )
+ }
+ signers = append(signers, signer)
+ }
+ srv.HostSigners = signers
+
go listen(&srv)
}
+// wrapSigner wraps a signer and overrides its public key type with the provided algorithm
+type wrapSigner struct {
+ ssh.Signer
+ algorithm string
+}
+
+// PublicKey returns an associated PublicKey instance.
+func (s *wrapSigner) PublicKey() gossh.PublicKey {
+ return &wrapPublicKey{
+ PublicKey: s.Signer.PublicKey(),
+ algorithm: s.algorithm,
+ }
+}
+
+// Sign returns raw signature for the given data. This method
+// will apply the hash specified for the keytype to the data using
+// the algorithm assigned for this key
+func (s *wrapSigner) Sign(rand io.Reader, data []byte) (*gossh.Signature, error) {
+ return s.Signer.(gossh.AlgorithmSigner).SignWithAlgorithm(rand, data, s.algorithm)
+}
+
+// wrapPublicKey wraps a PublicKey and overrides its type
+type wrapPublicKey struct {
+ gossh.PublicKey
+ algorithm string
+}
+
+// Type returns the algorithm
+func (k *wrapPublicKey) Type() string {
+ return k.algorithm
+}
+
// GenKeyPair make a pair of public and private keys for SSH access.
// Public key is encoded in the format for inclusion in an OpenSSH authorized_keys file.
// Private Key generated is PEM encoded
diff --git a/modules/storage/minio.go b/modules/storage/minio.go
index 724445c0ab..f78ba6aa27 100644
--- a/modules/storage/minio.go
+++ b/modules/storage/minio.go
@@ -151,7 +151,7 @@ type minioFileInfo struct {
}
func (m minioFileInfo) Name() string {
- return m.ObjectInfo.Key
+ return path.Base(m.ObjectInfo.Key)
}
func (m minioFileInfo) Size() int64 {
@@ -219,7 +219,7 @@ func (m *MinioStorage) IterateObjects(fn func(path string, obj Object) error) er
}
if err := func(object *minio.Object, fn func(path string, obj Object) error) error {
defer object.Close()
- return fn(strings.TrimPrefix(m.basePath, mObjInfo.Key), &minioObject{object})
+ return fn(strings.TrimPrefix(mObjInfo.Key, m.basePath), &minioObject{object})
}(object, fn); err != nil {
return convertMinioErr(err)
}
diff --git a/modules/storage/storage.go b/modules/storage/storage.go
index 9f87e58b60..2532ceb35d 100644
--- a/modules/storage/storage.go
+++ b/modules/storage/storage.go
@@ -91,6 +91,7 @@ func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, sr
// Clean delete all the objects in this storage
func Clean(storage ObjectStorage) error {
return storage.IterateObjects(func(path string, obj Object) error {
+ _ = obj.Close()
return storage.Delete(path)
})
}
diff --git a/modules/svg/discover_bindata.go b/modules/svg/discover_bindata.go
index 1f7af0c9f8..e11951ff7e 100644
--- a/modules/svg/discover_bindata.go
+++ b/modules/svg/discover_bindata.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build bindata
// +build bindata
package svg
diff --git a/modules/svg/discover_nobindata.go b/modules/svg/discover_nobindata.go
index d667da2d66..8d857551dc 100644
--- a/modules/svg/discover_nobindata.go
+++ b/modules/svg/discover_nobindata.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !bindata
// +build !bindata
package svg
diff --git a/modules/task/migrate.go b/modules/task/migrate.go
index d7655112d3..7a989ac98f 100644
--- a/modules/task/migrate.go
+++ b/modules/task/migrate.go
@@ -58,6 +58,9 @@ func runMigrateTask(t *models.Task) (err error) {
t.EndTime = timeutil.TimeStampNow()
t.Status = structs.TaskStatusFailed
t.Message = err.Error()
+ // Ensure that the repo loaded before we zero out the repo ID from the task - thus ensuring that we can delete it
+ _ = t.LoadRepo()
+
t.RepoID = 0
if err := t.UpdateCols("status", "errors", "repo_id", "end_time"); err != nil {
log.Error("Task UpdateCols failed: %v", err)
@@ -93,7 +96,6 @@ func runMigrateTask(t *models.Task) (err error) {
}
opts.MigrateToRepoID = t.RepoID
- var repo *models.Repository
ctx, cancel := context.WithCancel(graceful.GetManager().ShutdownContext())
defer cancel()
@@ -107,7 +109,7 @@ func runMigrateTask(t *models.Task) (err error) {
return
}
- repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
+ t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
message := models.TranslatableMessage{
Format: format,
Args: args,
@@ -118,7 +120,7 @@ func runMigrateTask(t *models.Task) (err error) {
_ = t.UpdateCols("message")
})
if err == nil {
- log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
+ log.Trace("Repository migrated [%d]: %s/%s", t.Repo.ID, t.Owner.Name, t.Repo.Name)
return
}
diff --git a/modules/task/task.go b/modules/task/task.go
index 1c0a87e1f6..15e0f30ecc 100644
--- a/modules/task/task.go
+++ b/modules/task/task.go
@@ -92,7 +92,7 @@ func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models.
return nil, err
}
- var task = models.Task{
+ var task = &models.Task{
DoerID: doer.ID,
OwnerID: u.ID,
Type: structs.TaskTypeMigrateRepo,
@@ -100,7 +100,7 @@ func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models.
PayloadContent: string(bs),
}
- if err := models.CreateTask(&task); err != nil {
+ if err := models.CreateTask(task); err != nil {
return nil, err
}
@@ -128,5 +128,5 @@ func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models.
return nil, err
}
- return &task, nil
+ return task, nil
}
diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go
index 160e4e05f2..4732fce421 100644
--- a/modules/templates/dynamic.go
+++ b/modules/templates/dynamic.go
@@ -1,9 +1,10 @@
-// +build !bindata
-
// Copyright 2016 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !bindata
+// +build !bindata
+
package templates
import (
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index f9b2dafd22..29d83c198f 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -390,6 +390,7 @@ func NewFuncMap() []template.FuncMap {
html += ""
return template.HTML(html)
},
+ "QueryEscape": url.QueryEscape,
}}
}
@@ -510,6 +511,7 @@ func NewTextFuncMap() []texttmpl.FuncMap {
}
return sum
},
+ "QueryEscape": url.QueryEscape,
}}
}
@@ -848,6 +850,11 @@ func ActionContent2Commits(act Actioner) *repository.PushCommits {
if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil {
log.Error("json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err)
}
+
+ if push.Len == 0 {
+ push.Len = len(push.Commits)
+ }
+
return push
}
diff --git a/modules/templates/static.go b/modules/templates/static.go
index 7f95d77ad3..27449f926d 100644
--- a/modules/templates/static.go
+++ b/modules/templates/static.go
@@ -1,3 +1,4 @@
+//go:build bindata
// +build bindata
// Copyright 2016 The Gitea Authors. All rights reserved.
diff --git a/modules/templates/templates_bindata.go b/modules/templates/templates_bindata.go
index 5a59286c7a..887f9eeba2 100644
--- a/modules/templates/templates_bindata.go
+++ b/modules/templates/templates_bindata.go
@@ -2,7 +2,8 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-//+build bindata
+//go:build bindata
+// +build bindata
package templates
diff --git a/modules/timeutil/timestamp.go b/modules/timeutil/timestamp.go
index b1c60c3084..1fe8d4fcb1 100644
--- a/modules/timeutil/timestamp.go
+++ b/modules/timeutil/timestamp.go
@@ -13,8 +13,24 @@ import (
// TimeStamp defines a timestamp
type TimeStamp int64
+// mock is NOT concurrency-safe!!
+var mock time.Time
+
+// Set sets the time to a mocked time.Time
+func Set(now time.Time) {
+ mock = now
+}
+
+// Unset will unset the mocked time.Time
+func Unset() {
+ mock = time.Time{}
+}
+
// TimeStampNow returns now int64
func TimeStampNow() TimeStamp {
+ if !mock.IsZero() {
+ return TimeStamp(mock.Unix())
+ }
return TimeStamp(time.Now().Unix())
}
diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go
index d257b8179b..9e29b3557c 100644
--- a/modules/typesniffer/typesniffer.go
+++ b/modules/typesniffer/typesniffer.go
@@ -10,6 +10,8 @@ import (
"net/http"
"regexp"
"strings"
+
+ "code.gitea.io/gitea/modules/util"
)
// Use at most this many bytes to determine Content Type.
@@ -86,8 +88,8 @@ func DetectContentType(data []byte) SniffedType {
// DetectContentTypeFromReader guesses the content type contained in the reader.
func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) {
buf := make([]byte, sniffLen)
- n, err := r.Read(buf)
- if err != nil && err != io.EOF {
+ n, err := util.ReadAtMost(r, buf)
+ if err != nil {
return SniffedType{}, fmt.Errorf("DetectContentTypeFromReader io error: %w", err)
}
buf = buf[:n]
diff --git a/modules/uri/uri.go b/modules/uri/uri.go
index 0967a0802f..74410f43f9 100644
--- a/modules/uri/uri.go
+++ b/modules/uri/uri.go
@@ -31,7 +31,10 @@ func Open(uriStr string) (io.ReadCloser, error) {
switch strings.ToLower(u.Scheme) {
case "http", "https":
f, err := http.Get(uriStr)
- return f.Body, err
+ if err != nil {
+ return nil, err
+ }
+ return f.Body, nil
case "file":
return os.Open(u.Path)
default:
diff --git a/modules/util/io.go b/modules/util/io.go
new file mode 100644
index 0000000000..b467c0ac8a
--- /dev/null
+++ b/modules/util/io.go
@@ -0,0 +1,20 @@
+// Copyright 2021 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 util
+
+import (
+ "io"
+)
+
+// ReadAtMost reads at most len(buf) bytes from r into buf.
+// It returns the number of bytes copied. n is only less then len(buf) if r provides fewer bytes.
+// If EOF occurs while reading, err will be nil.
+func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
+ n, err = io.ReadFull(r, buf)
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
+ err = nil
+ }
+ return
+}
diff --git a/modules/util/net.go b/modules/util/net.go
new file mode 100644
index 0000000000..54c0a2ca39
--- /dev/null
+++ b/modules/util/net.go
@@ -0,0 +1,19 @@
+// Copyright 2021 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 util
+
+import (
+ "net"
+)
+
+// IsIPPrivate for net.IP.IsPrivate. TODO: replace with `ip.IsPrivate()` if min go version is bumped to 1.17
+func IsIPPrivate(ip net.IP) bool {
+ if ip4 := ip.To4(); ip4 != nil {
+ return ip4[0] == 10 ||
+ (ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
+ (ip4[0] == 192 && ip4[1] == 168)
+ }
+ return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
+}
diff --git a/modules/util/remove.go b/modules/util/remove.go
index 2310436525..d05ee9fe4a 100644
--- a/modules/util/remove.go
+++ b/modules/util/remove.go
@@ -6,10 +6,13 @@ package util
import (
"os"
+ "runtime"
"syscall"
"time"
)
+const windowsSharingViolationError syscall.Errno = 32
+
// Remove removes the named file or (empty) directory with at most 5 attempts.
func Remove(name string) error {
var err error
@@ -25,6 +28,12 @@ func Remove(name string) error {
continue
}
+ if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
+ // try again
+ <-time.After(100 * time.Millisecond)
+ continue
+ }
+
if unwrapped == syscall.ENOENT {
// it's already gone
return nil
@@ -48,6 +57,12 @@ func RemoveAll(name string) error {
continue
}
+ if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
+ // try again
+ <-time.After(100 * time.Millisecond)
+ continue
+ }
+
if unwrapped == syscall.ENOENT {
// it's already gone
return nil
@@ -64,13 +79,19 @@ func Rename(oldpath, newpath string) error {
if err == nil {
break
}
- unwrapped := err.(*os.PathError).Err
+ unwrapped := err.(*os.LinkError).Err
if unwrapped == syscall.EBUSY || unwrapped == syscall.ENOTEMPTY || unwrapped == syscall.EPERM || unwrapped == syscall.EMFILE || unwrapped == syscall.ENFILE {
// try again
<-time.After(100 * time.Millisecond)
continue
}
+ if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" {
+ // try again
+ <-time.After(100 * time.Millisecond)
+ continue
+ }
+
if i == 0 && os.IsNotExist(err) {
return err
}
diff --git a/modules/util/truncate.go b/modules/util/truncate.go
new file mode 100644
index 0000000000..8d0f630973
--- /dev/null
+++ b/modules/util/truncate.go
@@ -0,0 +1,35 @@
+// Copyright 2021 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 util
+
+import "unicode/utf8"
+
+// SplitStringAtByteN splits a string at byte n accounting for rune boundaries. (Combining characters are not accounted for.)
+func SplitStringAtByteN(input string, n int) (left, right string) {
+ if len(input) <= n {
+ left = input
+ return
+ }
+
+ if !utf8.ValidString(input) {
+ left = input[:n-3] + "..."
+ right = "..." + input[n-3:]
+ return
+ }
+
+ // in UTF8 "…" is 3 bytes so doesn't really gain us anything...
+ end := 0
+ for end <= n-3 {
+ _, size := utf8.DecodeRuneInString(input[end:])
+ if end+size > n-3 {
+ break
+ }
+ end += size
+ }
+
+ left = input[:end] + "…"
+ right = "…" + input[end:]
+ return
+}
diff --git a/modules/web/route.go b/modules/web/route.go
index 319d08f598..3c6513da62 100644
--- a/modules/web/route.go
+++ b/modules/web/route.go
@@ -269,6 +269,26 @@ func (r *Route) Get(pattern string, h ...interface{}) {
r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
}
+// Options delegate options method
+func (r *Route) Options(pattern string, h ...interface{}) {
+ var middlewares = r.getMiddlewares(h)
+ r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
+}
+
+// GetOptions delegate get and options method
+func (r *Route) GetOptions(pattern string, h ...interface{}) {
+ var middlewares = r.getMiddlewares(h)
+ r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
+ r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
+}
+
+// PostOptions delegate post and options method
+func (r *Route) PostOptions(pattern string, h ...interface{}) {
+ var middlewares = r.getMiddlewares(h)
+ r.R.Post(r.getPattern(pattern), Wrap(middlewares...))
+ r.R.Options(r.getPattern(pattern), Wrap(middlewares...))
+}
+
// Head delegate head method
func (r *Route) Head(pattern string, h ...interface{}) {
var middlewares = r.getMiddlewares(h)
diff --git a/options/locale/locale_bg-BG.ini b/options/locale/locale_bg-BG.ini
index 6f61799f27..bd5e2ed07d 100644
--- a/options/locale/locale_bg-BG.ini
+++ b/options/locale/locale_bg-BG.ini
@@ -534,7 +534,7 @@ migrate.clone_address_desc=HTTP(S) или Git URL за клониране на
migrate.clone_local_path=или път към локален сървър
migrate.permission_denied=Недостатъчни права за импорт на локални хранилища.
migrate.failed=Грешка при миграция: %v
-migrated_from_fake=Мигриран от %[1]с
+migrated_from_fake=Мигриран от %[1]s
migrate.migrating=Мигриране от %s...
migrate.migrating_failed=Мигрирането от %s беше неуспешно.
diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index 0538e0f7b1..2c9ae6db1f 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -1248,8 +1248,8 @@ issues.dependency.remove=Odstranit
issues.dependency.remove_info=Odstranit tuto závislost
issues.dependency.added_dependency=`přidal(a) novou závislost %s`
issues.dependency.removed_dependency=`odstranil(a) závislost %s`
-issues.dependency.issue_closing_blockedby=Uzavření tohoto požadavku na natažení je blokováno následujícími úkoly
-issues.dependency.pr_closing_blockedby=Uzavření tohoto úkolu je blokováno následujícími úkoly
+issues.dependency.pr_closing_blockedby=Uzavření tohoto požadavku na natažení je blokováno následujícími úkoly
+issues.dependency.issue_closing_blockedby=Uzavření tohoto úkolu je blokováno následujícími úkoly
issues.dependency.issue_close_blocks=Tento úkol blokuje uzavření následujících úkolů
issues.dependency.pr_close_blocks=Tento požadavek na natažení blokuje uzavření následujících úkolů
issues.dependency.issue_close_blocked=Musíte zavřít všechny úkoly, které blokují tento úkol, aby jej bylo možné zavřít.
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index bd6067b5cf..5f5a2be67a 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -326,7 +326,7 @@ hi_user_x=Hallo %s,
activate_account=Bitte aktiviere dein Konto
activate_account.title=%s, bitte aktiviere dein Konto
-activate_account.text_1=Hallo %[1]s, danke für deine Registrierung bei %[2]!
+activate_account.text_1=Hallo %[1]s, danke für deine Registrierung bei %[2]s!
activate_account.text_2=Bitte klicke innerhalb von %s auf folgenden Link, um dein Konto zu aktivieren:
activate_email=Bestätige deine E-Mail-Adresse
@@ -1326,8 +1326,8 @@ issues.dependency.remove=Entfernen
issues.dependency.remove_info=Abhängigkeit löschen
issues.dependency.added_dependency=`hat eine neue Abhängigkeit %s hinzugefügt`
issues.dependency.removed_dependency=`hat eine Abhängigkeit %s entfernt`
-issues.dependency.issue_closing_blockedby=Das Schließen dieses Pull-Requests wird von den folgenden Issues blockiert
-issues.dependency.pr_closing_blockedby=Das Schließen dieses Issues wird von den folgenden Issues blockiert
+issues.dependency.pr_closing_blockedby=Das Schließen dieses Pull-Requests wird von den folgenden Issues blockiert
+issues.dependency.issue_closing_blockedby=Das Schließen dieses Issues wird von den folgenden Issues blockiert
issues.dependency.issue_close_blocks=Dieses Issue blockiert die Schließung der folgenden Issues
issues.dependency.pr_close_blocks=Dieser Pull-Request blockiert die Schließung der folgenden Issues
issues.dependency.issue_close_blocked=Du musst alle Issues, die dieses Issue blockieren, schließen, bevor du es schließen kannst.
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 0ead1dfd6d..8e6352a910 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -345,8 +345,8 @@ reset_password.text = Please click the following link to recover your account wi
register_success = Registration successful
-issue_assigned.pull = @%[1]s assigned you to the pull request %[2]s in repository %[3]s.
-issue_assigned.issue = @%[1]s assigned you to the issue %[2]s in repository %[3]s.
+issue_assigned.pull = @%[1]s assigned you to pull request %[2]s in repository %[3]s.
+issue_assigned.issue = @%[1]s assigned you to issue %[2]s in repository %[3]s.
issue.x_mentioned_you = @%s mentioned you:
issue.action.force_push = %[1]s force-pushed the %[2]s from %[3]s to %[4]s.
@@ -896,11 +896,12 @@ migrate.migrate = Migrate From %s
migrate.migrating = Migrating from %s ...
migrate.migrating_failed = Migrating from %s failed.
migrate.migrating_failed.error = Error: %s
-migrate.github.description = Migrating data from Github.com or Github Enterprise.
-migrate.git.description = Migrating or Mirroring git data from Git services
-migrate.gitlab.description = Migrating data from GitLab.com or Self-Hosted gitlab server.
-migrate.gitea.description = Migrating data from Gitea.com or Self-Hosted Gitea server.
-migrate.gogs.description = Migrating data from notabug.org or other Self-Hosted Gogs server.
+migrate.github.description = Migrate data from github.com or other Github instances.
+migrate.git.description = Migrate a repository only from any Git service.
+migrate.gitlab.description = Migrate data from gitlab.com or other GitLab instances.
+migrate.gitea.description = Migrate data from gitea.com or other Gitea instances.
+migrate.gogs.description = Migrate data from notabug.org or other Gogs instances.
+migrate.onedev.description = Migrate data from code.onedev.io or other OneDev instances.
migrate.migrating_git = Migrating Git Data
migrate.migrating_topics = Migrating Topics
migrate.migrating_milestones = Migrating Milestones
@@ -1326,8 +1327,8 @@ issues.dependency.remove = Remove
issues.dependency.remove_info = Remove this dependency
issues.dependency.added_dependency = `added a new dependency %s`
issues.dependency.removed_dependency = `removed a dependency %s`
-issues.dependency.issue_closing_blockedby = Closing this pull request is blocked by the following issues
-issues.dependency.pr_closing_blockedby = Closing this issue is blocked by the following issues
+issues.dependency.pr_closing_blockedby = Closing this pull request is blocked by the following issues
+issues.dependency.issue_closing_blockedby = Closing this issue is blocked by the following issues
issues.dependency.issue_close_blocks = This issue blocks closing of the following issues
issues.dependency.pr_close_blocks = This pull request blocks closing of the following issues
issues.dependency.issue_close_blocked = You need to close all issues blocking this issue before you can close it.
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index 2a10e289ed..9053c7a644 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -326,7 +326,7 @@ hi_user_x=Hola %s,
activate_account=Por favor, active su cuenta
activate_account.title=%s, por favor activa tu cuenta
-activate_account.text_1=¡Hola %[1]s, gracias por registrarse en %[2]!
+activate_account.text_1=¡Hola %[1]s, gracias por registrarse en %[2]s!
activate_account.text_2=Por favor, haga clic en el siguiente enlace para activar su cuenta dentro de %s:
activate_email=Verifique su correo electrónico
@@ -1326,8 +1326,8 @@ issues.dependency.remove=Eliminar
issues.dependency.remove_info=Eliminar esta dependencia
issues.dependency.added_dependency=`añadida una nueva dependencia %s`
issues.dependency.removed_dependency=`eliminada una dependencia %s`
-issues.dependency.issue_closing_blockedby=Cerrar este pull request está bloqueado por las siguientes issues
-issues.dependency.pr_closing_blockedby=Cierre de esta incidencia es bloqueado por las siguientes incidencias
+issues.dependency.pr_closing_blockedby=Cerrar este pull request está bloqueado por las siguientes issues
+issues.dependency.issue_closing_blockedby=Cierre de esta incidencia es bloqueado por las siguientes incidencias
issues.dependency.issue_close_blocks=Esta incidencia bloquea el cierre de las siguientes incidencias
issues.dependency.pr_close_blocks=Este pull request bloquea el cierre de las siguientes incidencias
issues.dependency.issue_close_blocked=Necesita cerrar todos las incidencias que bloquean esta incidencia antes de que se puede cerrar.
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index cf36d01996..a26aeb3725 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -1070,8 +1070,8 @@ issues.dependency.remove=حذف/ساقط کردن
issues.dependency.remove_info=حذف این وابستگی
issues.dependency.added_dependency=`%s یک مخزن جدید اضافه کرد`
issues.dependency.removed_dependency=`%s یک وابستگی را حذف کرد`
-issues.dependency.issue_closing_blockedby=بستن این تقاضای واکشی وسط موضوعات زیر رد/ مسدود شده است
-issues.dependency.pr_closing_blockedby=بستن این موضوع وسط موضوعات زیر رد/ مسدود شده است
+issues.dependency.pr_closing_blockedby=بستن این تقاضای واکشی وسط موضوعات زیر رد/ مسدود شده است
+issues.dependency.issue_closing_blockedby=بستن این موضوع وسط موضوعات زیر رد/ مسدود شده است
issues.dependency.issue_close_blocks=این مسئله با توجه به موضوعات مطرح شده مسدود شده است
issues.dependency.pr_close_blocks=این تقاضای واکشی با توجه به موضوعات مطرح شده مسدود شده است
issues.dependency.issue_close_blocked=شما نیاز به بستن تمامی مسائل مسدود شده مسئله قبل بستن آن هستید.
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index b5a64da268..d70c49a12c 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -1277,8 +1277,8 @@ issues.dependency.remove=Supprimer
issues.dependency.remove_info=Supprimer cette dépendance
issues.dependency.added_dependency=`a ajouté une nouvelle dépendance %s`
issues.dependency.removed_dependency=`a supprimé une dépendance %s`
-issues.dependency.issue_closing_blockedby=La clôture de cette demande d'ajout est bloquée par les tickets suivants
-issues.dependency.pr_closing_blockedby=La clôture de ce ticket est bloquée par les tickets suivants
+issues.dependency.pr_closing_blockedby=La clôture de cette demande d'ajout est bloquée par les tickets suivants
+issues.dependency.issue_closing_blockedby=La clôture de ce ticket est bloquée par les tickets suivants
issues.dependency.issue_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants
issues.dependency.pr_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants
issues.dependency.issue_close_blocked=Vous devez fermer tous les tickets qui bloquent ce ticket avant de pouvoir le fermer.
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 2f0bff1cae..ab381c8357 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -1057,7 +1057,7 @@ issues.action_milestone=Pietra Miliare
issues.action_milestone_no_select=Nessuna pietra miliare
issues.action_assignee=Assegnatario
issues.action_assignee_no_select=Nessun assegnatario
-issues.opened_by=aperto %[1]s da %[3]s
+issues.opened_by=aperto %[1]s da %[3]s
issues.closed_by=del %[3]s chiuso %[1]s
issues.closed_by_fake=della %[2]s chiusa %[1]s
issues.previous=Pagina precedente
@@ -1179,8 +1179,8 @@ issues.dependency.remove=Rimuovi
issues.dependency.remove_info=Rimuovi questa dipendenza
issues.dependency.added_dependency=`ha aggiunto una nuova dipendenza %s`
issues.dependency.removed_dependency=`ha rimosso una dipendenza %s`
-issues.dependency.issue_closing_blockedby=La chiusura di questa richiesta pull è bloccata per i seguenti problemi
-issues.dependency.pr_closing_blockedby=La chiusura di questo problema è bloccata per i seguenti problemi
+issues.dependency.pr_closing_blockedby=La chiusura di questa richiesta pull è bloccata per i seguenti problemi
+issues.dependency.issue_closing_blockedby=La chiusura di questo problema è bloccata per i seguenti problemi
issues.dependency.issue_close_blocks=Questo problema impedisce la chiusura dei seguenti problemi
issues.dependency.pr_close_blocks=Questa richiesta di pull impedisce la chiusura dei seguenti problemi
issues.dependency.issue_close_blocked=Devi chiudere tutte le anomalie che bloiccano questo problema prima di chiudelo.
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 86c92dc935..bf01961973 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -1312,8 +1312,8 @@ issues.dependency.remove=削除
issues.dependency.remove_info=この依存関係を削除
issues.dependency.added_dependency=`が新しい依存関係を追加 %s`
issues.dependency.removed_dependency=`が依存関係を削除 %s`
-issues.dependency.issue_closing_blockedby=このプルリクエストのクローズは、これらの課題によりブロックされています
-issues.dependency.pr_closing_blockedby=この課題のクローズは、これらの課題によりブロックされています
+issues.dependency.pr_closing_blockedby=このプルリクエストのクローズは、これらの課題によりブロックされています
+issues.dependency.issue_closing_blockedby=この課題のクローズは、これらの課題によりブロックされています
issues.dependency.issue_close_blocks=この課題は、これらの課題のクローズをブロックしています
issues.dependency.pr_close_blocks=このプルリクエストは、これらの課題のクローズをブロックしています
issues.dependency.issue_close_blocked=この課題をクローズするには、ブロックしている課題をすべてクローズする必要があります。
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index f1e802afbc..03a786df10 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -1326,8 +1326,8 @@ issues.dependency.remove=Noņemt
issues.dependency.remove_info=Noņemt šo atkarību
issues.dependency.added_dependency=`pievienoja jaunu atkarību %s`
issues.dependency.removed_dependency=`noņema atkarību %s`
-issues.dependency.issue_closing_blockedby=Šī izmaiņu pieprasījuma sapludināšanu bloķē sekojošas problēmas
-issues.dependency.pr_closing_blockedby=Šīs problēmas aizvēršanu bloķē sekojošas problēmas
+issues.dependency.pr_closing_blockedby=Šī izmaiņu pieprasījuma sapludināšanu bloķē sekojošas problēmas
+issues.dependency.issue_closing_blockedby=Šīs problēmas aizvēršanu bloķē sekojošas problēmas
issues.dependency.issue_close_blocks=Šī problēma bloķē sekojošu problēmu aizvēršanu
issues.dependency.pr_close_blocks=Šis izmaiņu pieprasījums bloķē sekojošu problēmu aizvēršanu
issues.dependency.issue_close_blocked=Nepieciešams aizvērt visas problēmas, kas bloķē šo problēmu, lai to varētu aizērt.
diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini
index 4fb2bdbe8a..83f519de68 100644
--- a/options/locale/locale_ml-IN.ini
+++ b/options/locale/locale_ml-IN.ini
@@ -738,8 +738,8 @@ issues.deleted_milestone=`(ഇല്ലാതാക്കി)`
issues.filter_type.all_issues=എല്ലാ ഇഷ്യൂകളും
issues.label_open_issues=%d തുറന്നനിലയിലുള്ള ഇഷ്യൂകള്
issues.label_deletion_desc=ഒരു ലേബൽ ഇല്ലാതാക്കിയാല്, അതു് നിയുകതമാക്കിയ എല്ലാ ഇഷ്യൂകളില് നിന്നും നീക്കംചെയ്യും. തുടരട്ടെ?
-issues.dependency.issue_closing_blockedby=ഈ ലയന അഭ്യര്ത്ഥന അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള് തടയുന്നു്
-issues.dependency.pr_closing_blockedby=ഈ ഇഷ്യു അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ലയന അഭ്യര്ത്ഥന തടയുന്നു്
+issues.dependency.pr_closing_blockedby=ഈ ലയന അഭ്യര്ത്ഥന അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള് തടയുന്നു്
+issues.dependency.issue_closing_blockedby=ഈ ഇഷ്യു അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ലയന അഭ്യര്ത്ഥന തടയുന്നു്
issues.dependency.issue_close_blocks=ഈ ഇഷ്യു അടയ്ക്കുന്നത് ഇനിപ്പറയുന്ന ഇഷ്യൂകള് തടയുന്നു്
issues.dependency.pr_close_blocks=ഈ ഇഷ്യൂകള് അടയ്ക്കുന്നത് ഈ ലയന അഭ്യര്ത്ഥന തടയുന്നു്
issues.dependency.issue_close_blocked=ഈ ഇഷ്യൂ അടയ്ക്കുന്നതിന് മുമ്പ് ഇതിനെ തടയുന്ന എല്ലാ ഇഷ്യൂകളും നിങ്ങൾ അടയ്ക്കേണ്ടതുണ്ട്.
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 551a05ed0d..5309eac53e 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -1046,9 +1046,9 @@ issues.action_milestone=Mijlpaal
issues.action_milestone_no_select=Geen mijlpaal
issues.action_assignee=Toegewezene
issues.action_assignee_no_select=Geen verantwoordelijke
-issues.opened_by=%[1]s werd geopend door %[3]s
+issues.opened_by=%[1]s geopend door %[3]s
issues.closed_by=door %[3]s gesloten %[1]s
-issues.closed_by_fake=met %[2]gesloten %[1]s
+issues.closed_by_fake=met %[2]s gesloten %[1]s
issues.previous=Vorige
issues.next=Volgende
issues.open_title=Open
@@ -1168,8 +1168,8 @@ issues.dependency.remove=Verwijder
issues.dependency.remove_info=Verwijder afhankelijkheid
issues.dependency.added_dependency=`voegde een nieuwe afhankelijkheid %s toe `
issues.dependency.removed_dependency=`verwijderde een afhankelijkheid %s`
-issues.dependency.issue_closing_blockedby=Het sluiten van deze pull-aanvraag is geblokkeerd door de volgende kwesties
-issues.dependency.pr_closing_blockedby=Het sluiten van deze kwestie is geblokkeerd door de volgende kwesties
+issues.dependency.pr_closing_blockedby=Het sluiten van deze pull-aanvraag is geblokkeerd door de volgende kwesties
+issues.dependency.issue_closing_blockedby=Het sluiten van deze kwestie is geblokkeerd door de volgende kwesties
issues.dependency.issue_close_blocks=Deze kwestie blokkeert het sluiten van de volgende kwesties
issues.dependency.pr_close_blocks=Deze pull-aanvraag blokkeert het sluiten van de volgende kwesties
issues.dependency.issue_close_blocked=Je moet alle kwesties die deze kwestie blokkeren sluiten voordat je deze kan sluiten.
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 4b243de3cf..e013b985b4 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -1092,8 +1092,8 @@ issues.dependency.remove=Usuń
issues.dependency.remove_info=Usuń tę zależność
issues.dependency.added_dependency=`dodał nową zależność %s`
issues.dependency.removed_dependency=`usunął zależność %s`
-issues.dependency.issue_closing_blockedby=Zamknięcie tego Pull Requesta jest blokowane przez następujące zgłoszenia
-issues.dependency.pr_closing_blockedby=Zamknięcie tego zgłoszenia jest blokowane przez następujące zgłoszenia
+issues.dependency.pr_closing_blockedby=Zamknięcie tego Pull Requesta jest blokowane przez następujące zgłoszenia
+issues.dependency.issue_closing_blockedby=Zamknięcie tego zgłoszenia jest blokowane przez następujące zgłoszenia
issues.dependency.issue_close_blocks=To zgłoszenie blokuje zamknięcie następujących zgłoszeń
issues.dependency.pr_close_blocks=Ten Pull Request blokuje zamknięcie następujących zgłoszeń
issues.dependency.issue_close_blocked=Musisz zamknąć wszystkie zgłoszenia blokujące to zgłoszenie zanim je zamkniesz.
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index e95b388df0..f06422db60 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -1186,8 +1186,8 @@ issues.dependency.add=Adicione…
issues.dependency.cancel=Cancelar
issues.dependency.remove=Remover
issues.dependency.remove_info=Remover esta dependência
-issues.dependency.issue_closing_blockedby=Fechamento deste pull request está bloqueado pelas seguintes issues
-issues.dependency.pr_closing_blockedby=Fechamento desta issue está bloqueado pelas seguintes issues
+issues.dependency.pr_closing_blockedby=Fechamento deste pull request está bloqueado pelas seguintes issues
+issues.dependency.issue_closing_blockedby=Fechamento desta issue está bloqueado pelas seguintes issues
issues.dependency.issue_close_blocks=Esta issue bloqueia o fechamento das seguintes issues
issues.dependency.pr_close_blocks=Este pull request bloqueia o fechamento das seguintes issues
issues.dependency.issue_close_blocked=Você precisa fechar todas as issues que bloqueiam esta issue antes de poder fechá-la.
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 24b54a9189..1a581d40d4 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -1326,8 +1326,8 @@ issues.dependency.remove=Remover
issues.dependency.remove_info=Remover esta dependência
issues.dependency.added_dependency=`adicionou uma nova dependência %s`
issues.dependency.removed_dependency=`removeu uma dependência %s`
-issues.dependency.issue_closing_blockedby=O encerramento deste pedido de integração está bloqueado pelas seguintes questões
-issues.dependency.pr_closing_blockedby=O encerramento desta questão está bloqueado pelas seguintes questões
+issues.dependency.pr_closing_blockedby=O encerramento deste pedido de integração está bloqueado pelas seguintes questões
+issues.dependency.issue_closing_blockedby=O encerramento desta questão está bloqueado pelas seguintes questões
issues.dependency.issue_close_blocks=Esta questão bloqueia o encerramento das seguintes questões
issues.dependency.pr_close_blocks=Este pedido de integração bloqueia o encerramento das seguintes questões
issues.dependency.issue_close_blocked=Tem que encerrar todas as questões que bloqueiam esta questão antes de a poder encerrar.
@@ -2245,7 +2245,7 @@ dashboard.task.cancelled=Tarefa: %[1]s cancelada: %[3]s
dashboard.task.error=Erro na tarefa: %[1]s: %[3]s
dashboard.task.finished=Tarefa: %[1]s iniciada por %[2]s foi concluída
dashboard.task.unknown=Tarefa desconhecida: %[1]s
-dashboard.cron.started=Cron iniciado: %[1]
+dashboard.cron.started=Cron iniciado: %[1]s
dashboard.cron.process=Cron: %[1]s
dashboard.cron.cancelled=Cron: %s cancelado: %[3]s
dashboard.cron.error=Erro no cron: %s: %[3]s
@@ -2698,7 +2698,7 @@ notices.delete_success=As notificações do sistema foram eliminadas.
[action]
create_repo=criou o repositório %s
-rename_repo=renomeou o repositório de %[1]s
para %[3]
+rename_repo=renomeou o repositório de %[1]s
para %[3]s
commit_repo=enviou para %[3]s em %[4]s
create_issue=`abriu a questão %s#%[2]s`
close_issue=`fechou a questão %s#%[2]s`
@@ -2724,7 +2724,7 @@ reject_pull_request=`sugeriu modificações para %s#%[2]s<
publish_release=`lançou "%[4]s" à %[3]s`
review_dismissed=`descartou a revisão de %[4]s para %[3]s#%[2]s`
review_dismissed_reason=Motivo:
-create_branch=criou o ramo %[3]s em %[4]
+create_branch=criou o ramo %[3]s em %[4]s
[tool]
ago=há %s
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 103abcf312..9a15c4eac5 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -334,7 +334,7 @@ activate_email.title=%s, пожалуйста, подтвердите ваш а
activate_email.text=Пожалуйста, перейдите по ссылке, чтобы подтвердить ваш адрес электронной почты в течение %s:
register_notify=Добро пожаловать на Gitea
-register_notify.title=%[1], добро пожаловать в %[2]
+register_notify.title=%[1]s, добро пожаловать в %[2]s
register_notify.text_1=это письмо с вашим подтверждением регистрации в %s!
register_notify.text_2=Теперь вы можете войти через логин: %s.
register_notify.text_3=Если эта учетная запись была создана для вас, пожалуйста, сначала установите пароль.
@@ -345,7 +345,7 @@ reset_password.text=Пожалуйста, перейдите по ссылке,
register_success=Регистрация прошла успешно
-issue_assigned.pull=@%[1] назначил вам запрос на слияние %[2] в репозитории %[3].
+issue_assigned.pull=@%[1]s назначил вам запрос на слияние %[2]s в репозитории %[3]s.
issue_assigned.issue=@%[1]s назначил вам задачу %[2]s в репозитории %[3]s.
issue.x_mentioned_you=@%s упомянул вас:
@@ -1219,8 +1219,8 @@ issues.reopened_at=`переоткрыл(а) эту проблему %[2]s`
issues.ref_issue_from=`ссылка на эту проблему %[4]s %[2]s`
issues.ref_pull_from=`ссылается на этот Pull Request %[4]s %[2]s`
-issues.ref_closing_from=`ссылается на Pull Request %[4], который закроет эту задачу %[2]s`
-issues.ref_reopening_from=`ссылается на Pull Request %[4], который вновь откроет эту задачу %[2]s`
+issues.ref_closing_from=`ссылается на Pull Request %[4]s, который закроет эту задачу %[2]s`
+issues.ref_reopening_from=`ссылается на Pull Request %[4]s, который вновь откроет эту задачу %[2]s`
issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s`
issues.ref_reopened_from=`переоткрыл эту задачу %[4]s %[2]s`
issues.ref_from=`из %[1]s`
@@ -1303,7 +1303,7 @@ issues.error_modifying_due_date=Не удалось изменить срок в
issues.error_removing_due_date=Не удалось убрать срок выполнения.
issues.push_commit_1=добавил(а) %d коммит %s
issues.push_commits_n=добавил(а) %d коммитов %s
-issues.force_push_codes=`принудительно залито %[1]s от %[2]
к %[4]s
%[6]s`
+issues.force_push_codes=`принудительно залито %[1]s от %[2]s
к %[4]s
%[6]s`
issues.due_date_form=гггг-мм-дд
issues.due_date_form_add=Добавить срок выполнения
issues.due_date_form_edit=Редактировать
@@ -1324,8 +1324,8 @@ issues.dependency.remove=Удалить
issues.dependency.remove_info=Удалить эту зависимость
issues.dependency.added_dependency=`добавить новую зависимость %s`
issues.dependency.removed_dependency=`убрал зависимость %s`
-issues.dependency.issue_closing_blockedby=Закрытие этого запроса на слияние невозможно до закрытия следующих задач
-issues.dependency.pr_closing_blockedby=Закрытие этой задачи блокируется следующими задачами
+issues.dependency.pr_closing_blockedby=Закрытие этого запроса на слияние невозможно до закрытия следующих задач
+issues.dependency.issue_closing_blockedby=Закрытие этой задачи блокируется следующими задачами
issues.dependency.issue_close_blocks=Эта задача блокирует закрытие следующих задач
issues.dependency.pr_close_blocks=Этот запрос на слияние блокирует закрытие следующих задач
issues.dependency.issue_close_blocked=Вам необходимо закрыть все задачи, блокирующие эту задачу, прежде чем вы сможете её закрыть.
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index a1c13f2e41..8089e8f79e 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -1121,8 +1121,8 @@ issues.dependency.remove=Ta bort
issues.dependency.remove_info=Ta bort detta beroende
issues.dependency.added_dependency=`lade till ett nytt beroende %s`
issues.dependency.removed_dependency=`tog bort ett beroende %s`
-issues.dependency.issue_closing_blockedby=En stängning av denna pull-förfrågan blockeras av följande ärenden
-issues.dependency.pr_closing_blockedby=En stängning av ärendet blockeras av följande ärenden
+issues.dependency.pr_closing_blockedby=En stängning av denna pull-förfrågan blockeras av följande ärenden
+issues.dependency.issue_closing_blockedby=En stängning av ärendet blockeras av följande ärenden
issues.dependency.issue_close_blocks=Detta ärende blockerar en stängning av följande ärenden
issues.dependency.pr_close_blocks=Denna pull-förfrågan blockerar stängning av följande ärenden
issues.dependency.issue_close_blocked=Du måste stänga alla ärenden som blockerar det här ärendet innan du kan stänga det.
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 69ac3367c1..24628212c8 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -1147,7 +1147,7 @@ issues.reopened_at=`%[2]s konusunu yeniden açt
issues.commit_ref_at=`%[2]s işlemesinde bu konuyu işaret etti`
issues.ref_issue_from=`bu konuya referansta bulundu %[4]s %[2]s`
issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[4]s %[2]s`
-issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4] bu konu kapatılacak %[2]s`
+issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu kapatılacak %[2]s`
issues.ref_reopening_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu yeniden açılacak %[2]s`
issues.ref_closed_from=`bu konuyu kapat%[4]s %[2]s`
issues.ref_reopened_from=`konuyu yeniden aç%[4]s %[2]s`
@@ -1252,8 +1252,8 @@ issues.dependency.remove=Kaldır
issues.dependency.remove_info=Bu bağımlılığı kaldır
issues.dependency.added_dependency=`yeni bir %s bağımlılığı eklendi`
issues.dependency.removed_dependency=`bir %s bağımlılığı kaldırıldı`
-issues.dependency.issue_closing_blockedby=Bu değişiklik isteğinin kapatılması aşağıdaki konular nedeniyle engelleniyor
-issues.dependency.pr_closing_blockedby=Bu konunun kapatılması aşağıdaki konular tarafından engelleniyor
+issues.dependency.pr_closing_blockedby=Bu değişiklik isteğinin kapatılması aşağıdaki konular nedeniyle engelleniyor
+issues.dependency.issue_closing_blockedby=Bu konunun kapatılması aşağıdaki konular tarafından engelleniyor
issues.dependency.issue_close_blocks=Bu konu aşağıdaki konuların kapatılmasını engelliyor
issues.dependency.pr_close_blocks=Bu değişiklik isteği aşağıdaki sorunların kapatılmasını engelliyor
issues.dependency.issue_close_blocked=Kapatmadan önce bu konuyu engelleyen tüm konuları kapatmanız gerekir.
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index a2a789ee77..614a8e9b43 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -348,7 +348,7 @@ issue.x_mentioned_you=@%s згадав вас:
issue.action.force_push=%[1]s force-pushed %[2]s з %[3]s в %[4]s.
issue.action.push_n=@%[1]s відправив %[3]d коміти до %[2]s
issue.action.close=@%[1]s закрито #%[2]d.
-issue.action.reopen=@%[1] заново відкрив #%[2]d.
+issue.action.reopen=@%[1]s заново відкрив #%[2]d.
issue.action.merge=@%[1]s об'єднав #%[2]d до %[3]s.
issue.action.approve=@%[1]s затвердили цей запит на злиття.
issue.action.reject=@%[1]s запитують зміни на цей запит на злиття.
@@ -555,7 +555,7 @@ delete_email=Видалити
email_deletion=Видалити адресу електронної пошти
email_deletion_desc=Електронна адреса та пов'язана з нею інформація буде видалена з вашого облікового запису. Git коміти, здійснені через цю електронну адресу, залишиться без змін. Продовжити?
email_deletion_success=Адресу електронної пошти було видалено.
-theme_update_success=Тему оновлено.
+theme_update_success=Тему оновлено.
theme_update_error=Вибрана тема не існує.
openid_deletion=Видалити адресу OpenID
openid_deletion_desc=Видалення цієї OpenID-адреси з вашого облікового запису забороняє вам входити з ним. Продовжити?
@@ -1171,7 +1171,7 @@ issues.action_milestone_no_select=Етап відсутній
issues.action_assignee=Виконавець
issues.action_assignee_no_select=Немає виконавеця
issues.opened_by=%[1]s відкрито %[3]s
-pulls.merged_by=до %[3] злито %[1]s
+pulls.merged_by=до %[3]s злито %[1]s
pulls.merged_by_fake=%[2]s об'єднаний %[1]s
issues.closed_by=закрито %[3]s %[1]s
issues.opened_by_fake=%[2]s відкрив(ла) %[1]s
@@ -1285,7 +1285,7 @@ issues.error_modifying_due_date=Не вдалося змінити дату за
issues.error_removing_due_date=Не вдалося видалити дату завершення.
issues.push_commit_1=додав %d коміт %s
issues.push_commits_n=додав %d коміти(-ів) %s
-issues.force_push_codes=`примусово залито %[1]s з %[2]
до %[4]s
%[6]s`
+issues.force_push_codes=`примусово залито %[1]s з %[2]s
до %[4]s
%[6]s`
issues.due_date_form=рррр-мм-дд
issues.due_date_form_add=Додати дату завершення
issues.due_date_form_edit=Редагувати
@@ -1306,8 +1306,8 @@ issues.dependency.remove=Видалити
issues.dependency.remove_info=Видалити цю залежність
issues.dependency.added_dependency=`додав нову залежність %s`
issues.dependency.removed_dependency=`видалив залежність %s`
-issues.dependency.issue_closing_blockedby=Закриття цього запиту на злиття заблокує наступні проблеми
-issues.dependency.pr_closing_blockedby=Закриття цієї проблеми заблокує наступні проблеми
+issues.dependency.pr_closing_blockedby=Закриття цього запиту на злиття заблокує наступні проблеми
+issues.dependency.issue_closing_blockedby=Закриття цієї проблеми заблокує наступні проблеми
issues.dependency.issue_close_blocks=Ця проблема блокує закриття залежних проблем
issues.dependency.pr_close_blocks=Цей пулл-реквест блокує закриття залежних проблем
issues.dependency.issue_close_blocked=Вам потрібно закрити всі проблеми, що блокують цю проблему, перед її закриттям.
@@ -2221,7 +2221,7 @@ dashboard.clean_unbind_oauth_success=Всі незавершені зв'язки
dashboard.task.started=Запущено завдання: %[1]s
dashboard.task.process=Завдання: %[1]s
dashboard.task.cancelled=Завдання: %[1]s скасовано: %[3]s
-dashboard.task.error=Помилка у завданні: %[1]:%[3]s
+dashboard.task.error=Помилка у завданні: %[1]s :%[3]s
dashboard.task.finished=Завершилося завдання, яке запустив %[2]s: %[1]s
dashboard.task.unknown=Невідоме завдання: %[1]s
dashboard.cron.started=Запущено Cron: %[1]s
@@ -2701,7 +2701,7 @@ mirror_sync_delete=синхронізовано й видалено посила
approve_pull_request=`схвалив %s#%[2]s`
reject_pull_request=`запропонував зміни до %s#%[2]s`
publish_release=`опублікував випуск "%[4]s" з %[3]s`
-review_dismissed=`відхилений відгук від %[4] у %[3]s#%[2]s`
+review_dismissed=`відхилений відгук від %[4]s у %[3]s#%[2]s`
review_dismissed_reason=Причина:
create_branch=створено гілку %[3]s у %[4]s
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 7c4cadb4db..1f91e0ac7f 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -630,8 +630,8 @@ last_used=上次使用在
no_activity=没有最近活动
can_read_info=读取
can_write_info=写入
-key_state_desc=7 天内使用过该密钥
-token_state_desc=7 天内使用过该密钥
+key_state_desc=7 天内使用过该密钥
+token_state_desc=7 天内使用过该密钥
principal_state_desc=7 天内使用过该规则
show_openid=在个人信息上显示
hide_openid=在个人信息上隐藏
@@ -808,7 +808,7 @@ watchers=关注者
stargazers=称赞者
forks=派生仓库
pick_reaction=选择你的表情
-reactions_more=再加载 %d
+reactions_more=再加载 %d
unit_disabled=站点管理员已禁用此仓库单元。
language_other=其它
adopt_search=输入用户名以搜索未被收录的仓库... (留空以查找全部)
@@ -1322,8 +1322,8 @@ issues.dependency.remove=删除
issues.dependency.remove_info=删除此依赖项
issues.dependency.added_dependency=`添加了一个新的依赖项 %s`
issues.dependency.removed_dependency=`移除了一个依赖项 %s`
-issues.dependency.issue_closing_blockedby=以下工单阻止了关闭此合并请求
-issues.dependency.pr_closing_blockedby=以下工单阻止了关闭此工单
+issues.dependency.pr_closing_blockedby=以下工单阻止了关闭此合并请求
+issues.dependency.issue_closing_blockedby=以下工单阻止了关闭此工单
issues.dependency.issue_close_blocks=此工单阻止了以下工单的关闭
issues.dependency.pr_close_blocks=此合并请求阻止以下工单的关闭
issues.dependency.issue_close_blocked=您需要关闭所有阻止此工单的工单, 然后才能关闭它。
@@ -1380,7 +1380,7 @@ pulls.filter_branch=过滤分支
pulls.no_results=未找到结果
pulls.nothing_to_compare=分支内容相同,无需创建合并请求。
pulls.nothing_to_compare_and_allow_empty_pr=这些分支是相等的,此合并请求将为空。
-pulls.has_pull_request="在这些分支之间的合并请求已存在: %[2]s%#[3]d"
+pulls.has_pull_request="在这些分支之间的合并请求已存在: %[2]s#%[3]d"
pulls.create=创建合并请求
pulls.title_desc=请求将 %[1]d 次代码提交从 %[2]s
合并至 %[3]s
pulls.merged_title_desc=于 %[4]s 将 %[1]d 次代码提交从 %[2]s
合并至 %[3]s
@@ -1611,7 +1611,7 @@ search=搜索
search.search_repo=搜索仓库...
search.fuzzy=模糊
search.match=匹配
-search.results=在 %s 中搜索 "%s" 的结果
+search.results=在 %[3]s 中搜索 "%[1]s" 的结果
settings=设置
settings.desc=设置是你可以管理仓库设置的地方
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 7b49562c8e..0864ea154f 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -1311,8 +1311,8 @@ issues.dependency.remove=移除
issues.dependency.remove_info=移除此先決條件
issues.dependency.added_dependency=`加入了新的先決條件 %s`
issues.dependency.removed_dependency=`移除了先決條件 %s`
-issues.dependency.issue_closing_blockedby=此合併請求被下列問題阻擋而無法關閉
-issues.dependency.pr_closing_blockedby=此問題被下列問題阻擋而無法關閉
+issues.dependency.pr_closing_blockedby=此合併請求被下列問題阻擋而無法關閉
+issues.dependency.issue_closing_blockedby=此問題被下列問題阻擋而無法關閉
issues.dependency.issue_close_blocks=因為此問題的阻擋,下列問題無法被關閉
issues.dependency.pr_close_blocks=因為此合併請求的阻擋,下列問題無法被關閉
issues.dependency.issue_close_blocked=在您關閉此問題以前,您必須先關閉所有阻擋它的問題。
diff --git a/package-lock.json b/package-lock.json
index 43f1b6f45f..4d44050376 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,10 @@
"@claviska/jquery-minicolors": "2.3.5",
"@primer/octicons": "13.0.0",
"add-asset-webpack-plugin": "2.0.1",
- "codemirror": "5.61.0",
+ "codemirror": "5.65.0",
"css-loader": "5.2.4",
"dropzone": "5.9.2",
- "easymde": "2.15.0",
+ "easymde": "2.16.1",
"esbuild-loader": "2.13.0",
"escape-goat": "4.0.0",
"fast-glob": "3.2.5",
@@ -22,7 +22,7 @@
"less": "4.1.1",
"less-loader": "8.1.1",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "8.10.1",
+ "mermaid": "8.13.8",
"mini-css-extract-plugin": "1.6.0",
"monaco-editor": "0.24.0",
"monaco-editor-webpack-plugin": "3.1.0",
@@ -1160,9 +1160,9 @@
}
},
"node_modules/@types/codemirror": {
- "version": "0.0.109",
- "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.109.tgz",
- "integrity": "sha512-cSdiHeeLjvGn649lRTNeYrVCDOgDrtP+bDDSFDd1TF+i0jKGPDRozno2NOJ9lTniso+taiv4kiVS8dgM8Jm5lg==",
+ "version": "5.60.5",
+ "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz",
+ "integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==",
"dependencies": {
"@types/tern": "*"
}
@@ -1235,9 +1235,9 @@
"dev": true
},
"node_modules/@types/marked": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@types/marked/-/marked-2.0.2.tgz",
- "integrity": "sha512-P4zanhCQKs4tiWPPBGpB7lHflgFCP9DFGNI5YtpW9MALKoy2qs9rHNWJ+z55cegD9uCfnmsKuaosq9FNvbxrOw=="
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.1.tgz",
+ "integrity": "sha512-ZigEmCWdNUU7IjZEuQ/iaimYdDHWHfTe3kg8ORfKjyGYd9RWumPoOJRQXB0bO+XLkNwzCthW3wUIQtANaEZ1ag=="
},
"node_modules/@types/mdast": {
"version": "3.0.3",
@@ -1284,9 +1284,9 @@
"dev": true
},
"node_modules/@types/tern": {
- "version": "0.23.3",
- "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz",
- "integrity": "sha512-imDtS4TAoTcXk0g7u4kkWqedB3E4qpjXzCpD2LU5M5NAXHzCDsypyvXSaG7mM8DKYkCRa7tFp4tS/lp/Wo7Q3w==",
+ "version": "0.23.4",
+ "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz",
+ "integrity": "sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==",
"dependencies": {
"@types/estree": "*"
}
@@ -2356,15 +2356,6 @@
"node": ">=6"
}
},
- "node_modules/camel-case": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
- "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
- "dependencies": {
- "no-case": "^2.2.0",
- "upper-case": "^1.1.1"
- }
- },
"node_modules/camelcase": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
@@ -2403,9 +2394,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001228",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
- "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A==",
+ "version": "1.0.30001300",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz",
+ "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
@@ -2617,25 +2608,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/clean-css": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
- "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
- "dependencies": {
- "source-map": "~0.6.0"
- },
- "engines": {
- "node": ">= 4.0"
- }
- },
- "node_modules/clean-css/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/clean-regexp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
@@ -2709,9 +2681,9 @@
}
},
"node_modules/codemirror": {
- "version": "5.61.0",
- "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.61.0.tgz",
- "integrity": "sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg=="
+ "version": "5.65.0",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.0.tgz",
+ "integrity": "sha512-gWEnHKEcz1Hyz7fsQWpK7P0sPI2/kSkRX2tc7DFA6TmZuDN75x/1ejnH/Pn8adYKrLEA1V2ww6L00GudHZbSKw=="
},
"node_modules/codemirror-spell-checker": {
"version": "1.1.2",
@@ -2897,17 +2869,6 @@
"node": ">= 8"
}
},
- "node_modules/css-b64-images": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
- "integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI=",
- "bin": {
- "css-b64-images": "bin/css-b64-images"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/css-loader": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz",
@@ -3120,6 +3081,17 @@
"d3-array": "^1.1.1"
}
},
+ "node_modules/d3-delaunay": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
+ "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/d3-dispatch": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
@@ -3363,6 +3335,7 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -3461,6 +3434,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/delaunator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+ "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+ "dependencies": {
+ "robust-predicates": "^3.0.0"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -3583,6 +3564,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
+ "node_modules/dompurify": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.4.tgz",
+ "integrity": "sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ=="
+ },
"node_modules/domutils": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz",
@@ -3603,15 +3589,15 @@
"integrity": "sha512-5t2z51DzIsWDbTpwcJIvUlwxBbvcwdCApz0yb9ecKJwG155Xm92KMEZmHW1B0MzoXOKvFwdd0nPu5cpeVcvPHQ=="
},
"node_modules/easymde": {
- "version": "2.15.0",
- "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.15.0.tgz",
- "integrity": "sha512-9jMRIVvKt1d0UjRN45yotUYECAM4xvw0TTAQw8sYDONP++keWJVnd8Xrn+V+vQEN/v9/X0SWEoo1rFSgCooGpw==",
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.16.1.tgz",
+ "integrity": "sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g==",
"dependencies": {
- "@types/codemirror": "0.0.109",
- "@types/marked": "^2.0.2",
- "codemirror": "^5.61.0",
+ "@types/codemirror": "^5.60.4",
+ "@types/marked": "^4.0.1",
+ "codemirror": "^5.63.1",
"codemirror-spell-checker": "1.1.2",
- "marked": "^2.0.3"
+ "marked": "^4.0.10"
}
},
"node_modules/ecc-jsbn": {
@@ -3711,14 +3697,6 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
- "node_modules/entity-decode": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/entity-decode/-/entity-decode-2.0.2.tgz",
- "integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==",
- "dependencies": {
- "he": "^1.1.1"
- }
- },
"node_modules/envinfo": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
@@ -5754,26 +5732,6 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
- "node_modules/html-minifier": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
- "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
- "dependencies": {
- "camel-case": "^3.0.0",
- "clean-css": "^4.2.1",
- "commander": "^2.19.0",
- "he": "^1.2.0",
- "param-case": "^2.1.1",
- "relateurl": "^0.2.7",
- "uglify-js": "^3.5.1"
- },
- "bin": {
- "html-minifier": "cli.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/html-tags": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
@@ -5983,6 +5941,14 @@
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/interpret": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
@@ -7800,11 +7766,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/lower-case": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
- "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
- },
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -7883,14 +7844,14 @@
}
},
"node_modules/marked": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz",
- "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==",
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz",
+ "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==",
"bin": {
- "marked": "bin/marked"
+ "marked": "bin/marked.js"
},
"engines": {
- "node": ">= 8.16.2"
+ "node": ">= 12"
}
},
"node_modules/mathml-tag-names": {
@@ -8144,21 +8105,397 @@
}
},
"node_modules/mermaid": {
- "version": "8.10.1",
- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.10.1.tgz",
- "integrity": "sha512-KxwKEJDKy303TQdz5TQMFb/4u+gUL21CefUMGOfuigDh9powcYaNmuJ5BkHmO0jB3Y1z2zlsuKvHZ2CusWH5+A==",
+ "version": "8.13.8",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.8.tgz",
+ "integrity": "sha512-Z5v31rvo8P7BPTiGicdJl9BbzyUe9s5sXILK8sM1g7ijkagpfFjPtXZVsq5P1WlN8m/fUp2PPNXVF9SqeTM91w==",
"dependencies": {
"@braintree/sanitize-url": "^3.1.0",
- "d3": "^5.7.0",
- "dagre": "^0.8.4",
+ "d3": "^7.0.0",
+ "dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
- "entity-decode": "^2.0.2",
- "graphlib": "^2.1.7",
- "he": "^1.2.0",
- "khroma": "^1.1.0",
- "minify": "^4.1.1",
- "moment-mini": "^2.22.1",
- "stylis": "^3.5.2"
+ "dompurify": "2.3.4",
+ "graphlib": "^2.1.8",
+ "khroma": "^1.4.1",
+ "moment-mini": "^2.24.0",
+ "stylis": "^4.0.10"
+ }
+ },
+ "node_modules/mermaid/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz",
+ "integrity": "sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw==",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "3",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-array": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz",
+ "integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-color": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
+ "integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-contour": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz",
+ "integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-geo": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz",
+ "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-hierarchy": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz",
+ "integrity": "sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-path": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
+ "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-scale-chromatic": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+ "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-shape": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz",
+ "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-time": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
+ "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/mermaid/node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/mermaid/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
}
},
"node_modules/micromark": {
@@ -8278,26 +8615,6 @@
"source-map": "~0.6.1"
}
},
- "node_modules/minify": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/minify/-/minify-4.1.3.tgz",
- "integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==",
- "dependencies": {
- "clean-css": "^4.1.6",
- "css-b64-images": "~0.2.5",
- "debug": "^4.1.0",
- "html-minifier": "^4.0.0",
- "terser": "^4.0.0",
- "try-catch": "^2.0.0",
- "try-to-catch": "^1.0.2"
- },
- "bin": {
- "minify": "bin/minify.js"
- },
- "engines": {
- "node": ">= 8.0.0"
- }
- },
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -8403,7 +8720,8 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "devOptional": true
},
"node_modules/multimap": {
"version": "1.1.0",
@@ -8487,14 +8805,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
- "node_modules/no-case": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
- "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
- "dependencies": {
- "lower-case": "^1.1.1"
- }
- },
"node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
@@ -8898,14 +9208,6 @@
"node": ">=6"
}
},
- "node_modules/param-case": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
- "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
- "dependencies": {
- "no-case": "^2.2.0"
- }
- },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -10160,14 +10462,6 @@
"url": "https://github.com/sponsors/mysticatea"
}
},
- "node_modules/relateurl": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
- "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
- "engines": {
- "node": ">= 0.10"
- }
- },
"node_modules/remark": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz",
@@ -10443,6 +10737,11 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
+ "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
+ },
"node_modules/rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -11838,9 +12137,9 @@
}
},
"node_modules/stylis": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
- "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
+ "version": "4.0.13",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+ "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
},
"node_modules/sugarss": {
"version": "2.0.0",
@@ -12076,9 +12375,9 @@
}
},
"node_modules/tar": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
- "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz",
+ "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==",
"dev": true,
"dependencies": {
"chownr": "^2.0.0",
@@ -12108,22 +12407,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/terser": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
- "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
- "dependencies": {
- "commander": "^2.20.0",
- "source-map": "~0.6.1",
- "source-map-support": "~0.5.12"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/terser-webpack-plugin": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz",
@@ -12193,14 +12476,6 @@
"node": ">= 8"
}
},
- "node_modules/terser/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -12357,19 +12632,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/try-catch": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/try-catch/-/try-catch-2.0.1.tgz",
- "integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg==",
- "engines": {
- "node": ">=0.4"
- }
- },
- "node_modules/try-to-catch": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-1.1.1.tgz",
- "integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA=="
- },
"node_modules/tsconfig-paths": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
@@ -12472,17 +12734,6 @@
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.0.tgz",
"integrity": "sha512-dELuLBVa2jvWdU/CHTKi2L/POYaRupv942k+vRsFXsM17acXesQGAiGCio82RW7fvcr7bkuD/Zj8XpUh6aPC2A=="
},
- "node_modules/uglify-js": {
- "version": "3.13.6",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.6.tgz",
- "integrity": "sha512-rRprLwl8RVaS+Qvx3Wh5hPfPBn9++G6xkGlUupya0s5aDmNjI7z3lnRLB3u7sN4OmbB0pWgzhM9BEJyiWAwtAA==",
- "bin": {
- "uglifyjs": "bin/uglifyjs"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
"node_modules/unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@@ -12654,11 +12905,6 @@
"node": ">= 12.20.0"
}
},
- "node_modules/upper-case": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
- "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
- },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -14486,9 +14732,9 @@
}
},
"@types/codemirror": {
- "version": "0.0.109",
- "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.109.tgz",
- "integrity": "sha512-cSdiHeeLjvGn649lRTNeYrVCDOgDrtP+bDDSFDd1TF+i0jKGPDRozno2NOJ9lTniso+taiv4kiVS8dgM8Jm5lg==",
+ "version": "5.60.5",
+ "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz",
+ "integrity": "sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg==",
"requires": {
"@types/tern": "*"
}
@@ -14561,9 +14807,9 @@
"dev": true
},
"@types/marked": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@types/marked/-/marked-2.0.2.tgz",
- "integrity": "sha512-P4zanhCQKs4tiWPPBGpB7lHflgFCP9DFGNI5YtpW9MALKoy2qs9rHNWJ+z55cegD9uCfnmsKuaosq9FNvbxrOw=="
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.1.tgz",
+ "integrity": "sha512-ZigEmCWdNUU7IjZEuQ/iaimYdDHWHfTe3kg8ORfKjyGYd9RWumPoOJRQXB0bO+XLkNwzCthW3wUIQtANaEZ1ag=="
},
"@types/mdast": {
"version": "3.0.3",
@@ -14610,9 +14856,9 @@
"dev": true
},
"@types/tern": {
- "version": "0.23.3",
- "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz",
- "integrity": "sha512-imDtS4TAoTcXk0g7u4kkWqedB3E4qpjXzCpD2LU5M5NAXHzCDsypyvXSaG7mM8DKYkCRa7tFp4tS/lp/Wo7Q3w==",
+ "version": "0.23.4",
+ "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz",
+ "integrity": "sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==",
"requires": {
"@types/estree": "*"
}
@@ -15454,15 +15700,6 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
- "camel-case": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
- "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
- "requires": {
- "no-case": "^2.2.0",
- "upper-case": "^1.1.1"
- }
- },
"camelcase": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
@@ -15488,9 +15725,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001228",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz",
- "integrity": "sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A=="
+ "version": "1.0.30001300",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001300.tgz",
+ "integrity": "sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA=="
},
"capture-exit": {
"version": "2.0.0",
@@ -15650,21 +15887,6 @@
}
}
},
- "clean-css": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
- "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
- "requires": {
- "source-map": "~0.6.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
"clean-regexp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
@@ -15724,9 +15946,9 @@
"dev": true
},
"codemirror": {
- "version": "5.61.0",
- "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.61.0.tgz",
- "integrity": "sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg=="
+ "version": "5.65.0",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.0.tgz",
+ "integrity": "sha512-gWEnHKEcz1Hyz7fsQWpK7P0sPI2/kSkRX2tc7DFA6TmZuDN75x/1ejnH/Pn8adYKrLEA1V2ww6L00GudHZbSKw=="
},
"codemirror-spell-checker": {
"version": "1.1.2",
@@ -15881,11 +16103,6 @@
"which": "^2.0.1"
}
},
- "css-b64-images": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
- "integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI="
- },
"css-loader": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz",
@@ -16065,6 +16282,14 @@
"d3-array": "^1.1.1"
}
},
+ "d3-delaunay": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
+ "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
+ "requires": {
+ "delaunator": "5"
+ }
+ },
"d3-dispatch": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
@@ -16291,6 +16516,7 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+ "dev": true,
"requires": {
"ms": "2.1.2"
}
@@ -16362,6 +16588,14 @@
"isobject": "^3.0.1"
}
},
+ "delaunator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+ "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+ "requires": {
+ "robust-predicates": "^3.0.0"
+ }
+ },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -16449,6 +16683,11 @@
"domelementtype": "^2.2.0"
}
},
+ "dompurify": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.4.tgz",
+ "integrity": "sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ=="
+ },
"domutils": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz",
@@ -16466,15 +16705,15 @@
"integrity": "sha512-5t2z51DzIsWDbTpwcJIvUlwxBbvcwdCApz0yb9ecKJwG155Xm92KMEZmHW1B0MzoXOKvFwdd0nPu5cpeVcvPHQ=="
},
"easymde": {
- "version": "2.15.0",
- "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.15.0.tgz",
- "integrity": "sha512-9jMRIVvKt1d0UjRN45yotUYECAM4xvw0TTAQw8sYDONP++keWJVnd8Xrn+V+vQEN/v9/X0SWEoo1rFSgCooGpw==",
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.16.1.tgz",
+ "integrity": "sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g==",
"requires": {
- "@types/codemirror": "0.0.109",
- "@types/marked": "^2.0.2",
- "codemirror": "^5.61.0",
+ "@types/codemirror": "^5.60.4",
+ "@types/marked": "^4.0.1",
+ "codemirror": "^5.63.1",
"codemirror-spell-checker": "1.1.2",
- "marked": "^2.0.3"
+ "marked": "^4.0.10"
}
},
"ecc-jsbn": {
@@ -16552,14 +16791,6 @@
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true
},
- "entity-decode": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/entity-decode/-/entity-decode-2.0.2.tgz",
- "integrity": "sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==",
- "requires": {
- "he": "^1.1.1"
- }
- },
"envinfo": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
@@ -18156,20 +18387,6 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
- "html-minifier": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
- "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
- "requires": {
- "camel-case": "^3.0.0",
- "clean-css": "^4.2.1",
- "commander": "^2.19.0",
- "he": "^1.2.0",
- "param-case": "^2.1.1",
- "relateurl": "^0.2.7",
- "uglify-js": "^3.5.1"
- }
- },
"html-tags": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz",
@@ -18314,6 +18531,11 @@
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
},
+ "internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="
+ },
"interpret": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
@@ -19698,11 +19920,6 @@
"integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==",
"dev": true
},
- "lower-case": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
- "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
- },
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -19759,9 +19976,9 @@
}
},
"marked": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz",
- "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA=="
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz",
+ "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw=="
},
"mathml-tag-names": {
"version": "2.1.3",
@@ -19958,21 +20175,289 @@
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
"mermaid": {
- "version": "8.10.1",
- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.10.1.tgz",
- "integrity": "sha512-KxwKEJDKy303TQdz5TQMFb/4u+gUL21CefUMGOfuigDh9powcYaNmuJ5BkHmO0jB3Y1z2zlsuKvHZ2CusWH5+A==",
+ "version": "8.13.8",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.13.8.tgz",
+ "integrity": "sha512-Z5v31rvo8P7BPTiGicdJl9BbzyUe9s5sXILK8sM1g7ijkagpfFjPtXZVsq5P1WlN8m/fUp2PPNXVF9SqeTM91w==",
"requires": {
"@braintree/sanitize-url": "^3.1.0",
- "d3": "^5.7.0",
- "dagre": "^0.8.4",
+ "d3": "^7.0.0",
+ "dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
- "entity-decode": "^2.0.2",
- "graphlib": "^2.1.7",
- "he": "^1.2.0",
- "khroma": "^1.1.0",
- "minify": "^4.1.1",
- "moment-mini": "^2.22.1",
- "stylis": "^3.5.2"
+ "dompurify": "2.3.4",
+ "graphlib": "^2.1.8",
+ "khroma": "^1.4.1",
+ "moment-mini": "^2.24.0",
+ "stylis": "^4.0.10"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+ },
+ "d3": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz",
+ "integrity": "sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw==",
+ "requires": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "3",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ }
+ },
+ "d3-array": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz",
+ "integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==",
+ "requires": {
+ "internmap": "1 - 2"
+ }
+ },
+ "d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="
+ },
+ "d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ }
+ },
+ "d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "requires": {
+ "d3-path": "1 - 3"
+ }
+ },
+ "d3-color": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
+ "integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw=="
+ },
+ "d3-contour": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz",
+ "integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==",
+ "requires": {
+ "d3-array": "2 - 3"
+ }
+ },
+ "d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="
+ },
+ "d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ }
+ },
+ "d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "requires": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ }
+ },
+ "d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
+ },
+ "d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "requires": {
+ "d3-dsv": "1 - 3"
+ }
+ },
+ "d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ }
+ },
+ "d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
+ },
+ "d3-geo": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz",
+ "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==",
+ "requires": {
+ "d3-array": "2.5.0 - 3"
+ }
+ },
+ "d3-hierarchy": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz",
+ "integrity": "sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA=="
+ },
+ "d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "requires": {
+ "d3-color": "1 - 3"
+ }
+ },
+ "d3-path": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
+ "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w=="
+ },
+ "d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="
+ },
+ "d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="
+ },
+ "d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="
+ },
+ "d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "requires": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ }
+ },
+ "d3-scale-chromatic": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+ "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
+ "requires": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ }
+ },
+ "d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
+ },
+ "d3-shape": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz",
+ "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==",
+ "requires": {
+ "d3-path": "1 - 3"
+ }
+ },
+ "d3-time": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
+ "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
+ "requires": {
+ "d3-array": "2 - 3"
+ }
+ },
+ "d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "requires": {
+ "d3-time": "1 - 3"
+ }
+ },
+ "d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="
+ },
+ "d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "requires": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ }
+ },
+ "d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ }
}
},
"micromark": {
@@ -20050,20 +20535,6 @@
}
}
},
- "minify": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/minify/-/minify-4.1.3.tgz",
- "integrity": "sha512-ykuscavxivSmVpcCzsXmsVTukWYLUUtPhHj0w2ILvHDGqC+hsuTCihBn9+PJBd58JNvWTNg9132J9nrrI2anzA==",
- "requires": {
- "clean-css": "^4.1.6",
- "css-b64-images": "~0.2.5",
- "debug": "^4.1.0",
- "html-minifier": "^4.0.0",
- "terser": "^4.0.0",
- "try-catch": "^2.0.0",
- "try-to-catch": "^1.0.2"
- }
- },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -20144,7 +20615,8 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "devOptional": true
},
"multimap": {
"version": "1.1.0",
@@ -20215,14 +20687,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
- "no-case": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
- "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
- "requires": {
- "lower-case": "^1.1.1"
- }
- },
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
@@ -20530,14 +20994,6 @@
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
- "param-case": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
- "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
- "requires": {
- "no-case": "^2.2.0"
- }
- },
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -21493,11 +21949,6 @@
"integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
"dev": true
},
- "relateurl": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
- "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
- },
"remark": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz",
@@ -21703,6 +22154,11 @@
"glob": "^7.1.3"
}
},
+ "robust-predicates": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
+ "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
+ },
"rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -22822,9 +23278,9 @@
}
},
"stylis": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
- "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
+ "version": "4.0.13",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+ "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
},
"sugarss": {
"version": "2.0.0",
@@ -23018,9 +23474,9 @@
"integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw=="
},
"tar": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
- "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
+ "version": "6.1.6",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz",
+ "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==",
"dev": true,
"requires": {
"chownr": "^2.0.0",
@@ -23041,23 +23497,6 @@
"supports-hyperlinks": "^2.0.0"
}
},
- "terser": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
- "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
- "requires": {
- "commander": "^2.20.0",
- "source-map": "~0.6.1",
- "source-map-support": "~0.5.12"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- }
- }
- },
"terser-webpack-plugin": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz",
@@ -23232,16 +23671,6 @@
"integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==",
"dev": true
},
- "try-catch": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/try-catch/-/try-catch-2.0.1.tgz",
- "integrity": "sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg=="
- },
- "try-to-catch": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-1.1.1.tgz",
- "integrity": "sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA=="
- },
"tsconfig-paths": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
@@ -23325,11 +23754,6 @@
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.0.tgz",
"integrity": "sha512-dELuLBVa2jvWdU/CHTKi2L/POYaRupv942k+vRsFXsM17acXesQGAiGCio82RW7fvcr7bkuD/Zj8XpUh6aPC2A=="
},
- "uglify-js": {
- "version": "3.13.6",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.6.tgz",
- "integrity": "sha512-rRprLwl8RVaS+Qvx3Wh5hPfPBn9++G6xkGlUupya0s5aDmNjI7z3lnRLB3u7sN4OmbB0pWgzhM9BEJyiWAwtAA=="
- },
"unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@@ -23460,11 +23884,6 @@
"integrity": "sha512-cC/jeGLoeMiu0NteTQsFZTQ9p1aLYs9uODV3HbS3Zx7fAk+dY0GsrUCC8C153szTH3X9NkPtYp0FpLLS2qIKMw==",
"dev": true
},
- "upper-case": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
- "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
- },
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
diff --git a/package.json b/package.json
index bbd5658031..007c913cdc 100644
--- a/package.json
+++ b/package.json
@@ -9,10 +9,10 @@
"@claviska/jquery-minicolors": "2.3.5",
"@primer/octicons": "13.0.0",
"add-asset-webpack-plugin": "2.0.1",
- "codemirror": "5.61.0",
+ "codemirror": "5.65.0",
"css-loader": "5.2.4",
"dropzone": "5.9.2",
- "easymde": "2.15.0",
+ "easymde": "2.16.1",
"esbuild-loader": "2.13.0",
"escape-goat": "4.0.0",
"fast-glob": "3.2.5",
@@ -22,7 +22,7 @@
"less": "4.1.1",
"less-loader": "8.1.1",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "8.10.1",
+ "mermaid": "8.13.8",
"mini-css-extract-plugin": "1.6.0",
"monaco-editor": "0.24.0",
"monaco-editor-webpack-plugin": "3.1.0",
diff --git a/public/img/svg/gitea-github.svg b/public/img/svg/gitea-github.svg
index b9c0621749..1634cbb119 100644
--- a/public/img/svg/gitea-github.svg
+++ b/public/img/svg/gitea-github.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go
index 1356276f07..d20c17fed3 100644
--- a/routers/api/v1/admin/org.go
+++ b/routers/api/v1/admin/org.go
@@ -106,6 +106,7 @@ func GetAllOrgs(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
+ Actor: ctx.User,
Type: models.UserTypeOrganization,
OrderBy: models.SearchOrderByAlphabetically,
ListOptions: listOptions,
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index 6bc9b849b1..86ec548a8e 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -9,12 +9,14 @@ import (
"errors"
"fmt"
"net/http"
+ "strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/password"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/user"
@@ -166,6 +168,10 @@ func EditUser(ctx *context.APIContext) {
}
if len(form.Password) != 0 {
+ if len(form.Password) < setting.MinPasswordLength {
+ ctx.Error(http.StatusBadRequest, "PasswordTooShort", fmt.Errorf("password must be at least %d characters", setting.MinPasswordLength))
+ return
+ }
if !password.IsComplexEnough(form.Password) {
err := errors.New("PasswordComplexity")
ctx.Error(http.StatusBadRequest, "PasswordComplexity", err)
@@ -199,12 +205,20 @@ func EditUser(ctx *context.APIContext) {
if form.FullName != nil {
u.FullName = *form.FullName
}
+ var emailChanged bool
if form.Email != nil {
- u.Email = *form.Email
- if len(u.Email) == 0 {
+ email := strings.TrimSpace(*form.Email)
+ if len(email) == 0 {
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("email is not allowed to be empty string"))
return
}
+ if err := models.ValidateEmail(email); err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+
+ emailChanged = !strings.EqualFold(u.Email, email)
+ u.Email = email
}
if form.Website != nil {
u.Website = *form.Website
@@ -243,7 +257,7 @@ func EditUser(ctx *context.APIContext) {
u.IsRestricted = *form.Restricted
}
- if err := models.UpdateUser(u); err != nil {
+ if err := models.UpdateUser(u, emailChanged); err != nil {
if models.IsErrEmailAlreadyUsed(err) || models.IsErrEmailInvalid(err) {
ctx.Error(http.StatusUnprocessableEntity, "", err)
} else {
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 4258ea5dc3..6f6609be3c 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -87,7 +87,6 @@ import (
"code.gitea.io/gitea/services/forms"
"gitea.com/go-chi/binding"
- "gitea.com/go-chi/session"
"github.com/go-chi/cors"
)
@@ -547,20 +546,10 @@ func bind(obj interface{}) http.HandlerFunc {
}
// Routes registers all v1 APIs routes to web application.
-func Routes() *web.Route {
+func Routes(sessioner func(next http.Handler) http.Handler) *web.Route {
var m = web.NewRoute()
- m.Use(session.Sessioner(session.Options{
- Provider: setting.SessionConfig.Provider,
- ProviderConfig: setting.SessionConfig.ProviderConfig,
- CookieName: setting.SessionConfig.CookieName,
- CookiePath: setting.SessionConfig.CookiePath,
- Gclifetime: setting.SessionConfig.Gclifetime,
- Maxlifetime: setting.SessionConfig.Maxlifetime,
- Secure: setting.SessionConfig.Secure,
- SameSite: setting.SessionConfig.SameSite,
- Domain: setting.SessionConfig.Domain,
- }))
+ m.Use(sessioner)
m.Use(securityHeaders())
if setting.CORSConfig.Enabled {
m.Use(cors.Handler(cors.Options{
@@ -569,6 +558,7 @@ func Routes() *web.Route {
//setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option
AllowedMethods: setting.CORSConfig.Methods,
AllowCredentials: setting.CORSConfig.AllowCredentials,
+ AllowedHeaders: []string{"Authorization", "X-CSRFToken", "X-Gitea-OTP"},
MaxAge: int(setting.CORSConfig.MaxAge.Seconds()),
}))
}
diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go
index 5c16594f89..860fec77cd 100644
--- a/routers/api/v1/org/org.go
+++ b/routers/api/v1/org/org.go
@@ -130,6 +130,7 @@ func GetAll(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
publicOrgs, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
+ Actor: ctx.User,
ListOptions: listOptions,
Type: models.UserTypeOrganization,
OrderBy: models.SearchOrderByAlphabetically,
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go
index e6427ea4f4..db98955bee 100644
--- a/routers/api/v1/repo/file.go
+++ b/routers/api/v1/repo/file.go
@@ -119,13 +119,15 @@ func GetArchive(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
repoPath := models.RepoPath(ctx.Params(":username"), ctx.Params(":reponame"))
- gitRepo, err := git.OpenRepository(repoPath)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
- return
+ if ctx.Repo.GitRepo == nil {
+ gitRepo, err := git.OpenRepository(repoPath)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
+ return
+ }
+ ctx.Repo.GitRepo = gitRepo
+ defer gitRepo.Close()
}
- ctx.Repo.GitRepo = gitRepo
- defer gitRepo.Close()
repo.Download(ctx.Context)
}
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 5a7d10b36f..72807afd09 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -752,6 +752,15 @@ func EditIssue(ctx *context.APIContext) {
}
}
if form.State != nil {
+ if issue.IsPull {
+ if pr, err := issue.GetPullRequest(); err != nil {
+ ctx.Error(http.StatusInternalServerError, "GetPullRequest", err)
+ return
+ } else if pr.HasMerged {
+ ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
+ return
+ }
+ }
issue.IsClosed = api.StateClosed == api.StateType(*form.State)
}
statusChangeComment, titleChanged, err := models.UpdateIssueByAPI(issue, ctx.User)
diff --git a/routers/api/v1/repo/issue_stopwatch.go b/routers/api/v1/repo/issue_stopwatch.go
index a4a2261b9a..2ab6457d7f 100644
--- a/routers/api/v1/repo/issue_stopwatch.go
+++ b/routers/api/v1/repo/issue_stopwatch.go
@@ -55,8 +55,8 @@ func StartIssueStopwatch(ctx *context.APIContext) {
return
}
- if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err)
+ if err := models.CreateIssueStopwatch(ctx.User, issue); err != nil {
+ ctx.Error(http.StatusInternalServerError, "CreateIssueStopwatch", err)
return
}
@@ -104,8 +104,8 @@ func StopIssueStopwatch(ctx *context.APIContext) {
return
}
- if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil {
- ctx.Error(http.StatusInternalServerError, "CreateOrStopIssueStopwatch", err)
+ if err := models.FinishIssueStopwatch(ctx.User, issue); err != nil {
+ ctx.Error(http.StatusInternalServerError, "FinishIssueStopwatch", err)
return
}
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 66bcabfd38..ba8bf2cc57 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -115,7 +115,7 @@ func ListPullRequests(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
return
}
- apiPrs[i] = convert.ToAPIPullRequest(prs[i])
+ apiPrs[i] = convert.ToAPIPullRequest(prs[i], ctx.User)
}
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
@@ -172,7 +172,7 @@ func GetPullRequest(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
return
}
- ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr))
+ ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr, ctx.User))
}
// DownloadPullDiff render a pull's raw diff
@@ -437,7 +437,7 @@ func CreatePullRequest(ctx *context.APIContext) {
}
log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
- ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
+ ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr, ctx.User))
}
// EditPullRequest does what it says
@@ -584,6 +584,10 @@ func EditPullRequest(ctx *context.APIContext) {
}
if form.State != nil {
+ if pr.HasMerged {
+ ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
+ return
+ }
issue.IsClosed = api.StateClosed == api.StateType(*form.State)
}
statusChangeComment, titleChanged, err := models.UpdateIssueByAPI(issue, ctx.User)
@@ -605,7 +609,7 @@ func EditPullRequest(ctx *context.APIContext) {
}
// change pull target branch
- if len(form.Base) != 0 && form.Base != pr.BaseBranch {
+ if !pr.HasMerged && len(form.Base) != 0 && form.Base != pr.BaseBranch {
if !ctx.Repo.GitRepo.IsBranchExist(form.Base) {
ctx.Error(http.StatusNotFound, "NewBaseBranchNotExist", fmt.Errorf("new base '%s' not exist", form.Base))
return
@@ -640,7 +644,7 @@ func EditPullRequest(ctx *context.APIContext) {
}
// TODO this should be 200, not 201
- ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr))
+ ctx.JSON(http.StatusCreated, convert.ToAPIPullRequest(pr, ctx.User))
}
// IsPullRequestMerged checks if a PR exists given an index
@@ -1130,6 +1134,9 @@ func UpdatePullRequest(ctx *context.APIContext) {
if models.IsErrMergeConflicts(err) {
ctx.Error(http.StatusConflict, "Update", "merge failed because of conflict")
return
+ } else if models.IsErrRebaseConflicts(err) {
+ ctx.Error(http.StatusConflict, "Update", "rebase failed because of conflict")
+ return
}
ctx.Error(http.StatusInternalServerError, "pull_service.Update", err)
return
@@ -1254,5 +1261,6 @@ func GetPullRequestCommits(ctx *context.APIContext) {
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalNumberOfCommits))
ctx.Header().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
+ ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link")
ctx.JSON(http.StatusOK, &apiCommits)
}
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index 1b52de55ff..19c5f743e1 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -115,7 +115,7 @@ func ListReleases(ctx *context.APIContext) {
opts := models.FindReleasesOptions{
ListOptions: listOptions,
- IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite,
+ IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite || ctx.Repo.UnitAccessMode(models.UnitTypeReleases) >= models.AccessModeWrite,
IncludeTags: false,
IsDraft: ctx.QueryOptionalBool("draft"),
IsPreRelease: ctx.QueryOptionalBool("pre-release"),
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index b671ef2435..5e0228fdbe 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -374,16 +374,21 @@ func Generate(ctx *context.APIContext) {
ctxUser := ctx.User
var err error
if form.Owner != ctxUser.Name {
- ctxUser, err = models.GetOrgByName(form.Owner)
+ ctxUser, err = models.GetUserByName(form.Owner)
if err != nil {
- if models.IsErrOrgNotExist(err) {
+ if models.IsErrUserNotExist(err) {
ctx.JSON(http.StatusNotFound, map[string]interface{}{
- "error": "request owner `" + form.Name + "` is not exist",
+ "error": "request owner `" + form.Owner + "` does not exist",
})
return
}
- ctx.Error(http.StatusInternalServerError, "GetOrgByName", err)
+ ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+ return
+ }
+
+ if !ctx.User.IsAdmin && !ctxUser.IsOrganization() {
+ ctx.Error(http.StatusForbidden, "", "Only admin can generate repository for other user.")
return
}
diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go
index 2e052aa4ff..11e56defe7 100644
--- a/routers/api/v1/repo/transfer.go
+++ b/routers/api/v1/repo/transfer.go
@@ -96,6 +96,11 @@ func Transfer(ctx *context.APIContext) {
}
}
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ ctx.Repo.GitRepo = nil
+ }
+
if err := repo_service.StartRepositoryTransfer(ctx.User, newOwner, ctx.Repo.Repository, teams); err != nil {
if models.IsErrRepoTransferInProgress(err) {
ctx.Error(http.StatusConflict, "CreatePendingRepositoryTransfer", err)
diff --git a/routers/api/v1/swagger/app.go b/routers/api/v1/swagger/app.go
index 8be2c85574..9783abe1a0 100644
--- a/routers/api/v1/swagger/app.go
+++ b/routers/api/v1/swagger/app.go
@@ -14,3 +14,10 @@ type swaggerResponseOAuth2Application struct {
// in:body
Body api.OAuth2Application `json:"body"`
}
+
+// AccessToken represents an API access token.
+// swagger:response AccessToken
+type swaggerResponseAccessToken struct {
+ // in:body
+ Body api.AccessToken `json:"body"`
+}
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index 0ae96a9203..3f0c6e2d52 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -164,6 +164,9 @@ type swaggerParameterBodies struct {
// in:body
CreateTagOption api.CreateTagOption
+ // in:body
+ CreateAccessTokenOption api.CreateAccessTokenOption
+
// in:body
UserSettingsOptions api.UserSettingsOptions
}
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index 9f355a8289..afd209f2f0 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -76,15 +76,10 @@ func CreateAccessToken(ctx *context.APIContext) {
// description: username of user
// type: string
// required: true
- // - name: accessToken
+ // - name: userCreateToken
// in: body
// schema:
- // type: object
- // required:
- // - name
- // properties:
- // name:
- // type: string
+ // "$ref": "#/definitions/CreateAccessTokenOption"
// responses:
// "201":
// "$ref": "#/responses/AccessToken"
diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go
index b4548e7443..b0f2a908ef 100644
--- a/routers/api/v1/user/settings.go
+++ b/routers/api/v1/user/settings.go
@@ -74,7 +74,7 @@ func UpdateUserSettings(ctx *context.APIContext) {
ctx.User.KeepActivityPrivate = *form.HideActivity
}
- if err := models.UpdateUser(ctx.User); err != nil {
+ if err := models.UpdateUser(ctx.User, false); err != nil {
ctx.InternalServerError(err)
return
}
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index ac543d597d..4ca0fc2439 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -56,15 +56,13 @@ func Search(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
- opts := &models.SearchUserOptions{
+ users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
Actor: ctx.User,
Keyword: strings.Trim(ctx.Query("q"), " "),
UID: ctx.QueryInt64("uid"),
Type: models.UserTypeIndividual,
ListOptions: listOptions,
- }
-
- users, maxResults, err := models.SearchUsers(opts)
+ })
if err != nil {
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"ok": false,
diff --git a/routers/common/middleware.go b/routers/common/middleware.go
index 1d96522dd9..dfb3d8a8e8 100644
--- a/routers/common/middleware.go
+++ b/routers/common/middleware.go
@@ -22,6 +22,9 @@ func Middlewares() []func(http.Handler) http.Handler {
var handlers = []func(http.Handler) http.Handler{
func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+ // First of all escape the URL RawPath to ensure that all routing is done using a correctly escaped URL
+ req.URL.RawPath = req.URL.EscapedPath()
+
next.ServeHTTP(context.NewResponse(resp), req)
})
},
diff --git a/routers/common/repo.go b/routers/common/repo.go
index 8d33fb07fb..e5b1a0493a 100644
--- a/routers/common/repo.go
+++ b/routers/common/repo.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/typesniffer"
+ "code.gitea.io/gitea/modules/util"
)
// ServeBlob download a git.Blob
@@ -42,8 +43,8 @@ func ServeBlob(ctx *context.Context, blob *git.Blob) error {
// ServeData download file from io.Reader
func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error {
buf := make([]byte, 1024)
- n, err := reader.Read(buf)
- if err != nil && err != io.EOF {
+ n, err := util.ReadAtMost(reader, buf)
+ if err != nil {
return err
}
if n >= 0 {
diff --git a/routers/init.go b/routers/init.go
index 3ee7c73572..9addd22261 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -40,6 +40,8 @@ import (
pull_service "code.gitea.io/gitea/services/pull"
"code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/services/webhook"
+
+ "gitea.com/go-chi/session"
)
// NewServices init new services
@@ -144,8 +146,20 @@ func NormalRoutes() *web.Route {
r.Use(middle)
}
- r.Mount("/", web_routers.Routes())
- r.Mount("/api/v1", apiv1.Routes())
+ sessioner := session.Sessioner(session.Options{
+ Provider: setting.SessionConfig.Provider,
+ ProviderConfig: setting.SessionConfig.ProviderConfig,
+ CookieName: setting.SessionConfig.CookieName,
+ CookiePath: setting.SessionConfig.CookiePath,
+ Gclifetime: setting.SessionConfig.Gclifetime,
+ Maxlifetime: setting.SessionConfig.Maxlifetime,
+ Secure: setting.SessionConfig.Secure,
+ SameSite: setting.SessionConfig.SameSite,
+ Domain: setting.SessionConfig.Domain,
+ })
+
+ r.Mount("/", web_routers.Routes(sessioner))
+ r.Mount("/api/v1", apiv1.Routes(sessioner))
r.Mount("/api/internal", private.Routes())
return r
}
diff --git a/routers/install/install.go b/routers/install/install.go
index ad985cf184..3059278c46 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -15,6 +15,7 @@ import (
"time"
"code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/generate"
@@ -207,7 +208,7 @@ func SubmitInstall(ctx *context.Context) {
}
// Set test engine.
- if err = models.NewTestEngine(); err != nil {
+ if err = models.NewInstallTestEngine(ctx, migrations.Migrate); err != nil {
if strings.Contains(err.Error(), `Unknown database type: sqlite3`) {
ctx.Data["Err_DbType"] = true
ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "https://docs.gitea.io/en-us/install-from-binary/"), tplInstall, &form)
diff --git a/routers/private/hook.go b/routers/private/hook.go
index 9f5579b6ae..f936325152 100644
--- a/routers/private/hook.go
+++ b/routers/private/hook.go
@@ -392,11 +392,6 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
})
return
}
- } else {
- log.Error("Unexpected ref: %s", refFullName)
- ctx.JSON(http.StatusInternalServerError, private.Response{
- Err: fmt.Sprintf("Unexpected ref: %s", refFullName),
- })
}
}
diff --git a/routers/private/manager_unix.go b/routers/private/manager_unix.go
index 60ae9b68e8..1738c06a05 100644
--- a/routers/private/manager_unix.go
+++ b/routers/private/manager_unix.go
@@ -1,9 +1,10 @@
-// +build !windows
-
// 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.
+//go:build !windows
+// +build !windows
+
package private
import (
diff --git a/routers/private/manager_windows.go b/routers/private/manager_windows.go
index f6c9b7ec8f..a8a477313f 100644
--- a/routers/private/manager_windows.go
+++ b/routers/private/manager_windows.go
@@ -1,9 +1,10 @@
-// +build windows
-
// 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.
+//go:build windows
+// +build windows
+
package private
import (
diff --git a/routers/private/serv.go b/routers/private/serv.go
index 6e39790eb5..5fcb9da414 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -278,7 +278,12 @@ func ServCommand(ctx *context.PrivateContext) {
}
// Permissions checking:
- if repoExist && (mode > models.AccessModeRead || repo.IsPrivate || setting.Service.RequireSignInView) {
+ if repoExist &&
+ (mode > models.AccessModeRead ||
+ repo.IsPrivate ||
+ owner.Visibility.IsPrivate() ||
+ (user != nil && user.IsRestricted) || // user will be nil if the key is a deploykey
+ setting.Service.RequireSignInView) {
if key.Type == models.KeyTypeDeploy {
if deployKey.Mode < mode {
ctx.JSON(http.StatusUnauthorized, private.ErrServCommand{
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index acccc516bb..31035c16dd 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -273,6 +273,11 @@ func EditUserPost(ctx *context.Context) {
ctx.RenderWithErr(errMsg, tplUserNew, &form)
return
}
+ if err := models.ValidateEmail(form.Email); err != nil {
+ ctx.Data["Err_Email"] = true
+ ctx.RenderWithErr(ctx.Tr("form.email_error"), tplUserNew, &form)
+ return
+ }
if u.Salt, err = models.GetUserSalt(); err != nil {
ctx.ServerError("UpdateUser", err)
return
@@ -307,7 +312,9 @@ func EditUserPost(ctx *context.Context) {
u.LoginName = form.LoginName
u.FullName = form.FullName
- u.Email = form.Email
+ email := strings.TrimSpace(form.Email)
+ emailChanged := !strings.EqualFold(u.Email, email)
+ u.Email = email
u.Website = form.Website
u.Location = form.Location
u.MaxRepoCreation = form.MaxRepoCreation
@@ -327,7 +334,7 @@ func EditUserPost(ctx *context.Context) {
u.ProhibitLogin = form.ProhibitLogin
}
- if err := models.UpdateUser(u); err != nil {
+ if err := models.UpdateUser(u, emailChanged); err != nil {
if models.IsErrEmailAlreadyUsed(err) {
ctx.Data["Err_Email"] = true
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form)
diff --git a/routers/web/base.go b/routers/web/base.go
index f079be51f0..5a0e528f93 100644
--- a/routers/web/base.go
+++ b/routers/web/base.go
@@ -14,7 +14,6 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
@@ -131,14 +130,6 @@ func Recovery() func(next http.Handler) http.Handler {
log.Error("%v", combinedErr)
sessionStore := session.GetSession(req)
- if sessionStore == nil {
- if setting.IsProd() {
- http.Error(w, http.StatusText(500), 500)
- } else {
- http.Error(w, combinedErr, 500)
- }
- return
- }
var lc = middleware.Locale(w, req)
var store = dataStore{
@@ -147,15 +138,7 @@ func Recovery() func(next http.Handler) http.Handler {
"i18n": lc,
}
- var user *models.User
- if apiContext := context.GetAPIContext(req); apiContext != nil {
- user = apiContext.User
- }
- if user == nil {
- if ctx := context.GetContext(req); ctx != nil {
- user = ctx.User
- }
- }
+ var user = context.GetContextUser(req)
if user == nil {
// Get user from session if logged in - do not attempt to sign-in
user = auth.SessionUser(sessionStore)
diff --git a/routers/web/org/org_labels.go b/routers/web/org/org_labels.go
index 26e232bcc9..c58a250b7a 100644
--- a/routers/web/org/org_labels.go
+++ b/routers/web/org/org_labels.go
@@ -94,7 +94,7 @@ func DeleteLabel(ctx *context.Context) {
func InitializeLabels(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.InitializeLabelsForm)
if ctx.HasError() {
- ctx.Redirect(ctx.Repo.RepoLink + "/labels")
+ ctx.Redirect(ctx.Org.OrgLink + "/labels")
return
}
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index e848939187..999a9b3566 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -65,7 +65,7 @@ func SettingsPost(ctx *context.Context) {
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
return
} else if err = models.ChangeUserName(org, form.Name); err != nil {
- if err == models.ErrUserNameIllegal {
+ if models.IsErrNameReserved(err) || models.IsErrNamePatternNotAllowed(err) {
ctx.Data["OrgName"] = true
ctx.RenderWithErr(ctx.Tr("form.illegal_username"), tplSettingsOptions, &form)
} else {
@@ -96,7 +96,7 @@ func SettingsPost(ctx *context.Context) {
visibilityChanged := form.Visibility != org.Visibility
org.Visibility = form.Visibility
- if err := models.UpdateUser(org); err != nil {
+ if err := models.UpdateUser(org, false); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go
index 5becbea271..a32bc3e140 100644
--- a/routers/web/repo/attachment.go
+++ b/routers/web/repo/attachment.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/upload"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/common"
)
@@ -43,10 +44,8 @@ func uploadAttachment(ctx *context.Context, allowedTypes string) {
defer file.Close()
buf := make([]byte, 1024)
- n, _ := file.Read(buf)
- if n > 0 {
- buf = buf[:n]
- }
+ n, _ := util.ReadAtMost(file, buf)
+ buf = buf[:n]
err = upload.Verify(buf, header.Filename, allowedTypes)
if err != nil {
diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index 4ade9e9a93..69574111b2 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -7,7 +7,6 @@ package repo
import (
"container/list"
"fmt"
- "html"
gotemplate "html/template"
"net/http"
"strings"
@@ -244,7 +243,7 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
br.PreviousSha = previousSha
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, previousSha, ctx.Repo.TreePath)
br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, part.Sha)
- br.CommitMessage = html.EscapeString(commit.CommitMessage)
+ br.CommitMessage = commit.CommitMessage
br.CommitSince = commitSince
}
diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go
index da72940144..d91a73d574 100644
--- a/routers/web/repo/branch.go
+++ b/routers/web/repo/branch.go
@@ -217,7 +217,7 @@ func loadBranches(ctx *context.Context, skip, limit int) ([]*Branch, int) {
branches = append(branches, deletedBranches...)
}
- return branches, totalNumOfBranches - 1
+ return branches, totalNumOfBranches
}
func loadOneBranch(ctx *context.Context, rawBranch *git.Branch, protectedBranches []*models.ProtectedBranch,
@@ -343,17 +343,15 @@ func CreateBranch(ctx *context.Context) {
var err error
if form.CreateTag {
- if ctx.Repo.IsViewTag {
- err = release_service.CreateNewTag(ctx.User, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName, "")
- } else {
- err = release_service.CreateNewTag(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName, "")
+ target := ctx.Repo.CommitID
+ if ctx.Repo.IsViewBranch {
+ target = ctx.Repo.BranchName
}
+ err = release_service.CreateNewTag(ctx.User, ctx.Repo.Repository, target, form.NewBranchName, "")
} else if ctx.Repo.IsViewBranch {
err = repo_module.CreateNewBranch(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName)
- } else if ctx.Repo.IsViewTag {
- err = repo_module.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName)
} else {
- err = repo_module.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName)
+ err = repo_module.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName)
}
if err != nil {
if models.IsErrTagAlreadyExists(err) {
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 45ef22f498..f555ecf3b5 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -80,7 +80,7 @@ func Commits(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["CommitCount"] = commitsCount
- ctx.Data["Branch"] = ctx.Repo.BranchName
+ ctx.Data["RefName"] = ctx.Repo.RefName
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
pager.SetDefaultParams(ctx)
@@ -156,7 +156,7 @@ func Graph(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["CommitCount"] = commitsCount
- ctx.Data["Branch"] = ctx.Repo.BranchName
+ ctx.Data["RefName"] = ctx.Repo.RefName
paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5)
paginator.AddParam(ctx, "mode", "Mode")
paginator.AddParam(ctx, "hide-pr-refs", "HidePRRefs")
@@ -205,7 +205,7 @@ func SearchCommits(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["CommitCount"] = commits.Len()
- ctx.Data["Branch"] = ctx.Repo.BranchName
+ ctx.Data["RefName"] = ctx.Repo.RefName
ctx.HTML(http.StatusOK, tplCommits)
}
@@ -219,8 +219,7 @@ func FileHistory(ctx *context.Context) {
return
}
- branchName := ctx.Repo.BranchName
- commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(branchName, fileName)
+ commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefName, fileName)
if err != nil {
ctx.ServerError("FileCommitsCount", err)
return
@@ -234,7 +233,7 @@ func FileHistory(ctx *context.Context) {
page = 1
}
- commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(branchName, fileName, page)
+ commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(ctx.Repo.RefName, fileName, page)
if err != nil {
ctx.ServerError("CommitsByFileAndRange", err)
return
@@ -248,7 +247,7 @@ func FileHistory(ctx *context.Context) {
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["FileName"] = fileName
ctx.Data["CommitCount"] = commitsCount
- ctx.Data["Branch"] = branchName
+ ctx.Data["RefName"] = ctx.Repo.RefName
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
pager.SetDefaultParams(ctx)
@@ -268,9 +267,8 @@ func Diff(ctx *context.Context) {
repoName := ctx.Repo.Repository.Name
commitID := ctx.Params(":sha")
var (
- gitRepo *git.Repository
- err error
- repoPath string
+ gitRepo *git.Repository
+ err error
)
if ctx.Data["PageIsWiki"] != nil {
@@ -279,10 +277,9 @@ func Diff(ctx *context.Context) {
ctx.ServerError("Repo.GitRepo.GetCommit", err)
return
}
- repoPath = ctx.Repo.Repository.WikiPath()
+ defer gitRepo.Close()
} else {
gitRepo = ctx.Repo.GitRepo
- repoPath = models.RepoPath(userName, repoName)
}
commit, err := gitRepo.GetCommit(commitID)
@@ -306,7 +303,7 @@ func Diff(ctx *context.Context) {
ctx.Data["CommitStatus"] = models.CalcCommitStatus(statuses)
ctx.Data["CommitStatuses"] = statuses
- diff, err := gitdiff.GetDiffCommitWithWhitespaceBehavior(repoPath,
+ diff, err := gitdiff.GetDiffCommitWithWhitespaceBehavior(gitRepo,
commitID, setting.Git.MaxGitDiffLines,
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles,
gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)))
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index fddfc4a63a..982a1b0008 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"html"
+ "io"
"net/http"
"path"
"path/filepath"
@@ -24,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/upload"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
)
@@ -103,30 +105,36 @@ func setCsvCompareContext(ctx *context.Context) {
errTooLarge := errors.New(ctx.Locale.Tr("repo.error.csv.too_large"))
- csvReaderFromCommit := func(c *git.Commit) (*csv.Reader, error) {
+ csvReaderFromCommit := func(c *git.Commit) (*csv.Reader, io.Closer, error) {
blob, err := c.GetBlobByPath(diffFile.Name)
if err != nil {
- return nil, err
+ return nil, nil, err
}
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < blob.Size() {
- return nil, errTooLarge
+ return nil, nil, errTooLarge
}
reader, err := blob.DataAsync()
if err != nil {
- return nil, err
+ return nil, nil, err
}
- defer reader.Close()
- return csv_module.CreateReaderAndGuessDelimiter(charset.ToUTF8WithFallbackReader(reader))
+ csvReader, err := csv_module.CreateReaderAndGuessDelimiter(charset.ToUTF8WithFallbackReader(reader))
+ return csvReader, reader, err
}
- baseReader, err := csvReaderFromCommit(baseCommit)
+ baseReader, baseBlobCloser, err := csvReaderFromCommit(baseCommit)
+ if baseBlobCloser != nil {
+ defer baseBlobCloser.Close()
+ }
if err == errTooLarge {
return CsvDiffResult{nil, err.Error()}
}
- headReader, err := csvReaderFromCommit(headCommit)
+ headReader, headBlobCloser, err := csvReaderFromCommit(headCommit)
+ if headBlobCloser != nil {
+ defer headBlobCloser.Close()
+ }
if err == errTooLarge {
return CsvDiffResult{nil, err.Error()}
}
@@ -525,7 +533,7 @@ func PrepareCompareDiff(
return true
}
- diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(models.RepoPath(headUser.Name, headRepo.Name),
+ diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(headGitRepo,
compareInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines,
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, whitespaceBehavior)
if err != nil {
@@ -567,6 +575,18 @@ func PrepareCompareDiff(
} else {
title = headBranch
}
+ if len(title) > 255 {
+ var trailer string
+ title, trailer = util.SplitStringAtByteN(title, 255)
+ if len(trailer) > 0 {
+ if ctx.Data["content"] != nil {
+ ctx.Data["content"] = fmt.Sprintf("%s\n\n%s", trailer, ctx.Data["content"])
+ } else {
+ ctx.Data["content"] = trailer + "\n"
+ }
+ }
+ }
+
ctx.Data["title"] = title
ctx.Data["Username"] = headUser.Name
ctx.Data["Reponame"] = headRepo.Name
@@ -605,11 +625,14 @@ func getBranchesAndTagsForRepo(user *models.User, repo *models.Repository) (bool
// CompareDiff show different from one commit to another commit
func CompareDiff(ctx *context.Context) {
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
-
+ defer func() {
+ if headGitRepo != nil {
+ headGitRepo.Close()
+ }
+ }()
if ctx.Written() {
return
}
- defer headGitRepo.Close()
nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch,
gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)))
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index 0f978c7b01..f7a8778344 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -114,7 +114,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
ctx.Data["FileName"] = blob.Name()
buf := make([]byte, 1024)
- n, _ := dataRc.Read(buf)
+ n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
// Only some file types are editable online as text.
@@ -747,7 +747,7 @@ func UploadFileToServer(ctx *context.Context) {
defer file.Close()
buf := make([]byte, 1024)
- n, _ := file.Read(buf)
+ n, _ := util.ReadAtMost(file, buf)
if n > 0 {
buf = buf[:n]
}
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 9639ea8201..4c55dc748b 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -51,17 +51,15 @@ const (
issueTemplateTitleKey = "IssueTemplateTitle"
)
-var (
- // IssueTemplateCandidates issue templates
- IssueTemplateCandidates = []string{
- "ISSUE_TEMPLATE.md",
- "issue_template.md",
- ".gitea/ISSUE_TEMPLATE.md",
- ".gitea/issue_template.md",
- ".github/ISSUE_TEMPLATE.md",
- ".github/issue_template.md",
- }
-)
+// IssueTemplateCandidates issue templates
+var IssueTemplateCandidates = []string{
+ "ISSUE_TEMPLATE.md",
+ "issue_template.md",
+ ".gitea/ISSUE_TEMPLATE.md",
+ ".gitea/issue_template.md",
+ ".github/ISSUE_TEMPLATE.md",
+ ".github/issue_template.md",
+}
// MustAllowUserComment checks to make sure if an issue is locked.
// If locked and user has permissions to write to the repository,
@@ -239,7 +237,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
}
}
- var issueList = models.IssueList(issues)
+ issueList := models.IssueList(issues)
approvalCounts, err := issueList.GetApprovalCounts()
if err != nil {
ctx.ServerError("ApprovalCounts", err)
@@ -419,13 +417,9 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos
}
handleTeamMentions(ctx)
- if ctx.Written() {
- return
- }
}
func retrieveProjects(ctx *context.Context, repo *models.Repository) {
-
var err error
ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{
@@ -784,7 +778,7 @@ func NewIssue(ctx *context.Context) {
milestoneID := ctx.QueryInt64("milestone")
if milestoneID > 0 {
- milestone, err := models.GetMilestoneByID(milestoneID)
+ milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
if err != nil {
log.Error("GetMilestoneByID: %d: %v", milestoneID, err)
} else {
@@ -868,7 +862,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull
// Check milestone.
milestoneID := form.MilestoneID
if milestoneID > 0 {
- ctx.Data["Milestone"], err = repo.GetMilestoneByID(milestoneID)
+ ctx.Data["Milestone"], err = models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
if err != nil {
ctx.ServerError("GetMilestoneByID", err)
return nil, nil, 0, 0
@@ -1022,7 +1016,7 @@ func commentTag(repo *models.Repository, poster *models.User, issue *models.Issu
return models.CommentTagNone, err
}
- if perm.CanWrite(models.UnitTypeCode) {
+ if perm.CanWriteIssuesOrPulls(issue.IsPull) {
return models.CommentTagWriter, nil
}
@@ -1138,6 +1132,7 @@ func ViewIssue(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, issue.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -1303,6 +1298,7 @@ func ViewIssue(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -1379,6 +1375,7 @@ func ViewIssue(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -1728,16 +1725,19 @@ func UpdateIssueContent(ctx *context.Context) {
return
}
- files := ctx.QueryStrings("files[]")
- if err := updateAttachments(issue, files); err != nil {
- ctx.ServerError("UpdateAttachments", err)
- return
+ // when update the request doesn't intend to update attachments (eg: change checkbox state), ignore attachment updates
+ if !ctx.QueryBool("ignore_attachments") {
+ if err := updateAttachments(issue, ctx.QueryStrings("files[]")); err != nil {
+ ctx.ServerError("UpdateAttachments", err)
+ return
+ }
}
content, err := markdown.RenderString(&markup.RenderContext{
URLPrefix: ctx.Query("context"),
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, issue.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -2128,13 +2128,6 @@ func UpdateCommentContent(ctx *context.Context) {
return
}
- if comment.Type == models.CommentTypeComment {
- if err := comment.LoadAttachments(); err != nil {
- ctx.ServerError("LoadAttachments", err)
- return
- }
- }
-
if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
ctx.Error(http.StatusForbidden)
return
@@ -2156,16 +2149,26 @@ func UpdateCommentContent(ctx *context.Context) {
return
}
- files := ctx.QueryStrings("files[]")
- if err := updateAttachments(comment, files); err != nil {
- ctx.ServerError("UpdateAttachments", err)
- return
+ if comment.Type == models.CommentTypeComment {
+ if err := comment.LoadAttachments(); err != nil {
+ ctx.ServerError("LoadAttachments", err)
+ return
+ }
+ }
+
+ // when the update request doesn't intend to update attachments (eg: change checkbox state), ignore attachment updates
+ if !ctx.QueryBool("ignore_attachments") {
+ if err := updateAttachments(comment, ctx.QueryStrings("files[]")); err != nil {
+ ctx.ServerError("UpdateAttachments", err)
+ return
+ }
}
content, err := markdown.RenderString(&markup.RenderContext{
URLPrefix: ctx.Query("context"),
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, comment.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -2440,7 +2443,7 @@ func filterXRefComments(ctx *context.Context, issue *models.Issue) error {
// GetIssueAttachments returns attachments for the issue
func GetIssueAttachments(ctx *context.Context) {
issue := GetActionIssue(ctx)
- var attachments = make([]*api.Attachment, len(issue.Attachments))
+ attachments := make([]*api.Attachment, len(issue.Attachments))
for i := 0; i < len(issue.Attachments); i++ {
attachments[i] = convert.ToReleaseAttachment(issue.Attachments[i])
}
@@ -2454,7 +2457,7 @@ func GetCommentAttachments(ctx *context.Context) {
ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
return
}
- var attachments = make([]*api.Attachment, 0)
+ attachments := make([]*api.Attachment, 0)
if comment.Type == models.CommentTypeComment {
if err := comment.LoadAttachments(); err != nil {
ctx.ServerError("LoadAttachments", err)
diff --git a/routers/web/repo/lfs.go b/routers/web/repo/lfs.go
index 173ffb773f..937d623e5a 100644
--- a/routers/web/repo/lfs.go
+++ b/routers/web/repo/lfs.go
@@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/typesniffer"
+ "code.gitea.io/gitea/modules/util"
)
const (
@@ -272,7 +273,7 @@ func LFSFileGet(ctx *context.Context) {
}
defer dataRc.Close()
buf := make([]byte, 1024)
- n, err := dataRc.Read(buf)
+ n, err := util.ReadAtMost(dataRc, buf)
if err != nil {
ctx.ServerError("Data", err)
return
@@ -297,10 +298,10 @@ func LFSFileGet(ctx *context.Context) {
break
}
- buf := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc))
+ rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc))
// Building code view blocks with line number on server side.
- fileContent, _ := ioutil.ReadAll(buf)
+ fileContent, _ := ioutil.ReadAll(rd)
var output bytes.Buffer
lines := strings.Split(string(fileContent), "\n")
diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go
index 521a856dae..91f3fb2e49 100644
--- a/routers/web/repo/migrate.go
+++ b/routers/web/repo/migrate.go
@@ -77,7 +77,14 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam
case migrations.IsTwoFactorAuthError(err):
ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tpl, form)
case models.IsErrReachLimitOfRepo(err):
- ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
+ var msg string
+ maxCreationLimit := owner.MaxCreationLimit()
+ if maxCreationLimit == 1 {
+ msg = ctx.Tr("repo.form.reach_limit_of_creation_1", maxCreationLimit)
+ } else {
+ msg = ctx.Tr("repo.form.reach_limit_of_creation_n", maxCreationLimit)
+ }
+ ctx.RenderWithErr(msg, tpl, form)
case models.IsErrRepoAlreadyExist(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go
index 4cdca38dd0..bf6027a930 100644
--- a/routers/web/repo/milestone.go
+++ b/routers/web/repo/milestone.go
@@ -89,6 +89,7 @@ func Milestones(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, m.Content)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -267,7 +268,7 @@ func DeleteMilestone(ctx *context.Context) {
// MilestoneIssuesAndPulls lists all the issues and pull requests of the milestone
func MilestoneIssuesAndPulls(ctx *context.Context) {
milestoneID := ctx.ParamsInt64(":id")
- milestone, err := models.GetMilestoneByID(milestoneID)
+ milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
if err != nil {
if models.IsErrMilestoneNotExist(err) {
ctx.NotFound("GetMilestoneByID", err)
@@ -282,6 +283,7 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, milestone.Content)
if err != nil {
ctx.ServerError("RenderString", err)
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index c7490893d5..1d95db4167 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -82,6 +82,7 @@ func Projects(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, projects[i].Description)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -324,6 +325,7 @@ func ViewProject(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, project.Description)
if err != nil {
ctx.ServerError("RenderString", err)
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index a299799647..42c5818e33 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -587,10 +587,9 @@ func ViewPullFiles(ctx *context.Context) {
pull := issue.PullRequest
var (
- diffRepoPath string
startCommitID string
endCommitID string
- gitRepo *git.Repository
+ gitRepo = ctx.Repo.GitRepo
)
var prInfo *git.CompareInfo
@@ -607,9 +606,6 @@ func ViewPullFiles(ctx *context.Context) {
return
}
- diffRepoPath = ctx.Repo.GitRepo.Path
- gitRepo = ctx.Repo.GitRepo
-
headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
if err != nil {
ctx.ServerError("GetRefCommitID", err)
@@ -623,7 +619,7 @@ func ViewPullFiles(ctx *context.Context) {
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["AfterCommitID"] = endCommitID
- diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(diffRepoPath,
+ diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(gitRepo,
startCommitID, endCommitID, setting.Git.MaxGitDiffLines,
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles,
gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)))
@@ -756,6 +752,21 @@ func UpdatePullRequest(ctx *context.Context) {
ctx.Flash.Error(flashError)
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
return
+ } else if models.IsErrRebaseConflicts(err) {
+ conflictError := err.(models.ErrRebaseConflicts)
+ flashError, err := ctx.HTMLString(string(tplAlertDetails), map[string]interface{}{
+ "Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)),
+ "Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"),
+ "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut),
+ })
+ if err != nil {
+ ctx.ServerError("UpdatePullRequest.HTMLString", err)
+ return
+ }
+ ctx.Flash.Error(flashError)
+ ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ return
+
}
ctx.Flash.Error(err.Error())
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
@@ -1001,10 +1012,14 @@ func CompareAndPullRequestPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
ctx.Data["PageIsComparePull"] = true
ctx.Data["IsDiffCompare"] = true
+ ctx.Data["IsRepoToolbarCommits"] = true
+ ctx.Data["RequireTribute"] = true
+ ctx.Data["RequireSimpleMDE"] = true
ctx.Data["RequireHighlightJS"] = true
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
+ ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
var (
repo = ctx.Repo.Repository
@@ -1012,10 +1027,14 @@ func CompareAndPullRequestPost(ctx *context.Context) {
)
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
+ defer func() {
+ if headGitRepo != nil {
+ headGitRepo.Close()
+ }
+ }()
if ctx.Written() {
return
}
- defer headGitRepo.Close()
labelIDs, assigneeIDs, milestoneID, _ := ValidateRepoMetas(ctx, *form, true)
if ctx.Written() {
@@ -1037,6 +1056,14 @@ func CompareAndPullRequestPost(ctx *context.Context) {
return
}
+ if len(form.Title) > 255 {
+ var trailer string
+ form.Title, trailer = util.SplitStringAtByteN(form.Title, 255)
+
+ form.Content = trailer + "\n\n" + form.Content
+ }
+ middleware.AssignForm(form, ctx.Data)
+
ctx.HTML(http.StatusOK, tplCompareDiff)
return
}
@@ -1280,30 +1307,16 @@ func DownloadPullPatch(ctx *context.Context) {
// DownloadPullDiffOrPatch render a pull's raw diff or patch
func DownloadPullDiffOrPatch(ctx *context.Context, patch bool) {
- issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
+ pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
- if models.IsErrIssueNotExist(err) {
- ctx.NotFound("GetIssueByIndex", err)
+ if models.IsErrPullRequestNotExist(err) {
+ ctx.NotFound("GetPullRequestByIndex", err)
} else {
- ctx.ServerError("GetIssueByIndex", err)
+ ctx.ServerError("GetPullRequestByIndex", err)
}
return
}
- // Return not found if it's not a pull request
- if !issue.IsPull {
- ctx.NotFound("DownloadPullDiff",
- fmt.Errorf("Issue is not a pull request"))
- return
- }
-
- if err = issue.LoadPullRequest(); err != nil {
- ctx.ServerError("LoadPullRequest", err)
- return
- }
-
- pr := issue.PullRequest
-
if err := pull_service.DownloadDiffOrPatch(pr, ctx, patch); err != nil {
ctx.ServerError("DownloadDiffOrPatch", err)
return
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 0665496d44..a56e693ce3 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -146,6 +146,7 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, r.Note)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -215,6 +216,7 @@ func SingleRelease(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, release.Note)
if err != nil {
ctx.ServerError("RenderString", err)
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 919fd4620d..4aa4f2211c 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -158,7 +158,14 @@ func Create(ctx *context.Context) {
func handleCreateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
switch {
case models.IsErrReachLimitOfRepo(err):
- ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
+ var msg string
+ maxCreationLimit := owner.MaxCreationLimit()
+ if maxCreationLimit == 1 {
+ msg = ctx.Tr("repo.form.reach_limit_of_creation_1", maxCreationLimit)
+ } else {
+ msg = ctx.Tr("repo.form.reach_limit_of_creation_n", maxCreationLimit)
+ }
+ ctx.RenderWithErr(msg, tpl, form)
case models.IsErrRepoAlreadyExist(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
@@ -319,6 +326,11 @@ func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error {
}
if accept {
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ ctx.Repo.GitRepo = nil
+ }
+
if err := repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil {
return err
}
@@ -371,7 +383,11 @@ func Download(ctx *context.Context) {
uri := ctx.Params("*")
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri)
if err != nil {
- ctx.ServerError("archiver_service.NewRequest", err)
+ if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
+ ctx.Error(http.StatusBadRequest, err.Error())
+ } else {
+ ctx.ServerError("archiver_service.NewRequest", err)
+ }
return
}
if aReq == nil {
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index 0a84f15bf0..16a1cb039b 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -533,15 +533,18 @@ func SettingsPost(ctx *context.Context) {
}
if !ctx.Repo.Owner.CanCreateRepo() {
- ctx.Flash.Error(ctx.Tr("repo.form.reach_limit_of_creation", ctx.User.MaxCreationLimit()))
+ maxCreationLimit := ctx.Repo.Owner.MaxCreationLimit()
+ if maxCreationLimit == 1 {
+ ctx.Flash.Error(ctx.Tr("repo.form.reach_limit_of_creation_1", maxCreationLimit))
+ } else {
+ ctx.Flash.Error(ctx.Tr("repo.form.reach_limit_of_creation_n", maxCreationLimit))
+ }
ctx.Redirect(repo.Link() + "/settings")
return
}
- repo.IsFork = false
- repo.ForkID = 0
- if err := models.UpdateRepository(repo, false); err != nil {
- log.Error("Unable to update repository %-v whilst converting from fork", repo)
+ if err := repository.ConvertForkToNormalRepository(repo); err != nil {
+ log.Error("Unable to convert repository %-v from fork. Error: %v", repo, err)
ctx.ServerError("Convert Fork", err)
return
}
@@ -742,6 +745,7 @@ func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.R
default:
ctx.ServerError("Unknown error", err)
}
+ return
}
ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, form)
}
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 21bd80c406..4000e49252 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -31,6 +31,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/typesniffer"
+ "code.gitea.io/gitea/modules/util"
)
const (
@@ -264,7 +265,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
defer dataRc.Close()
buf := make([]byte, 1024)
- n, _ := dataRc.Read(buf)
+ n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
st := typesniffer.DetectContentType(buf)
@@ -299,7 +300,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
defer dataRc.Close()
buf = make([]byte, 1024)
- n, err = dataRc.Read(buf)
+ n, err = util.ReadAtMost(dataRc, buf)
if err != nil {
ctx.ServerError("Data", err)
return
@@ -353,6 +354,10 @@ func renderDirectory(ctx *context.Context, treeLink string) {
}
} else {
ctx.Data["IsRenderedHTML"] = true
+ buf, err = ioutil.ReadAll(rd)
+ if err != nil {
+ log.Error("ReadAll failed: %v", err)
+ }
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(string(buf)), "\n", `
`,
)
@@ -401,7 +406,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
}
defer dataRc.Close()
- ctx.Data["Title"] = ctx.Data["Title"].(string) + " - " + ctx.Repo.TreePath + " at " + ctx.Repo.BranchName
+ ctx.Data["Title"] = ctx.Data["Title"].(string) + " - " + ctx.Repo.TreePath + " at " + ctx.Repo.RefName
fileSize := blob.Size()
ctx.Data["FileIsSymlink"] = entry.IsLink()
@@ -409,7 +414,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
buf := make([]byte, 1024)
- n, _ := dataRc.Read(buf)
+ n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
st := typesniffer.DetectContentType(buf)
@@ -441,10 +446,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
defer dataRc.Close()
buf = make([]byte, 1024)
- n, err = dataRc.Read(buf)
- // Error EOF don't mean there is an error, it just means we read to
- // the end
- if err != nil && err != io.EOF {
+ n, err = util.ReadAtMost(dataRc, buf)
+ if err != nil {
ctx.ServerError("Data", err)
return
}
@@ -616,6 +619,13 @@ func Home(ctx *context.Context) {
if ctx.Repo.Repository.IsBeingCreated() {
task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
if err != nil {
+ if models.IsErrTaskDoesNotExist(err) {
+ ctx.Data["Repo"] = ctx.Repo
+ ctx.Data["CloneAddr"] = ""
+ ctx.Data["Failed"] = true
+ ctx.HTML(http.StatusOK, tplMigrating)
+ return
+ }
ctx.ServerError("models.GetMigratingTask", err)
return
}
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index 5271fe9b4a..523fc6ed06 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -135,6 +135,9 @@ func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName strin
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
wikiRepo, commit, err := findWikiRepoCommit(ctx)
if err != nil {
+ if wikiRepo != nil {
+ wikiRepo.Close()
+ }
if !git.IsErrNotExist(err) {
ctx.ServerError("GetBranchCommit", err)
}
@@ -222,6 +225,9 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
var buf strings.Builder
if err := markdown.Render(rctx, bytes.NewReader(data), &buf); err != nil {
+ if wikiRepo != nil {
+ wikiRepo.Close()
+ }
ctx.ServerError("Render", err)
return nil, nil
}
@@ -229,6 +235,9 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
buf.Reset()
if err := markdown.Render(rctx, bytes.NewReader(sidebarContent), &buf); err != nil {
+ if wikiRepo != nil {
+ wikiRepo.Close()
+ }
ctx.ServerError("Render", err)
return nil, nil
}
@@ -237,6 +246,9 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
buf.Reset()
if err := markdown.Render(rctx, bytes.NewReader(footerContent), &buf); err != nil {
+ if wikiRepo != nil {
+ wikiRepo.Close()
+ }
ctx.ServerError("Render", err)
return nil, nil
}
@@ -380,17 +392,14 @@ func Wiki(ctx *context.Context) {
}
wikiRepo, entry := renderViewPage(ctx)
- if ctx.Written() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- return
- }
defer func() {
if wikiRepo != nil {
wikiRepo.Close()
}
}()
+ if ctx.Written() {
+ return
+ }
if entry == nil {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.HTML(http.StatusOK, tplWikiStart)
@@ -425,17 +434,15 @@ func WikiRevision(ctx *context.Context) {
}
wikiRepo, entry := renderRevisionPage(ctx)
- if ctx.Written() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- return
- }
defer func() {
if wikiRepo != nil {
wikiRepo.Close()
}
}()
+
+ if ctx.Written() {
+ return
+ }
if entry == nil {
ctx.Data["Title"] = ctx.Tr("repo.wiki")
ctx.HTML(http.StatusOK, tplWikiStart)
@@ -472,13 +479,14 @@ func WikiPages(ctx *context.Context) {
}
return
}
-
- entries, err := commit.ListEntries()
- if err != nil {
+ defer func() {
if wikiRepo != nil {
wikiRepo.Close()
}
+ }()
+ entries, err := commit.ListEntries()
+ if err != nil {
ctx.ServerError("ListEntries", err)
return
}
@@ -489,10 +497,6 @@ func WikiPages(ctx *context.Context) {
}
c, err := wikiRepo.GetCommitByPath(entry.Name())
if err != nil {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
-
ctx.ServerError("GetCommit", err)
return
}
@@ -501,10 +505,6 @@ func WikiPages(ctx *context.Context) {
if models.IsErrWikiInvalidFileName(err) {
continue
}
- if wikiRepo != nil {
- wikiRepo.Close()
- }
-
ctx.ServerError("WikiFilenameToName", err)
return
}
@@ -516,21 +516,25 @@ func WikiPages(ctx *context.Context) {
}
ctx.Data["Pages"] = pages
- defer func() {
- if wikiRepo != nil {
- wikiRepo.Close()
- }
- }()
ctx.HTML(http.StatusOK, tplWikiPages)
}
// WikiRaw outputs raw blob requested by user (image for example)
func WikiRaw(ctx *context.Context) {
wikiRepo, commit, err := findWikiRepoCommit(ctx)
- if err != nil {
+ defer func() {
if wikiRepo != nil {
+ wikiRepo.Close()
+ }
+ }()
+
+ if err != nil {
+ if git.IsErrNotExist(err) {
+ ctx.NotFound("findEntryForFile", nil)
return
}
+ ctx.ServerError("findEntryForfile", err)
+ return
}
providedPath := ctx.Params("*")
@@ -546,9 +550,7 @@ func WikiRaw(ctx *context.Context) {
if entry == nil {
// Try to find a wiki page with that name
- if strings.HasSuffix(providedPath, ".md") {
- providedPath = providedPath[:len(providedPath)-3]
- }
+ providedPath = strings.TrimSuffix(providedPath, ".md")
wikiPath := wiki_service.NameToFilename(providedPath)
entry, err = findEntryForFile(commit, wikiPath)
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index 8934a6619f..bcdb8023ac 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -81,7 +81,7 @@ func TestWiki(t *testing.T) {
Wiki(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
assert.EqualValues(t, "Home", ctx.Data["Title"])
- assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name"}, ctx.Data["Pages"])
+ assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
}
func TestWikiPages(t *testing.T) {
@@ -91,7 +91,7 @@ func TestWikiPages(t *testing.T) {
test.LoadRepo(t, ctx, 1)
WikiPages(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
- assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name"}, ctx.Data["Pages"])
+ assertPagesMetas(t, []string{"Home", "Page With Image", "Page With Spaced Name", "Unescaped File"}, ctx.Data["Pages"])
}
func TestNewWiki(t *testing.T) {
diff --git a/routers/web/user/auth.go b/routers/web/user/auth.go
index 4095d2956e..72b4d82d30 100644
--- a/routers/web/user/auth.go
+++ b/routers/web/user/auth.go
@@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/password"
"code.gitea.io/gitea/modules/recaptcha"
+ "code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
@@ -87,6 +88,10 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
isSucceed = true
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ return false, fmt.Errorf("unable to RegenerateSession: Error: %w", err)
+ }
+
// Set session IDs
if err := ctx.Session.Set("uid", u.ID); err != nil {
return false, err
@@ -98,10 +103,33 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
return false, err
}
+ if err := resetLocale(ctx, u); err != nil {
+ return false, err
+ }
+
middleware.DeleteCSRFCookie(ctx.Resp)
return true, nil
}
+func resetLocale(ctx *context.Context, u *models.User) error {
+ // Language setting of the user overwrites the one previously set
+ // If the user does not have a locale set, we save the current one.
+ if len(u.Language) == 0 {
+ u.Language = ctx.Locale.Language()
+ if err := models.UpdateUserCols(u, "language"); err != nil {
+ return err
+ }
+ }
+
+ middleware.SetLocaleCookie(ctx.Resp, u.Language, 0)
+
+ if ctx.Locale.Language() != u.Language {
+ ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
+ }
+
+ return nil
+}
+
func checkAutoLogin(ctx *context.Context) bool {
// Check auto-login.
isSucceed, err := AutoSignIn(ctx)
@@ -212,6 +240,11 @@ func SignInPost(ctx *context.Context) {
return
}
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ ctx.ServerError("UserSignIn: Unable to set regenerate session", err)
+ return
+ }
+
// User needs to use 2FA, save data and redirect to 2FA page.
if err := ctx.Session.Set("twofaUid", u.ID); err != nil {
ctx.ServerError("UserSignIn: Unable to set twofaUid in session", err)
@@ -372,6 +405,9 @@ func TwoFactorScratchPost(ctx *context.Context) {
}
handleSignInFull(ctx, u, remember, false)
+ if ctx.Written() {
+ return
+ }
ctx.Flash.Info(ctx.Tr("auth.twofa_scratch_used"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
return
@@ -451,7 +487,7 @@ func U2FSign(ctx *context.Context) {
for _, reg := range regs {
r, err := reg.Parse()
if err != nil {
- log.Fatal("parsing u2f registration: %v", err)
+ log.Error("parsing u2f registration: %v", err)
continue
}
newCounter, authErr := r.Authenticate(*signResp, *challenge, reg.Counter)
@@ -482,6 +518,9 @@ func U2FSign(ctx *context.Context) {
}
}
redirect := handleSignInFull(ctx, user, remember, false)
+ if ctx.Written() {
+ return
+ }
if redirect == "" {
redirect = setting.AppSubURL + "/"
}
@@ -494,7 +533,11 @@ func U2FSign(ctx *context.Context) {
// This handles the final part of the sign-in process of the user.
func handleSignIn(ctx *context.Context, u *models.User, remember bool) {
- handleSignInFull(ctx, u, remember, true)
+ redirect := handleSignInFull(ctx, u, remember, true)
+ if ctx.Written() {
+ return
+ }
+ ctx.Redirect(redirect)
}
func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyRedirect bool) string {
@@ -505,6 +548,12 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
setting.CookieRememberName, u.Name, days)
}
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ ctx.ServerError("RegenerateSession", err)
+ return setting.AppSubURL + "/"
+ }
+
+ // Delete the openid, 2fa and linkaccount data
_ = ctx.Session.Delete("openid_verified_uri")
_ = ctx.Session.Delete("openid_signin_remember")
_ = ctx.Session.Delete("openid_determined_email")
@@ -528,13 +577,17 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
if len(u.Language) == 0 {
u.Language = ctx.Locale.Language()
if err := models.UpdateUserCols(u, "language"); err != nil {
- log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language))
+ ctx.ServerError("UpdateUserCols Language", fmt.Errorf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language))
return setting.AppSubURL + "/"
}
}
middleware.SetLocaleCookie(ctx.Resp, u.Language, 0)
+ if ctx.Locale.Language() != u.Language {
+ ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
+ }
+
// Clear whatever CSRF has right now, force to generate a new one
middleware.DeleteCSRFCookie(ctx.Resp)
@@ -617,7 +670,7 @@ func SignInOAuthCallback(ctx *context.Context) {
}
if u == nil {
- if !(setting.Service.DisableRegistration || setting.Service.AllowOnlyInternalRegistration) && setting.OAuth2Client.EnableAutoRegistration {
+ if !setting.Service.AllowOnlyInternalRegistration && setting.OAuth2Client.EnableAutoRegistration {
// create new user with details from oauth2 provider
var missingFields []string
if gothUser.UserID == "" {
@@ -674,6 +727,11 @@ func getUserName(gothUser *goth.User) string {
}
func showLinkingLogin(ctx *context.Context, gothUser goth.User) {
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ ctx.ServerError("RegenerateSession", err)
+ return
+ }
+
if err := ctx.Session.Set("linkAccountGothUser", gothUser); err != nil {
log.Error("Error setting linkAccountGothUser in session: %v", err)
}
@@ -713,6 +771,11 @@ func handleOAuth2SignIn(ctx *context.Context, u *models.User, gothUser goth.User
return
}
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ ctx.ServerError("RegenerateSession", err)
+ return
+ }
+
if err := ctx.Session.Set("uid", u.ID); err != nil {
log.Error("Error setting uid in session: %v", err)
}
@@ -738,6 +801,11 @@ func handleOAuth2SignIn(ctx *context.Context, u *models.User, gothUser goth.User
log.Error("UpdateExternalUser failed: %v", err)
}
+ if err := resetLocale(ctx, u); err != nil {
+ ctx.ServerError("resetLocale", err)
+ return
+ }
+
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 {
middleware.DeleteRedirectToCookie(ctx.Resp)
ctx.RedirectToFirst(redirectTo)
@@ -748,6 +816,11 @@ func handleOAuth2SignIn(ctx *context.Context, u *models.User, gothUser goth.User
return
}
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ ctx.ServerError("RegenerateSession", err)
+ return
+ }
+
// User needs to use 2FA, save data and redirect to 2FA page.
if err := ctx.Session.Set("twofaUid", u.ID); err != nil {
log.Error("Error setting twofaUid in session: %v", err)
@@ -937,6 +1010,11 @@ func linkAccount(ctx *context.Context, u *models.User, gothUser goth.User, remem
return
}
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ ctx.ServerError("RegenerateSession", err)
+ return
+ }
+
// User needs to use 2FA, save data and redirect to 2FA page.
if err := ctx.Session.Set("twofaUid", u.ID); err != nil {
log.Error("Error setting twofaUid in session: %v", err)
@@ -1074,7 +1152,7 @@ func LinkAccountPostRegister(ctx *context.Context) {
return
}
- ctx.Redirect(setting.AppSubURL + "/user/login")
+ handleSignIn(ctx, u, false)
}
// HandleSignOut resets the session and sets the cookies
@@ -1216,7 +1294,7 @@ func SignUpPost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("auth.sign_up_successful"))
- handleSignInFull(ctx, u, false, true)
+ handleSignIn(ctx, u, false)
}
// createAndHandleCreatedUser calls createUserInContext and
@@ -1437,6 +1515,13 @@ func handleAccountActivation(ctx *context.Context, user *models.User) {
log.Trace("User activated: %s", user.Name)
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ log.Error("Unable to regenerate session for user: %-v with email: %s: %v", user, user.Email, err)
+ ctx.ServerError("ActivateUserEmail", err)
+ return
+ }
+
+ // Set session IDs
if err := ctx.Session.Set("uid", user.ID); err != nil {
log.Error("Error setting uid in session[%s]: %v", ctx.Session.ID(), err)
}
@@ -1447,6 +1532,11 @@ func handleAccountActivation(ctx *context.Context, user *models.User) {
log.Error("Error storing session[%s]: %v", ctx.Session.ID(), err)
}
+ if err := resetLocale(ctx, user); err != nil {
+ ctx.ServerError("resetLocale", err)
+ return
+ }
+
ctx.Flash.Success(ctx.Tr("auth.account_activated"))
ctx.Redirect(setting.AppSubURL + "/")
}
@@ -1704,11 +1794,14 @@ func ResetPasswdPost(ctx *context.Context) {
handleSignInFull(ctx, u, remember, false)
ctx.Flash.Info(ctx.Tr("auth.twofa_scratch_used"))
+ if ctx.Written() {
+ return
+ }
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
return
}
- handleSignInFull(ctx, u, remember, true)
+ handleSignIn(ctx, u, remember)
}
// MustChangePassword renders the page to change a user's password
@@ -1748,8 +1841,23 @@ func MustChangePasswordPost(ctx *context.Context) {
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplMustChangePassword, &form)
return
}
+ if !password.IsComplexEnough(form.Password) {
+ ctx.Data["Err_Password"] = true
+ ctx.RenderWithErr(password.BuildComplexityError(ctx), tplMustChangePassword, &form)
+ return
+ }
+ pwned, err := password.IsPwned(ctx, form.Password)
+ if pwned {
+ ctx.Data["Err_Password"] = true
+ errMsg := ctx.Tr("auth.password_pwned")
+ if err != nil {
+ log.Error(err.Error())
+ errMsg = ctx.Tr("auth.password_pwned_err")
+ }
+ ctx.RenderWithErr(errMsg, tplMustChangePassword, &form)
+ return
+ }
- var err error
if err = u.SetPassword(form.Password); err != nil {
ctx.ServerError("UpdateUser", err)
return
diff --git a/routers/web/user/auth_openid.go b/routers/web/user/auth_openid.go
index 1a73a08c48..74f87424ed 100644
--- a/routers/web/user/auth_openid.go
+++ b/routers/web/user/auth_openid.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/hcaptcha"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/recaptcha"
+ "code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
@@ -231,6 +232,11 @@ func signInOpenIDVerify(ctx *context.Context) {
}
}
+ if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil {
+ ctx.ServerError("RegenerateSession", err)
+ return
+ }
+
if err := ctx.Session.Set("openid_verified_uri", id); err != nil {
log.Error("signInOpenIDVerify: Could not set openid_verified_uri in session: %v", err)
}
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index d3fc36c730..4777e6c6bc 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -49,7 +49,7 @@ func getDashboardContextUser(ctx *context.Context) *models.User {
}
ctx.Data["ContextUser"] = ctxUser
- orgs, err := models.GetUserOrgsList(ctx.User.ID)
+ orgs, err := models.GetUserOrgsList(ctx.User)
if err != nil {
ctx.ServerError("GetUserOrgsList", err)
return nil
@@ -272,6 +272,7 @@ func Milestones(ctx *context.Context) {
milestones[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
URLPrefix: milestones[i].Repo.Link(),
Metas: milestones[i].Repo.ComposeMetas(),
+ Ctx: ctx,
}, milestones[i].Content)
if err != nil {
ctx.ServerError("RenderString", err)
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index 523e945db9..851af5d647 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -50,6 +50,7 @@ func Notifications(c *context.Context) {
return
}
if c.QueryBool("div-only") {
+ c.Data["SequenceNumber"] = c.Query("sequence-number")
c.HTML(http.StatusOK, tplNotificationDiv)
return
}
@@ -175,6 +176,7 @@ func NotificationStatusPost(c *context.Context) {
return
}
c.Data["Link"] = setting.AppURL + "notifications"
+ c.Data["SequenceNumber"] = c.Req.PostFormValue("sequence-number")
c.HTML(http.StatusOK, tplNotificationDiv)
}
diff --git a/routers/web/user/oauth.go b/routers/web/user/oauth.go
index 72295b4447..a281048563 100644
--- a/routers/web/user/oauth.go
+++ b/routers/web/user/oauth.go
@@ -24,7 +24,7 @@ import (
"code.gitea.io/gitea/services/forms"
"gitea.com/go-chi/binding"
- "github.com/dgrijalva/jwt-go"
+ "github.com/golang-jwt/jwt"
jsoniter "github.com/json-iterator/go"
)
@@ -187,7 +187,7 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, signingKey oauth2.JWTSign
ErrorDescription: "cannot find application",
}
}
- err = app.LoadUser()
+ user, err := models.GetUserByID(grant.UserID)
if err != nil {
if models.IsErrUserNotExist(err) {
return nil, &AccessTokenError{
@@ -212,17 +212,17 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, signingKey oauth2.JWTSign
Nonce: grant.Nonce,
}
if grant.ScopeContains("profile") {
- idToken.Name = app.User.FullName
- idToken.PreferredUsername = app.User.Name
- idToken.Profile = app.User.HTMLURL()
- idToken.Picture = app.User.AvatarLink()
- idToken.Website = app.User.Website
- idToken.Locale = app.User.Language
- idToken.UpdatedAt = app.User.UpdatedUnix
+ idToken.Name = user.FullName
+ idToken.PreferredUsername = user.Name
+ idToken.Profile = user.HTMLURL()
+ idToken.Picture = user.AvatarLink()
+ idToken.Website = user.Website
+ idToken.Locale = user.Language
+ idToken.UpdatedAt = user.UpdatedUnix
}
if grant.ScopeContains("email") {
- idToken.Email = app.User.Email
- idToken.EmailVerified = app.User.IsActive
+ idToken.Email = user.Email
+ idToken.EmailVerified = user.IsActive
}
signedIDToken, err = idToken.SignToken(signingKey)
diff --git a/routers/web/user/oauth_test.go b/routers/web/user/oauth_test.go
new file mode 100644
index 0000000000..bc7e6ff209
--- /dev/null
+++ b/routers/web/user/oauth_test.go
@@ -0,0 +1,75 @@
+// Copyright 2021 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 user
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/auth/oauth2"
+
+ "github.com/golang-jwt/jwt"
+ "github.com/stretchr/testify/assert"
+)
+
+func createAndParseToken(t *testing.T, grant *models.OAuth2Grant) *models.OIDCToken {
+ signingKey, err := oauth2.CreateJWTSingingKey("HS256", make([]byte, 32))
+ assert.NoError(t, err)
+ assert.NotNil(t, signingKey)
+ oauth2.DefaultSigningKey = signingKey
+
+ response, terr := newAccessTokenResponse(grant, signingKey)
+ assert.Nil(t, terr)
+ assert.NotNil(t, response)
+
+ parsedToken, err := jwt.ParseWithClaims(response.IDToken, &models.OIDCToken{}, func(token *jwt.Token) (interface{}, error) {
+ assert.NotNil(t, token.Method)
+ assert.Equal(t, signingKey.SigningMethod().Alg(), token.Method.Alg())
+ return signingKey.VerifyKey(), nil
+ })
+ assert.NoError(t, err)
+ assert.True(t, parsedToken.Valid)
+
+ oidcToken, ok := parsedToken.Claims.(*models.OIDCToken)
+ assert.True(t, ok)
+ assert.NotNil(t, oidcToken)
+
+ return oidcToken
+}
+
+func TestNewAccessTokenResponse_OIDCToken(t *testing.T) {
+ assert.NoError(t, models.PrepareTestDatabase())
+
+ grants, err := models.GetOAuth2GrantsByUserID(3)
+ assert.NoError(t, err)
+ assert.Len(t, grants, 1)
+
+ // Scopes: openid
+ oidcToken := createAndParseToken(t, grants[0])
+ assert.Empty(t, oidcToken.Name)
+ assert.Empty(t, oidcToken.PreferredUsername)
+ assert.Empty(t, oidcToken.Profile)
+ assert.Empty(t, oidcToken.Picture)
+ assert.Empty(t, oidcToken.Website)
+ assert.Empty(t, oidcToken.UpdatedAt)
+ assert.Empty(t, oidcToken.Email)
+ assert.False(t, oidcToken.EmailVerified)
+
+ user := models.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
+ grants, err = models.GetOAuth2GrantsByUserID(user.ID)
+ assert.NoError(t, err)
+ assert.Len(t, grants, 1)
+
+ // Scopes: openid profile email
+ oidcToken = createAndParseToken(t, grants[0])
+ assert.Equal(t, user.FullName, oidcToken.Name)
+ assert.Equal(t, user.Name, oidcToken.PreferredUsername)
+ assert.Equal(t, user.HTMLURL(), oidcToken.Profile)
+ assert.Equal(t, user.AvatarLink(), oidcToken.Picture)
+ assert.Equal(t, user.Website, oidcToken.Website)
+ assert.Equal(t, user.UpdatedUnix, oidcToken.UpdatedAt)
+ assert.Equal(t, user.Email, oidcToken.Email)
+ assert.Equal(t, user.IsActive, oidcToken.EmailVerified)
+}
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 631ca21135..885fece39f 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -124,6 +124,7 @@ func Profile(ctx *context.Context) {
URLPrefix: ctx.Repo.RepoLink,
Metas: map[string]string{"mode": "document"},
GitRepo: ctx.Repo.GitRepo,
+ Ctx: ctx,
}, ctxUser.Description)
if err != nil {
ctx.ServerError("RenderString", err)
@@ -320,7 +321,7 @@ func Action(ctx *context.Context) {
}
var err error
- switch ctx.Params(":action") {
+ switch ctx.Query("action") {
case "follow":
err = models.FollowUser(ctx.User.ID, u.ID)
case "unfollow":
@@ -328,7 +329,7 @@ func Action(ctx *context.Context) {
}
if err != nil {
- ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
+ ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Query("action")), err)
return
}
diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go
index b2d918784f..509418717d 100644
--- a/routers/web/user/setting/adopt.go
+++ b/routers/web/user/setting/adopt.go
@@ -27,7 +27,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
action := ctx.Query("action")
ctxUser := ctx.User
- root := filepath.Join(models.UserPath(ctxUser.LowerName))
+ root := models.UserPath(ctxUser.LowerName)
// check not a repo
has, err := models.IsRepositoryExist(ctxUser, dir)
diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go
index d875d84a76..e87ee1dabf 100644
--- a/routers/web/user/setting/keys.go
+++ b/routers/web/user/setting/keys.go
@@ -208,7 +208,7 @@ func DeleteKey(ctx *context.Context) {
return
}
if external {
- ctx.Flash.Error(ctx.Tr("setting.ssh_externally_managed"))
+ ctx.Flash.Error(ctx.Tr("settings.ssh_externally_managed"))
ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
return
}
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index 682f920578..8ea726a499 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -246,7 +246,7 @@ func Repos(ctx *context.Context) {
repoNames := make([]string, 0, setting.UI.Admin.UserPagingNum)
repos := map[string]*models.Repository{}
// We're going to iterate by pagesize.
- root := filepath.Join(models.UserPath(ctxUser.Name))
+ root := models.UserPath(ctxUser.Name)
if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
if os.IsNotExist(err) {
diff --git a/routers/web/user/setting/security_twofa.go b/routers/web/user/setting/security_twofa.go
index 7b08a05939..b2530f52fe 100644
--- a/routers/web/user/setting/security_twofa.go
+++ b/routers/web/user/setting/security_twofa.go
@@ -32,7 +32,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
t, err := models.GetTwoFactorByUID(ctx.User.ID)
if err != nil {
if models.IsErrTwoFactorNotEnrolled(err) {
- ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
+ ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
}
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
@@ -62,7 +62,7 @@ func DisableTwoFactor(ctx *context.Context) {
t, err := models.GetTwoFactorByUID(ctx.User.ID)
if err != nil {
if models.IsErrTwoFactorNotEnrolled(err) {
- ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
+ ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
}
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
@@ -150,7 +150,7 @@ func EnrollTwoFactor(ctx *context.Context) {
if t != nil {
// already enrolled - we should redirect back!
log.Warn("Trying to re-enroll %-v in twofa when already enrolled", ctx.User)
- ctx.Flash.Error(ctx.Tr("setting.twofa_is_enrolled"))
+ ctx.Flash.Error(ctx.Tr("settings.twofa_is_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
return
}
@@ -175,7 +175,7 @@ func EnrollTwoFactorPost(ctx *context.Context) {
t, err := models.GetTwoFactorByUID(ctx.User.ID)
if t != nil {
// already enrolled
- ctx.Flash.Error(ctx.Tr("setting.twofa_is_enrolled"))
+ ctx.Flash.Error(ctx.Tr("settings.twofa_is_enrolled"))
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
return
}
diff --git a/routers/web/user/task.go b/routers/web/user/task.go
index 8e7b66ef95..edbc2a89f2 100644
--- a/routers/web/user/task.go
+++ b/routers/web/user/task.go
@@ -6,6 +6,7 @@ package user
import (
"net/http"
+ "strconv"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@@ -16,6 +17,12 @@ import (
func TaskStatus(ctx *context.Context) {
task, opts, err := models.GetMigratingTaskByID(ctx.ParamsInt64("task"), ctx.User.ID)
if err != nil {
+ if models.IsErrTaskDoesNotExist(err) {
+ ctx.JSON(http.StatusNotFound, map[string]interface{}{
+ "error": "task `" + strconv.FormatInt(ctx.ParamsInt64("task"), 10) + "` does not exist",
+ })
+ return
+ }
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": err,
})
diff --git a/routers/web/web.go b/routers/web/web.go
index 7a47f479c0..2ba7a44c11 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -39,7 +39,6 @@ import (
_ "code.gitea.io/gitea/modules/session"
"gitea.com/go-chi/captcha"
- "gitea.com/go-chi/session"
"github.com/NYTimes/gziphandler"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
@@ -71,7 +70,7 @@ func CorsHandler() func(next http.Handler) http.Handler {
}
// Routes returns all web routes
-func Routes() *web.Route {
+func Routes(sessioner func(next http.Handler) http.Handler) *web.Route {
routes := web.NewRoute()
routes.Use(public.AssetsHandler(&public.Options{
@@ -80,17 +79,7 @@ func Routes() *web.Route {
CorsHandler: CorsHandler(),
}))
- routes.Use(session.Sessioner(session.Options{
- Provider: setting.SessionConfig.Provider,
- ProviderConfig: setting.SessionConfig.ProviderConfig,
- CookieName: setting.SessionConfig.CookieName,
- CookiePath: setting.SessionConfig.CookiePath,
- Gclifetime: setting.SessionConfig.Gclifetime,
- Maxlifetime: setting.SessionConfig.Maxlifetime,
- Secure: setting.SessionConfig.Secure,
- SameSite: setting.SessionConfig.SameSite,
- Domain: setting.SessionConfig.Domain,
- }))
+ routes.Use(sessioner)
routes.Use(Recovery())
@@ -461,9 +450,7 @@ func RegisterRoutes(m *web.Route) {
m.Get("/attachments/{uuid}", repo.GetAttachment)
}, ignSignIn)
- m.Group("/{username}", func() {
- m.Post("/action/{action}", user.Action)
- }, reqSignIn)
+ m.Post("/{username}", reqSignIn, user.Action)
if !setting.IsProd() {
m.Get("/template/*", dev.TemplatePreview)
@@ -822,9 +809,14 @@ func RegisterRoutes(m *web.Route) {
}
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
})
- m.Get("/attachments/{uuid}", repo.GetAttachment)
+
}, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader)
+ // to maintain compatibility with old attachments
+ m.Group("/{username}/{reponame}", func() {
+ m.Get("/attachments/{uuid}", repo.GetAttachment)
+ }, ignSignIn, context.RepoAssignment, context.UnitTypes())
+
m.Group("/{username}/{reponame}", func() {
m.Post("/topics", repo.TopicsPost)
}, context.RepoAssignment, context.RepoMustNotBeArchived(), reqRepoAdmin)
@@ -868,7 +860,7 @@ func RegisterRoutes(m *web.Route) {
m.Get("/_pages", repo.WikiPages)
m.Get("/{page}/_revision", repo.WikiRevision)
m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
- m.Get("/commit/{sha:[a-f0-9]{7,40}}.{:patch|diff}", repo.RawDiff)
+ m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff)
m.Group("", func() {
m.Combo("/_new").Get(repo.NewWiki).
@@ -1006,17 +998,17 @@ func RegisterRoutes(m *web.Route) {
}, ignSignInAndCsrf, lfsServerEnabled)
m.Group("", func() {
- m.Post("/git-upload-pack", repo.ServiceUploadPack)
- m.Post("/git-receive-pack", repo.ServiceReceivePack)
- m.Get("/info/refs", repo.GetInfoRefs)
- m.Get("/HEAD", repo.GetTextFile("HEAD"))
- m.Get("/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
- m.Get("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
- m.Get("/objects/info/packs", repo.GetInfoPacks)
- m.Get("/objects/info/{file:[^/]*}", repo.GetTextFile(""))
- m.Get("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
- m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
- m.Get("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
+ m.PostOptions("/git-upload-pack", repo.ServiceUploadPack)
+ m.PostOptions("/git-receive-pack", repo.ServiceReceivePack)
+ m.GetOptions("/info/refs", repo.GetInfoRefs)
+ m.GetOptions("/HEAD", repo.GetTextFile("HEAD"))
+ m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
+ m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
+ m.GetOptions("/objects/info/packs", repo.GetInfoPacks)
+ m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile(""))
+ m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
+ m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
+ m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
}, ignSignInAndCsrf)
m.Head("/tasks/trigger", repo.TriggerTask)
@@ -1033,4 +1025,9 @@ func RegisterRoutes(m *web.Route) {
if setting.API.EnableSwagger {
m.Get("/swagger.v1.json", SwaggerV1Json)
}
+ m.NotFound(func(w http.ResponseWriter, req *http.Request) {
+ ctx := context.GetContext(req)
+ ctx.NotFound("", nil)
+ })
+
}
diff --git a/services/archiver/archiver.go b/services/archiver/archiver.go
index 00c0281306..cf9c49764b 100644
--- a/services/archiver/archiver.go
+++ b/services/archiver/archiver.go
@@ -38,6 +38,22 @@ type ArchiveRequest struct {
// the way to 64.
var shaRegex = regexp.MustCompile(`^[0-9a-f]{4,64}$`)
+// ErrUnknownArchiveFormat request archive format is not supported
+type ErrUnknownArchiveFormat struct {
+ RequestFormat string
+}
+
+// Error implements error
+func (err ErrUnknownArchiveFormat) Error() string {
+ return fmt.Sprintf("unknown format: %s", err.RequestFormat)
+}
+
+// Is implements error
+func (ErrUnknownArchiveFormat) Is(err error) bool {
+ _, ok := err.(ErrUnknownArchiveFormat)
+ return ok
+}
+
// NewRequest creates an archival request, based on the URI. The
// resulting ArchiveRequest is suitable for being passed to ArchiveRepository()
// if it's determined that the request still needs to be satisfied.
@@ -55,7 +71,7 @@ func NewRequest(repoID int64, repo *git.Repository, uri string) (*ArchiveRequest
ext = ".tar.gz"
r.Type = git.TARGZ
default:
- return nil, fmt.Errorf("Unknown format: %s", uri)
+ return nil, ErrUnknownArchiveFormat{RequestFormat: uri}
}
r.refName = strings.TrimSuffix(uri, ext)
@@ -132,9 +148,11 @@ func doArchive(r *ArchiveRequest) (*models.RepoArchiver, error) {
if err == nil {
if archiver.Status == models.RepoArchiverGenerating {
archiver.Status = models.RepoArchiverReady
- return archiver, models.UpdateRepoArchiverStatus(ctx, archiver)
+ if err = models.UpdateRepoArchiverStatus(ctx, archiver); err != nil {
+ return nil, err
+ }
}
- return archiver, nil
+ return archiver, commiter.Commit()
}
if !errors.Is(err, os.ErrNotExist) {
diff --git a/services/archiver/archiver_test.go b/services/archiver/archiver_test.go
index 3f3f369987..f9aff7aec3 100644
--- a/services/archiver/archiver_test.go
+++ b/services/archiver/archiver_test.go
@@ -5,6 +5,7 @@
package archiver
import (
+ "errors"
"path/filepath"
"testing"
"time"
@@ -19,10 +20,6 @@ func TestMain(m *testing.M) {
models.MainTest(m, filepath.Join("..", ".."))
}
-func waitForCount(t *testing.T, num int) {
-
-}
-
func TestArchive_Basic(t *testing.T) {
assert.NoError(t, models.PrepareTestDatabase())
@@ -83,11 +80,8 @@ func TestArchive_Basic(t *testing.T) {
inFlight[2] = secondReq
ArchiveRepository(zipReq)
- waitForCount(t, 1)
ArchiveRepository(tgzReq)
- waitForCount(t, 2)
ArchiveRepository(secondReq)
- waitForCount(t, 3)
// Make sure sending an unprocessed request through doesn't affect the queue
// count.
@@ -132,3 +126,8 @@ func TestArchive_Basic(t *testing.T) {
assert.NotEqual(t, zipReq.GetArchiveName(), tgzReq.GetArchiveName())
assert.NotEqual(t, zipReq.GetArchiveName(), secondReq.GetArchiveName())
}
+
+func TestErrUnknownArchiveFormat(t *testing.T) {
+ var err = ErrUnknownArchiveFormat{RequestFormat: "master"}
+ assert.True(t, errors.Is(err, ErrUnknownArchiveFormat{}))
+}
diff --git a/services/auth/auth.go b/services/auth/auth.go
index 5492a8b74e..8424a72912 100644
--- a/services/auth/auth.go
+++ b/services/auth/auth.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware"
)
@@ -80,11 +81,11 @@ func isAttachmentDownload(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET"
}
-var gitRawPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|raw/)`)
+var gitRawReleasePathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/))`)
var lfsPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`)
-func isGitRawOrLFSPath(req *http.Request) bool {
- if gitRawPathRe.MatchString(req.URL.Path) {
+func isGitRawReleaseOrLFSPath(req *http.Request) bool {
+ if gitRawReleasePathRe.MatchString(req.URL.Path) {
return true
}
if setting.LFS.StartServer {
@@ -95,6 +96,14 @@ func isGitRawOrLFSPath(req *http.Request) bool {
// handleSignIn clears existing session variables and stores new ones for the specified user object
func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *models.User) {
+ // We need to regenerate the session...
+ newSess, err := session.RegenerateSession(resp, req)
+ if err != nil {
+ log.Error(fmt.Sprintf("Error regenerating session: %v", err))
+ } else {
+ sess = newSess
+ }
+
_ = sess.Delete("openid_verified_uri")
_ = sess.Delete("openid_signin_remember")
_ = sess.Delete("openid_determined_email")
@@ -103,7 +112,7 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
_ = sess.Delete("twofaRemember")
_ = sess.Delete("u2fChallenge")
_ = sess.Delete("linkAccount")
- err := sess.Set("uid", user.ID)
+ err = sess.Set("uid", user.ID)
if err != nil {
log.Error(fmt.Sprintf("Error setting session: %v", err))
}
diff --git a/services/auth/auth_test.go b/services/auth/auth_test.go
index f6b43835f4..b0d23bb4e9 100644
--- a/services/auth/auth_test.go
+++ b/services/auth/auth_test.go
@@ -83,6 +83,10 @@ func Test_isGitRawOrLFSPath(t *testing.T) {
"/owner/repo/commit/123456789012345678921234567893124567894",
false,
},
+ {
+ "/owner/repo/releases/download/tag/repo.tar.gz",
+ true,
+ },
}
lfsTests := []string{
"/owner/repo/info/lfs/",
@@ -102,11 +106,11 @@ func Test_isGitRawOrLFSPath(t *testing.T) {
t.Run(tt.path, func(t *testing.T) {
req, _ := http.NewRequest("POST", "http://localhost"+tt.path, nil)
setting.LFS.StartServer = false
- if got := isGitRawOrLFSPath(req); got != tt.want {
+ if got := isGitRawReleaseOrLFSPath(req); got != tt.want {
t.Errorf("isGitOrLFSPath() = %v, want %v", got, tt.want)
}
setting.LFS.StartServer = true
- if got := isGitRawOrLFSPath(req); got != tt.want {
+ if got := isGitRawReleaseOrLFSPath(req); got != tt.want {
t.Errorf("isGitOrLFSPath() = %v, want %v", got, tt.want)
}
})
@@ -115,11 +119,11 @@ func Test_isGitRawOrLFSPath(t *testing.T) {
t.Run(tt, func(t *testing.T) {
req, _ := http.NewRequest("POST", tt, nil)
setting.LFS.StartServer = false
- if got := isGitRawOrLFSPath(req); got != setting.LFS.StartServer {
- t.Errorf("isGitOrLFSPath(%q) = %v, want %v, %v", tt, got, setting.LFS.StartServer, gitRawPathRe.MatchString(tt))
+ if got := isGitRawReleaseOrLFSPath(req); got != setting.LFS.StartServer {
+ t.Errorf("isGitOrLFSPath(%q) = %v, want %v, %v", tt, got, setting.LFS.StartServer, gitRawReleasePathRe.MatchString(tt))
}
setting.LFS.StartServer = true
- if got := isGitRawOrLFSPath(req); got != setting.LFS.StartServer {
+ if got := isGitRawReleaseOrLFSPath(req); got != setting.LFS.StartServer {
t.Errorf("isGitOrLFSPath(%q) = %v, want %v", tt, got, setting.LFS.StartServer)
}
})
diff --git a/services/auth/basic.go b/services/auth/basic.go
index 0bce4f1d06..36684bb10d 100644
--- a/services/auth/basic.go
+++ b/services/auth/basic.go
@@ -49,7 +49,7 @@ func (b *Basic) Free() error {
// Returns nil if header is empty or validation fails.
func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
// Basic authentication should only fire on API, Download or on Git or LFSPaths
- if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawOrLFSPath(req) {
+ if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {
return nil
}
diff --git a/services/auth/placeholder.go b/services/auth/placeholder.go
index 50e3061885..d9a0ceae7c 100644
--- a/services/auth/placeholder.go
+++ b/services/auth/placeholder.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
package auth
diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go
index f958d28c9a..d1487718f3 100644
--- a/services/auth/reverseproxy.go
+++ b/services/auth/reverseproxy.go
@@ -78,7 +78,7 @@ func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store Da
}
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
- if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawOrLFSPath(req) {
+ if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {
if sess != nil && (sess.Get("uid") == nil || sess.Get("uid").(int64) != user.ID) {
handleSignIn(w, req, sess, user)
}
diff --git a/services/gitdiff/csv.go b/services/gitdiff/csv.go
index f4310d8772..099660b172 100644
--- a/services/gitdiff/csv.go
+++ b/services/gitdiff/csv.go
@@ -21,10 +21,12 @@ type TableDiffCellType uint8
// TableDiffCellType possible values.
const (
- TableDiffCellEqual TableDiffCellType = iota + 1
+ TableDiffCellUnchanged TableDiffCellType = iota + 1
TableDiffCellChanged
TableDiffCellAdd
TableDiffCellDel
+ TableDiffCellMovedUnchanged
+ TableDiffCellMovedChanged
)
// TableDiffCell represents a cell of a TableDiffRow
@@ -53,6 +55,9 @@ type csvReader struct {
eof bool
}
+// ErrorUndefinedCell is for when a row, column coordinates do not exist in the CSV
+var ErrorUndefinedCell = errors.New("undefined cell")
+
// createCsvReader creates a csvReader and fills the buffer
func createCsvReader(reader *csv.Reader, bufferRowCount int) (*csvReader, error) {
csv := &csvReader{reader: reader}
@@ -70,7 +75,7 @@ func createCsvReader(reader *csv.Reader, bufferRowCount int) (*csvReader, error)
// GetRow gets a row from the buffer if present or advances the reader to the requested row. On the end of the file only nil gets returned.
func (csv *csvReader) GetRow(row int) ([]string, error) {
- if row < len(csv.buffer) {
+ if row < len(csv.buffer) && row >= 0 {
return csv.buffer[row], nil
}
if csv.eof {
@@ -131,7 +136,11 @@ func createCsvDiffSingle(reader *csv.Reader, celltype TableDiffCellType) ([]*Tab
}
cells := make([]*TableDiffCell, len(row))
for j := 0; j < len(row); j++ {
- cells[j] = &TableDiffCell{LeftCell: row[j], Type: celltype}
+ if celltype == TableDiffCellDel {
+ cells[j] = &TableDiffCell{LeftCell: row[j], Type: celltype}
+ } else {
+ cells[j] = &TableDiffCell{RightCell: row[j], Type: celltype}
+ }
}
rows = append(rows, &TableDiffRow{RowIdx: i, Cells: cells})
i++
@@ -141,185 +150,267 @@ func createCsvDiffSingle(reader *csv.Reader, celltype TableDiffCellType) ([]*Tab
}
func createCsvDiff(diffFile *DiffFile, baseReader *csv.Reader, headReader *csv.Reader) ([]*TableDiffSection, error) {
- a, err := createCsvReader(baseReader, maxRowsToInspect)
+ // Given the baseReader and headReader, we are going to create CSV Reader for each, baseCSVReader and b respectively
+ baseCSVReader, err := createCsvReader(baseReader, maxRowsToInspect)
+ if err != nil {
+ return nil, err
+ }
+ headCSVReader, err := createCsvReader(headReader, maxRowsToInspect)
if err != nil {
return nil, err
}
- b, err := createCsvReader(headReader, maxRowsToInspect)
- if err != nil {
- return nil, err
+ // Initializing the mappings of base to head (a2bColMap) and head to base (b2aColMap) columns
+ a2bColMap, b2aColMap := getColumnMapping(baseCSVReader, headCSVReader)
+
+ // Determines how many cols there will be in the diff table, which includes deleted columns from base and added columns to base
+ numDiffTableCols := len(a2bColMap) + countUnmappedColumns(b2aColMap)
+ if len(a2bColMap) < len(b2aColMap) {
+ numDiffTableCols = len(b2aColMap) + countUnmappedColumns(a2bColMap)
}
- a2b, b2a := getColumnMapping(a, b)
-
- columns := len(a2b) + countUnmappedColumns(b2a)
- if len(a2b) < len(b2a) {
- columns = len(b2a) + countUnmappedColumns(a2b)
- }
-
- createDiffRow := func(aline int, bline int) (*TableDiffRow, error) {
- cells := make([]*TableDiffCell, columns)
-
- if aline == 0 || bline == 0 {
- var (
- row []string
- celltype TableDiffCellType
- err error
- )
- if bline == 0 {
- row, err = a.GetRow(aline - 1)
- celltype = TableDiffCellDel
- } else {
- row, err = b.GetRow(bline - 1)
- celltype = TableDiffCellAdd
- }
+ // createDiffTableRow takes the row # of the `a` line and `b` line of a diff (starting from 1), 0 if the line doesn't exist (undefined)
+ // in the base or head respectively.
+ // Returns a TableDiffRow which has the row index
+ createDiffTableRow := func(aLineNum int, bLineNum int) (*TableDiffRow, error) {
+ // diffTableCells is a row of the diff table. It will have a cells for added, deleted, changed, and unchanged content, thus either
+ // the same size as the head table or bigger
+ diffTableCells := make([]*TableDiffCell, numDiffTableCols)
+ var bRow *[]string
+ if bLineNum > 0 {
+ row, err := headCSVReader.GetRow(bLineNum - 1)
if err != nil {
return nil, err
}
- if row == nil {
- return nil, nil
+ bRow = &row
+ }
+ var aRow *[]string
+ if aLineNum > 0 {
+ row, err := baseCSVReader.GetRow(aLineNum - 1)
+ if err != nil {
+ return nil, err
}
- for i := 0; i < len(row); i++ {
- cells[i] = &TableDiffCell{LeftCell: row[i], Type: celltype}
- }
- return &TableDiffRow{RowIdx: bline, Cells: cells}, nil
+ aRow = &row
}
-
- arow, err := a.GetRow(aline - 1)
- if err != nil {
- return nil, err
- }
- brow, err := b.GetRow(bline - 1)
- if err != nil {
- return nil, err
- }
- if len(arow) == 0 && len(brow) == 0 {
+ if aRow == nil && bRow == nil {
+ // No content
return nil, nil
}
- for i := 0; i < len(a2b); i++ {
- acell, _ := getCell(arow, i)
- if a2b[i] == unmappedColumn {
- cells[i] = &TableDiffCell{LeftCell: acell, Type: TableDiffCellDel}
- } else {
- bcell, _ := getCell(brow, a2b[i])
+ aIndex := 0 // tracks where we are in the a2bColMap
+ bIndex := 0 // tracks where we are in the b2aColMap
+ colsAdded := 0 // incremented whenever we found a column was added
+ colsDeleted := 0 // incrememted whenever a column was deleted
- celltype := TableDiffCellChanged
- if acell == bcell {
- celltype = TableDiffCellEqual
+ // We loop until both the aIndex and bIndex are greater than their col map, which then we are done
+ for aIndex < len(a2bColMap) || bIndex < len(b2aColMap) {
+ // Starting from where aIndex is currently pointing, we see if the map is -1 (dleeted) and if is, create column to note that, increment, and look at the next aIndex
+ for aIndex < len(a2bColMap) && a2bColMap[aIndex] == -1 && (bIndex >= len(b2aColMap) || aIndex <= bIndex) {
+ var aCell string
+ if aRow != nil {
+ if cell, err := getCell(*aRow, aIndex); err != nil {
+ if err != ErrorUndefinedCell {
+ return nil, err
+ }
+ } else {
+ aCell = cell
+ }
+ }
+ diffTableCells[bIndex+colsDeleted] = &TableDiffCell{LeftCell: aCell, Type: TableDiffCellDel}
+ aIndex++
+ colsDeleted++
+ }
+
+ // aIndex is now pointing to a column that also exists in b, or is at the end of a2bColMap. If the former,
+ // we can just increment aIndex until it points to a -1 column or one greater than the current bIndex
+ for aIndex < len(a2bColMap) && a2bColMap[aIndex] != -1 {
+ aIndex++
+ }
+
+ // Starting from where bIndex is currently pointing, we see if the map is -1 (added) and if is, create column to note that, increment, and look at the next aIndex
+ for bIndex < len(b2aColMap) && b2aColMap[bIndex] == -1 && (aIndex >= len(a2bColMap) || bIndex < aIndex) {
+ var bCell string
+ cellType := TableDiffCellAdd
+ if bRow != nil {
+ if cell, err := getCell(*bRow, bIndex); err != nil {
+ if err != ErrorUndefinedCell {
+ return nil, err
+ }
+ } else {
+ bCell = cell
+ }
+ } else {
+ cellType = TableDiffCellDel
+ }
+ diffTableCells[bIndex+colsDeleted] = &TableDiffCell{RightCell: bCell, Type: cellType}
+ bIndex++
+ colsAdded++
+ }
+
+ // aIndex is now pointing to a column that also exists in a, or is at the end of b2aColMap. If the former,
+ // we get the a col and b col values (if they exist), figure out if they are the same or not, and if the column moved, and add it to the diff table
+ for bIndex < len(b2aColMap) && b2aColMap[bIndex] != -1 && (aIndex >= len(a2bColMap) || bIndex < aIndex) {
+ var diffTableCell TableDiffCell
+
+ var aCell *string
+ // get the aCell value if the aRow exists
+ if aRow != nil {
+ if cell, err := getCell(*aRow, b2aColMap[bIndex]); err != nil {
+ if err != ErrorUndefinedCell {
+ return nil, err
+ }
+ } else {
+ aCell = &cell
+ diffTableCell.LeftCell = cell
+ }
+ } else {
+ diffTableCell.Type = TableDiffCellAdd
}
- cells[i] = &TableDiffCell{LeftCell: acell, RightCell: bcell, Type: celltype}
- }
- }
- for i := 0; i < len(b2a); i++ {
- if b2a[i] == unmappedColumn {
- bcell, _ := getCell(brow, i)
- cells[i] = &TableDiffCell{LeftCell: bcell, Type: TableDiffCellAdd}
+ var bCell *string
+ // get the bCell value if the bRow exists
+ if bRow != nil {
+ if cell, err := getCell(*bRow, bIndex); err != nil {
+ if err != ErrorUndefinedCell {
+ return nil, err
+ }
+ } else {
+ bCell = &cell
+ diffTableCell.RightCell = cell
+ }
+ } else {
+ diffTableCell.Type = TableDiffCellDel
+ }
+
+ // if both a and b have a row that exists, compare the value and determine if the row has moved
+ if aCell != nil && bCell != nil {
+ moved := ((bIndex + colsDeleted) != (b2aColMap[bIndex] + colsAdded))
+ if *aCell != *bCell {
+ if moved {
+ diffTableCell.Type = TableDiffCellMovedChanged
+ } else {
+ diffTableCell.Type = TableDiffCellChanged
+ }
+ } else {
+ if moved {
+ diffTableCell.Type = TableDiffCellMovedUnchanged
+ } else {
+ diffTableCell.Type = TableDiffCellUnchanged
+ }
+ diffTableCell.LeftCell = ""
+ }
+ }
+
+ // Add the diff column to the diff row
+ diffTableCells[bIndex+colsDeleted] = &diffTableCell
+ bIndex++
}
}
- return &TableDiffRow{RowIdx: bline, Cells: cells}, nil
+ return &TableDiffRow{RowIdx: bLineNum, Cells: diffTableCells}, nil
}
- var sections []*TableDiffSection
+ // diffTableSections are TableDiffSections which represent the diffTableSections we get when doing a diff, each will be its own table in the view
+ var diffTableSections []*TableDiffSection
for i, section := range diffFile.Sections {
- var rows []*TableDiffRow
+ // Each section has multiple diffTableRows
+ var diffTableRows []*TableDiffRow
lines := tryMergeLines(section.Lines)
+ // Loop through the merged lines to get each row of the CSV diff table for this section
for j, line := range lines {
if i == 0 && j == 0 && (line[0] != 1 || line[1] != 1) {
- diffRow, err := createDiffRow(1, 1)
+ diffTableRow, err := createDiffTableRow(1, 1)
if err != nil {
return nil, err
}
- if diffRow != nil {
- rows = append(rows, diffRow)
+ if diffTableRow != nil {
+ diffTableRows = append(diffTableRows, diffTableRow)
}
}
- diffRow, err := createDiffRow(line[0], line[1])
+ diffTableRow, err := createDiffTableRow(line[0], line[1])
if err != nil {
return nil, err
}
- if diffRow != nil {
- rows = append(rows, diffRow)
+ if diffTableRow != nil {
+ diffTableRows = append(diffTableRows, diffTableRow)
}
}
- if len(rows) > 0 {
- sections = append(sections, &TableDiffSection{Rows: rows})
+ if len(diffTableRows) > 0 {
+ diffTableSections = append(diffTableSections, &TableDiffSection{Rows: diffTableRows})
}
}
- return sections, nil
+ return diffTableSections, nil
}
// getColumnMapping creates a mapping of columns between a and b
-func getColumnMapping(a *csvReader, b *csvReader) ([]int, []int) {
- arow, _ := a.GetRow(0)
- brow, _ := b.GetRow(0)
+func getColumnMapping(baseCSVReader *csvReader, headCSVReader *csvReader) ([]int, []int) {
+ baseRow, _ := baseCSVReader.GetRow(0)
+ headRow, _ := headCSVReader.GetRow(0)
- a2b := []int{}
- b2a := []int{}
+ base2HeadColMap := []int{}
+ head2BaseColMap := []int{}
- if arow != nil {
- a2b = make([]int, len(arow))
+ if baseRow != nil {
+ base2HeadColMap = make([]int, len(baseRow))
}
- if brow != nil {
- b2a = make([]int, len(brow))
+ if headRow != nil {
+ head2BaseColMap = make([]int, len(headRow))
}
- for i := 0; i < len(b2a); i++ {
- b2a[i] = unmappedColumn
+ // Initializes all head2base mappings to be unmappedColumn (-1)
+ for i := 0; i < len(head2BaseColMap); i++ {
+ head2BaseColMap[i] = unmappedColumn
}
- bcol := 0
- for i := 0; i < len(a2b); i++ {
- a2b[i] = unmappedColumn
-
- acell, ea := getCell(arow, i)
- if ea == nil {
- for j := bcol; j < len(b2a); j++ {
- bcell, eb := getCell(brow, j)
- if eb == nil && acell == bcell {
- a2b[i] = j
- b2a[j] = i
- bcol = j + 1
- break
+ // Loops through the baseRow and see if there is a match in the head row
+ for i := 0; i < len(baseRow); i++ {
+ base2HeadColMap[i] = unmappedColumn
+ baseCell, err := getCell(baseRow, i)
+ if err == nil {
+ for j := 0; j < len(headRow); j++ {
+ if head2BaseColMap[j] == -1 {
+ headCell, err := getCell(headRow, j)
+ if err == nil && baseCell == headCell {
+ base2HeadColMap[i] = j
+ head2BaseColMap[j] = i
+ break
+ }
}
}
}
}
- tryMapColumnsByContent(a, a2b, b, b2a)
- tryMapColumnsByContent(b, b2a, a, a2b)
+ tryMapColumnsByContent(baseCSVReader, base2HeadColMap, headCSVReader, head2BaseColMap)
+ tryMapColumnsByContent(headCSVReader, head2BaseColMap, baseCSVReader, base2HeadColMap)
- return a2b, b2a
+ return base2HeadColMap, head2BaseColMap
}
// tryMapColumnsByContent tries to map missing columns by the content of the first lines.
-func tryMapColumnsByContent(a *csvReader, a2b []int, b *csvReader, b2a []int) {
- start := 0
- for i := 0; i < len(a2b); i++ {
- if a2b[i] == unmappedColumn {
- if b2a[start] == unmappedColumn {
- rows := util.Min(maxRowsToInspect, util.Max(0, util.Min(len(a.buffer), len(b.buffer))-1))
+func tryMapColumnsByContent(baseCSVReader *csvReader, base2HeadColMap []int, headCSVReader *csvReader, head2BaseColMap []int) {
+ for i := 0; i < len(base2HeadColMap); i++ {
+ headStart := 0
+ for base2HeadColMap[i] == unmappedColumn && headStart < len(head2BaseColMap) {
+ if head2BaseColMap[headStart] == unmappedColumn {
+ rows := util.Min(maxRowsToInspect, util.Max(0, util.Min(len(baseCSVReader.buffer), len(headCSVReader.buffer))-1))
same := 0
for j := 1; j <= rows; j++ {
- acell, ea := getCell(a.buffer[j], i)
- bcell, eb := getCell(b.buffer[j], start+1)
- if ea == nil && eb == nil && acell == bcell {
+ baseCell, baseErr := getCell(baseCSVReader.buffer[j], i)
+ headCell, headErr := getCell(headCSVReader.buffer[j], headStart)
+ if baseErr == nil && headErr == nil && baseCell == headCell {
same++
}
}
if (float32(same) / float32(rows)) > minRatioToMatch {
- a2b[i] = start + 1
- b2a[start+1] = i
+ base2HeadColMap[i] = headStart
+ head2BaseColMap[headStart] = i
}
}
+ headStart++
}
- start = a2b[i]
}
}
@@ -328,7 +419,7 @@ func getCell(row []string, column int) (string, error) {
if column < len(row) {
return row[column], nil
}
- return "", errors.New("Undefined column")
+ return "", ErrorUndefinedCell
}
// countUnmappedColumns returns the count of unmapped columns.
diff --git a/services/gitdiff/csv_test.go b/services/gitdiff/csv_test.go
index fb84d6ed06..1710e91c58 100644
--- a/services/gitdiff/csv_test.go
+++ b/services/gitdiff/csv_test.go
@@ -19,9 +19,9 @@ func TestCSVDiff(t *testing.T) {
diff string
base string
head string
- cells [][2]TableDiffCellType
+ cells [][]TableDiffCellType
}{
- // case 0
+ // case 0 - initial commit of a csv
{
diff: `diff --git a/unittest.csv b/unittest.csv
--- a/unittest.csv
@@ -29,11 +29,14 @@ func TestCSVDiff(t *testing.T) {
@@ -0,0 +1,2 @@
+col1,col2
+a,a`,
- base: "",
- head: "col1,col2\na,a",
- cells: [][2]TableDiffCellType{{TableDiffCellAdd, TableDiffCellAdd}, {TableDiffCellAdd, TableDiffCellAdd}},
+ base: "",
+ head: `col1,col2
+a,a`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellAdd, TableDiffCellAdd},
+ {TableDiffCellAdd, TableDiffCellAdd}},
},
- // case 1
+ // case 1 - adding 1 row at end
{
diff: `diff --git a/unittest.csv b/unittest.csv
--- a/unittest.csv
@@ -43,11 +46,17 @@ func TestCSVDiff(t *testing.T) {
-a,a
+a,a
+b,b`,
- base: "col1,col2\na,a",
- head: "col1,col2\na,a\nb,b",
- cells: [][2]TableDiffCellType{{TableDiffCellEqual, TableDiffCellEqual}, {TableDiffCellEqual, TableDiffCellEqual}, {TableDiffCellAdd, TableDiffCellAdd}},
+ base: `col1,col2
+a,a`,
+ head: `col1,col2
+a,a
+b,b`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellUnchanged, TableDiffCellUnchanged}, {TableDiffCellUnchanged, TableDiffCellUnchanged},
+ {TableDiffCellAdd, TableDiffCellAdd},
+ },
},
- // case 2
+ // case 2 - row deleted
{
diff: `diff --git a/unittest.csv b/unittest.csv
--- a/unittest.csv
@@ -56,11 +65,17 @@ func TestCSVDiff(t *testing.T) {
col1,col2
-a,a
b,b`,
- base: "col1,col2\na,a\nb,b",
- head: "col1,col2\nb,b",
- cells: [][2]TableDiffCellType{{TableDiffCellEqual, TableDiffCellEqual}, {TableDiffCellDel, TableDiffCellDel}, {TableDiffCellEqual, TableDiffCellEqual}},
+ base: `col1,col2
+a,a
+b,b`,
+ head: `col1,col2
+b,b`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellUnchanged, TableDiffCellUnchanged}, {TableDiffCellDel, TableDiffCellDel},
+ {TableDiffCellUnchanged, TableDiffCellUnchanged},
+ },
},
- // case 3
+ // case 3 - row changed
{
diff: `diff --git a/unittest.csv b/unittest.csv
--- a/unittest.csv
@@ -69,11 +84,16 @@ func TestCSVDiff(t *testing.T) {
col1,col2
-b,b
+b,c`,
- base: "col1,col2\nb,b",
- head: "col1,col2\nb,c",
- cells: [][2]TableDiffCellType{{TableDiffCellEqual, TableDiffCellEqual}, {TableDiffCellEqual, TableDiffCellChanged}},
+ base: `col1,col2
+b,b`,
+ head: `col1,col2
+b,c`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellUnchanged, TableDiffCellUnchanged},
+ {TableDiffCellUnchanged, TableDiffCellChanged},
+ },
},
- // case 4
+ // case 4 - all deleted
{
diff: `diff --git a/unittest.csv b/unittest.csv
--- a/unittest.csv
@@ -81,9 +101,88 @@ func TestCSVDiff(t *testing.T) {
@@ -1,2 +0,0 @@
-col1,col2
-b,c`,
- base: "col1,col2\nb,c",
- head: "",
- cells: [][2]TableDiffCellType{{TableDiffCellDel, TableDiffCellDel}, {TableDiffCellDel, TableDiffCellDel}},
+ base: `col1,col2
+b,c`,
+ head: "",
+ cells: [][]TableDiffCellType{
+ {TableDiffCellDel, TableDiffCellDel},
+ {TableDiffCellDel, TableDiffCellDel},
+ },
+ },
+ // case 5 - renames first column
+ {
+ diff: `diff --git a/unittest.csv b/unittest.csv
+--- a/unittest.csv
++++ b/unittest.csv
+@@ -1,3 +1,3 @@
+-col1,col2,col3
++cola,col2,col3
+ a,b,c`,
+ base: `col1,col2,col3
+a,b,c`,
+ head: `cola,col2,col3
+a,b,c`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellDel, TableDiffCellAdd, TableDiffCellUnchanged, TableDiffCellUnchanged},
+ {TableDiffCellDel, TableDiffCellAdd, TableDiffCellUnchanged, TableDiffCellUnchanged},
+ },
+ },
+ // case 6 - inserts a column after first, deletes last column
+ {
+ diff: `diff --git a/unittest.csv b/unittest.csv
+--- a/unittest.csv
++++ b/unittest.csv
+@@ -1,2 +1,2 @@
+-col1,col2,col3
+-a,b,c
++col1,col1a,col2
++a,d,b`,
+ base: `col1,col2,col3
+a,b,c`,
+ head: `col1,col1a,col2
+a,d,b`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellUnchanged, TableDiffCellAdd, TableDiffCellDel, TableDiffCellMovedUnchanged},
+ {TableDiffCellUnchanged, TableDiffCellAdd, TableDiffCellDel, TableDiffCellMovedUnchanged},
+ },
+ },
+ // case 7 - deletes first column, inserts column after last
+ {
+ diff: `diff --git a/unittest.csv b/unittest.csv
+--- a/unittest.csv
++++ b/unittest.csv
+@@ -1,2 +1,2 @@
+-col1,col2,col3
+-a,b,c
++col2,col3,col4
++b,c,d`,
+ base: `col1,col2,col3
+a,b,c`,
+ head: `col2,col3,col4
+b,c,d`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellDel, TableDiffCellUnchanged, TableDiffCellUnchanged, TableDiffCellAdd},
+ {TableDiffCellDel, TableDiffCellUnchanged, TableDiffCellUnchanged, TableDiffCellAdd},
+ },
+ },
+ // case 8 - two columns deleted, 2 added
+ {
+ diff: `diff --git a/unittest.csv b/unittest.csv
+--- a/unittest.csv
++++ b/unittest.csv
+@@ -1,2 +1,2 @@
+-col1,col2,col
+-a,b,c
++col3,col4,col5
++c,d,e`,
+ base: `col1,col2,col3
+a,b,c`,
+ head: `col3,col4,col5
+c,d,e`,
+ cells: [][]TableDiffCellType{
+ {TableDiffCellDel, TableDiffCellMovedUnchanged, TableDiffCellDel, TableDiffCellAdd, TableDiffCellAdd},
+ {TableDiffCellDel, TableDiffCellMovedUnchanged, TableDiffCellDel, TableDiffCellAdd, TableDiffCellAdd},
+ },
},
}
@@ -116,7 +215,7 @@ func TestCSVDiff(t *testing.T) {
assert.Len(t, section.Rows, len(c.cells), "case %d: should be %d rows", n, len(c.cells))
for i, row := range section.Rows {
- assert.Len(t, row.Cells, 2, "case %d: row %d should have two cells", n, i)
+ assert.Len(t, row.Cells, len(c.cells[i]), "case %d: row %d should have %d cells", n, i, len(c.cells[i]))
for j, cell := range row.Cells {
assert.Equal(t, c.cells[i][j], cell.Type, "case %d: row %d cell %d should be equal", n, i, j)
}
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index d50e41eb40..0f58511d89 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -20,6 +20,7 @@ import (
"regexp"
"sort"
"strings"
+ "time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/charset"
@@ -75,6 +76,7 @@ const (
type DiffLine struct {
LeftIdx int
RightIdx int
+ Match int
Type DiffLineType
Content string
Comments []*models.Comment
@@ -829,7 +831,12 @@ parsingLoop:
case strings.HasPrefix(line, "--- "):
// Handle ambiguous filenames
if curFile.IsAmbiguous {
- if len(line) > 6 && line[4] == 'a' {
+ // The shortest string that can end up here is:
+ // "--- a\t\n" without the qoutes.
+ // This line has a len() of 7 but doesn't contain a oldName.
+ // So the amount that the line need is at least 8 or more.
+ // The code will otherwise panic for a out-of-bounds.
+ if len(line) > 7 && line[4] == 'a' {
curFile.OldName = line[6 : len(line)-1]
if line[len(line)-2] == '\t' {
curFile.OldName = curFile.OldName[:len(curFile.OldName)-1]
@@ -943,6 +950,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
curFileLFSPrefix bool
)
+ lastLeftIdx := -1
leftLine, rightLine := 1, 1
for {
@@ -993,6 +1001,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
// Create a new section to represent this hunk
curSection = &DiffSection{}
+ lastLeftIdx = -1
curFile.Sections = append(curFile.Sections, curSection)
lineSectionInfo := getDiffLineSectionInfo(curFile.Name, line, leftLine-1, rightLine-1)
@@ -1027,12 +1036,21 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
curFile.IsIncomplete = true
continue
}
- diffLine := &DiffLine{Type: DiffLineAdd, RightIdx: rightLine}
+ diffLine := &DiffLine{Type: DiffLineAdd, RightIdx: rightLine, Match: -1}
rightLine++
if curSection == nil {
// Create a new section to represent this hunk
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
+ lastLeftIdx = -1
+ }
+ if lastLeftIdx > -1 {
+ diffLine.Match = lastLeftIdx
+ curSection.Lines[lastLeftIdx].Match = len(curSection.Lines)
+ lastLeftIdx++
+ if lastLeftIdx >= len(curSection.Lines) || curSection.Lines[lastLeftIdx].Type != DiffLineDel {
+ lastLeftIdx = -1
+ }
}
curSection.Lines = append(curSection.Lines, diffLine)
case '-':
@@ -1042,7 +1060,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
curFile.IsIncomplete = true
continue
}
- diffLine := &DiffLine{Type: DiffLineDel, LeftIdx: leftLine}
+ diffLine := &DiffLine{Type: DiffLineDel, LeftIdx: leftLine, Match: -1}
if leftLine > 0 {
leftLine++
}
@@ -1050,6 +1068,10 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
// Create a new section to represent this hunk
curSection = &DiffSection{}
curFile.Sections = append(curFile.Sections, curSection)
+ lastLeftIdx = -1
+ }
+ if len(curSection.Lines) == 0 || curSection.Lines[len(curSection.Lines)-1].Type != DiffLineDel {
+ lastLeftIdx = len(curSection.Lines)
}
curSection.Lines = append(curSection.Lines, diffLine)
case ' ':
@@ -1061,6 +1083,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
diffLine := &DiffLine{Type: DiffLinePlain, LeftIdx: leftLine, RightIdx: rightLine}
leftLine++
rightLine++
+ lastLeftIdx = -1
if curSection == nil {
// Create a new section to represent this hunk
curSection = &DiffSection{}
@@ -1106,6 +1129,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio
curFile.IsBin = true
curFile.IsLFSFile = true
curSection.Lines = nil
+ lastLeftIdx = -1
}
}
}
@@ -1167,6 +1191,10 @@ func readFileName(rd *strings.Reader) (string, bool) {
_ = rd.UnreadByte()
if char == '"' {
fmt.Fscanf(rd, "%q ", &name)
+ if len(name) == 0 {
+ log.Error("Reader has no file name: %v", rd)
+ return "", true
+ }
if name[0] == '\\' {
name = name[1:]
}
@@ -1191,31 +1219,20 @@ func readFileName(rd *strings.Reader) (string, bool) {
return name[2:], ambiguity
}
-// GetDiffRange builds a Diff between two commits of a repository.
-// passing the empty string as beforeCommitID returns a diff from the
-// parent commit.
-func GetDiffRange(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) {
- return GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID, maxLines, maxLineCharacters, maxFiles, "")
-}
-
// GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository.
// Passing the empty string as beforeCommitID returns a diff from the parent commit.
// The whitespaceBehavior is either an empty string or a git flag
-func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string) (*Diff, error) {
- gitRepo, err := git.OpenRepository(repoPath)
- if err != nil {
- return nil, err
- }
- defer gitRepo.Close()
+func GetDiffRangeWithWhitespaceBehavior(gitRepo *git.Repository, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string) (*Diff, error) {
+ repoPath := gitRepo.Path
commit, err := gitRepo.GetCommit(afterCommitID)
if err != nil {
return nil, err
}
- // FIXME: graceful: These commands should likely have a timeout
- ctx, cancel := context.WithCancel(git.DefaultContext)
+ ctx, cancel := context.WithTimeout(git.DefaultContext, time.Duration(setting.Git.Timeout.Default)*time.Second)
defer cancel()
+
var cmd *exec.Cmd
if (len(beforeCommitID) == 0 || beforeCommitID == git.EmptySHA) && commit.ParentCount() == 0 {
diffArgs := []string{"diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"}
@@ -1289,15 +1306,10 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID
return diff, nil
}
-// GetDiffCommit builds a Diff representing the given commitID.
-func GetDiffCommit(repoPath, commitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) {
- return GetDiffRangeWithWhitespaceBehavior(repoPath, "", commitID, maxLines, maxLineCharacters, maxFiles, "")
-}
-
// GetDiffCommitWithWhitespaceBehavior builds a Diff representing the given commitID.
// The whitespaceBehavior is either an empty string or a git flag
-func GetDiffCommitWithWhitespaceBehavior(repoPath, commitID string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string) (*Diff, error) {
- return GetDiffRangeWithWhitespaceBehavior(repoPath, "", commitID, maxLines, maxLineCharacters, maxFiles, whitespaceBehavior)
+func GetDiffCommitWithWhitespaceBehavior(gitRepo *git.Repository, commitID string, maxLines, maxLineCharacters, maxFiles int, whitespaceBehavior string) (*Diff, error) {
+ return GetDiffRangeWithWhitespaceBehavior(gitRepo, "", commitID, maxLines, maxLineCharacters, maxFiles, whitespaceBehavior)
}
// CommentAsDiff returns c.Patch as *Diff
diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go
index 2386552efe..c72fe776b2 100644
--- a/services/gitdiff/gitdiff_test.go
+++ b/services/gitdiff/gitdiff_test.go
@@ -13,6 +13,7 @@ import (
"testing"
"code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/setting"
jsoniter "github.com/json-iterator/go"
@@ -513,8 +514,13 @@ func TestDiffLine_GetCommentSide(t *testing.T) {
}
func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) {
+ gitRepo, err := git.OpenRepository("./testdata/academic-module")
+ if !assert.NoError(t, err) {
+ return
+ }
+ defer gitRepo.Close()
for _, behavior := range []string{"-w", "--ignore-space-at-eol", "-b", ""} {
- diffs, err := GetDiffRangeWithWhitespaceBehavior("./testdata/academic-module", "559c156f8e0178b71cb44355428f24001b08fc68", "bd7063cc7c04689c4d082183d32a604ed27a24f9",
+ diffs, err := GetDiffRangeWithWhitespaceBehavior(gitRepo, "559c156f8e0178b71cb44355428f24001b08fc68", "bd7063cc7c04689c4d082183d32a604ed27a24f9",
setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles, behavior)
assert.NoError(t, err, fmt.Sprintf("Error when diff with %s", behavior))
for _, f := range diffs.Files {
@@ -533,3 +539,22 @@ func TestDiffToHTML_14231(t *testing.T) {
assertEqual(t, expected, output)
}
+
+func TestNoCrashes(t *testing.T) {
+ type testcase struct {
+ gitdiff string
+ }
+
+ tests := []testcase{
+ {
+ gitdiff: "diff --git \n--- a\t\n",
+ },
+ {
+ gitdiff: "diff --git \"0\n",
+ },
+ }
+ for _, testcase := range tests {
+ // It shouldn't crash, so don't care about the output.
+ ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(testcase.gitdiff))
+ }
+}
diff --git a/modules/repofiles/action.go b/services/issue/commit.go
similarity index 85%
rename from modules/repofiles/action.go
rename to services/issue/commit.go
index d7e3ff4525..0a74d08949 100644
--- a/modules/repofiles/action.go
+++ b/services/issue/commit.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repofiles
+package issue
import (
"fmt"
@@ -13,7 +13,6 @@ import (
"time"
"code.gitea.io/gitea/models"
- "code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/repository"
)
@@ -95,33 +94,6 @@ func issueAddTime(issue *models.Issue, doer *models.User, time time.Time, timeLo
return err
}
-func changeIssueStatus(repo *models.Repository, issue *models.Issue, doer *models.User, closed bool) error {
- stopTimerIfAvailable := func(doer *models.User, issue *models.Issue) error {
-
- if models.StopwatchExists(doer.ID, issue.ID) {
- if err := models.CreateOrStopIssueStopwatch(doer, issue); err != nil {
- return err
- }
- }
-
- return nil
- }
-
- issue.Repo = repo
- comment, err := issue.ChangeStatus(doer, closed)
- if err != nil {
- // Don't return an error when dependencies are open as this would let the push fail
- if models.IsErrDependenciesLeft(err) {
- return stopTimerIfAvailable(doer, issue)
- }
- return err
- }
-
- notification.NotifyIssueChangeStatus(doer, issue, comment, closed)
-
- return stopTimerIfAvailable(doer, issue)
-}
-
// UpdateIssuesCommit checks if issues are manipulated by commit message.
func UpdateIssuesCommit(doer *models.User, repo *models.Repository, commits []*repository.PushCommit, branchName string) error {
// Commits are appended in the reverse order.
@@ -208,7 +180,10 @@ func UpdateIssuesCommit(doer *models.User, repo *models.Repository, commits []*r
}
}
if close != refIssue.IsClosed {
- if err := changeIssueStatus(refRepo, refIssue, doer, close); err != nil {
+ if refIssue.Repo != nil {
+ refIssue.Repo = refRepo
+ }
+ if err := ChangeStatus(refIssue, doer, close); err != nil {
return err
}
}
diff --git a/modules/repofiles/action_test.go b/services/issue/commit_test.go
similarity index 99%
rename from modules/repofiles/action_test.go
rename to services/issue/commit_test.go
index 97632df68f..f3502d3e0a 100644
--- a/modules/repofiles/action_test.go
+++ b/services/issue/commit_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package repofiles
+package issue
import (
"testing"
diff --git a/services/issue/status.go b/services/issue/status.go
index b01ce4bbb8..35f67bf928 100644
--- a/services/issue/status.go
+++ b/services/issue/status.go
@@ -13,7 +13,19 @@ import (
func ChangeStatus(issue *models.Issue, doer *models.User, isClosed bool) (err error) {
comment, err := issue.ChangeStatus(doer, isClosed)
if err != nil {
- return
+ // Don't return an error when dependencies are open as this would let the push fail
+ if models.IsErrDependenciesLeft(err) {
+ if isClosed {
+ return models.FinishIssueStopwatchIfPossible(doer, issue)
+ }
+ }
+ return err
+ }
+
+ if isClosed {
+ if err := models.FinishIssueStopwatchIfPossible(doer, issue); err != nil {
+ return err
+ }
}
notification.NotifyIssueChangeStatus(doer, issue, comment, isClosed)
diff --git a/services/lfs/server.go b/services/lfs/server.go
index 9954534b5e..424dc91c8c 100644
--- a/services/lfs/server.go
+++ b/services/lfs/server.go
@@ -5,7 +5,9 @@
package lfs
import (
+ "crypto/sha256"
"encoding/base64"
+ "encoding/hex"
"errors"
"fmt"
"io"
@@ -21,7 +23,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/dgrijalva/jwt-go"
+ "github.com/golang-jwt/jwt"
jsoniter "github.com/json-iterator/go"
)
@@ -213,14 +215,22 @@ func BatchHandler(ctx *context.Context) {
}
}
- if exists {
- if meta == nil {
+ if exists && meta == nil {
+ accessible, err := models.LFSObjectAccessible(ctx.User, p.Oid)
+ if err != nil {
+ log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err)
+ writeStatus(ctx, http.StatusInternalServerError)
+ return
+ }
+ if accessible {
_, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
if err != nil {
log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
writeStatus(ctx, http.StatusInternalServerError)
return
}
+ } else {
+ exists = false
}
}
@@ -270,29 +280,50 @@ func UploadHandler(ctx *context.Context) {
return
}
- meta, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
- if err != nil {
- log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
- writeStatus(ctx, http.StatusInternalServerError)
- return
- }
-
contentStore := lfs_module.NewContentStore()
-
exists, err := contentStore.Exists(p)
if err != nil {
log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, err)
writeStatus(ctx, http.StatusInternalServerError)
return
}
- if meta.Existing || exists {
- ctx.Resp.WriteHeader(http.StatusOK)
- return
+
+ uploadOrVerify := func() error {
+ if exists {
+ accessible, err := models.LFSObjectAccessible(ctx.User, p.Oid)
+ if err != nil {
+ log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err)
+ return err
+ }
+ if !accessible {
+ // The file exists but the user has no access to it.
+ // The upload gets verified by hashing and size comparison to prove access to it.
+ hash := sha256.New()
+ written, err := io.Copy(hash, ctx.Req.Body)
+ if err != nil {
+ log.Error("Error creating hash. Error: %v", err)
+ return err
+ }
+
+ if written != p.Size {
+ return lfs_module.ErrSizeMismatch
+ }
+ if hex.EncodeToString(hash.Sum(nil)) != p.Oid {
+ return lfs_module.ErrHashMismatch
+ }
+ }
+ } else if err := contentStore.Put(p, ctx.Req.Body); err != nil {
+ log.Error("Error putting LFS MetaObject [%s] into content store. Error: %v", p.Oid, err)
+ return err
+ }
+ _, err := models.NewLFSMetaObject(&models.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
+ return err
}
defer ctx.Req.Body.Close()
- if err := contentStore.Put(meta.Pointer, ctx.Req.Body); err != nil {
+ if err := uploadOrVerify(); err != nil {
if errors.Is(err, lfs_module.ErrSizeMismatch) || errors.Is(err, lfs_module.ErrHashMismatch) {
+ log.Error("Upload does not match LFS MetaObject [%s]. Error: %v", p.Oid, err)
writeStatusMessage(ctx, http.StatusUnprocessableEntity, err.Error())
} else {
writeStatus(ctx, http.StatusInternalServerError)
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 14512d7d65..bf90046360 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -344,6 +344,16 @@ func sanitizeSubject(subject string) string {
// SendIssueAssignedMail composes and sends issue assigned email
func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, recipients []*models.User) error {
+ if setting.MailService == nil {
+ // No mail service configured
+ return nil
+ }
+
+ if err := issue.LoadRepo(); err != nil {
+ log.Error("Unable to load repo [%d] for issue #%d [%d]. Error: %v", issue.RepoID, issue.Index, issue.ID, err)
+ return err
+ }
+
langMap := make(map[string][]*models.User)
for _, user := range recipients {
langMap[user.Language] = append(langMap[user.Language], user)
diff --git a/services/pull/check.go b/services/pull/check.go
index 9db1654cfb..7d365e1223 100644
--- a/services/pull/check.go
+++ b/services/pull/check.go
@@ -126,7 +126,7 @@ func getMergeCommit(pr *models.PullRequest) (*git.Commit, error) {
commit, err := gitRepo.GetCommit(mergeCommit[:40])
if err != nil {
- return nil, fmt.Errorf("GetCommit: %v", err)
+ return nil, fmt.Errorf("GetMergeCommit[%v]: %v", mergeCommit[:40], err)
}
return commit, nil
@@ -254,7 +254,7 @@ func CheckPrsForBaseBranch(baseRepo *models.Repository, baseBranchName string) e
// Init runs the task queue to test all the checking status pull requests
func Init() error {
- prQueue = queue.CreateUniqueQueue("pr_patch_checker", handle, "").(queue.UniqueQueue)
+ prQueue = queue.CreateUniqueQueue("pr_patch_checker", handle, "")
if prQueue == nil {
return fmt.Errorf("Unable to create pr_patch_checker Queue")
diff --git a/services/pull/merge.go b/services/pull/merge.go
index 7e6a214b87..8c0eeadd79 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -273,8 +273,8 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge
filepath.Join(tmpBasePath, ".git", "rebase-merge", "stopped-sha"), // Git >= 2.26
}
for _, failingCommitPath := range failingCommitPaths {
- if _, statErr := os.Stat(filepath.Join(failingCommitPath)); statErr == nil {
- commitShaBytes, readErr := ioutil.ReadFile(filepath.Join(failingCommitPath))
+ if _, statErr := os.Stat(failingCommitPath); statErr == nil {
+ commitShaBytes, readErr := ioutil.ReadFile(failingCommitPath)
if readErr != nil {
// Abandon this attempt to handle the error
log.Error("git rebase staging on to base [%s:%s -> %s:%s]: %v\n%s\n%s", pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseRepo.FullName(), pr.BaseBranch, err, outbuf.String(), errbuf.String())
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 6b3acd2004..b938dd1f70 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -713,7 +713,8 @@ func GetIssuesLastCommitStatus(issues models.IssueList) (map[int64]*models.Commi
if !ok {
gitRepo, err = git.OpenRepository(issue.Repo.RepoPath())
if err != nil {
- return nil, err
+ log.Error("Cannot open git repository %-v for issue #%d[%d]. Error: %v", issue.Repo, issue.Index, issue.ID, err)
+ continue
}
gitRepos[issue.RepoID] = gitRepo
}
diff --git a/services/release/release.go b/services/release/release.go
index 6f5aa02c85..8ba739d8e5 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -44,7 +44,7 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool,
commit, err := gitRepo.GetCommit(rel.Target)
if err != nil {
- return false, fmt.Errorf("GetCommit: %v", err)
+ return false, fmt.Errorf("createTag::GetCommit[%v]: %v", rel.Target, err)
}
// Trim '--' prefix to prevent command line argument vulnerability.
@@ -69,13 +69,17 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool,
created = true
rel.LowerTagName = strings.ToLower(rel.TagName)
+ commits := repository.NewPushCommits()
+ commits.HeadCommit = repository.CommitToPushCommit(commit)
+ commits.CompareURL = rel.Repo.ComposeCompareURL(git.EmptySHA, commit.ID.String())
+
notification.NotifyPushCommits(
rel.Publisher, rel.Repo,
&repository.PushUpdateOptions{
RefFullName: git.TagPrefix + rel.TagName,
OldCommitID: git.EmptySHA,
NewCommitID: commit.ID.String(),
- }, repository.NewPushCommits())
+ }, commits)
notification.NotifyCreateRef(rel.Publisher, rel.Repo, "tag", git.TagPrefix+rel.TagName)
rel.CreatedUnix = timeutil.TimeStampNow()
}
diff --git a/services/repository/push.go b/services/repository/push.go
index 26df6b8e45..93131d0636 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -16,9 +16,9 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/queue"
- "code.gitea.io/gitea/modules/repofiles"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
+ issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
)
@@ -36,7 +36,7 @@ func handle(data ...queue.Data) {
}
func initPushQueue() error {
- pushQueue = queue.CreateQueue("push_update", handle, []*repo_module.PushUpdateOptions{}).(queue.Queue)
+ pushQueue = queue.CreateQueue("push_update", handle, []*repo_module.PushUpdateOptions{})
if pushQueue == nil {
return fmt.Errorf("Unable to create push_update Queue")
}
@@ -115,13 +115,22 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
delTags = append(delTags, tagName)
notification.NotifyDeleteRef(pusher, repo, "tag", opts.RefFullName)
} else { // is new tag
+ newCommit, err := gitRepo.GetCommit(opts.NewCommitID)
+ if err != nil {
+ return fmt.Errorf("gitRepo.GetCommit: %v", err)
+ }
+
+ commits := repo_module.NewPushCommits()
+ commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
+ commits.CompareURL = repo.ComposeCompareURL(git.EmptySHA, opts.NewCommitID)
+
notification.NotifyPushCommits(
pusher, repo,
&repo_module.PushUpdateOptions{
RefFullName: git.TagPrefix + tagName,
OldCommitID: git.EmptySHA,
NewCommitID: opts.NewCommitID,
- }, repo_module.NewPushCommits())
+ }, commits)
addTags = append(addTags, tagName)
notification.NotifyCreateRef(pusher, repo, "tag", opts.RefFullName)
@@ -194,7 +203,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
commits := repo_module.ListToPushCommits(l)
commits.HeadCommit = repo_module.CommitToPushCommit(newCommit)
- if err := repofiles.UpdateIssuesCommit(pusher, repo, commits.Commits, refName); err != nil {
+ if err := issue_service.UpdateIssuesCommit(pusher, repo, commits.Commits, refName); err != nil {
log.Error("updateIssuesCommit: %v", err)
}
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index bb323c1c0a..5c29b60365 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -8,6 +8,7 @@ import (
"fmt"
"code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/sync"
)
@@ -53,6 +54,8 @@ func TransferOwnership(doer, newOwner *models.User, repo *models.Repository, tea
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoName string) error {
+ log.Trace("ChangeRepositoryName: %s/%s -> %s", doer.Name, repo.Name, newRepoName)
+
oldRepoName := repo.Name
// Change repository directory name. We must lock the local copy of the
@@ -66,6 +69,7 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam
}
repoWorkingPool.CheckOut(fmt.Sprint(repo.ID))
+ repo.Name = newRepoName
notification.NotifyRenameRepository(doer, repo, oldRepoName)
return nil
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go
index 8243fde1bb..4fcb167efe 100644
--- a/services/webhook/deliver.go
+++ b/services/webhook/deliver.go
@@ -20,6 +20,7 @@ import (
"strconv"
"strings"
"sync"
+ "syscall"
"time"
"code.gitea.io/gitea/models"
@@ -29,6 +30,8 @@ import (
"github.com/gobwas/glob"
)
+var contextKeyWebhookRequest interface{} = "contextKeyWebhookRequest"
+
// Deliver deliver hook task
func Deliver(t *models.HookTask) error {
w, err := models.GetWebhookByID(t.HookID)
@@ -166,7 +169,7 @@ func Deliver(t *models.HookTask) error {
return fmt.Errorf("Webhook task skipped (webhooks disabled): [%d]", t.ID)
}
- resp, err := webhookHTTPClient.Do(req)
+ resp, err := webhookHTTPClient.Do(req.WithContext(context.WithValue(req.Context(), contextKeyWebhookRequest, req)))
if err != nil {
t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err)
return err
@@ -288,14 +291,29 @@ func InitDeliverHooks() {
timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second
webhookHTTPClient = &http.Client{
+ Timeout: timeout,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify},
Proxy: webhookProxy(),
- Dial: func(netw, addr string) (net.Conn, error) {
- return net.DialTimeout(netw, addr, timeout) // dial timeout
+ DialContext: func(ctx context.Context, network, addrOrHost string) (net.Conn, error) {
+ dialer := net.Dialer{
+ Timeout: timeout,
+ Control: func(network, ipAddr string, c syscall.RawConn) error {
+ // in Control func, the addr was already resolved to IP:PORT format, there is no cost to do ResolveTCPAddr here
+ tcpAddr, err := net.ResolveTCPAddr(network, ipAddr)
+ req := ctx.Value(contextKeyWebhookRequest).(*http.Request)
+ if err != nil {
+ return fmt.Errorf("webhook can only call HTTP servers via TCP, deny '%s(%s:%s)', err=%v", req.Host, network, ipAddr, err)
+ }
+ if !setting.Webhook.AllowedHostList.MatchesHostOrIP(req.Host, tcpAddr.IP) {
+ return fmt.Errorf("webhook can only call allowed HTTP servers (check your webhook.ALLOWED_HOST_LIST setting), deny '%s(%s)'", req.Host, ipAddr)
+ }
+ return nil
+ },
+ }
+ return dialer.DialContext(ctx, network, addrOrHost)
},
},
- Timeout: timeout, // request timeout
}
go graceful.GetManager().RunWithShutdownContext(DeliverHooks)
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 16301034da..e1590f461e 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -88,8 +88,11 @@ func prepareWikiFileName(gitRepo *git.Repository, wikiName string) (bool, string
escaped := NameToFilename(wikiName)
// Look for both files
- filesInIndex, err := gitRepo.LsFiles(unescaped, escaped)
+ filesInIndex, err := gitRepo.LsTree("master", unescaped, escaped)
if err != nil {
+ if strings.Contains(err.Error(), "Not a valid object name master") {
+ return false, escaped, nil
+ }
log.Error("%v", err)
return false, escaped, err
}
@@ -308,14 +311,9 @@ func DeleteWikiPage(doer *models.User, repo *models.Repository, wikiName string)
return fmt.Errorf("Unable to read HEAD tree to index in: %s %v", basePath, err)
}
- wikiPath := NameToFilename(wikiName)
- filesInIndex, err := gitRepo.LsFiles(wikiPath)
- found := false
- for _, file := range filesInIndex {
- if file == wikiPath {
- found = true
- break
- }
+ found, wikiPath, err := prepareWikiFileName(gitRepo, wikiName)
+ if err != nil {
+ return err
}
if found {
err := gitRepo.RemoveFilesFromIndex(wikiPath)
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index b35b86d655..6c861d556a 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -5,11 +5,15 @@
package wiki
import (
+ "io/ioutil"
+ "os"
"path/filepath"
"testing"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/util"
+
"github.com/stretchr/testify/assert"
)
@@ -210,3 +214,79 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
_, err = masterTree.GetTreeEntryByPath(wikiPath)
assert.Error(t, err)
}
+
+func TestPrepareWikiFileName(t *testing.T) {
+ models.PrepareTestEnv(t)
+ repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
+ gitRepo, err := git.OpenRepository(repo.WikiPath())
+ defer gitRepo.Close()
+ assert.NoError(t, err)
+
+ tests := []struct {
+ name string
+ arg string
+ existence bool
+ wikiPath string
+ wantErr bool
+ }{{
+ name: "add suffix",
+ arg: "Home",
+ existence: true,
+ wikiPath: "Home.md",
+ wantErr: false,
+ }, {
+ name: "test special chars",
+ arg: "home of and & or wiki page!",
+ existence: false,
+ wikiPath: "home-of-and-%26-or-wiki-page%21.md",
+ wantErr: false,
+ }, {
+ name: "fount unescaped cases",
+ arg: "Unescaped File",
+ existence: true,
+ wikiPath: "Unescaped File.md",
+ wantErr: false,
+ }}
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ existence, newWikiPath, err := prepareWikiFileName(gitRepo, tt.arg)
+ if (err != nil) != tt.wantErr {
+ assert.NoError(t, err)
+ return
+ }
+ if existence != tt.existence {
+ if existence {
+ t.Errorf("expect to find no escaped file but we detect one")
+ } else {
+ t.Errorf("expect to find an escaped file but we could not detect one")
+ }
+ }
+ assert.Equal(t, tt.wikiPath, newWikiPath)
+ })
+ }
+}
+
+func TestPrepareWikiFileName_FirstPage(t *testing.T) {
+ models.PrepareTestEnv(t)
+
+ // Now create a temporaryDirectory
+ tmpDir, err := ioutil.TempDir("", "empty-wiki")
+ assert.NoError(t, err)
+ defer func() {
+ if _, err := os.Stat(tmpDir); !os.IsNotExist(err) {
+ _ = util.RemoveAll(tmpDir)
+ }
+ }()
+
+ err = git.InitRepository(tmpDir, true)
+ assert.NoError(t, err)
+
+ gitRepo, err := git.OpenRepository(tmpDir)
+ defer gitRepo.Close()
+ assert.NoError(t, err)
+
+ existence, newWikiPath, err := prepareWikiFileName(gitRepo, "Home")
+ assert.False(t, existence)
+ assert.NoError(t, err)
+ assert.Equal(t, "Home.md", newWikiPath)
+}
diff --git a/templates/admin/user/new.tmpl b/templates/admin/user/new.tmpl
index a433c5a7cc..d81d9d2d88 100644
--- a/templates/admin/user/new.tmpl
+++ b/templates/admin/user/new.tmpl
@@ -28,7 +28,7 @@
-
+
{{if .DefaultUserVisibilityMode.IsPublic}}{{.i18n.Tr "settings.visibility.public"}}{{end}}
{{if .DefaultUserVisibilityMode.IsLimited}}{{.i18n.Tr "settings.visibility.limited"}}{{end}}
diff --git a/templates/base/delete_modal_actions.tmpl b/templates/base/delete_modal_actions.tmpl
index ba9a94d0b6..324be8a707 100644
--- a/templates/base/delete_modal_actions.tmpl
+++ b/templates/base/delete_modal_actions.tmpl
@@ -1,6 +1,6 @@
- {{svg "octicon-trash"}}
+ {{svg "octicon-x"}}
{{.i18n.Tr "modal.no"}}
diff --git a/templates/mail/auth/activate_email.tmpl b/templates/mail/auth/activate_email.tmpl
index 6a8de50112..a1d7ec37ec 100644
--- a/templates/mail/auth/activate_email.tmpl
+++ b/templates/mail/auth/activate_email.tmpl
@@ -5,7 +5,7 @@
{{.i18n.Tr "mail.activate_email.title" .DisplayName}}
-{{ $activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl .Code .Email}}
+{{ $activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl .Code (QueryEscape .Email)}}
{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}
{{.i18n.Tr "mail.activate_email.text" .ActiveCodeLives | Str2html}}
{{$activate_url}}
diff --git a/templates/mail/auth/reset_passwd.tmpl b/templates/mail/auth/reset_passwd.tmpl
index 2a85abc6c5..7cab33bf4e 100644
--- a/templates/mail/auth/reset_passwd.tmpl
+++ b/templates/mail/auth/reset_passwd.tmpl
@@ -9,7 +9,7 @@
{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}
{{.i18n.Tr "mail.reset_password.text" .ResetPwdCodeLives | Str2html}}
{{$recover_url}}
-
{{.i18n.Tr "mail.link_not_working_do_paste" .DisplayName AppName | Str2html}}
+
{{.i18n.Tr "mail.link_not_working_do_paste"}}
© {{AppName}}
diff --git a/templates/mail/issue/assigned.tmpl b/templates/mail/issue/assigned.tmpl
index 61e4a44f02..1c3b930978 100644
--- a/templates/mail/issue/assigned.tmpl
+++ b/templates/mail/issue/assigned.tmpl
@@ -8,7 +8,7 @@
{{.Subject}}
-{{$repo_url := printf "
%s" .Release.Repo.HTMLURL .Release.Repo.FullName}}
+{{$repo_url := printf "
%s" .Issue.Repo.HTMLURL .Issue.Repo.FullName}}
{{$link := printf "
#%d" .Link .Issue.Index}}
diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl
index 071acdcbcf..40d69839b0 100644
--- a/templates/mail/issue/default.tmpl
+++ b/templates/mail/issue/default.tmpl
@@ -62,7 +62,7 @@
{{end -}}
{{- range .ReviewComments}}
- {{.i18n.Tr "mail.issue.in_tree_path" .TreePath}}
+ {{$.i18n.Tr "mail.issue.in_tree_path" .TreePath}}
{{.Patch}}
{{.RenderedContent | Safe}}
diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl
index 1605fa91a7..c8829e9559 100644
--- a/templates/repo/branch_dropdown.tmpl
+++ b/templates/repo/branch_dropdown.tmpl
@@ -7,9 +7,9 @@
{{if $release}}
{{.root.i18n.Tr "repo.release.compare"}}
{{else}}
- {{svg "octicon-git-branch"}}
- {{if .root.IsViewBranch}}{{.root.i18n.Tr "repo.branch"}}{{else}}{{.root.i18n.Tr "repo.tree"}}{{end}}:
-
{{if .root.IsViewBranch}}{{.root.BranchName}}{{else}}{{ShortSha .root.BranchName}}{{end}}
+ {{if .root.IsViewTag}}{{svg "octicon-tag"}}{{else}}{{svg "octicon-git-branch"}}{{end}}
+ {{if .root.IsViewBranch}}{{.root.i18n.Tr "repo.branch"}}{{else if .root.IsViewTag}}{{.root.i18n.Tr "repo.tag"}}{{else}}{{.root.i18n.Tr "repo.tree"}}{{end}}:
+
{{if .root.IsViewBranch}}{{.root.BranchName}}{{else if .root.IsViewTag}}{{.root.TagName}}{{else}}{{ShortSha .root.CommitID}}{{end}}
{{end}}
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
@@ -66,8 +66,10 @@
{{if or .root.IsViewBranch $release}}
{{.root.i18n.Tr "repo.branch.create_from" .root.BranchName}}
+ {{else if .root.IsViewTag}}
+ {{.root.i18n.Tr "repo.branch.create_from" .root.TagName}}
{{else}}
- {{.root.i18n.Tr "repo.branch.create_from" (ShortSha .root.BranchName)}}
+ {{.root.i18n.Tr "repo.branch.create_from" (ShortSha .root.CommitID)}}
{{end}}
diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl
index 66138e2138..6f41e6d693 100644
--- a/templates/repo/commits_list.tmpl
+++ b/templates/repo/commits_list.tmpl
@@ -76,7 +76,11 @@
{{RenderCommitBody .Message $.RepoLink $.Repository.ComposeMetas}}
{{end}}
-
{{TimeSince .Author.When $.Lang}} |
+ {{if .Committer}}
+
{{TimeSince .Committer.When $.Lang}} |
+ {{else}}
+
{{TimeSince .Author.When $.Lang}} |
+ {{end}}
{{end}}
diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl
index 3ef2339843..7b287c1f10 100644
--- a/templates/repo/commits_table.tmpl
+++ b/templates/repo/commits_table.tmpl
@@ -1,9 +1,9 @@