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+"]]",
 		"

\""+result+"\"

") } + +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 @@