mirror of
https://github.com/go-gitea/gitea
synced 2024-12-22 16:44:26 +00:00
Merge branch 'main' into lunny/refactor-issue
This commit is contained in:
commit
29a65b3aa1
104
.eslintrc.yaml
104
.eslintrc.yaml
@ -16,10 +16,10 @@ parserOptions:
|
||||
parser: "@typescript-eslint/parser" # for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
|
||||
|
||||
settings:
|
||||
import/extensions: [".js", ".ts"]
|
||||
import/parsers:
|
||||
import-x/extensions: [".js", ".ts"]
|
||||
import-x/parsers:
|
||||
"@typescript-eslint/parser": [".js", ".ts"]
|
||||
import/resolver:
|
||||
import-x/resolver:
|
||||
typescript: true
|
||||
|
||||
plugins:
|
||||
@ -28,7 +28,7 @@ plugins:
|
||||
- "@typescript-eslint/eslint-plugin"
|
||||
- eslint-plugin-array-func
|
||||
- eslint-plugin-github
|
||||
- eslint-plugin-i
|
||||
- eslint-plugin-import-x
|
||||
- eslint-plugin-no-jquery
|
||||
- eslint-plugin-no-use-extend-native
|
||||
- eslint-plugin-regexp
|
||||
@ -58,15 +58,15 @@ overrides:
|
||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
|
||||
- files: ["*.config.*"]
|
||||
rules:
|
||||
i/no-unused-modules: [0]
|
||||
import-x/no-unused-modules: [0]
|
||||
- files: ["**/*.d.ts"]
|
||||
rules:
|
||||
i/no-unused-modules: [0]
|
||||
import-x/no-unused-modules: [0]
|
||||
"@typescript-eslint/consistent-type-definitions": [0]
|
||||
"@typescript-eslint/consistent-type-imports": [0]
|
||||
- files: ["web_src/js/types.ts"]
|
||||
rules:
|
||||
i/no-unused-modules: [0]
|
||||
import-x/no-unused-modules: [0]
|
||||
- files: ["**/*.test.*", "web_src/js/test/setup.ts"]
|
||||
env:
|
||||
vitest-globals/env: true
|
||||
@ -394,49 +394,49 @@ rules:
|
||||
id-blacklist: [0]
|
||||
id-length: [0]
|
||||
id-match: [0]
|
||||
i/consistent-type-specifier-style: [0]
|
||||
i/default: [0]
|
||||
i/dynamic-import-chunkname: [0]
|
||||
i/export: [2]
|
||||
i/exports-last: [0]
|
||||
i/extensions: [2, always, {ignorePackages: true}]
|
||||
i/first: [2]
|
||||
i/group-exports: [0]
|
||||
i/max-dependencies: [0]
|
||||
i/named: [2]
|
||||
i/namespace: [0]
|
||||
i/newline-after-import: [0]
|
||||
i/no-absolute-path: [0]
|
||||
i/no-amd: [2]
|
||||
i/no-anonymous-default-export: [0]
|
||||
i/no-commonjs: [2]
|
||||
i/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
|
||||
i/no-default-export: [0]
|
||||
i/no-deprecated: [0]
|
||||
i/no-dynamic-require: [0]
|
||||
i/no-empty-named-blocks: [2]
|
||||
i/no-extraneous-dependencies: [2]
|
||||
i/no-import-module-exports: [0]
|
||||
i/no-internal-modules: [0]
|
||||
i/no-mutable-exports: [0]
|
||||
i/no-named-as-default-member: [0]
|
||||
i/no-named-as-default: [0]
|
||||
i/no-named-default: [0]
|
||||
i/no-named-export: [0]
|
||||
i/no-namespace: [0]
|
||||
i/no-nodejs-modules: [0]
|
||||
i/no-relative-packages: [0]
|
||||
i/no-relative-parent-imports: [0]
|
||||
i/no-restricted-paths: [0]
|
||||
i/no-self-import: [2]
|
||||
i/no-unassigned-import: [0]
|
||||
i/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}]
|
||||
i/no-unused-modules: [2, {unusedExports: true}]
|
||||
i/no-useless-path-segments: [2, {commonjs: true}]
|
||||
i/no-webpack-loader-syntax: [2]
|
||||
i/order: [0]
|
||||
i/prefer-default-export: [0]
|
||||
i/unambiguous: [0]
|
||||
import-x/consistent-type-specifier-style: [0]
|
||||
import-x/default: [0]
|
||||
import-x/dynamic-import-chunkname: [0]
|
||||
import-x/export: [2]
|
||||
import-x/exports-last: [0]
|
||||
import-x/extensions: [2, always, {ignorePackages: true}]
|
||||
import-x/first: [2]
|
||||
import-x/group-exports: [0]
|
||||
import-x/max-dependencies: [0]
|
||||
import-x/named: [2]
|
||||
import-x/namespace: [0]
|
||||
import-x/newline-after-import: [0]
|
||||
import-x/no-absolute-path: [0]
|
||||
import-x/no-amd: [2]
|
||||
import-x/no-anonymous-default-export: [0]
|
||||
import-x/no-commonjs: [2]
|
||||
import-x/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
|
||||
import-x/no-default-export: [0]
|
||||
import-x/no-deprecated: [0]
|
||||
import-x/no-dynamic-require: [0]
|
||||
import-x/no-empty-named-blocks: [2]
|
||||
import-x/no-extraneous-dependencies: [2]
|
||||
import-x/no-import-module-exports: [0]
|
||||
import-x/no-internal-modules: [0]
|
||||
import-x/no-mutable-exports: [0]
|
||||
import-x/no-named-as-default-member: [0]
|
||||
import-x/no-named-as-default: [0]
|
||||
import-x/no-named-default: [0]
|
||||
import-x/no-named-export: [0]
|
||||
import-x/no-namespace: [0]
|
||||
import-x/no-nodejs-modules: [0]
|
||||
import-x/no-relative-packages: [0]
|
||||
import-x/no-relative-parent-imports: [0]
|
||||
import-x/no-restricted-paths: [0]
|
||||
import-x/no-self-import: [2]
|
||||
import-x/no-unassigned-import: [0]
|
||||
import-x/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}]
|
||||
import-x/no-unused-modules: [2, {unusedExports: true}]
|
||||
import-x/no-useless-path-segments: [2, {commonjs: true}]
|
||||
import-x/no-webpack-loader-syntax: [2]
|
||||
import-x/order: [0]
|
||||
import-x/prefer-default-export: [0]
|
||||
import-x/unambiguous: [0]
|
||||
init-declarations: [0]
|
||||
line-comment-position: [0]
|
||||
logical-assignment-operators: [0]
|
||||
@ -524,6 +524,7 @@ rules:
|
||||
no-jquery/no-data: [0]
|
||||
no-jquery/no-deferred: [2]
|
||||
no-jquery/no-delegate: [2]
|
||||
no-jquery/no-done-fail: [2]
|
||||
no-jquery/no-each-collection: [0]
|
||||
no-jquery/no-each-util: [0]
|
||||
no-jquery/no-each: [0]
|
||||
@ -538,6 +539,7 @@ rules:
|
||||
no-jquery/no-find-util: [2]
|
||||
no-jquery/no-find: [0]
|
||||
no-jquery/no-fx-interval: [2]
|
||||
no-jquery/no-fx: [2]
|
||||
no-jquery/no-global-eval: [2]
|
||||
no-jquery/no-global-selector: [0]
|
||||
no-jquery/no-grep: [2]
|
||||
@ -816,7 +818,7 @@ rules:
|
||||
unicorn/consistent-destructuring: [2]
|
||||
unicorn/consistent-empty-array-spread: [2]
|
||||
unicorn/consistent-existence-index-check: [0]
|
||||
unicorn/consistent-function-scoping: [2]
|
||||
unicorn/consistent-function-scoping: [0]
|
||||
unicorn/custom-error-definition: [0]
|
||||
unicorn/empty-brace-spaces: [2]
|
||||
unicorn/error-message: [0]
|
||||
|
2
.github/actionlint.yaml
vendored
2
.github/actionlint.yaml
vendored
@ -3,3 +3,5 @@ self-hosted-runner:
|
||||
- actuated-4cpu-8gb
|
||||
- actuated-4cpu-16gb
|
||||
- nscloud
|
||||
- namespace-profile-gitea-release-docker
|
||||
- namespace-profile-gitea-release-binary
|
||||
|
6
.github/workflows/release-nightly.yml
vendored
6
.github/workflows/release-nightly.yml
vendored
@ -10,7 +10,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
nightly-binary:
|
||||
runs-on: nscloud
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
@ -58,7 +58,7 @@ jobs:
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
nightly-docker-rootful:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
@ -95,7 +95,7 @@ jobs:
|
||||
push: true
|
||||
tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
||||
nightly-docker-rootless:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
6
.github/workflows/release-tag-rc.yml
vendored
6
.github/workflows/release-tag-rc.yml
vendored
@ -11,7 +11,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
binary:
|
||||
runs-on: nscloud
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
@ -68,7 +68,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
@ -99,7 +99,7 @@ jobs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
docker-rootless:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
6
.github/workflows/release-tag-version.yml
vendored
6
.github/workflows/release-tag-version.yml
vendored
@ -13,7 +13,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
binary:
|
||||
runs-on: nscloud
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
@ -70,7 +70,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
@ -105,7 +105,7 @@ jobs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
docker-rootless:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,6 +28,7 @@ _testmain.go
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
*.tsbuildinfo
|
||||
|
||||
*coverage.out
|
||||
coverage.all
|
||||
|
@ -19,6 +19,8 @@ linters:
|
||||
- revive
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- tenv
|
||||
- testifylint
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unused
|
||||
@ -34,6 +36,10 @@ output:
|
||||
show-stats: true
|
||||
|
||||
linters-settings:
|
||||
testifylint:
|
||||
disable:
|
||||
- go-require
|
||||
- require-error
|
||||
stylecheck:
|
||||
checks: ["all", "-ST1005", "-ST1003"]
|
||||
nakedret:
|
||||
|
393
CHANGELOG.md
393
CHANGELOG.md
@ -4,6 +4,399 @@ This changelog goes through 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.com).
|
||||
|
||||
## [1.22.4](https://github.com/go-gitea/gitea/releases/tag/v1.22.4) - 2024-11-14
|
||||
|
||||
* SECURITY
|
||||
* Fix basic auth with webauthn (#32531) (#32536)
|
||||
* Refactor internal routers (partial backport, auth token const time comparing) (#32473) (#32479)
|
||||
* PERFORMANCE
|
||||
* Remove transaction for archive download (#32186) (#32520)
|
||||
* BUGFIXES
|
||||
* Fix `missing signature key` error when pulling Docker images with `SERVE_DIRECT` enabled (#32365) (#32397)
|
||||
* Fix get reviewers fails when selecting user without pull request permissions unit (#32415) (#32616)
|
||||
* Fix adding index files to tmp directory (#32360) (#32593)
|
||||
* Fix PR creation on forked repositories via API (#31863) (#32591)
|
||||
* Fix missing menu tabs in organization project view page (#32313) (#32592)
|
||||
* Support HTTP POST requests to `/userinfo`, aligning to OpenID Core specification (#32578) (#32594)
|
||||
* Fix debian package clean up cron job (#32351) (#32590)
|
||||
* Fix GetInactiveUsers (#32540) (#32588)
|
||||
* Allow the actions user to login via the jwt token (#32527) (#32580)
|
||||
* Fix submodule parsing (#32571) (#32577)
|
||||
* Refactor find forks and fix possible bugs that weaken permissions check (#32528) (#32547)
|
||||
* Fix some places that don't respect org full name setting (#32243) (#32550)
|
||||
* Refactor push mirror find and add check for updating push mirror (#32539) (#32549)
|
||||
* Fix basic auth with webauthn (#32531) (#32536)
|
||||
* Fix artifact v4 upload above 8MB (#31664) (#32523)
|
||||
* Fix oauth2 error handle not return immediately (#32514) (#32516)
|
||||
* Fix action not triggered when commit message is too long (#32498) (#32507)
|
||||
* Fix `GetRepoLink` nil pointer dereference on dashboard feed page when repo is deleted with actions enabled (#32501) (#32502)
|
||||
* Fix `missing signature key` error when pulling Docker images with `SERVE_DIRECT` enabled (#32397) (#32397)
|
||||
* Fix the permission check for user search API and limit the number of returned users for `/user/search` (#32310)
|
||||
* Fix SearchIssues swagger docs (#32208) (#32298)
|
||||
* Fix dropdown content overflow (#31610) (#32250)
|
||||
* Disable Oauth check if oauth disabled (#32368) (#32480)
|
||||
* Respect renamed dependencies of Cargo registry (#32430) (#32478)
|
||||
* Fix mermaid diagram height when initially hidden (#32457) (#32464)
|
||||
* Fix broken releases when re-pushing tags (#32435) (#32449)
|
||||
* Only provide the commit summary for Discord webhook push events (#32432) (#32447)
|
||||
* Only query team tables if repository is under org when getting assignees (#32414) (#32426)
|
||||
* Fix created_unix for mirroring (#32342) (#32406)
|
||||
* Respect UI.ExploreDefaultSort setting again (#32357) (#32385)
|
||||
* Fix broken image when editing comment with non-image attachments (#32319) (#32345)
|
||||
* Fix disable 2fa bug (#32320) (#32330)
|
||||
* Always update expiration time when creating an artifact (#32281) (#32285)
|
||||
* Fix null errors on conversation holder (#32258) (#32266) (#32282)
|
||||
* Only rename a user when they should receive a different name (#32247) (#32249)
|
||||
* Fix checkbox bug on private/archive filter (#32236) (#32240)
|
||||
* Add a doctor check to disable the "Actions" unit for mirrors (#32424) (#32497)
|
||||
* Quick fix milestone deadline 9999 (#32423)
|
||||
* Make `show stats` work when only one file changed (#32244) (#32268)
|
||||
* Make `owner/repo/pulls` handlers use "PR reader" permission (#32254) (#32265)
|
||||
* Update scheduled tasks even if changes are pushed by "ActionsUser" (#32246) (#32252)
|
||||
* MISC
|
||||
* Remove unnecessary code: `GetPushMirrorsByRepoID` called on all repo pages (#32560) (#32567)
|
||||
* Improve some sanitizer rules (#32534)
|
||||
* Update nix development environment vor v1.22.x (#32495)
|
||||
* Add warn log when deleting inactive users (#32318) (#32321)
|
||||
* Update github.com/go-enry/go-enry to v2.9.1 (#32295) (#32296)
|
||||
* Warn users when they try to use a non-root-url to sign in/up (#32272) (#32273)
|
||||
|
||||
## [1.22.3](https://github.com/go-gitea/gitea/releases/tag/v1.22.3) - 2024-10-08
|
||||
|
||||
* SECURITY
|
||||
* Fix bug when a token is given public only (#32204) (#32218)
|
||||
* PERFORMANCE
|
||||
* Increase `cacheContextLifetime` to reduce false reports (#32011) (#32023)
|
||||
* Don't join repository when loading action table data (#32127) (#32143)
|
||||
* BUGFIXES
|
||||
* Fix javascript error when an anonymous user visits migration page (#32144) (#32179)
|
||||
* Don't init signing keys if oauth2 provider is disabled (#32177)
|
||||
* Fix wrong status of `Set up Job` when first step is skipped (#32120) (#32125)
|
||||
* Fix bug when deleting a migrated branch (#32075) (#32123)
|
||||
* Truncate commit message during Discord webhook push events (#31970) (#32121)
|
||||
* Allow to set branch protection in an empty repository (#32095) (#32119)
|
||||
* Fix panic when cloning with wrong ssh format. (#32076) (#32118)
|
||||
* Fix rename branch permission bug (#32066) (#32108)
|
||||
* Fix: database not update release when using `git push --tags --force` (#32040) (#32074)
|
||||
* Add missing comment reply handling (#32050) (#32065)
|
||||
* Do not escape relative path in RPM primary index (#32038) (#32054)
|
||||
* Fix `/repos/{owner}/{repo}/pulls/{index}/files` endpoint not populating `previous_filename` (#32017) (#32028)
|
||||
* Support allowed hosts for migrations to work with proxy (#32025) (#32026)
|
||||
* Fix the logic of finding the latest pull review commit ID (#32139) (#32165)
|
||||
* Fix bug in getting merged pull request by commit (#32079) (#32117)
|
||||
* Fix wrong last modify time (#32102) (#32104)
|
||||
* Fix incorrect `/tokens` api (#32085) (#32092)
|
||||
* Handle invalid target when creating releases using API (#31841) (#32043)
|
||||
* Check if the `due_date` is nil when editing issues (#32035) (#32042)
|
||||
* Fix container parallel upload bugs (#32022)
|
||||
* Fixed race condition when deleting documents by repoId in ElasticSearch (#32185) (#32188)
|
||||
* Refactor CSRF protector (#32057) (#32069)
|
||||
* Fix Bug in Issue/pulls list (#32081) (#32115)
|
||||
* Include collaboration repositories on dashboard source/forks/mirrors list (#31946) (#32122)
|
||||
* Add null check for responseData.invalidTopics (#32212) (#32217)
|
||||
* TESTING
|
||||
* Fix mssql ci with a new mssql version on ci (#32094)
|
||||
* MISC
|
||||
* Upgrade some dependencies include minio-go (#32166)
|
||||
* Add bin to Composer Metadata (#32099) (#32106)
|
||||
* Lazy load avatar images (#32051) (#32063)
|
||||
* Upgrade cache to v0.2.1 (#32003) (#32009)
|
||||
|
||||
## [1.22.2](https://github.com/go-gitea/gitea/releases/tag/v1.22.2) - 2024-08-28
|
||||
|
||||
* Security
|
||||
* Replace v-html with v-text in search inputbox (#31966) (#31973)
|
||||
* Fix nuget/conan/container packages upload bugs (#31967) (#31982)
|
||||
* PERFORMANCE
|
||||
* Refactor the usage of batch catfile (#31754) (#31889)
|
||||
* BUGFIXES
|
||||
* Fix overflowing content in action run log (#31842) (#31853)
|
||||
* Scroll images in project issues separately from the remaining issue (#31683) (#31823)
|
||||
* Add `:focus-visible` style to buttons (#31799) (#31819)
|
||||
* Fix the display of project type for deleted projects (#31732) (#31734)
|
||||
* Fix API owner ID should be zero when created repo secret (#31715) (#31811)
|
||||
* Set owner id to zero when GetRegistrationToken for repo (#31725) (#31729)
|
||||
* Fix API endpoint for registration-token (#31722) (#31728)
|
||||
* Add permission check when creating PR (#31033) (#31720)
|
||||
* Don't return 500 if mirror url contains special chars (#31859) (#31895)
|
||||
* Fix agit automerge (#31207) (#31881)
|
||||
* Add CfTurnstileSitekey context data to all captcha templates (#31874) (#31876)
|
||||
* Avoid returning without written ctx when posting PR (#31843) (#31848)
|
||||
* Fix raw wiki links (#31825) (#31845)
|
||||
* Fix panic of ssh public key page after deletion of auth source (#31829) (#31836)
|
||||
* Fixes for unreachable project issues when transfer repository from organization (#31770) (#31828)
|
||||
* Show lock owner instead of repo owner on LFS setting page (#31788) (#31817)
|
||||
* Fix `IsObjectExist` with gogit (#31790) (#31806)
|
||||
* Fix protected branch files detection on pre_receive hook (#31778) (#31796)
|
||||
* Add `TAGS` to `TEST_TAGS` and fix bugs found with gogit (#31791) (#31795)
|
||||
* Rename head branch of pull requests when renaming a branch (#31759) (#31774)
|
||||
* Fix wiki revision pagination (#31760) (#31772)
|
||||
* Bump vue-bar-graph (#31705) (#31753)
|
||||
* Distinguish LFS object errors to ignore missing objects during migration (#31702) (#31745)
|
||||
* Make GetRepositoryByName more safer (#31712) (#31718)
|
||||
* Fix a branch divergence cache bug (#31659) (#31661)
|
||||
* Allow org team names of length 255 in create team form (#31564) (#31603)
|
||||
* Use old behavior for telegram webhook (#31588)
|
||||
* Bug fix for translation in ru (#31892)
|
||||
* Fix actions notify bug (#31866) (#31875)
|
||||
* Fix the component of access token list not mounted (#31824) (#31868)
|
||||
* Add missing repository type filter parameters to pager (#31832) (#31837)
|
||||
* Fix dates displaying in a wrong manner when we're close to the end of… (#31750)
|
||||
* Fix "Filter by commit" Dropdown (#31695) (#31696)
|
||||
* Properly filter issue list given no assignees filter (#31522) (#31685)
|
||||
* Prevent update pull refs manually and will not affect other refs update (#31931)(#31955)
|
||||
* Fix sort order for organization home and user profile page (#31921) (#31922)
|
||||
* Fix search team (#31923) (#31942)
|
||||
* Fix 500 error when state params is set when editing issue/PR by API (#31880) (#31952)
|
||||
* Fix index too many file names bug (#31903) (#31953)
|
||||
* Add lock for parallel maven upload (#31851) (#31954)
|
||||
* MISC
|
||||
* Remove "dsa-1024" testcases from Test_SSHParsePublicKey and Test_calcFingerprint (#31905) (#31914)
|
||||
* Upgrade bleve to 2.4.2 (#31894)
|
||||
* Remove unneccessary uses of `word-break: break-all` (#31637) (#31652)
|
||||
* Return an empty string when a repo has no avatar in the repo API (#31187) (#31567)
|
||||
* Upgrade micromatch to 4.0.8 (#31944)
|
||||
* Update webpack to 5.94.0 (#31941)
|
||||
|
||||
## [1.22.1](https://github.com/go-gitea/gitea/releases/tag/v1.22.1) - 2024-07-04
|
||||
|
||||
* SECURITY
|
||||
* Add replacement module for `mholt/archiver` (#31267) (#31270)
|
||||
* API
|
||||
* Fix missing images in editor preview due to wrong links (#31299) (#31393)
|
||||
* Fix duplicate sub-path for avatars (#31365) (#31368)
|
||||
* Reduce memory usage for chunked artifact uploads to MinIO (#31325) (#31338)
|
||||
* Remove sub-path from container registry realm (#31293) (#31300)
|
||||
* Fix NuGet Package API for $filter with Id equality (#31188) (#31242)
|
||||
* Add an immutable tarball link to archive download headers for Nix (#31139) (#31145)
|
||||
* Add missed return after `ctx.ServerError` (#31130) (#31133)
|
||||
* BUGFIXES
|
||||
* Fix avatar radius problem on the new issue page (#31506) (#31508)
|
||||
* Fix overflow menu flickering on mobile (#31484) (#31488)
|
||||
* Fix poor table column width due to breaking words (#31473) (#31477)
|
||||
* Support relative paths to videos from Wiki pages (#31061) (#31453)
|
||||
* Fix new issue/pr avatar (#31419) (#31424)
|
||||
* Increase max length of org team names from 30 to 255 characters (#31410) (#31421)
|
||||
* Fix line number width in code preview (#31307) (#31316)
|
||||
* Optimize runner-tags layout to enhance visual experience (#31258) (#31263)
|
||||
* Fix overflow on push notification (#31179) (#31238)
|
||||
* Fix overflow on notifications (#31178) (#31237)
|
||||
* Fix overflow in issue card (#31203) (#31225)
|
||||
* Split sanitizer functions and fine-tune some tests (#31192) (#31200)
|
||||
* use correct l10n string (#31487) (#31490)
|
||||
* Fix dropzone JS error when attachment is disabled (#31486)
|
||||
* Fix web notification icon not updated once you read all notifications (#31447) (#31466)
|
||||
* Switch to "Write" tab when edit comment again (#31445) (#31461)
|
||||
* Fix the link for .git-blame-ignore-revs bypass (#31432) (#31442)
|
||||
* Fix the wrong line number in the diff view page when expanded twice. (#31431) (#31440)
|
||||
* Fix labels and projects menu overflow on issue page (#31435) (#31439)
|
||||
* Fix Account Linking UpdateMigrationsByType (#31428) (#31434)
|
||||
* Fix markdown math brackets render problem (#31420) (#31430)
|
||||
* Fix rendered wiki page link (#31398) (#31407)
|
||||
* Fix natural sort (#31384) (#31394)
|
||||
* Allow downloading attachments of draft releases (#31369) (#31380)
|
||||
* Fix repo graph JS (#31377)
|
||||
* Fix incorrect localization `explorer.go` (#31348) (#31350)
|
||||
* Fix hash render end with colon (#31319) (#31346)
|
||||
* Fix line number widths (#31341) (#31343)
|
||||
* Fix navbar `+` menu flashing on page load (#31281) (#31342)
|
||||
* Fix adopt repository has empty object name in database (#31333) (#31335)
|
||||
* Delete legacy cookie before setting new cookie (#31306) (#31317)
|
||||
* Fix some URLs whose sub-path is missing (#31289) (#31292)
|
||||
* Fix admin oauth2 custom URL settings (#31246) (#31247)
|
||||
* Make pasted "img" tag has the same behavior as markdown image (#31235) (#31243)
|
||||
* Fix agit checkout command line hint & fix ShowMergeInstructions checking (#31219) (#31222)
|
||||
* Fix the possible migration failure on 286 with postgres 16 (#31209) (#31218)
|
||||
* Fix branch order (#31174) (#31193)
|
||||
* Fix markup preview (#31158) (#31166)
|
||||
* Fix push multiple branches error with tests (#31151) (#31153)
|
||||
* Fix API repository object format missed (#31118) (#31132)
|
||||
* Fix missing memcache import (#31105) (#31109)
|
||||
* Upgrade `github.com/hashicorp/go-retryablehttp` (#31499)
|
||||
* Fix double border in system status table (#31363) (#31401)
|
||||
* Fix bug filtering issues which have no project (#31337) (#31367)
|
||||
* Fix #31185 try fix lfs download from bitbucket failed (#31201) (#31329)
|
||||
* Add nix flake for dev shell (#30967) (#31310)
|
||||
* Fix and clean up `ConfirmModal` (#31283) (#31291)
|
||||
* Optimize repo-list layout to enhance visual experience (#31272) (#31276)
|
||||
* fixed the dropdown menu for the top New button to expand to the left (#31273) (#31275)
|
||||
* Fix Activity Page Contributors dropdown (#31264) (#31269)
|
||||
* fix: allow actions artifacts storage migration to complete succesfully (#31251) (#31257)
|
||||
* Make blockquote attention recognize more syntaxes (#31240) (#31250)
|
||||
* Remove .segment from .project-column (#31204) (#31239)
|
||||
* Ignore FindRecentlyPushedNewBranches err (#31164) (#31171)
|
||||
* Use vertical layout for multiple code expander buttons (#31122) (#31152)
|
||||
* Remove duplicate `ProxyPreserveHost` in Apache httpd doc (#31143) (#31147)
|
||||
* Improve mobile review ui (#31091) (#31136)
|
||||
* Fix DashboardRepoList margin (#31121) (#31128)
|
||||
* Update pip related commands for docker (#31106) (#31111)
|
||||
|
||||
## [1.22.0](https://github.com/go-gitea/gitea/releases/tag/v1.22.0) - 2024-05-27
|
||||
|
||||
This release stands as a monumental milestone in our development journey with a record-breaking incorporation of [1528](https://github.com/go-gitea/gitea/pulls?q=is%3Apr+milestone%3A1.22.0+is%3Amerged) pull requests. It marks the most extensive update in Gitea's history, showcasing a plethora of new features and infrastructure improvements.
|
||||
|
||||
Noteworthy advancements in this release include the introduction of `HTMX` and `Tailwind`, signaling a strategic shift as we gradually phase out `jquery` and `Fomantic UI`. These changes reflect our commitment to embracing modern technologies and enhancing the user experience.
|
||||
|
||||
Key highlights of this release encompass significant changes categorized under `BREAKING`, `FEATURES`, `ENHANCEMENTS`, and `PERFORMANCE`, each contributing to a more robust and efficient Gitea platform.
|
||||
|
||||
* BREAKING
|
||||
* Improve reverse proxy documents and clarify the AppURL guessing behavior (#31003) (#31020)
|
||||
* Remember log in for a month by default (#30150)
|
||||
* Breaking summary for template refactoring (#29395)
|
||||
* All custom templates need to follow these changes
|
||||
* Recommend/convert to use case-sensitive collation for MySQL/MSSQL (#28662)
|
||||
* Make offline mode as default to not connect external avatar service by default (#28548)
|
||||
* Include public repos in the doer's dashboard for issue search (#28304)
|
||||
* Use restricted sanitizer for repository description (#28141)
|
||||
* Support storage base path as prefix (#27827)
|
||||
* Enhanced auth token / remember me (#27606)
|
||||
* Rename the default themes to `gitea-light`, `gitea-dark`, `gitea-auto` (#27419)
|
||||
* If you didn't see the new themes, please remove the `[ui].THEMES` config option from `app.ini`
|
||||
* Require MySQL 8.0, PostgreSQL 12, MSSQL 2012 (#27337)
|
||||
* FEATURES
|
||||
* Allow everyone to read or write a wiki by a repo unit setting (#30495)
|
||||
* Use raw Wiki links for non-renderable Wiki files (#30273)
|
||||
* Render embedded code preview by permalink in markdown (#30234) (#30249)
|
||||
* Support repo code search without setting up an indexer (#29998)
|
||||
* Support pasting URLs over markdown text (#29566)
|
||||
* Allow to change primary email before account activation (#29412)
|
||||
* Customizable "Open with" applications for repository clone (#29320)
|
||||
* Allow options to disable user deletion from the interface on app.ini (#29275)
|
||||
* Extend issue template YAML engine (#29274)
|
||||
* Add support for `linguist-detectable` and `linguist-documentation` (#29267)
|
||||
* Implement code frequency graph (#29191)
|
||||
* Show commit status for releases (#29149)
|
||||
* Add user blocking (#29028)
|
||||
* Actions Artifacts v4 backend (#28965)
|
||||
* Add merge style `fast-forward-only` (#28954)
|
||||
* Retarget depending pulls when the parent branch is deleted (#28686)
|
||||
* Add global setting on how timestamps should be rendered (#28657)
|
||||
* Implement actions badge SVGs (#28102)
|
||||
* Add skip ci functionality (#28075)
|
||||
* Show latest commit for file (#28067)
|
||||
* Allow to sync tags from the admin dashboard (#28045)
|
||||
* Add Profile Readme for Organisations (#27955)
|
||||
* Implement contributors graph (#27882)
|
||||
* Artifact deletion in actions ui (#27172)
|
||||
* Add API routes to get runner registration token (#27144)
|
||||
* Add support for forking single branch (#25821)
|
||||
* Add support for sha256 repositories (#23894)
|
||||
* Add admin API route for managing user's badges (#23106)
|
||||
* ENHANCEMENTS
|
||||
* Make gitea webhooks openproject compatible (#28435) (#31081)
|
||||
* Support using label names when changing issue labels (#30943) (#30958)
|
||||
* Fix various problems around project board view (#30696) (#30902)
|
||||
* Improve context popup rendering (#30824) (#30829)
|
||||
* Allow to save empty comment (#30706)
|
||||
* Prevent allow/reject reviews on merged/closed PRs (#30686)
|
||||
* Initial support for colorblindness-friendly themes (#30625)
|
||||
* Some NuGet package enhancements (#30280) (#30324)
|
||||
* Markup color and font size fixes (#30282) (#30310)
|
||||
* Show 12 lines in markup code preview (#30255) (#30257)
|
||||
* Add `[other].SHOW_FOOTER_POWERED_BY` setting to hide `Powered by` (#30253)
|
||||
* Pulse page improvements (#30149)
|
||||
* Render code tags in commit messages (#30146)
|
||||
* Prevent re-review and dismiss review actions on closed and merged PRs (#30065)
|
||||
* Cancel previous runs of the same PR automatically (#29961)
|
||||
* Drag-and-drop improvements for projects and issue pins (#29875)
|
||||
* Add default board to new projects, remove uncategorized pseudo-board (#29874)
|
||||
* Prevent layout shift in `<overflow-menu>` items (#29831)
|
||||
* Add skip ci support for pull request title (#29774)
|
||||
* Add more stats tables (#29730)
|
||||
* Update API to return 'source_id' for users (#29718)
|
||||
* Determine fuzziness of bleve indexer by keyword length (#29706)
|
||||
* Expose fuzzy search for issues/pulls (#29701)
|
||||
* Put an edit file button on pull request files to allow a quick operation (#29697)
|
||||
* Fix action runner offline label padding (#29691)
|
||||
* Update allowed attachment types (#29688)
|
||||
* Completely style the webkit autofill (#29683)
|
||||
* Highlight archived labels (#29680)
|
||||
* Add a warning for disallowed email domains (#29658)
|
||||
* Set user's 24h preference from their current OS locale (#29651)
|
||||
* Add setting to disable user features when user login type is not plain (#29615)
|
||||
* Improve natural sort (#29611)
|
||||
* Make wiki default branch name changeable (#29603)
|
||||
* Unify search boxes (#29530)
|
||||
* Add support for API blob upload of release attachments (#29507)
|
||||
* Detect broken git hooks (#29494)
|
||||
* Sync branches to DB immediately when handling git hook calling (#29493)
|
||||
* Allow options to disable user GPG key configuration from the interface on app.ini (#29486)
|
||||
* Allow options to disable user SSH key configuration from the interface on app.ini (#29447)
|
||||
* Use relative links for commits, mentions, and issues in markdown (#29427)
|
||||
* Add `<overflow-menu>`, rename webcomponents (#29400)
|
||||
* Include resource state events in Gitlab downloads (#29382)
|
||||
* Properly migrate target branch change GitLab comment (#29340)
|
||||
* Recolor dark theme to blue shade (#29283)
|
||||
* Partially enable MSSQL case-sensitive collation support (#29238)
|
||||
* Auto-update the system status in the admin dashboard (#29163)
|
||||
* Integrate alpine `noarch` packages into other architectures index (#29137)
|
||||
* Document how the TOC election process works (#29135)
|
||||
* Tweak repo header (#29134)
|
||||
* Make blockquote border size less aggressive (#29124)
|
||||
* Downscale pasted PNG images based on metadata (#29123)
|
||||
* Show `View at this point in history` for every commit (#29122)
|
||||
* Add support for action artifact serve direct (#29120)
|
||||
* Change webhook-type in create-view (#29114)
|
||||
* Drop "@" from the email sender to avoid spam filters (#29109)
|
||||
* Allow non-admin users to delete review requests (#29057)
|
||||
* Improve user search display name (#29002)
|
||||
* Include username in email headers (#28981)
|
||||
* Show whether a PR is WIP inside popups (#28975)
|
||||
* Also match weakly validated ETags (#28957)
|
||||
* Support nuspec manifest download for Nuget packages (#28921)
|
||||
* Fix hardcoded GitHub icon used as migrated release avatar (#28910)
|
||||
* Propagate install_if and provider_priority to APKINDEX (#28899)
|
||||
* Add artifacts v4 JWT to job message and accept it (#28885)
|
||||
* Enable/disable owner and repo projects independently (#28805)
|
||||
* Add non-JS fallback for reaction tooltips (#28785)
|
||||
* Add the ability to see open and closed issues at the same time (#28757)
|
||||
* Move sign-in labels to be above inputs (#28753)
|
||||
* Display the latest sync time for pull mirrors on the repo page (#28712)
|
||||
* Show in Web UI if the file is vendored and generated (#28620)
|
||||
* Add orphaned topic consistency check (#28507)
|
||||
* Add branch protection setting for ignoring stale approvals (#28498)
|
||||
* Add option to set language in admin user view (#28449)
|
||||
* Fix incorrect run order of action jobs (#28367)
|
||||
* Add missing exclusive in advanced label options (#28322)
|
||||
* Added instance-level variables (#28115)
|
||||
* Add edit option for README.md (#28071)
|
||||
* Fix link to `Code` tab on wiki commits (#28041)
|
||||
* Allow to set explore page default sort (#27951)
|
||||
* Improve PR diff view on mobile (#27883)
|
||||
* Properly migrate automatic merge GitLab comments (#27873)
|
||||
* Display issue task list on project cards (#27865)
|
||||
* Add Index to pull_auto_merge.doer_id (#27811)
|
||||
* Fix display member unit in the menu bar if there are no hidden members in public org (#27795)
|
||||
* List all Debian package versions in `Packages` (#27786)
|
||||
* Allow pull requests Manually Merged option to be used by non-admins (#27780)
|
||||
* Only show diff file tree when more than one file changed (#27775)
|
||||
* Show placeholder email in privacy popup (#27770)
|
||||
* Revamp repo header (#27760)
|
||||
* Add `must-change-password` command line parameter (#27626)
|
||||
* Unify password changing and invalidate auth tokens (#27625)
|
||||
* Add border to file tree 'sub-items' and add padding to 'item-file' (#27593)
|
||||
* Add slow SQL query warning (#27545)
|
||||
* Pre-register OAuth application for tea (#27509)
|
||||
* Differentiate between `push` and `pull` `mirror sync in progress` (#27390)
|
||||
* Link to file from its history (#27354)
|
||||
* Add a shortcut to user's profile page to admin user details (#27299)
|
||||
* Doctor: delete action entries without existing user (#27292)
|
||||
* Show total TrackedTime on issue/pull/milestone lists (#26672)
|
||||
* Don't show the new pull request button when the page is not compare pull (#26431)
|
||||
* Add `Hide/Show all checks` button to commit status check (#26284)
|
||||
* Improvements of releases list and tags list (#25859)
|
||||
* PERFORMANCE
|
||||
* Fix package list performance (#30520) (#30616)
|
||||
* Add commit status summary table to reduce query from commit status table (#30223)
|
||||
* Refactor markup/csv: don't read all to memory (#29760)
|
||||
* Lazy load object format with command line and don't do it in OpenRepository (#29712)
|
||||
* Add cache for branch divergence on branch list page (#29577)
|
||||
* Do some performance optimization for issues list and view issue/pull (#29515)
|
||||
* Cache repository default branch commit status to reduce query on commit status table (#29444)
|
||||
* Use `crypto/sha256` (#29386)
|
||||
* Some performance optimization on the dashboard and issues page (#29010)
|
||||
* Add combined index for issue_user.uid and issue_id (#28080)
|
||||
|
||||
## [1.21.11](https://github.com/go-gitea/gitea/releases/tag/v1.21.11) - 2024-04-07
|
||||
|
||||
* SECURITY
|
||||
|
@ -46,7 +46,6 @@ Wim <wim@42.be> (@42wim)
|
||||
Jason Song <i@wolfogre.com> (@wolfogre)
|
||||
Yarden Shoham <git@yardenshoham.com> (@yardenshoham)
|
||||
Yu Tian <zettat123@gmail.com> (@Zettat123)
|
||||
Eddie Yang <576951401@qq.com> (@yp05327)
|
||||
Dong Ge <gedong_1994@163.com> (@sillyguodong)
|
||||
Xinyi Gong <hestergong@gmail.com> (@HesterG)
|
||||
wxiaoguang <wxiaoguang@gmail.com> (@wxiaoguang)
|
||||
@ -63,3 +62,4 @@ Tim-Niclas Oelschläger <zokki.softwareschmiede@gmail.com> (@zokkis)
|
||||
Yu Liu <1240335630@qq.com> (@HEREYUA)
|
||||
Kemal Zebari <kemalzebra@gmail.com> (@kemzeb)
|
||||
Rowan Bohde <rowan.bohde@gmail.com> (@bohde)
|
||||
hiifong <i@hiif.ong> (@hiifong)
|
||||
|
6
Makefile
6
Makefile
@ -28,7 +28,7 @@ XGO_VERSION := go-1.23.x
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
||||
@ -377,12 +377,12 @@ lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
|
||||
.PHONY: lint-js
|
||||
lint-js: node_modules
|
||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
|
||||
# npx tsc
|
||||
npx vue-tsc
|
||||
|
||||
.PHONY: lint-js-fix
|
||||
lint-js-fix: node_modules
|
||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
|
||||
# npx tsc
|
||||
npx vue-tsc
|
||||
|
||||
.PHONY: lint-css
|
||||
lint-css: node_modules
|
||||
|
19
assets/go-licenses.json
generated
19
assets/go-licenses.json
generated
@ -1090,8 +1090,8 @@
|
||||
"licenseText": "MIT License\n\nCopyright (c) 2017 Asher\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/stretchr/testify/assert",
|
||||
"path": "github.com/stretchr/testify/assert/LICENSE",
|
||||
"name": "github.com/stretchr/testify",
|
||||
"path": "github.com/stretchr/testify/LICENSE",
|
||||
"licenseText": "MIT License\n\nCopyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
@ -1124,6 +1124,16 @@
|
||||
"path": "github.com/valyala/fastjson/LICENSE",
|
||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2018 Aliaksandr Valialkin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/wneessen/go-mail",
|
||||
"path": "github.com/wneessen/go-mail/LICENSE",
|
||||
"licenseText": "MIT License\n\nCopyright (c) 2022-2023 The go-mail Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/wneessen/go-mail/smtp",
|
||||
"path": "github.com/wneessen/go-mail/smtp/LICENSE",
|
||||
"licenseText": "Copyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
},
|
||||
{
|
||||
"name": "github.com/x448/float16",
|
||||
"path": "github.com/x448/float16/LICENSE",
|
||||
@ -1259,11 +1269,6 @@
|
||||
"path": "google.golang.org/protobuf/LICENSE",
|
||||
"licenseText": "Copyright (c) 2018 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "gopkg.in/gomail.v2",
|
||||
"path": "gopkg.in/gomail.v2/LICENSE",
|
||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Alexandre Cesaro\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "gopkg.in/ini.v1",
|
||||
"path": "gopkg.in/ini.v1/LICENSE",
|
||||
|
@ -1,80 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//nolint:forbidigo
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
)
|
||||
|
||||
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
|
||||
// go run -tags 'sqlite sqlite_unlock_notify' contrib/fixtures/fixture_generation.go [fixture...]
|
||||
|
||||
var (
|
||||
generators = []struct {
|
||||
gen func(ctx context.Context) (string, error)
|
||||
name string
|
||||
}{
|
||||
{
|
||||
models.GetYamlFixturesAccess, "access",
|
||||
},
|
||||
}
|
||||
fixturesDir string
|
||||
)
|
||||
|
||||
func main() {
|
||||
pathToGiteaRoot := "."
|
||||
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
|
||||
if err := unittest.CreateTestEngine(unittest.FixturesOptions{
|
||||
Dir: fixturesDir,
|
||||
}); err != nil {
|
||||
fmt.Printf("CreateTestEngine: %+v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := unittest.PrepareTestDatabase(); err != nil {
|
||||
fmt.Printf("PrepareTestDatabase: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
ctx := context.Background()
|
||||
if len(os.Args) == 0 {
|
||||
for _, r := range os.Args {
|
||||
if err := generate(ctx, r); err != nil {
|
||||
fmt.Printf("generate '%s': %+v\n", r, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, g := range generators {
|
||||
if err := generate(ctx, g.name); err != nil {
|
||||
fmt.Printf("generate '%s': %+v\n", g.name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generate(ctx context.Context, name string) error {
|
||||
for _, g := range generators {
|
||||
if g.name == name {
|
||||
data, err := g.gen(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(fixturesDir, name+".yml")
|
||||
if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
|
||||
return fmt.Errorf("%s: %+v", path, err)
|
||||
}
|
||||
fmt.Printf("%s created.\n", path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("generator not found")
|
||||
}
|
@ -784,6 +784,10 @@ LEVEL = Info
|
||||
;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
|
||||
;ENABLE_BASIC_AUTHENTICATION = true
|
||||
;;
|
||||
;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 login methods.
|
||||
;; If you set it to false, maybe it also needs to set ENABLE_BASIC_AUTHENTICATION to false to completely disable password-based authentication.
|
||||
;ENABLE_PASSWORD_SIGNIN_FORM = true
|
||||
;;
|
||||
;; More detail: https://github.com/gogits/gogs/issues/165
|
||||
;ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||
; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible.
|
||||
@ -1036,9 +1040,13 @@ LEVEL = Info
|
||||
;; Don't allow download source archive files from UI
|
||||
;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false
|
||||
|
||||
;; Allow fork repositories without maximum number limit
|
||||
;; Allow to fork repositories without maximum number limit
|
||||
;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true
|
||||
|
||||
;; Allow to fork repositories into the same owner (user or organization)
|
||||
;; This feature is experimental, not fully tested, and may be changed in the future
|
||||
;ALLOW_FORK_INTO_SAME_OWNER = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[repository.editor]
|
||||
@ -1944,6 +1952,13 @@ LEVEL = Info
|
||||
;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_SECRET_ACCESS_KEY =
|
||||
;;
|
||||
;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`.
|
||||
;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables
|
||||
;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,
|
||||
;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION),
|
||||
;; or the DefaultIAMRoleEndpoint if not provided otherwise.
|
||||
;MINIO_IAM_ENDPOINT =
|
||||
;;
|
||||
;; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_BUCKET = gitea
|
||||
;;
|
||||
@ -2688,6 +2703,13 @@ LEVEL = Info
|
||||
;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_SECRET_ACCESS_KEY =
|
||||
;;
|
||||
;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`.
|
||||
;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables
|
||||
;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,
|
||||
;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION),
|
||||
;; or the DefaultIAMRoleEndpoint if not provided otherwise.
|
||||
;MINIO_IAM_ENDPOINT =
|
||||
;;
|
||||
;; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_BUCKET = gitea
|
||||
;;
|
||||
|
13
go.mod
13
go.mod
@ -48,7 +48,7 @@ require (
|
||||
github.com/ethantkoenig/rupture v1.0.1
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gliderlabs/ssh v0.3.7
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c
|
||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
@ -114,23 +114,23 @@ require (
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.12
|
||||
github.com/urfave/cli/v2 v2.27.5
|
||||
github.com/wneessen/go-mail v0.5.2
|
||||
github.com/xanzy/go-gitlab v0.112.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/image v0.21.0
|
||||
golang.org/x/net v0.30.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/text v0.19.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/tools v0.26.0
|
||||
google.golang.org/grpc v1.67.1
|
||||
google.golang.org/protobuf v1.35.1
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/xurls/v2 v2.5.0
|
||||
@ -319,7 +319,6 @@ require (
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
44
go.sum
44
go.sum
@ -293,8 +293,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
|
||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c h1:82lzmsy5Nr6JA6HcLRVxGfbdSoWfW45C6jnY3zFS7Ks=
|
||||
@ -815,6 +815,8 @@ github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5
|
||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT8=
|
||||
github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw=
|
||||
@ -887,10 +889,13 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
@ -901,6 +906,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -920,8 +928,10 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
@ -934,8 +944,12 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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=
|
||||
@ -966,10 +980,14 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
@ -977,10 +995,13 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -989,9 +1010,12 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -1004,6 +1028,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -1024,8 +1050,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -1033,8 +1057,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
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/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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
|
@ -37,6 +37,7 @@ type ActionRun struct {
|
||||
TriggerUser *user_model.User `xorm:"-"`
|
||||
ScheduleID int64
|
||||
Ref string `xorm:"index"` // the commit/tag/… that caused the run
|
||||
IsRefDeleted bool `xorm:"-"`
|
||||
CommitSHA string
|
||||
IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.
|
||||
NeedApproval bool // may need approval if it's a fork pull request
|
||||
|
@ -137,7 +137,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
run.Status = aggregateJobStatus(jobs)
|
||||
run.Status = AggregateJobStatus(jobs)
|
||||
if run.Started.IsZero() && run.Status.IsRunning() {
|
||||
run.Started = timeutil.TimeStampNow()
|
||||
}
|
||||
@ -152,29 +152,35 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
func aggregateJobStatus(jobs []*ActionRunJob) Status {
|
||||
allDone := true
|
||||
allWaiting := true
|
||||
hasFailure := false
|
||||
func AggregateJobStatus(jobs []*ActionRunJob) Status {
|
||||
allSuccessOrSkipped := len(jobs) != 0
|
||||
allSkipped := len(jobs) != 0
|
||||
var hasFailure, hasCancelled, hasWaiting, hasRunning, hasBlocked bool
|
||||
for _, job := range jobs {
|
||||
if !job.Status.IsDone() {
|
||||
allDone = false
|
||||
}
|
||||
if job.Status != StatusWaiting && !job.Status.IsDone() {
|
||||
allWaiting = false
|
||||
}
|
||||
if job.Status == StatusFailure || job.Status == StatusCancelled {
|
||||
hasFailure = true
|
||||
}
|
||||
allSuccessOrSkipped = allSuccessOrSkipped && (job.Status == StatusSuccess || job.Status == StatusSkipped)
|
||||
allSkipped = allSkipped && job.Status == StatusSkipped
|
||||
hasFailure = hasFailure || job.Status == StatusFailure
|
||||
hasCancelled = hasCancelled || job.Status == StatusCancelled
|
||||
hasWaiting = hasWaiting || job.Status == StatusWaiting
|
||||
hasRunning = hasRunning || job.Status == StatusRunning
|
||||
hasBlocked = hasBlocked || job.Status == StatusBlocked
|
||||
}
|
||||
if allDone {
|
||||
if hasFailure {
|
||||
return StatusFailure
|
||||
}
|
||||
switch {
|
||||
case allSkipped:
|
||||
return StatusSkipped
|
||||
case allSuccessOrSkipped:
|
||||
return StatusSuccess
|
||||
}
|
||||
if allWaiting {
|
||||
case hasCancelled:
|
||||
return StatusCancelled
|
||||
case hasFailure:
|
||||
return StatusFailure
|
||||
case hasRunning:
|
||||
return StatusRunning
|
||||
case hasWaiting:
|
||||
return StatusWaiting
|
||||
case hasBlocked:
|
||||
return StatusBlocked
|
||||
default:
|
||||
return StatusUnknown // it shouldn't happen
|
||||
}
|
||||
return StatusRunning
|
||||
}
|
||||
|
85
models/actions/run_job_status_test.go
Normal file
85
models/actions/run_job_status_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAggregateJobStatus(t *testing.T) {
|
||||
testStatuses := func(expected Status, statuses []Status) {
|
||||
t.Helper()
|
||||
var jobs []*ActionRunJob
|
||||
for _, v := range statuses {
|
||||
jobs = append(jobs, &ActionRunJob{Status: v})
|
||||
}
|
||||
actual := AggregateJobStatus(jobs)
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
var statusStrings []string
|
||||
for _, s := range statuses {
|
||||
statusStrings = append(statusStrings, s.String())
|
||||
}
|
||||
t.Errorf("AggregateJobStatus(%v) = %v, want %v", statusStrings, statusNames[actual], statusNames[expected])
|
||||
}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
statuses []Status
|
||||
expected Status
|
||||
}{
|
||||
// unknown cases, maybe it shouldn't happen in real world
|
||||
{[]Status{}, StatusUnknown},
|
||||
{[]Status{StatusUnknown, StatusSuccess}, StatusUnknown},
|
||||
{[]Status{StatusUnknown, StatusSkipped}, StatusUnknown},
|
||||
{[]Status{StatusUnknown, StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusUnknown, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusUnknown, StatusWaiting}, StatusWaiting},
|
||||
{[]Status{StatusUnknown, StatusRunning}, StatusRunning},
|
||||
{[]Status{StatusUnknown, StatusBlocked}, StatusBlocked},
|
||||
|
||||
// success with other status
|
||||
{[]Status{StatusSuccess}, StatusSuccess},
|
||||
{[]Status{StatusSuccess, StatusSkipped}, StatusSuccess}, // skipped doesn't affect success
|
||||
{[]Status{StatusSuccess, StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusSuccess, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusSuccess, StatusWaiting}, StatusWaiting},
|
||||
{[]Status{StatusSuccess, StatusRunning}, StatusRunning},
|
||||
{[]Status{StatusSuccess, StatusBlocked}, StatusBlocked},
|
||||
|
||||
// any cancelled, then cancelled
|
||||
{[]Status{StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusSuccess}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusSkipped}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusFailure}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusWaiting}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusRunning}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusBlocked}, StatusCancelled},
|
||||
|
||||
// failure with other status, fail fast
|
||||
// Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast.
|
||||
{[]Status{StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusSuccess}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusSkipped}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusFailure, StatusWaiting}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusRunning}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusBlocked}, StatusFailure},
|
||||
|
||||
// skipped with other status
|
||||
// TODO: need to clarify whether a PR with "skipped" job status is considered as "mergeable" or not.
|
||||
{[]Status{StatusSkipped}, StatusSkipped},
|
||||
{[]Status{StatusSkipped, StatusSuccess}, StatusSuccess},
|
||||
{[]Status{StatusSkipped, StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusSkipped, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusSkipped, StatusWaiting}, StatusWaiting},
|
||||
{[]Status{StatusSkipped, StatusRunning}, StatusRunning},
|
||||
{[]Status{StatusSkipped, StatusBlocked}, StatusBlocked},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
testStatuses(c.expected, c.statuses)
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ func TestGetLatestRunnerToken(t *testing.T) {
|
||||
token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3})
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, token, expectedToken)
|
||||
assert.EqualValues(t, expectedToken, token)
|
||||
}
|
||||
|
||||
func TestNewRunnerToken(t *testing.T) {
|
||||
@ -26,7 +26,7 @@ func TestNewRunnerToken(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, token, expectedToken)
|
||||
assert.EqualValues(t, expectedToken, token)
|
||||
}
|
||||
|
||||
func TestUpdateRunnerToken(t *testing.T) {
|
||||
@ -36,5 +36,5 @@ func TestUpdateRunnerToken(t *testing.T) {
|
||||
assert.NoError(t, UpdateRunnerToken(db.DefaultContext, token))
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, token, expectedToken)
|
||||
assert.EqualValues(t, expectedToken, token)
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ func (a *Action) LoadActUser(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Action) loadRepo(ctx context.Context) {
|
||||
func (a *Action) LoadRepo(ctx context.Context) {
|
||||
if a.Repo != nil {
|
||||
return
|
||||
}
|
||||
@ -250,7 +250,7 @@ func (a *Action) GetActDisplayNameTitle(ctx context.Context) string {
|
||||
|
||||
// GetRepoUserName returns the name of the action repository owner.
|
||||
func (a *Action) GetRepoUserName(ctx context.Context) string {
|
||||
a.loadRepo(ctx)
|
||||
a.LoadRepo(ctx)
|
||||
if a.Repo == nil {
|
||||
return "(non-existing-repo)"
|
||||
}
|
||||
@ -265,7 +265,7 @@ func (a *Action) ShortRepoUserName(ctx context.Context) string {
|
||||
|
||||
// GetRepoName returns the name of the action repository.
|
||||
func (a *Action) GetRepoName(ctx context.Context) string {
|
||||
a.loadRepo(ctx)
|
||||
a.LoadRepo(ctx)
|
||||
if a.Repo == nil {
|
||||
return "(non-existing-repo)"
|
||||
}
|
||||
@ -448,65 +448,13 @@ type GetFeedsOptions struct {
|
||||
Date string // the day we want activity for: YYYY-MM-DD
|
||||
}
|
||||
|
||||
// GetFeeds returns actions according to the provided options
|
||||
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
|
||||
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
|
||||
return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
|
||||
}
|
||||
|
||||
cond, err := activityQueryCondition(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
actions := make([]*Action, 0, opts.PageSize)
|
||||
var count int64
|
||||
opts.SetDefaultValues()
|
||||
|
||||
if opts.Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
|
||||
sess := db.GetEngine(ctx).Where(cond)
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
|
||||
}
|
||||
} else {
|
||||
// First, only query which IDs are necessary, and only then query all actions to speed up the overall query
|
||||
sess := db.GetEngine(ctx).Where(cond).Select("`action`.id")
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
actionIDs := make([]int64, 0, opts.PageSize)
|
||||
if err := sess.Table("action").Desc("`action`.created_unix").Find(&actionIDs); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
|
||||
}
|
||||
|
||||
count, err = db.GetEngine(ctx).Where(cond).
|
||||
Table("action").
|
||||
Cols("`action`.id").Count()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||
}
|
||||
|
||||
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ActionList(actions).LoadAttributes(ctx); err != nil {
|
||||
return nil, 0, fmt.Errorf("LoadAttributes: %w", err)
|
||||
}
|
||||
|
||||
return actions, count, nil
|
||||
}
|
||||
|
||||
// ActivityReadable return whether doer can read activities of user
|
||||
func ActivityReadable(user, doer *user_model.User) bool {
|
||||
return !user.KeepActivityPrivate ||
|
||||
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
|
||||
}
|
||||
|
||||
func activityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) {
|
||||
func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) {
|
||||
cond := builder.NewCond()
|
||||
|
||||
if opts.RequestedTeam != nil && opts.RequestedUser == nil {
|
||||
@ -644,7 +592,7 @@ func NotifyWatchers(ctx context.Context, actions ...*Action) error {
|
||||
}
|
||||
|
||||
if repoChanged {
|
||||
act.loadRepo(ctx)
|
||||
act.LoadRepo(ctx)
|
||||
repo = act.Repo
|
||||
|
||||
// check repo owner exist.
|
||||
@ -770,7 +718,7 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64)
|
||||
// CountActionCreatedUnixString count actions where created_unix is an empty string
|
||||
func CountActionCreatedUnixString(ctx context.Context) (int64, error) {
|
||||
if setting.Database.Type.IsSQLite3() {
|
||||
return db.GetEngine(ctx).Where(`created_unix = ""`).Count(new(Action))
|
||||
return db.GetEngine(ctx).Where(`created_unix = ''`).Count(new(Action))
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
@ -778,7 +726,7 @@ func CountActionCreatedUnixString(ctx context.Context) (int64, error) {
|
||||
// FixActionCreatedUnixString set created_unix to zero if it is an empty string
|
||||
func FixActionCreatedUnixString(ctx context.Context) (int64, error) {
|
||||
if setting.Database.Type.IsSQLite3() {
|
||||
res, err := db.GetEngine(ctx).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
|
||||
res, err := db.GetEngine(ctx).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ''`)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -201,3 +201,55 @@ func (actions ActionList) LoadIssues(ctx context.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFeeds returns actions according to the provided options
|
||||
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
|
||||
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
|
||||
return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
|
||||
}
|
||||
|
||||
cond, err := ActivityQueryCondition(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
actions := make([]*Action, 0, opts.PageSize)
|
||||
var count int64
|
||||
opts.SetDefaultValues()
|
||||
|
||||
if opts.Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
|
||||
sess := db.GetEngine(ctx).Where(cond)
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
|
||||
}
|
||||
} else {
|
||||
// First, only query which IDs are necessary, and only then query all actions to speed up the overall query
|
||||
sess := db.GetEngine(ctx).Where(cond).Select("`action`.id")
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
actionIDs := make([]int64, 0, opts.PageSize)
|
||||
if err := sess.Table("action").Desc("`action`.created_unix").Find(&actionIDs); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
|
||||
}
|
||||
|
||||
count, err = db.GetEngine(ctx).Where(cond).
|
||||
Table("action").
|
||||
Cols("`action`.id").Count()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||
}
|
||||
|
||||
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ActionList(actions).LoadAttributes(ctx); err != nil {
|
||||
return nil, 0, fmt.Errorf("LoadAttributes: %w", err)
|
||||
}
|
||||
|
||||
return actions, count, nil
|
||||
}
|
||||
|
@ -42,114 +42,6 @@ func TestAction_GetRepoLink(t *testing.T) {
|
||||
assert.Equal(t, comment.HTMLURL(db.DefaultContext), action.GetCommentHTMLURL(db.DefaultContext))
|
||||
}
|
||||
|
||||
func TestGetFeeds(t *testing.T) {
|
||||
// test with an individual user
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
OnlyPerformedBy: false,
|
||||
IncludeDeleted: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, actions, 1) {
|
||||
assert.EqualValues(t, 1, actions[0].ID)
|
||||
assert.EqualValues(t, user.ID, actions[0].UserID)
|
||||
}
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: false,
|
||||
OnlyPerformedBy: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 0)
|
||||
assert.Equal(t, int64(0), count)
|
||||
}
|
||||
|
||||
func TestGetFeedsForRepos(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
privRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8})
|
||||
|
||||
// private repo & no login
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: privRepo,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 0)
|
||||
assert.Equal(t, int64(0), count)
|
||||
|
||||
// public repo & no login
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: pubRepo,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
// private repo and login
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: privRepo,
|
||||
IncludePrivate: true,
|
||||
Actor: user,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
// public repo & login
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: pubRepo,
|
||||
IncludePrivate: true,
|
||||
Actor: user,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
}
|
||||
|
||||
func TestGetFeeds2(t *testing.T) {
|
||||
// test with an organization user
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: org,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
OnlyPerformedBy: false,
|
||||
IncludeDeleted: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
if assert.Len(t, actions, 1) {
|
||||
assert.EqualValues(t, 2, actions[0].ID)
|
||||
assert.EqualValues(t, org.ID, actions[0].UserID)
|
||||
}
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: org,
|
||||
Actor: user,
|
||||
IncludePrivate: false,
|
||||
OnlyPerformedBy: false,
|
||||
IncludeDeleted: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 0)
|
||||
assert.Equal(t, int64(0), count)
|
||||
}
|
||||
|
||||
func TestActivityReadable(t *testing.T) {
|
||||
tt := []struct {
|
||||
desc string
|
||||
@ -227,26 +119,6 @@ func TestNotifyWatchers(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetFeedsCorrupted(t *testing.T) {
|
||||
// Now we will not check for corrupted data in the feeds
|
||||
// users should run doctor to fix their data
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ID: 8,
|
||||
RepoID: 1700,
|
||||
})
|
||||
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
}
|
||||
|
||||
func TestConsistencyUpdateAction(t *testing.T) {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
t.Skip("Test is only for SQLite database.")
|
||||
@ -256,7 +128,7 @@ func TestConsistencyUpdateAction(t *testing.T) {
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ID: int64(id),
|
||||
})
|
||||
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
|
||||
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = '' WHERE id = ?`, id)
|
||||
assert.NoError(t, err)
|
||||
actions := make([]*activities_model.Action, 0, 1)
|
||||
//
|
||||
@ -322,24 +194,3 @@ func TestDeleteIssueActions(t *testing.T) {
|
||||
assert.NoError(t, activities_model.DeleteIssueActions(db.DefaultContext, issue.RepoID, issue.ID, issue.Index))
|
||||
unittest.AssertCount(t, &activities_model.Action{}, 0)
|
||||
}
|
||||
|
||||
func TestRepoActions(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
_ = db.TruncateBeans(db.DefaultContext, &activities_model.Action{})
|
||||
for i := 0; i < 3; i++ {
|
||||
_ = db.Insert(db.DefaultContext, &activities_model.Action{
|
||||
UserID: 2 + int64(i),
|
||||
ActUserID: 2,
|
||||
RepoID: repo.ID,
|
||||
OpType: activities_model.ActionCommentIssue,
|
||||
})
|
||||
}
|
||||
count, _ := db.Count[activities_model.Action](db.DefaultContext, &db.ListOptions{})
|
||||
assert.EqualValues(t, 3, count)
|
||||
actions, _, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: repo,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
|
||||
groupByName = groupBy
|
||||
}
|
||||
|
||||
cond, err := activityQueryCondition(ctx, GetFeedsOptions{
|
||||
cond, err := ActivityQueryCondition(ctx, GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
RequestedTeam: team,
|
||||
Actor: doer,
|
||||
|
@ -4,7 +4,6 @@
|
||||
package activities_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -91,11 +90,11 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, contributions, "invalid action count: did the test data became too old?")
|
||||
assert.Equal(t, count, int64(contributions))
|
||||
assert.Equal(t, tc.CountResult, contributions, fmt.Sprintf("testcase '%s'", tc.desc))
|
||||
assert.Equal(t, tc.CountResult, contributions, "testcase '%s'", tc.desc)
|
||||
|
||||
// Test JSON rendering
|
||||
jsonData, err := json.Marshal(heatmap)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.JSONResult, string(jsonData))
|
||||
assert.JSONEq(t, tc.JSONResult, string(jsonData))
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
secret, err := app.GenerateClientSecret(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, len(secret) > 0)
|
||||
assert.NotEmpty(t, secret)
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
|
||||
code, err := grant.GenerateNewAuthorizationCode(db.DefaultContext, "https://example2.com/callback", "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg", "S256")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, code)
|
||||
assert.True(t, len(code.Code) > 32) // secret length > 32
|
||||
assert.Greater(t, len(code.Code), 32) // secret length > 32
|
||||
}
|
||||
|
||||
func TestOAuth2Grant_TableName(t *testing.T) {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
)
|
||||
|
||||
@ -89,14 +90,33 @@ func (cred *WebAuthnCredential) AfterLoad() {
|
||||
// WebAuthnCredentialList is a list of *WebAuthnCredential
|
||||
type WebAuthnCredentialList []*WebAuthnCredential
|
||||
|
||||
// newCredentialFlagsFromAuthenticatorFlags is copied from https://github.com/go-webauthn/webauthn/pull/337
|
||||
// to convert protocol.AuthenticatorFlags to webauthn.CredentialFlags
|
||||
func newCredentialFlagsFromAuthenticatorFlags(flags protocol.AuthenticatorFlags) webauthn.CredentialFlags {
|
||||
return webauthn.CredentialFlags{
|
||||
UserPresent: flags.HasUserPresent(),
|
||||
UserVerified: flags.HasUserVerified(),
|
||||
BackupEligible: flags.HasBackupEligible(),
|
||||
BackupState: flags.HasBackupState(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCredentials will convert all WebAuthnCredentials to webauthn.Credentials
|
||||
func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential {
|
||||
func (list WebAuthnCredentialList) ToCredentials(defaultAuthFlags ...protocol.AuthenticatorFlags) []webauthn.Credential {
|
||||
// TODO: at the moment, Gitea doesn't store or check the flags
|
||||
// so we need to use the default flags from the authenticator to make the login validation pass
|
||||
// In the future, we should:
|
||||
// 1. store the flags when registering the credential
|
||||
// 2. provide the stored flags when converting the credentials (for login)
|
||||
// 3. for old users, still use this fallback to the default flags
|
||||
defAuthFlags := util.OptionalArg(defaultAuthFlags)
|
||||
creds := make([]webauthn.Credential, 0, len(list))
|
||||
for _, cred := range list {
|
||||
creds = append(creds, webauthn.Credential{
|
||||
ID: cred.CredentialID,
|
||||
PublicKey: cred.PublicKey,
|
||||
AttestationType: cred.AttestationType,
|
||||
Flags: newCredentialFlagsFromAuthenticatorFlags(defAuthFlags),
|
||||
Authenticator: webauthn.Authenticator{
|
||||
AAGUID: cred.AAGUID,
|
||||
SignCount: cred.SignCount,
|
||||
|
@ -4,7 +4,7 @@
|
||||
package db // it's not db_test, because this file is for testing the private type halfCommitter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -80,7 +80,7 @@ func Test_halfCommitter(t *testing.T) {
|
||||
testWithCommitter(mockCommitter, func(committer Committer) error {
|
||||
defer committer.Close()
|
||||
if true {
|
||||
return fmt.Errorf("error")
|
||||
return errors.New("error")
|
||||
}
|
||||
return committer.Commit()
|
||||
})
|
||||
@ -94,7 +94,7 @@ func Test_halfCommitter(t *testing.T) {
|
||||
testWithCommitter(mockCommitter, func(committer Committer) error {
|
||||
committer.Close()
|
||||
committer.Commit()
|
||||
return fmt.Errorf("error")
|
||||
return errors.New("error")
|
||||
})
|
||||
|
||||
mockCommitter.Assert(t)
|
||||
|
@ -134,6 +134,9 @@ func SyncAllTables() error {
|
||||
func InitEngine(ctx context.Context) error {
|
||||
xormEngine, err := newXORMEngine()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "SQLite3 support") {
|
||||
return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
|
||||
}
|
||||
return fmt.Errorf("failed to connect to database: %w", err)
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,6 @@ func TestIterate(t *testing.T) {
|
||||
if !has {
|
||||
return db.ErrNotExist{Resource: "repo_unit", ID: repoUnit.ID}
|
||||
}
|
||||
assert.EqualValues(t, repoUnit.RepoID, repoUnit.RepoID)
|
||||
assert.EqualValues(t, repoUnit.CreatedUnix, repoUnit.CreatedUnix)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
@ -26,8 +26,10 @@ const (
|
||||
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
|
||||
)
|
||||
|
||||
const (
|
||||
// Which means a condition to filter the records which don't match any id.
|
||||
// It's different from zero which means the condition could be ignored.
|
||||
NoConditionID = -1
|
||||
)
|
||||
// NoConditionID means a condition to filter the records which don't match any id.
|
||||
// eg: "milestone_id=-1" means "find the items without any milestone.
|
||||
const NoConditionID int64 = -1
|
||||
|
||||
// NonExistingID means a condition to match no result (eg: a non-existing user)
|
||||
// It doesn't use -1 or -2 because they are used as builtin users.
|
||||
const NonExistingID int64 = -1000000
|
||||
|
@ -1,50 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
)
|
||||
|
||||
// GetYamlFixturesAccess returns a string containing the contents
|
||||
// for the access table, as recalculated using repo.RecalculateAccesses()
|
||||
func GetYamlFixturesAccess(ctx context.Context) (string, error) {
|
||||
repos := make([]*repo_model.Repository, 0, 50)
|
||||
if err := db.GetEngine(ctx).Find(&repos); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, repo := range repos {
|
||||
repo.MustOwner(ctx)
|
||||
if err := access_model.RecalculateAccesses(ctx, repo); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
accesses := make([]*access_model.Access, 0, 200)
|
||||
if err := db.GetEngine(ctx).OrderBy("user_id, repo_id").Find(&accesses); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i, a := range accesses {
|
||||
fmt.Fprintf(&b, "-\n")
|
||||
fmt.Fprintf(&b, " id: %d\n", i+1)
|
||||
fmt.Fprintf(&b, " user_id: %d\n", a.UserID)
|
||||
fmt.Fprintf(&b, " repo_id: %d\n", a.RepoID)
|
||||
fmt.Fprintf(&b, " mode: %d\n", a.Mode)
|
||||
if i < len(accesses)-1 {
|
||||
fmt.Fprintf(&b, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return b.String(), nil
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFixtureGeneration(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
test := func(ctx context.Context, gen func(ctx context.Context) (string, error), name string) {
|
||||
expected, err := gen(ctx)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
p := filepath.Join(unittest.FixturesDir(), name+".yml")
|
||||
bytes, err := os.ReadFile(p)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
data := string(util.NormalizeEOL(bytes))
|
||||
assert.EqualValues(t, expected, data, "Differences detected for %s", p)
|
||||
}
|
||||
|
||||
test(db.DefaultContext, GetYamlFixturesAccess, "access")
|
||||
}
|
@ -36,3 +36,41 @@
|
||||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
-
|
||||
id: 793
|
||||
title: "job output"
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
workflow_id: "test.yaml"
|
||||
index: 189
|
||||
trigger_user_id: 1
|
||||
ref: "refs/heads/master"
|
||||
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
|
||||
event: "push"
|
||||
is_fork_pull_request: 0
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
created: 1683636108
|
||||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
-
|
||||
id: 794
|
||||
title: "job output"
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
workflow_id: "test.yaml"
|
||||
index: 190
|
||||
trigger_user_id: 1
|
||||
ref: "refs/heads/test"
|
||||
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
|
||||
event: "push"
|
||||
is_fork_pull_request: 0
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
created: 1683636108
|
||||
updated: 1683636626
|
||||
need_approval: 0
|
||||
approved_by: 0
|
||||
|
@ -26,3 +26,46 @@
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
-
|
||||
id: 194
|
||||
run_id: 793
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
name: job1 (1)
|
||||
attempt: 1
|
||||
job_id: job1
|
||||
task_id: 49
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
-
|
||||
id: 195
|
||||
run_id: 793
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
name: job1 (2)
|
||||
attempt: 1
|
||||
job_id: job1
|
||||
task_id: 50
|
||||
status: 1
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
-
|
||||
id: 196
|
||||
run_id: 793
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
name: job2
|
||||
attempt: 1
|
||||
job_id: job2
|
||||
needs: [job1]
|
||||
task_id: 51
|
||||
status: 5
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
|
@ -57,3 +57,63 @@
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
-
|
||||
id: 49
|
||||
job_id: 194
|
||||
attempt: 1
|
||||
runner_id: 1
|
||||
status: 1 # success
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784220
|
||||
token_salt: ffffffffff
|
||||
token_last_eight: ffffffff
|
||||
log_filename: artifact-test2/2f/47.log
|
||||
log_in_storage: 1
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
-
|
||||
id: 50
|
||||
job_id: 195
|
||||
attempt: 1
|
||||
runner_id: 1
|
||||
status: 1 # success
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784221
|
||||
token_salt: ffffffffff
|
||||
token_last_eight: ffffffff
|
||||
log_filename: artifact-test2/2f/47.log
|
||||
log_in_storage: 1
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
-
|
||||
id: 51
|
||||
job_id: 196
|
||||
attempt: 1
|
||||
runner_id: 1
|
||||
status: 6 # running
|
||||
started: 1683636528
|
||||
stopped: 1683636626
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: 0
|
||||
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784222
|
||||
token_salt: ffffffffff
|
||||
token_last_eight: ffffffff
|
||||
log_filename: artifact-test2/2f/47.log
|
||||
log_in_storage: 1
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: 0
|
||||
|
20
models/fixtures/action_task_output.yml
Normal file
20
models/fixtures/action_task_output.yml
Normal file
@ -0,0 +1,20 @@
|
||||
-
|
||||
id: 1
|
||||
task_id: 49
|
||||
output_key: output_a
|
||||
output_value: abc
|
||||
-
|
||||
id: 2
|
||||
task_id: 49
|
||||
output_key: output_b
|
||||
output_value: ''
|
||||
-
|
||||
id: 3
|
||||
task_id: 50
|
||||
output_key: output_a
|
||||
output_value: ''
|
||||
-
|
||||
id: 4
|
||||
task_id: 50
|
||||
output_key: output_b
|
||||
output_value: bbb
|
@ -81,3 +81,15 @@
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
|
||||
-
|
||||
id: 15
|
||||
repo_id: 4
|
||||
name: 'master'
|
||||
commit_id: 'c7cd3cd144e6d23c9d6f3d07e52b2c1a956e0338'
|
||||
commit_message: 'add Readme'
|
||||
commit_time: 1588147171
|
||||
pusher_id: 13
|
||||
is_deleted: false
|
||||
deleted_by_id: 0
|
||||
deleted_unix: 0
|
||||
|
@ -4,6 +4,7 @@
|
||||
reviewer_id: 1
|
||||
issue_id: 2
|
||||
content: "Demo Review"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
-
|
||||
@ -12,6 +13,7 @@
|
||||
reviewer_id: 534543
|
||||
issue_id: 534543
|
||||
content: "Invalid Review #1"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
-
|
||||
@ -20,6 +22,7 @@
|
||||
reviewer_id: 1
|
||||
issue_id: 343545
|
||||
content: "Invalid Review #2"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
-
|
||||
@ -28,6 +31,7 @@
|
||||
reviewer_id: 1
|
||||
issue_id: 2
|
||||
content: "Pending Review"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
-
|
||||
@ -36,6 +40,7 @@
|
||||
reviewer_id: 1
|
||||
issue_id: 3
|
||||
content: "New review 1"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
-
|
||||
@ -61,8 +66,8 @@
|
||||
type: 1
|
||||
reviewer_id: 4
|
||||
issue_id: 3
|
||||
original_author_id: 0
|
||||
content: "New review 5"
|
||||
original_author_id: 0
|
||||
commit_id: 8091a55037cd59e47293aca02981b5a67076b364
|
||||
stale: true
|
||||
updated_unix: 946684813
|
||||
@ -73,9 +78,9 @@
|
||||
reviewer_id: 2
|
||||
issue_id: 3
|
||||
content: "New review 3 rejected"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684814
|
||||
created_unix: 946684814
|
||||
original_author_id: 0
|
||||
|
||||
-
|
||||
id: 10
|
||||
@ -83,6 +88,7 @@
|
||||
reviewer_id: 100
|
||||
issue_id: 3
|
||||
content: "a deleted user's review"
|
||||
original_author_id: 0
|
||||
official: true
|
||||
updated_unix: 946684815
|
||||
created_unix: 946684815
|
||||
@ -112,6 +118,7 @@
|
||||
reviewer_id: 5
|
||||
issue_id: 11
|
||||
content: "old review from user5"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684820
|
||||
created_unix: 946684820
|
||||
|
||||
@ -121,6 +128,7 @@
|
||||
reviewer_id: 5
|
||||
issue_id: 11
|
||||
content: "duplicate review from user5 (latest)"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684830
|
||||
created_unix: 946684830
|
||||
|
||||
@ -130,6 +138,7 @@
|
||||
reviewer_id: 6
|
||||
issue_id: 11
|
||||
content: "singular review from org6 and final review for this pr"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684831
|
||||
created_unix: 946684831
|
||||
|
||||
@ -139,6 +148,7 @@
|
||||
reviewer_id: 20
|
||||
issue_id: 20
|
||||
content: "review request for user20"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684832
|
||||
created_unix: 946684832
|
||||
|
||||
@ -148,6 +158,7 @@
|
||||
reviewer_id: 20
|
||||
issue_id: 20
|
||||
content: "review approved by user20"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684833
|
||||
created_unix: 946684833
|
||||
|
||||
@ -158,6 +169,7 @@
|
||||
reviewer_team_id: 5
|
||||
issue_id: 20
|
||||
content: "review request for team5"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684834
|
||||
created_unix: 946684834
|
||||
|
||||
@ -168,6 +180,7 @@
|
||||
reviewer_team_id: 0
|
||||
issue_id: 20
|
||||
content: "review request for user15"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684835
|
||||
created_unix: 946684835
|
||||
|
||||
@ -177,6 +190,7 @@
|
||||
reviewer_id: 1
|
||||
issue_id: 2
|
||||
content: "Review Comment"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684810
|
||||
created_unix: 946684810
|
||||
|
||||
@ -186,6 +200,7 @@
|
||||
reviewer_id: 5
|
||||
issue_id: 3
|
||||
content: "reviewed by user5"
|
||||
original_author_id: 0
|
||||
commit_id: 4a357436d925b5c974181ff12a994538ddc5a269
|
||||
updated_unix: 946684816
|
||||
created_unix: 946684816
|
||||
@ -196,5 +211,6 @@
|
||||
reviewer_id: 5
|
||||
issue_id: 3
|
||||
content: "review request for user5"
|
||||
original_author_id: 0
|
||||
updated_unix: 946684817
|
||||
created_unix: 946684817
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
@ -169,9 +170,22 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e
|
||||
return &branch, nil
|
||||
}
|
||||
|
||||
func GetBranches(ctx context.Context, repoID int64, branchNames []string) ([]*Branch, error) {
|
||||
func GetBranches(ctx context.Context, repoID int64, branchNames []string, includeDeleted bool) ([]*Branch, error) {
|
||||
branches := make([]*Branch, 0, len(branchNames))
|
||||
return branches, db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames).Find(&branches)
|
||||
|
||||
sess := db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames)
|
||||
if !includeDeleted {
|
||||
sess.And("is_deleted=?", false)
|
||||
}
|
||||
return branches, sess.Find(&branches)
|
||||
}
|
||||
|
||||
func BranchesToNamesSet(branches []*Branch) container.Set[string] {
|
||||
names := make(container.Set[string], len(branches))
|
||||
for _, branch := range branches {
|
||||
names.Add(branch.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func AddBranches(ctx context.Context, branches []*Branch) error {
|
||||
|
@ -34,7 +34,7 @@ func TestGetCommitStatuses(t *testing.T) {
|
||||
SHA: sha1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int(maxResults), 5)
|
||||
assert.Equal(t, 5, int(maxResults))
|
||||
assert.Len(t, statuses, 5)
|
||||
|
||||
assert.Equal(t, "ci/awesomeness", statuses[0].Context)
|
||||
@ -63,7 +63,7 @@ func TestGetCommitStatuses(t *testing.T) {
|
||||
SHA: sha1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int(maxResults), 5)
|
||||
assert.Equal(t, 5, int(maxResults))
|
||||
assert.Empty(t, statuses)
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ type ProtectedBranch struct {
|
||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
RuleName string `xorm:"'branch_name' UNIQUE(s)"` // a branch name or a glob match to branch name
|
||||
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
globRule glob.Glob `xorm:"-"`
|
||||
isPlainName bool `xorm:"-"`
|
||||
CanPush bool `xorm:"NOT NULL DEFAULT false"`
|
||||
@ -413,14 +414,27 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
|
||||
}
|
||||
protectBranch.ApprovalsWhitelistTeamIDs = whitelist
|
||||
|
||||
// Make sure protectBranch.ID is not 0 for whitelists
|
||||
// Looks like it's a new rule
|
||||
if protectBranch.ID == 0 {
|
||||
// as it's a new rule and if priority was not set, we need to calc it.
|
||||
if protectBranch.Priority == 0 {
|
||||
var lowestPrio int64
|
||||
// because of mssql we can not use builder or save xorm syntax, so raw sql it is
|
||||
if _, err := db.GetEngine(ctx).SQL(`SELECT MAX(priority) FROM protected_branch WHERE repo_id = ?`, protectBranch.RepoID).
|
||||
Get(&lowestPrio); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Trace("Create new ProtectedBranch at repo[%d] and detect current lowest priority '%d'", protectBranch.RepoID, lowestPrio)
|
||||
protectBranch.Priority = lowestPrio + 1
|
||||
}
|
||||
|
||||
if _, err = db.GetEngine(ctx).Insert(protectBranch); err != nil {
|
||||
return fmt.Errorf("Insert: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// update the rule
|
||||
if _, err = db.GetEngine(ctx).ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil {
|
||||
return fmt.Errorf("Update: %v", err)
|
||||
}
|
||||
@ -428,6 +442,24 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateProtectBranchPriorities(ctx context.Context, repo *repo_model.Repository, ids []int64) error {
|
||||
prio := int64(1)
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
for _, id := range ids {
|
||||
if _, err := db.GetEngine(ctx).
|
||||
ID(id).Where("repo_id = ?", repo.ID).
|
||||
Cols("priority").
|
||||
Update(&ProtectedBranch{
|
||||
Priority: prio,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
prio++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
|
||||
// the users from newWhitelist which have explicit read or write access to the repo.
|
||||
func updateApprovalWhitelist(ctx context.Context, repo *repo_model.Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
|
||||
|
@ -28,6 +28,13 @@ func (rules ProtectedBranchRules) sort() {
|
||||
sort.Slice(rules, func(i, j int) bool {
|
||||
rules[i].loadGlob()
|
||||
rules[j].loadGlob()
|
||||
|
||||
// if priority differ, use that to sort
|
||||
if rules[i].Priority != rules[j].Priority {
|
||||
return rules[i].Priority < rules[j].Priority
|
||||
}
|
||||
|
||||
// now we sort the old way
|
||||
if rules[i].isPlainName != rules[j].isPlainName {
|
||||
return rules[i].isPlainName // plain name comes first, so plain name means "less"
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ func TestBranchRuleMatchPriority(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBranchRuleSort(t *testing.T) {
|
||||
func TestBranchRuleSortLegacy(t *testing.T) {
|
||||
in := []*ProtectedBranch{{
|
||||
RuleName: "b",
|
||||
CreatedUnix: 1,
|
||||
@ -103,3 +103,37 @@ func TestBranchRuleSort(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, expect, got)
|
||||
}
|
||||
|
||||
func TestBranchRuleSortPriority(t *testing.T) {
|
||||
in := []*ProtectedBranch{{
|
||||
RuleName: "b",
|
||||
CreatedUnix: 1,
|
||||
Priority: 4,
|
||||
}, {
|
||||
RuleName: "b/*",
|
||||
CreatedUnix: 3,
|
||||
Priority: 2,
|
||||
}, {
|
||||
RuleName: "a/*",
|
||||
CreatedUnix: 2,
|
||||
Priority: 1,
|
||||
}, {
|
||||
RuleName: "c",
|
||||
CreatedUnix: 0,
|
||||
Priority: 0,
|
||||
}, {
|
||||
RuleName: "a",
|
||||
CreatedUnix: 4,
|
||||
Priority: 3,
|
||||
}}
|
||||
expect := []string{"c", "a/*", "b/*", "a", "b"}
|
||||
|
||||
pbr := ProtectedBranchRules(in)
|
||||
pbr.sort()
|
||||
|
||||
var got []string
|
||||
for i := range pbr {
|
||||
got = append(got, pbr[i].RuleName)
|
||||
}
|
||||
assert.Equal(t, expect, got)
|
||||
}
|
||||
|
@ -4,9 +4,12 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -72,7 +75,81 @@ func TestBranchRuleMatch(t *testing.T) {
|
||||
infact = " not"
|
||||
}
|
||||
assert.EqualValues(t, kase.ExpectedMatch, pb.Match(kase.BranchName),
|
||||
fmt.Sprintf("%s should%s match %s but it is%s", kase.BranchName, should, kase.Rule, infact),
|
||||
"%s should%s match %s but it is%s", kase.BranchName, should, kase.Rule, infact,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateProtectBranchPriorities(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
// Create some test protected branches with initial priorities
|
||||
protectedBranches := []*ProtectedBranch{
|
||||
{
|
||||
RepoID: repo.ID,
|
||||
RuleName: "master",
|
||||
Priority: 1,
|
||||
},
|
||||
{
|
||||
RepoID: repo.ID,
|
||||
RuleName: "develop",
|
||||
Priority: 2,
|
||||
},
|
||||
{
|
||||
RepoID: repo.ID,
|
||||
RuleName: "feature/*",
|
||||
Priority: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, pb := range protectedBranches {
|
||||
_, err := db.GetEngine(db.DefaultContext).Insert(pb)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Test updating priorities
|
||||
newPriorities := []int64{protectedBranches[2].ID, protectedBranches[0].ID, protectedBranches[1].ID}
|
||||
err := UpdateProtectBranchPriorities(db.DefaultContext, repo, newPriorities)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify new priorities
|
||||
pbs, err := FindRepoProtectedBranchRules(db.DefaultContext, repo.ID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedPriorities := map[string]int64{
|
||||
"feature/*": 1,
|
||||
"master": 2,
|
||||
"develop": 3,
|
||||
}
|
||||
|
||||
for _, pb := range pbs {
|
||||
assert.Equal(t, expectedPriorities[pb.RuleName], pb.Priority)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewProtectBranchPriority(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
err := UpdateProtectBranch(db.DefaultContext, repo, &ProtectedBranch{
|
||||
RepoID: repo.ID,
|
||||
RuleName: "branch-1",
|
||||
Priority: 1,
|
||||
}, WhitelistOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
newPB := &ProtectedBranch{
|
||||
RepoID: repo.ID,
|
||||
RuleName: "branch-2",
|
||||
// Priority intentionally omitted
|
||||
}
|
||||
|
||||
err = UpdateProtectBranch(db.DefaultContext, repo, newPB, WhitelistOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
savedPB2, err := GetFirstMatchProtectedBranchRule(db.DefaultContext, repo.ID, "branch-2")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), savedPB2.Priority)
|
||||
}
|
||||
|
@ -114,6 +114,8 @@ const (
|
||||
|
||||
CommentTypePin // 36 pin Issue
|
||||
CommentTypeUnpin // 37 unpin Issue
|
||||
|
||||
CommentTypeChangeTimeEstimate // 38 Change time estimate
|
||||
)
|
||||
|
||||
var commentStrings = []string{
|
||||
@ -155,6 +157,7 @@ var commentStrings = []string{
|
||||
"pull_cancel_scheduled_merge",
|
||||
"pin",
|
||||
"unpin",
|
||||
"change_time_estimate",
|
||||
}
|
||||
|
||||
func (t CommentType) String() string {
|
||||
@ -1092,7 +1095,7 @@ func FindComments(ctx context.Context, opts *FindCommentsOptions) (CommentList,
|
||||
sess.Join("INNER", "issue", "issue.id = comment.issue_id")
|
||||
}
|
||||
|
||||
if opts.Page != 0 {
|
||||
if opts.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, opts)
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/renderhelper"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
|
||||
"xorm.io/builder"
|
||||
@ -112,14 +112,8 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
|
||||
}
|
||||
|
||||
var err error
|
||||
if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
Repo: issue.Repo,
|
||||
Links: markup.Links{
|
||||
Base: issue.Repo.Link(),
|
||||
},
|
||||
Metas: issue.Repo.ComposeMetas(ctx),
|
||||
}, comment.Content); err != nil {
|
||||
rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo)
|
||||
if comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func TestFetchCodeComments(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAsCommentType(t *testing.T) {
|
||||
assert.Equal(t, issues_model.CommentType(0), issues_model.CommentTypeComment)
|
||||
assert.Equal(t, issues_model.CommentTypeComment, issues_model.CommentType(0))
|
||||
assert.Equal(t, issues_model.CommentTypeUndefined, issues_model.AsCommentType(""))
|
||||
assert.Equal(t, issues_model.CommentTypeUndefined, issues_model.AsCommentType("nonsense"))
|
||||
assert.Equal(t, issues_model.CommentTypeComment, issues_model.AsCommentType("comment"))
|
||||
|
@ -123,8 +123,11 @@ type Issue struct {
|
||||
IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not.
|
||||
PullRequest *PullRequest `xorm:"-"`
|
||||
NumComments int
|
||||
Ref string
|
||||
PinOrder int `xorm:"DEFAULT 0"`
|
||||
|
||||
// TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl"
|
||||
Ref string
|
||||
|
||||
PinOrder int `xorm:"DEFAULT 0"`
|
||||
|
||||
DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"`
|
||||
|
||||
@ -145,6 +148,9 @@ type Issue struct {
|
||||
|
||||
// For view issue page.
|
||||
ShowRole RoleDescriptor `xorm:"-"`
|
||||
|
||||
// Time estimate
|
||||
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
var (
|
||||
@ -639,7 +645,7 @@ func (issue *Issue) BlockedByDependencies(ctx context.Context, opts db.ListOptio
|
||||
Where("issue_id = ?", issue.ID).
|
||||
// sort by repo id then created date, with the issues of the same repo at the beginning of the list
|
||||
OrderBy("CASE WHEN issue.repo_id = ? THEN 0 ELSE issue.repo_id END, issue.created_unix DESC", issue.RepoID)
|
||||
if opts.Page != 0 {
|
||||
if opts.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
}
|
||||
err = sess.Find(&issueDeps)
|
||||
@ -932,3 +938,28 @@ func insertIssue(ctx context.Context, issue *Issue) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeIssueTimeEstimate changes the plan time of this issue, as the given user.
|
||||
func ChangeIssueTimeEstimate(ctx context.Context, issue *Issue, doer *user_model.User, timeEstimate int64) error {
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := UpdateIssueCols(ctx, &Issue{ID: issue.ID, TimeEstimate: timeEstimate}, "time_estimate"); err != nil {
|
||||
return fmt.Errorf("updateIssueCols: %w", err)
|
||||
}
|
||||
|
||||
if err := issue.LoadRepo(ctx); err != nil {
|
||||
return fmt.Errorf("loadRepo: %w", err)
|
||||
}
|
||||
|
||||
opts := &CreateCommentOptions{
|
||||
Type: CommentTypeChangeTimeEstimate,
|
||||
Doer: doer,
|
||||
Repo: issue.Repo,
|
||||
Issue: issue,
|
||||
Content: fmt.Sprintf("%d", timeEstimate),
|
||||
}
|
||||
if _, err := CreateComment(ctx, opts); err != nil {
|
||||
return fmt.Errorf("createComment: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -18,12 +18,12 @@ func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error {
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
var max int64
|
||||
if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
|
||||
var maxIndex int64
|
||||
if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&maxIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = db.SyncMaxResourceIndex(ctx, "issue_index", repoID, max); err != nil {
|
||||
if err = db.SyncMaxResourceIndex(ctx, "issue_index", repoID, maxIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ type IssuesOptions struct { //nolint
|
||||
RepoIDs []int64 // overwrites RepoCond if the length is not 0
|
||||
AllPublic bool // include also all public repositories
|
||||
RepoCond builder.Cond
|
||||
AssigneeID int64
|
||||
PosterID int64
|
||||
AssigneeID optional.Option[int64]
|
||||
PosterID optional.Option[int64]
|
||||
MentionedID int64
|
||||
ReviewRequestedID int64
|
||||
ReviewedID int64
|
||||
@ -231,15 +231,8 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) {
|
||||
sess.And("issue.is_closed=?", opts.IsClosed.Value())
|
||||
}
|
||||
|
||||
if opts.AssigneeID > 0 {
|
||||
applyAssigneeCondition(sess, opts.AssigneeID)
|
||||
} else if opts.AssigneeID == db.NoConditionID {
|
||||
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
|
||||
}
|
||||
|
||||
if opts.PosterID > 0 {
|
||||
applyPosterCondition(sess, opts.PosterID)
|
||||
}
|
||||
applyAssigneeCondition(sess, opts.AssigneeID)
|
||||
applyPosterCondition(sess, opts.PosterID)
|
||||
|
||||
if opts.MentionedID > 0 {
|
||||
applyMentionedCondition(sess, opts.MentionedID)
|
||||
@ -359,13 +352,27 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
|
||||
return cond
|
||||
}
|
||||
|
||||
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) {
|
||||
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
|
||||
And("issue_assignees.assignee_id = ?", assigneeID)
|
||||
func applyAssigneeCondition(sess *xorm.Session, assigneeID optional.Option[int64]) {
|
||||
// old logic: 0 is also treated as "not filtering assignee", because the "assignee" was read as FormInt64
|
||||
if !assigneeID.Has() || assigneeID.Value() == 0 {
|
||||
return
|
||||
}
|
||||
if assigneeID.Value() == db.NoConditionID {
|
||||
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
|
||||
} else {
|
||||
sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
|
||||
And("issue_assignees.assignee_id = ?", assigneeID.Value())
|
||||
}
|
||||
}
|
||||
|
||||
func applyPosterCondition(sess *xorm.Session, posterID int64) {
|
||||
sess.And("issue.poster_id=?", posterID)
|
||||
func applyPosterCondition(sess *xorm.Session, posterID optional.Option[int64]) {
|
||||
if !posterID.Has() {
|
||||
return
|
||||
}
|
||||
// poster doesn't need to support db.NoConditionID(-1), so just use the value as-is
|
||||
if posterID.Has() {
|
||||
sess.And("issue.poster_id=?", posterID.Value())
|
||||
}
|
||||
}
|
||||
|
||||
func applyMentionedCondition(sess *xorm.Session, mentionedID int64) {
|
||||
|
@ -151,15 +151,9 @@ func applyIssuesOptions(sess *xorm.Session, opts *IssuesOptions, issueIDs []int6
|
||||
|
||||
applyProjectCondition(sess, opts)
|
||||
|
||||
if opts.AssigneeID > 0 {
|
||||
applyAssigneeCondition(sess, opts.AssigneeID)
|
||||
} else if opts.AssigneeID == db.NoConditionID {
|
||||
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
|
||||
}
|
||||
applyAssigneeCondition(sess, opts.AssigneeID)
|
||||
|
||||
if opts.PosterID > 0 {
|
||||
applyPosterCondition(sess, opts.PosterID)
|
||||
}
|
||||
applyPosterCondition(sess, opts.PosterID)
|
||||
|
||||
if opts.MentionedID > 0 {
|
||||
applyMentionedCondition(sess, opts.MentionedID)
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -155,7 +156,7 @@ func TestIssues(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
issues_model.IssuesOptions{
|
||||
AssigneeID: 1,
|
||||
AssigneeID: optional.Some(int64(1)),
|
||||
SortType: "oldest",
|
||||
},
|
||||
[]int64{1, 6},
|
||||
@ -467,7 +468,7 @@ func assertCreateIssues(t *testing.T, isPull bool) {
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
|
||||
milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
|
||||
assert.EqualValues(t, milestone.ID, 1)
|
||||
assert.EqualValues(t, 1, milestone.ID)
|
||||
reaction := &issues_model.Reaction{
|
||||
Type: "heart",
|
||||
UserID: owner.ID,
|
||||
|
@ -105,7 +105,7 @@ func GetIssueWatchers(ctx context.Context, issueID int64, listOptions db.ListOpt
|
||||
And("`user`.prohibit_login = ?", false).
|
||||
Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id")
|
||||
|
||||
if listOptions.Page != 0 {
|
||||
if listOptions.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
watches := make([]*IssueWatch, 0, listOptions.PageSize)
|
||||
return watches, sess.Find(&watches)
|
||||
|
@ -48,17 +48,17 @@ func TestGetIssueWatchers(t *testing.T) {
|
||||
iws, err := issues_model.GetIssueWatchers(db.DefaultContext, 1, db.ListOptions{})
|
||||
assert.NoError(t, err)
|
||||
// Watcher is inactive, thus 0
|
||||
assert.Len(t, iws, 0)
|
||||
assert.Empty(t, iws)
|
||||
|
||||
iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 2, db.ListOptions{})
|
||||
assert.NoError(t, err)
|
||||
// Watcher is explicit not watching
|
||||
assert.Len(t, iws, 0)
|
||||
assert.Empty(t, iws)
|
||||
|
||||
iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 5, db.ListOptions{})
|
||||
assert.NoError(t, err)
|
||||
// Issue has no Watchers
|
||||
assert.Len(t, iws, 0)
|
||||
assert.Empty(t, iws)
|
||||
|
||||
iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 7, db.ListOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
@ -390,7 +390,7 @@ func GetLabelsByRepoID(ctx context.Context, repoID int64, sortType string, listO
|
||||
sess.Asc("name")
|
||||
}
|
||||
|
||||
if listOptions.Page != 0 {
|
||||
if listOptions.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
}
|
||||
|
||||
@ -462,7 +462,7 @@ func GetLabelsByOrgID(ctx context.Context, orgID int64, sortType string, listOpt
|
||||
sess.Asc("name")
|
||||
}
|
||||
|
||||
if listOptions.Page != 0 {
|
||||
if listOptions.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,12 @@ func TestLabel_LoadSelectedLabelsAfterClick(t *testing.T) {
|
||||
// First test : with negative and scope
|
||||
label.LoadSelectedLabelsAfterClick([]int64{1, -8}, []string{"", "scope"})
|
||||
assert.Equal(t, "1", label.QueryString)
|
||||
assert.Equal(t, true, label.IsSelected)
|
||||
assert.True(t, label.IsSelected)
|
||||
|
||||
// Second test : with duplicates
|
||||
label.LoadSelectedLabelsAfterClick([]int64{1, 7, 1, 7, 7}, []string{"", "scope", "", "scope", "scope"})
|
||||
assert.Equal(t, "1,8", label.QueryString)
|
||||
assert.Equal(t, false, label.IsSelected)
|
||||
assert.False(t, label.IsSelected)
|
||||
|
||||
// Third test : empty set
|
||||
label.LoadSelectedLabelsAfterClick([]int64{}, []string{})
|
||||
@ -248,7 +248,7 @@ func TestGetLabelsByIssueID(t *testing.T) {
|
||||
|
||||
labels, err = issues_model.GetLabelsByIssueID(db.DefaultContext, unittest.NonexistentID)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, labels, 0)
|
||||
assert.Empty(t, labels)
|
||||
}
|
||||
|
||||
func TestUpdateLabel(t *testing.T) {
|
||||
@ -271,7 +271,7 @@ func TestUpdateLabel(t *testing.T) {
|
||||
assert.EqualValues(t, label.Color, newLabel.Color)
|
||||
assert.EqualValues(t, label.Name, newLabel.Name)
|
||||
assert.EqualValues(t, label.Description, newLabel.Description)
|
||||
assert.EqualValues(t, newLabel.ArchivedUnix, 0)
|
||||
assert.EqualValues(t, 0, newLabel.ArchivedUnix)
|
||||
unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{})
|
||||
}
|
||||
|
||||
@ -406,7 +406,7 @@ func TestDeleteIssueLabel(t *testing.T) {
|
||||
PosterID: doerID,
|
||||
IssueID: issueID,
|
||||
LabelID: labelID,
|
||||
}, `content=""`)
|
||||
}, `content=''`)
|
||||
label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID})
|
||||
assert.EqualValues(t, expectedNumIssues, label.NumIssues)
|
||||
assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues)
|
||||
|
@ -87,7 +87,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
|
||||
IsClosed: optional.Some(false),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, milestones, 0)
|
||||
assert.Empty(t, milestones)
|
||||
}
|
||||
|
||||
func TestGetMilestones(t *testing.T) {
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
@ -240,6 +241,64 @@ func (prs PullRequestList) GetIssueIDs() []int64 {
|
||||
})
|
||||
}
|
||||
|
||||
func (prs PullRequestList) LoadReviewCommentsCounts(ctx context.Context) (map[int64]int, error) {
|
||||
issueIDs := prs.GetIssueIDs()
|
||||
countsMap := make(map[int64]int, len(issueIDs))
|
||||
counts := make([]struct {
|
||||
IssueID int64
|
||||
Count int
|
||||
}, 0, len(issueIDs))
|
||||
if err := db.GetEngine(ctx).Select("issue_id, count(*) as count").
|
||||
Table("comment").In("issue_id", issueIDs).And("type = ?", CommentTypeReview).
|
||||
GroupBy("issue_id").Find(&counts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range counts {
|
||||
countsMap[c.IssueID] = c.Count
|
||||
}
|
||||
return countsMap, nil
|
||||
}
|
||||
|
||||
func (prs PullRequestList) LoadReviews(ctx context.Context) (ReviewList, error) {
|
||||
issueIDs := prs.GetIssueIDs()
|
||||
reviews := make([]*Review, 0, len(issueIDs))
|
||||
|
||||
subQuery := builder.Select("max(id) as id").
|
||||
From("review").
|
||||
Where(builder.In("issue_id", issueIDs)).
|
||||
And(builder.In("`type`", ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest)).
|
||||
And(builder.Eq{
|
||||
"dismissed": false,
|
||||
"original_author_id": 0,
|
||||
"reviewer_team_id": 0,
|
||||
}).
|
||||
GroupBy("issue_id, reviewer_id")
|
||||
// Get latest review of each reviewer, sorted in order they were made
|
||||
if err := db.GetEngine(ctx).In("id", subQuery).OrderBy("review.updated_unix ASC").Find(&reviews); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
teamReviewRequests := make([]*Review, 0, 5)
|
||||
subQueryTeam := builder.Select("max(id) as id").
|
||||
From("review").
|
||||
Where(builder.In("issue_id", issueIDs)).
|
||||
And(builder.Eq{
|
||||
"original_author_id": 0,
|
||||
}).And(builder.Neq{
|
||||
"reviewer_team_id": 0,
|
||||
}).
|
||||
GroupBy("issue_id, reviewer_team_id")
|
||||
if err := db.GetEngine(ctx).In("id", subQueryTeam).OrderBy("review.updated_unix ASC").Find(&teamReviewRequests); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(teamReviewRequests) > 0 {
|
||||
reviews = append(reviews, teamReviewRequests...)
|
||||
}
|
||||
|
||||
return reviews, nil
|
||||
}
|
||||
|
||||
// HasMergedPullRequestInRepo returns whether the user(poster) has merged pull-request in the repo
|
||||
func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
|
64
models/issues/pull_list_test.go
Normal file
64
models/issues/pull_list_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package issues_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPullRequestList_LoadAttributes(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
prs := []*issues_model.PullRequest{
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
|
||||
}
|
||||
assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes(db.DefaultContext))
|
||||
for _, pr := range prs {
|
||||
assert.NotNil(t, pr.Issue)
|
||||
assert.Equal(t, pr.IssueID, pr.Issue.ID)
|
||||
}
|
||||
|
||||
assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes(db.DefaultContext))
|
||||
}
|
||||
|
||||
func TestPullRequestList_LoadReviewCommentsCounts(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
prs := []*issues_model.PullRequest{
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
|
||||
}
|
||||
reviewComments, err := issues_model.PullRequestList(prs).LoadReviewCommentsCounts(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, reviewComments, 2)
|
||||
for _, pr := range prs {
|
||||
assert.EqualValues(t, 1, reviewComments[pr.IssueID])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullRequestList_LoadReviews(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
prs := []*issues_model.PullRequest{
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
|
||||
}
|
||||
reviewList, err := issues_model.PullRequestList(prs).LoadReviews(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
// 1, 7, 8, 9, 10, 22
|
||||
assert.Len(t, reviewList, 6)
|
||||
assert.EqualValues(t, 1, reviewList[0].ID)
|
||||
assert.EqualValues(t, 7, reviewList[1].ID)
|
||||
assert.EqualValues(t, 8, reviewList[2].ID)
|
||||
assert.EqualValues(t, 9, reviewList[3].ID)
|
||||
assert.EqualValues(t, 10, reviewList[4].ID)
|
||||
assert.EqualValues(t, 22, reviewList[5].ID)
|
||||
}
|
@ -79,11 +79,11 @@ func TestPullRequestsNewest(t *testing.T) {
|
||||
func TestLoadRequestedReviewers(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
|
||||
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
|
||||
assert.NoError(t, pull.LoadIssue(db.DefaultContext))
|
||||
issue := pull.Issue
|
||||
assert.NoError(t, issue.LoadRepo(db.DefaultContext))
|
||||
assert.Len(t, pull.RequestedReviewers, 0)
|
||||
assert.Empty(t, pull.RequestedReviewers)
|
||||
|
||||
user1, err := user_model.GetUserByID(db.DefaultContext, 1)
|
||||
assert.NoError(t, err)
|
||||
@ -93,7 +93,7 @@ func TestLoadRequestedReviewers(t *testing.T) {
|
||||
assert.NotNil(t, comment)
|
||||
|
||||
assert.NoError(t, pull.LoadRequestedReviewers(db.DefaultContext))
|
||||
assert.Len(t, pull.RequestedReviewers, 1)
|
||||
assert.Len(t, pull.RequestedReviewers, 6)
|
||||
|
||||
comment, err = issues_model.RemoveReviewRequest(db.DefaultContext, issue, user1, &user_model.User{})
|
||||
assert.NoError(t, err)
|
||||
@ -234,22 +234,6 @@ func TestPullRequest_UpdateCols(t *testing.T) {
|
||||
unittest.CheckConsistencyFor(t, pr)
|
||||
}
|
||||
|
||||
func TestPullRequestList_LoadAttributes(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
prs := []*issues_model.PullRequest{
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
|
||||
}
|
||||
assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes(db.DefaultContext))
|
||||
for _, pr := range prs {
|
||||
assert.NotNil(t, pr.Issue)
|
||||
assert.Equal(t, pr.IssueID, pr.Issue.ID)
|
||||
}
|
||||
|
||||
assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes(db.DefaultContext))
|
||||
}
|
||||
|
||||
// TODO TestAddTestPullRequestTask
|
||||
|
||||
func TestPullRequest_IsWorkInProgress(t *testing.T) {
|
||||
|
@ -163,7 +163,7 @@ func FindReactions(ctx context.Context, opts FindReactionsOptions) (ReactionList
|
||||
Where(opts.toConds()).
|
||||
In("reaction.`type`", setting.UI.Reactions).
|
||||
Asc("reaction.issue_id", "reaction.comment_id", "reaction.created_unix", "reaction.id")
|
||||
if opts.Page != 0 {
|
||||
if opts.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
reactions := make([]*Reaction, 0, opts.PageSize)
|
||||
|
@ -47,14 +47,9 @@ func (reviews ReviewList) LoadReviewersTeams(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
teamsMap := make(map[int64]*organization_model.Team, 0)
|
||||
for _, teamID := range reviewersTeamsIDs {
|
||||
team, err := organization_model.GetTeamByID(ctx, teamID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
teamsMap[teamID] = team
|
||||
teamsMap, err := organization_model.GetTeamsByIDs(ctx, reviewersTeamsIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, review := range reviews {
|
||||
|
@ -126,42 +126,48 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
|
||||
expectedReviews := []*issues_model.Review{}
|
||||
expectedReviews = append(expectedReviews,
|
||||
&issues_model.Review{
|
||||
ID: 7,
|
||||
Reviewer: org3,
|
||||
Type: issues_model.ReviewTypeReject,
|
||||
UpdatedUnix: 946684812,
|
||||
},
|
||||
&issues_model.Review{
|
||||
ID: 8,
|
||||
Reviewer: user4,
|
||||
Type: issues_model.ReviewTypeApprove,
|
||||
UpdatedUnix: 946684813,
|
||||
},
|
||||
&issues_model.Review{
|
||||
ID: 9,
|
||||
Reviewer: user2,
|
||||
Type: issues_model.ReviewTypeReject,
|
||||
UpdatedUnix: 946684814,
|
||||
})
|
||||
},
|
||||
&issues_model.Review{
|
||||
ID: 10,
|
||||
Reviewer: user_model.NewGhostUser(),
|
||||
Type: issues_model.ReviewTypeReject,
|
||||
UpdatedUnix: 946684815,
|
||||
},
|
||||
&issues_model.Review{
|
||||
ID: 22,
|
||||
Reviewer: user5,
|
||||
Type: issues_model.ReviewTypeRequest,
|
||||
UpdatedUnix: 946684817,
|
||||
},
|
||||
)
|
||||
|
||||
allReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
|
||||
assert.NoError(t, err)
|
||||
for _, review := range allReviews {
|
||||
assert.NoError(t, review.LoadReviewer(db.DefaultContext))
|
||||
}
|
||||
if assert.Len(t, allReviews, 3) {
|
||||
for i, review := range allReviews {
|
||||
assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer)
|
||||
assert.Equal(t, expectedReviews[i].Type, review.Type)
|
||||
assert.Equal(t, expectedReviews[i].UpdatedUnix, review.UpdatedUnix)
|
||||
}
|
||||
}
|
||||
|
||||
allReviews, err = issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, allReviews.LoadReviewers(db.DefaultContext))
|
||||
if assert.Len(t, allReviews, 3) {
|
||||
if assert.Len(t, allReviews, 5) {
|
||||
for i, review := range allReviews {
|
||||
assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer)
|
||||
assert.Equal(t, expectedReviews[i].Type, review.Type)
|
||||
|
@ -96,7 +96,7 @@ func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) {
|
||||
func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) {
|
||||
sws := make([]*Stopwatch, 0, 8)
|
||||
sess := db.GetEngine(ctx).Where("stopwatch.user_id = ?", userID)
|
||||
if listOptions.Page != 0 {
|
||||
if listOptions.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ func TestCancelStopwatch(t *testing.T) {
|
||||
|
||||
_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID})
|
||||
|
||||
assert.Nil(t, issues_model.CancelStopwatch(db.DefaultContext, user1, issue2))
|
||||
assert.NoError(t, issues_model.CancelStopwatch(db.DefaultContext, user1, issue2))
|
||||
}
|
||||
|
||||
func TestStopwatchExists(t *testing.T) {
|
||||
|
@ -139,7 +139,7 @@ func (opts *FindTrackedTimesOptions) toSession(e db.Engine) db.Engine {
|
||||
|
||||
sess = sess.Where(opts.ToConds())
|
||||
|
||||
if opts.Page != 0 {
|
||||
if opts.Page > 0 {
|
||||
sess = db.SetSessionPagination(sess, opts)
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ func TestGetTrackedTimes(t *testing.T) {
|
||||
|
||||
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{IssueID: -1})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, times, 0)
|
||||
assert.Empty(t, times)
|
||||
|
||||
// by User
|
||||
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{UserID: 1})
|
||||
@ -60,7 +60,7 @@ func TestGetTrackedTimes(t *testing.T) {
|
||||
|
||||
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{UserID: 3})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, times, 0)
|
||||
assert.Empty(t, times)
|
||||
|
||||
// by Repo
|
||||
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 2})
|
||||
@ -69,7 +69,7 @@ func TestGetTrackedTimes(t *testing.T) {
|
||||
assert.Equal(t, int64(1), times[0].Time)
|
||||
issue, err := issues_model.GetIssueByID(db.DefaultContext, times[0].IssueID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, issue.RepoID, int64(2))
|
||||
assert.Equal(t, int64(2), issue.RepoID)
|
||||
|
||||
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 1})
|
||||
assert.NoError(t, err)
|
||||
@ -77,7 +77,7 @@ func TestGetTrackedTimes(t *testing.T) {
|
||||
|
||||
times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 10})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, times, 0)
|
||||
assert.Empty(t, times)
|
||||
}
|
||||
|
||||
func TestTotalTimesForEachUser(t *testing.T) {
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/testlogger"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
@ -33,15 +33,15 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
|
||||
ourSkip := 2
|
||||
ourSkip += skip
|
||||
deferFn := testlogger.PrintCurrentTest(t, ourSkip)
|
||||
assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
|
||||
require.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
|
||||
|
||||
if err := deleteDB(); err != nil {
|
||||
t.Errorf("unable to reset database: %v", err)
|
||||
t.Fatalf("unable to reset database: %v", err)
|
||||
return nil, deferFn
|
||||
}
|
||||
|
||||
x, err := newXORMEngine()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
if x != nil {
|
||||
oldDefer := deferFn
|
||||
deferFn = func() {
|
||||
|
@ -367,6 +367,8 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate),
|
||||
newMigration(308, "Add index(user_id, is_deleted) for action table", v1_23.AddNewIndexForUserDashboard),
|
||||
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
|
||||
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
|
||||
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
@ -56,8 +56,8 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
|
||||
err := x.Table("attachment").Where("issue_id > 0").Find(&issueAttachments)
|
||||
assert.NoError(t, err)
|
||||
for _, attach := range issueAttachments {
|
||||
assert.Greater(t, attach.RepoID, int64(0))
|
||||
assert.Greater(t, attach.IssueID, int64(0))
|
||||
assert.Positive(t, attach.RepoID)
|
||||
assert.Positive(t, attach.IssueID)
|
||||
var issue Issue
|
||||
has, err := x.ID(attach.IssueID).Get(&issue)
|
||||
assert.NoError(t, err)
|
||||
@ -69,8 +69,8 @@ func Test_AddRepoIDForAttachment(t *testing.T) {
|
||||
err = x.Table("attachment").Where("release_id > 0").Find(&releaseAttachments)
|
||||
assert.NoError(t, err)
|
||||
for _, attach := range releaseAttachments {
|
||||
assert.Greater(t, attach.RepoID, int64(0))
|
||||
assert.Greater(t, attach.ReleaseID, int64(0))
|
||||
assert.Positive(t, attach.RepoID)
|
||||
assert.Positive(t, attach.ReleaseID)
|
||||
var release Release
|
||||
has, err := x.ID(attach.ReleaseID).Get(&release)
|
||||
assert.NoError(t, err)
|
||||
|
@ -107,12 +107,12 @@ func Test_RepositoryFormat(t *testing.T) {
|
||||
repo = new(Repository)
|
||||
ok, err := x.ID(2).Get(repo)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, true, ok)
|
||||
assert.True(t, ok)
|
||||
assert.EqualValues(t, "sha1", repo.ObjectFormatName)
|
||||
|
||||
repo = new(Repository)
|
||||
ok, err = x.ID(id).Get(repo)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, true, ok)
|
||||
assert.True(t, ok)
|
||||
assert.EqualValues(t, "sha256", repo.ObjectFormatName)
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) {
|
||||
|
||||
tables, err := x.DBMetas()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, len(tables))
|
||||
assert.Len(t, tables, 1)
|
||||
found := false
|
||||
for _, index := range tables[0].Indexes {
|
||||
if index.Type == schemas.UniqueType {
|
||||
|
16
models/migrations/v1_23/v310.go
Normal file
16
models/migrations/v1_23/v310.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddPriorityToProtectedBranch(x *xorm.Engine) error {
|
||||
type ProtectedBranch struct {
|
||||
Priority int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
return x.Sync(new(ProtectedBranch))
|
||||
}
|
16
models/migrations/v1_23/v311.go
Normal file
16
models/migrations/v1_23/v311.go
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddTimeEstimateColumnToIssueTable(x *xorm.Engine) error {
|
||||
type Issue struct {
|
||||
TimeEstimate int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Issue))
|
||||
}
|
@ -16,6 +16,31 @@ import (
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type OrgList []*Organization
|
||||
|
||||
func (orgs OrgList) LoadTeams(ctx context.Context) (map[int64]TeamList, error) {
|
||||
if len(orgs) == 0 {
|
||||
return map[int64]TeamList{}, nil
|
||||
}
|
||||
|
||||
orgIDs := make([]int64, len(orgs))
|
||||
for i, org := range orgs {
|
||||
orgIDs[i] = org.ID
|
||||
}
|
||||
|
||||
teams, err := GetTeamsByOrgIDs(ctx, orgIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
teamMap := make(map[int64]TeamList, len(orgs))
|
||||
for _, team := range teams {
|
||||
teamMap[team.OrgID] = append(teamMap[team.OrgID], team)
|
||||
}
|
||||
|
||||
return teamMap, nil
|
||||
}
|
||||
|
||||
// SearchOrganizationsOptions options to filter organizations
|
||||
type SearchOrganizationsOptions struct {
|
||||
db.ListOptions
|
||||
|
@ -40,7 +40,7 @@ func TestFindOrgs(t *testing.T) {
|
||||
IncludePrivate: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, orgs, 0)
|
||||
assert.Empty(t, orgs)
|
||||
|
||||
total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{
|
||||
UserID: 4,
|
||||
@ -60,3 +60,14 @@ func TestGetUserOrgsList(t *testing.T) {
|
||||
assert.EqualValues(t, 2, orgs[0].NumRepos)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadOrgListTeams(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, orgs, 1)
|
||||
teamsMap, err := organization.OrgList(orgs).LoadTeams(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, teamsMap, 1)
|
||||
assert.Len(t, teamsMap[3], 5)
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ func TestGetOrgUsersByOrgID(t *testing.T) {
|
||||
OrgID: unittest.NonexistentID,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, orgUsers, 0)
|
||||
assert.Empty(t, orgUsers)
|
||||
}
|
||||
|
||||
func TestChangeOrgUserStatus(t *testing.T) {
|
||||
|
@ -126,3 +126,13 @@ func GetUserRepoTeams(ctx context.Context, orgID, userID, repoID int64) (teams T
|
||||
And("team_repo.repo_id=?", repoID).
|
||||
Find(&teams)
|
||||
}
|
||||
|
||||
func GetTeamsByOrgIDs(ctx context.Context, orgIDs []int64) (TeamList, error) {
|
||||
teams := make([]*Team, 0, 10)
|
||||
return teams, db.GetEngine(ctx).Where(builder.In("org_id", orgIDs)).Find(&teams)
|
||||
}
|
||||
|
||||
func GetTeamsByIDs(ctx context.Context, teamIDs []int64) (map[int64]*Team, error) {
|
||||
teams := make(map[int64]*Team, len(teamIDs))
|
||||
return teams, db.GetEngine(ctx).Where(builder.In("`id`", teamIDs)).Find(&teams)
|
||||
}
|
||||
|
25
models/organization/team_list_test.go
Normal file
25
models/organization/team_list_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package organization_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
org_model "code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_GetTeamsByIDs(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// 1 owner team, 2 normal team
|
||||
teams, err := org_model.GetTeamsByIDs(db.DefaultContext, []int64{1, 2})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, teams, 2)
|
||||
assert.Equal(t, "Owners", teams[1].Name)
|
||||
assert.Equal(t, "team1", teams[2].Name)
|
||||
}
|
@ -9,6 +9,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@ -83,3 +84,16 @@ func GetTeamsWithAccessToRepo(ctx context.Context, orgID, repoID int64, mode per
|
||||
OrderBy("name").
|
||||
Find(&teams)
|
||||
}
|
||||
|
||||
// GetTeamsWithAccessToRepoUnit returns all teams in an organization that have given access level to the repository special unit.
|
||||
func GetTeamsWithAccessToRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type) ([]*Team, error) {
|
||||
teams := make([]*Team, 0, 5)
|
||||
return teams, db.GetEngine(ctx).Where("team_unit.access_mode >= ?", mode).
|
||||
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
||||
Join("INNER", "team_unit", "team_unit.team_id = team.id").
|
||||
And("team_repo.org_id = ?", orgID).
|
||||
And("team_repo.repo_id = ?", repoID).
|
||||
And("team_unit.type = ?", unitType).
|
||||
OrderBy("name").
|
||||
Find(&teams)
|
||||
}
|
||||
|
31
models/organization/team_repo_test.go
Normal file
31
models/organization/team_repo_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package organization_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetTeamsWithAccessToRepoUnit(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
org41 := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 41})
|
||||
repo61 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 61})
|
||||
|
||||
teams, err := organization.GetTeamsWithAccessToRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, teams, 2) {
|
||||
assert.EqualValues(t, 21, teams[0].ID)
|
||||
assert.EqualValues(t, 22, teams[1].ID)
|
||||
}
|
||||
}
|
@ -197,3 +197,8 @@ func TestUsersInTeamsCount(t *testing.T) {
|
||||
test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2) // userid 2,4
|
||||
test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
|
||||
}
|
||||
|
||||
func TestIsUsableTeamName(t *testing.T) {
|
||||
assert.NoError(t, organization.IsUsableTeamName("usable"))
|
||||
assert.True(t, db.IsErrNameReserved(organization.IsUsableTeamName("new")))
|
||||
}
|
||||
|
38
models/packages/arch/search.go
Normal file
38
models/packages/arch/search.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package arch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
arch_module "code.gitea.io/gitea/modules/packages/arch"
|
||||
)
|
||||
|
||||
// GetRepositories gets all available repositories
|
||||
func GetRepositories(ctx context.Context, ownerID int64) ([]string, error) {
|
||||
return packages_model.GetDistinctPropertyValues(
|
||||
ctx,
|
||||
packages_model.TypeArch,
|
||||
ownerID,
|
||||
packages_model.PropertyTypeFile,
|
||||
arch_module.PropertyRepository,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// GetArchitectures gets all available architectures for the given repository
|
||||
func GetArchitectures(ctx context.Context, ownerID int64, repository string) ([]string, error) {
|
||||
return packages_model.GetDistinctPropertyValues(
|
||||
ctx,
|
||||
packages_model.TypeArch,
|
||||
ownerID,
|
||||
packages_model.PropertyTypeFile,
|
||||
arch_module.PropertyArchitecture,
|
||||
&packages_model.DistinctPropertyDependency{
|
||||
Name: arch_module.PropertyRepository,
|
||||
Value: repository,
|
||||
},
|
||||
)
|
||||
}
|
@ -13,6 +13,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/packages/alpine"
|
||||
"code.gitea.io/gitea/modules/packages/arch"
|
||||
"code.gitea.io/gitea/modules/packages/cargo"
|
||||
"code.gitea.io/gitea/modules/packages/chef"
|
||||
"code.gitea.io/gitea/modules/packages/composer"
|
||||
@ -150,6 +151,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
|
||||
switch p.Type {
|
||||
case TypeAlpine:
|
||||
metadata = &alpine.VersionMetadata{}
|
||||
case TypeArch:
|
||||
metadata = &arch.VersionMetadata{}
|
||||
case TypeCargo:
|
||||
metadata = &cargo.Metadata{}
|
||||
case TypeChef:
|
||||
|
@ -31,6 +31,7 @@ type Type string
|
||||
// List of supported packages
|
||||
const (
|
||||
TypeAlpine Type = "alpine"
|
||||
TypeArch Type = "arch"
|
||||
TypeCargo Type = "cargo"
|
||||
TypeChef Type = "chef"
|
||||
TypeComposer Type = "composer"
|
||||
@ -55,6 +56,7 @@ const (
|
||||
|
||||
var TypeList = []Type{
|
||||
TypeAlpine,
|
||||
TypeArch,
|
||||
TypeCargo,
|
||||
TypeChef,
|
||||
TypeComposer,
|
||||
@ -82,6 +84,8 @@ func (pt Type) Name() string {
|
||||
switch pt {
|
||||
case TypeAlpine:
|
||||
return "Alpine"
|
||||
case TypeArch:
|
||||
return "Arch"
|
||||
case TypeCargo:
|
||||
return "Cargo"
|
||||
case TypeChef:
|
||||
@ -131,6 +135,8 @@ func (pt Type) SVGName() string {
|
||||
switch pt {
|
||||
case TypeAlpine:
|
||||
return "gitea-alpine"
|
||||
case TypeArch:
|
||||
return "gitea-arch"
|
||||
case TypeCargo:
|
||||
return "gitea-cargo"
|
||||
case TypeChef:
|
||||
|
@ -221,6 +221,11 @@ func SearchFiles(ctx context.Context, opts *PackageFileSearchOptions) ([]*Packag
|
||||
return pfs, count, err
|
||||
}
|
||||
|
||||
// HasFiles tests if there are files of packages matching the search options
|
||||
func HasFiles(ctx context.Context, opts *PackageFileSearchOptions) (bool, error) {
|
||||
return db.Exist[PackageFile](ctx, opts.toConds())
|
||||
}
|
||||
|
||||
// CalculateFileSize sums up all blob sizes matching the search options.
|
||||
// It does NOT respect the deduplication of blobs.
|
||||
func CalculateFileSize(ctx context.Context, opts *PackageFileSearchOptions) (int64, error) {
|
||||
|
@ -15,7 +15,7 @@ func TestAccessMode(t *testing.T) {
|
||||
m := ParseAccessMode(name)
|
||||
assert.Equal(t, AccessMode(i), m)
|
||||
}
|
||||
assert.Equal(t, AccessMode(4), AccessModeOwner)
|
||||
assert.Equal(t, AccessModeOwner, AccessMode(4))
|
||||
assert.Equal(t, "owner", AccessModeOwner.ToString())
|
||||
assert.Equal(t, AccessModeNone, ParseAccessMode("owner"))
|
||||
assert.Equal(t, AccessModeNone, ParseAccessMode("invalid"))
|
||||
|
@ -5,7 +5,6 @@ package project
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@ -66,7 +65,7 @@ func Test_moveIssuesToAnotherColumn(t *testing.T) {
|
||||
|
||||
issues, err = column1.GetIssues(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, issues, 0)
|
||||
assert.Empty(t, issues)
|
||||
|
||||
issues, err = column2.GetIssues(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
@ -123,5 +122,5 @@ func Test_NewColumn(t *testing.T) {
|
||||
ProjectID: project1.ID,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "maximum number of columns reached"))
|
||||
assert.Contains(t, err.Error(), "maximum number of columns reached")
|
||||
}
|
||||
|
53
models/renderhelper/commit_checker.go
Normal file
53
models/renderhelper/commit_checker.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
type commitChecker struct {
|
||||
ctx context.Context
|
||||
commitCache map[string]bool
|
||||
gitRepoFacade gitrepo.Repository
|
||||
|
||||
gitRepo *git.Repository
|
||||
gitRepoCloser io.Closer
|
||||
}
|
||||
|
||||
func newCommitChecker(ctx context.Context, gitRepo gitrepo.Repository) *commitChecker {
|
||||
return &commitChecker{ctx: ctx, commitCache: make(map[string]bool), gitRepoFacade: gitRepo}
|
||||
}
|
||||
|
||||
func (c *commitChecker) Close() error {
|
||||
if c != nil && c.gitRepoCloser != nil {
|
||||
return c.gitRepoCloser.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
|
||||
exist, inCache := c.commitCache[commitID]
|
||||
if inCache {
|
||||
return exist
|
||||
}
|
||||
|
||||
if c.gitRepo == nil {
|
||||
r, closer, err := gitrepo.RepositoryFromContextOrOpen(c.ctx, c.gitRepoFacade)
|
||||
if err != nil {
|
||||
log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(c.gitRepoFacade), err)
|
||||
return false
|
||||
}
|
||||
c.gitRepo, c.gitRepoCloser = r, closer
|
||||
}
|
||||
|
||||
exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
|
||||
c.commitCache[commitID] = exist
|
||||
return exist
|
||||
}
|
27
models/renderhelper/main_test.go
Normal file
27
models/renderhelper/main_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
FixtureFiles: []string{"repository.yml", "user.yml"},
|
||||
SetUp: func() error {
|
||||
markup.RenderBehaviorForTesting.DisableAdditionalAttributes = true
|
||||
markup.Init(&markup.RenderHelperFuncs{
|
||||
IsUsernameMentionable: func(ctx context.Context, username string) bool {
|
||||
return username == "user2"
|
||||
},
|
||||
})
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
73
models/renderhelper/repo_comment.go
Normal file
73
models/renderhelper/repo_comment.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
type RepoComment struct {
|
||||
ctx *markup.RenderContext
|
||||
opts RepoCommentOptions
|
||||
|
||||
commitChecker *commitChecker
|
||||
repoLink string
|
||||
}
|
||||
|
||||
func (r *RepoComment) CleanUp() {
|
||||
_ = r.commitChecker.Close()
|
||||
}
|
||||
|
||||
func (r *RepoComment) IsCommitIDExisting(commitID string) bool {
|
||||
return r.commitChecker.IsCommitIDExisting(commitID)
|
||||
}
|
||||
|
||||
func (r *RepoComment) ResolveLink(link string, likeType markup.LinkType) (finalLink string) {
|
||||
switch likeType {
|
||||
case markup.LinkTypeApp:
|
||||
finalLink = r.ctx.ResolveLinkApp(link)
|
||||
default:
|
||||
finalLink = r.ctx.ResolveLinkRelative(r.repoLink, r.opts.CurrentRefPath, link)
|
||||
}
|
||||
return finalLink
|
||||
}
|
||||
|
||||
var _ markup.RenderHelper = (*RepoComment)(nil)
|
||||
|
||||
type RepoCommentOptions struct {
|
||||
DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
|
||||
DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
|
||||
CurrentRefPath string // eg: "branch/main" or "commit/11223344"
|
||||
}
|
||||
|
||||
func NewRenderContextRepoComment(ctx context.Context, repo *repo_model.Repository, opts ...RepoCommentOptions) *markup.RenderContext {
|
||||
helper := &RepoComment{
|
||||
repoLink: repo.Link(),
|
||||
opts: util.OptionalArg(opts),
|
||||
}
|
||||
rctx := markup.NewRenderContext(ctx)
|
||||
helper.ctx = rctx
|
||||
if repo != nil {
|
||||
helper.repoLink = repo.Link()
|
||||
helper.commitChecker = newCommitChecker(ctx, repo)
|
||||
rctx = rctx.WithMetas(repo.ComposeMetas(ctx))
|
||||
} else {
|
||||
// this is almost dead code, only to pass the incorrect tests
|
||||
helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
|
||||
rctx = rctx.WithMetas(map[string]string{
|
||||
"user": helper.opts.DeprecatedOwnerName,
|
||||
"repo": helper.opts.DeprecatedRepoName,
|
||||
|
||||
"markdownLineBreakStyle": "comment",
|
||||
"markupAllowShortIssuePattern": "true",
|
||||
})
|
||||
}
|
||||
rctx = rctx.WithHelper(helper)
|
||||
return rctx
|
||||
}
|
76
models/renderhelper/repo_comment_test.go
Normal file
76
models/renderhelper/repo_comment_test.go
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepoComment(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
t.Run("AutoLink", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
65f1bf27bc3bf70f64657658635e66094edbcb4d
|
||||
#1
|
||||
@user2
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
`<p><a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow"><code>65f1bf27bc</code></a><br/>
|
||||
<a href="/user2/repo1/issues/1" class="ref-issue" rel="nofollow">#1</a><br/>
|
||||
<a href="/user2" rel="nofollow">@user2</a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("AbsoluteAndRelative", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoComment(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
|
||||
|
||||
// It is Gitea's old behavior, the relative path is resolved to the repo path
|
||||
// It is different from GitHub, GitHub resolves relative links to current page's path
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
[/test](/test)
|
||||
[./test](./test)
|
||||
![/image](/image)
|
||||
![./image](./image)
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
`<p><a href="/user2/repo1/test" rel="nofollow">/test</a><br/>
|
||||
<a href="/user2/repo1/test" rel="nofollow">./test</a><br/>
|
||||
<a href="/user2/repo1/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/image" alt="/image"/></a><br/>
|
||||
<a href="/user2/repo1/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/image" alt="./image"/></a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("WithCurrentRefPath", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoComment(context.Background(), repo1, RepoCommentOptions{CurrentRefPath: "/commit/1234"}).
|
||||
WithMarkupType(markdown.MarkupName)
|
||||
|
||||
// the ref path is only used to render commit message: a commit message is rendered at the commit page with its commit ID path
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
[/test](/test)
|
||||
[./test](./test)
|
||||
![/image](/image)
|
||||
![./image](./image)
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<p><a href="/user2/repo1/test" rel="nofollow">/test</a><br/>
|
||||
<a href="/user2/repo1/commit/1234/test" rel="nofollow">./test</a><br/>
|
||||
<a href="/user2/repo1/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/image" alt="/image"/></a><br/>
|
||||
<a href="/user2/repo1/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/commit/1234/image" alt="./image"/></a></p>
|
||||
`, rendered)
|
||||
})
|
||||
}
|
77
models/renderhelper/repo_file.go
Normal file
77
models/renderhelper/repo_file.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
type RepoFile struct {
|
||||
ctx *markup.RenderContext
|
||||
opts RepoFileOptions
|
||||
|
||||
commitChecker *commitChecker
|
||||
repoLink string
|
||||
}
|
||||
|
||||
func (r *RepoFile) CleanUp() {
|
||||
_ = r.commitChecker.Close()
|
||||
}
|
||||
|
||||
func (r *RepoFile) IsCommitIDExisting(commitID string) bool {
|
||||
return r.commitChecker.IsCommitIDExisting(commitID)
|
||||
}
|
||||
|
||||
func (r *RepoFile) ResolveLink(link string, likeType markup.LinkType) string {
|
||||
finalLink := link
|
||||
switch likeType {
|
||||
case markup.LinkTypeApp:
|
||||
finalLink = r.ctx.ResolveLinkApp(link)
|
||||
case markup.LinkTypeDefault:
|
||||
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "src", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
|
||||
case markup.LinkTypeRaw:
|
||||
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "raw", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
|
||||
case markup.LinkTypeMedia:
|
||||
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "media", r.opts.CurrentRefPath), r.opts.CurrentTreePath, link)
|
||||
}
|
||||
return finalLink
|
||||
}
|
||||
|
||||
var _ markup.RenderHelper = (*RepoFile)(nil)
|
||||
|
||||
type RepoFileOptions struct {
|
||||
DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
|
||||
DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
|
||||
|
||||
CurrentRefPath string // eg: "branch/main"
|
||||
CurrentTreePath string // eg: "path/to/file" in the repo
|
||||
}
|
||||
|
||||
func NewRenderContextRepoFile(ctx context.Context, repo *repo_model.Repository, opts ...RepoFileOptions) *markup.RenderContext {
|
||||
helper := &RepoFile{opts: util.OptionalArg(opts)}
|
||||
rctx := markup.NewRenderContext(ctx)
|
||||
helper.ctx = rctx
|
||||
if repo != nil {
|
||||
helper.repoLink = repo.Link()
|
||||
helper.commitChecker = newCommitChecker(ctx, repo)
|
||||
rctx = rctx.WithMetas(repo.ComposeDocumentMetas(ctx))
|
||||
} else {
|
||||
// this is almost dead code, only to pass the incorrect tests
|
||||
helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
|
||||
rctx = rctx.WithMetas(map[string]string{
|
||||
"user": helper.opts.DeprecatedOwnerName,
|
||||
"repo": helper.opts.DeprecatedRepoName,
|
||||
|
||||
"markdownLineBreakStyle": "document",
|
||||
})
|
||||
}
|
||||
rctx = rctx.WithHelper(helper)
|
||||
return rctx
|
||||
}
|
122
models/renderhelper/repo_file_test.go
Normal file
122
models/renderhelper/repo_file_test.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
|
||||
_ "code.gitea.io/gitea/modules/markup/orgmode"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepoFile(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
t.Run("AutoLink", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoFile(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
65f1bf27bc3bf70f64657658635e66094edbcb4d
|
||||
#1
|
||||
@user2
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
`<p><a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow"><code>65f1bf27bc</code></a>
|
||||
#1
|
||||
<a href="/user2" rel="nofollow">@user2</a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("AbsoluteAndRelative", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "branch/main"}).
|
||||
WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
[/test](/test)
|
||||
[./test](./test)
|
||||
![/image](/image)
|
||||
![./image](./image)
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
`<p><a href="/user2/repo1/src/branch/main/test" rel="nofollow">/test</a>
|
||||
<a href="/user2/repo1/src/branch/main/test" rel="nofollow">./test</a>
|
||||
<a href="/user2/repo1/media/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="/image"/></a>
|
||||
<a href="/user2/repo1/media/branch/main/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/branch/main/image" alt="./image"/></a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("WithCurrentRefPath", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{CurrentRefPath: "/commit/1234"}).
|
||||
WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
[/test](/test)
|
||||
![/image](/image)
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<p><a href="/user2/repo1/src/commit/1234/test" rel="nofollow">/test</a>
|
||||
<a href="/user2/repo1/media/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/image" alt="/image"/></a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("WithCurrentRefPathByTag", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{
|
||||
CurrentRefPath: "/commit/1234",
|
||||
CurrentTreePath: "my-dir",
|
||||
}).
|
||||
WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
<img src="LINK">
|
||||
<video src="LINK">
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<a href="/user2/repo1/media/commit/1234/my-dir/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/media/commit/1234/my-dir/LINK"/></a>
|
||||
<video src="/user2/repo1/media/commit/1234/my-dir/LINK">
|
||||
</video>`, rendered)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepoFileOrgMode(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
t.Run("Links", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{
|
||||
CurrentRefPath: "/commit/1234",
|
||||
CurrentTreePath: "my-dir",
|
||||
}).WithRelativePath("my-dir/a.org")
|
||||
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
[[https://google.com/]]
|
||||
[[ImageLink.svg][The Image Desc]]
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<p>
|
||||
<a href="https://google.com/" rel="nofollow">https://google.com/</a>
|
||||
<a href="/user2/repo1/media/commit/1234/my-dir/ImageLink.svg" rel="nofollow">The Image Desc</a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("CodeHighlight", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoFile(context.Background(), repo1, RepoFileOptions{}).WithRelativePath("my-dir/a.org")
|
||||
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
#+begin_src c
|
||||
int a = 1;
|
||||
#+end_src
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<div>
|
||||
<pre><code class="chroma language-c"><span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span></code></pre>
|
||||
</div>
|
||||
`, rendered)
|
||||
})
|
||||
}
|
80
models/renderhelper/repo_wiki.go
Normal file
80
models/renderhelper/repo_wiki.go
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
type RepoWiki struct {
|
||||
ctx *markup.RenderContext
|
||||
opts RepoWikiOptions
|
||||
|
||||
commitChecker *commitChecker
|
||||
repoLink string
|
||||
}
|
||||
|
||||
func (r *RepoWiki) CleanUp() {
|
||||
_ = r.commitChecker.Close()
|
||||
}
|
||||
|
||||
func (r *RepoWiki) IsCommitIDExisting(commitID string) bool {
|
||||
return r.commitChecker.IsCommitIDExisting(commitID)
|
||||
}
|
||||
|
||||
func (r *RepoWiki) ResolveLink(link string, likeType markup.LinkType) string {
|
||||
finalLink := link
|
||||
switch likeType {
|
||||
case markup.LinkTypeApp:
|
||||
finalLink = r.ctx.ResolveLinkApp(link)
|
||||
case markup.LinkTypeDefault:
|
||||
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki", r.opts.currentRefPath), r.opts.currentTreePath, link)
|
||||
case markup.LinkTypeMedia:
|
||||
finalLink = r.ctx.ResolveLinkRelative(path.Join(r.repoLink, "wiki/raw", r.opts.currentRefPath), r.opts.currentTreePath, link)
|
||||
case markup.LinkTypeRaw: // wiki doesn't use it
|
||||
}
|
||||
|
||||
return finalLink
|
||||
}
|
||||
|
||||
var _ markup.RenderHelper = (*RepoWiki)(nil)
|
||||
|
||||
type RepoWikiOptions struct {
|
||||
DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
|
||||
DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
|
||||
|
||||
// these options are not used at the moment because Wiki doesn't support sub-path, nor branch
|
||||
currentRefPath string // eg: "branch/main"
|
||||
currentTreePath string // eg: "path/to/file" in the repo
|
||||
}
|
||||
|
||||
func NewRenderContextRepoWiki(ctx context.Context, repo *repo_model.Repository, opts ...RepoWikiOptions) *markup.RenderContext {
|
||||
helper := &RepoWiki{opts: util.OptionalArg(opts)}
|
||||
rctx := markup.NewRenderContext(ctx).WithMarkupType(markdown.MarkupName)
|
||||
if repo != nil {
|
||||
helper.repoLink = repo.Link()
|
||||
helper.commitChecker = newCommitChecker(ctx, repo)
|
||||
rctx = rctx.WithMetas(repo.ComposeWikiMetas(ctx))
|
||||
} else {
|
||||
// this is almost dead code, only to pass the incorrect tests
|
||||
helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
|
||||
rctx = rctx.WithMetas(map[string]string{
|
||||
"user": helper.opts.DeprecatedOwnerName,
|
||||
"repo": helper.opts.DeprecatedRepoName,
|
||||
|
||||
"markdownLineBreakStyle": "document",
|
||||
"markupAllowShortIssuePattern": "true",
|
||||
})
|
||||
}
|
||||
rctx = rctx.WithHelper(helper)
|
||||
helper.ctx = rctx
|
||||
return rctx
|
||||
}
|
65
models/renderhelper/repo_wiki_test.go
Normal file
65
models/renderhelper/repo_wiki_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepoWiki(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
t.Run("AutoLink", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
65f1bf27bc3bf70f64657658635e66094edbcb4d
|
||||
#1
|
||||
@user2
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
`<p><a href="/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d" rel="nofollow"><code>65f1bf27bc</code></a>
|
||||
<a href="/user2/repo1/issues/1" class="ref-issue" rel="nofollow">#1</a>
|
||||
<a href="/user2" rel="nofollow">@user2</a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("AbsoluteAndRelative", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
[/test](/test)
|
||||
[./test](./test)
|
||||
![/image](/image)
|
||||
![./image](./image)
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
`<p><a href="/user2/repo1/wiki/test" rel="nofollow">/test</a>
|
||||
<a href="/user2/repo1/wiki/test" rel="nofollow">./test</a>
|
||||
<a href="/user2/repo1/wiki/raw/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="/image"/></a>
|
||||
<a href="/user2/repo1/wiki/raw/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/image" alt="./image"/></a></p>
|
||||
`, rendered)
|
||||
})
|
||||
|
||||
t.Run("PathInTag", func(t *testing.T) {
|
||||
rctx := NewRenderContextRepoWiki(context.Background(), repo1).WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
<img src="LINK">
|
||||
<video src="LINK">
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `<a href="/user2/repo1/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/wiki/raw/LINK"/></a>
|
||||
<video src="/user2/repo1/wiki/raw/LINK">
|
||||
</video>`, rendered)
|
||||
})
|
||||
}
|
29
models/renderhelper/simple_document.go
Normal file
29
models/renderhelper/simple_document.go
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
)
|
||||
|
||||
type SimpleDocument struct {
|
||||
*markup.SimpleRenderHelper
|
||||
ctx *markup.RenderContext
|
||||
baseLink string
|
||||
}
|
||||
|
||||
func (r *SimpleDocument) ResolveLink(link string, likeType markup.LinkType) string {
|
||||
return r.ctx.ResolveLinkRelative(r.baseLink, "", link)
|
||||
}
|
||||
|
||||
var _ markup.RenderHelper = (*SimpleDocument)(nil)
|
||||
|
||||
func NewRenderContextSimpleDocument(ctx context.Context, baseLink string) *markup.RenderContext {
|
||||
helper := &SimpleDocument{baseLink: baseLink}
|
||||
rctx := markup.NewRenderContext(ctx).WithHelper(helper).WithMetas(markup.ComposeSimpleDocumentMetas())
|
||||
helper.ctx = rctx
|
||||
return rctx
|
||||
}
|
40
models/renderhelper/simple_document_test.go
Normal file
40
models/renderhelper/simple_document_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package renderhelper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSimpleDocument(t *testing.T) {
|
||||
unittest.PrepareTestEnv(t)
|
||||
rctx := NewRenderContextSimpleDocument(context.Background(), "/base").WithMarkupType(markdown.MarkupName)
|
||||
rendered, err := markup.RenderString(rctx, `
|
||||
65f1bf27bc3bf70f64657658635e66094edbcb4d
|
||||
#1
|
||||
@user2
|
||||
|
||||
[/test](/test)
|
||||
[./test](./test)
|
||||
![/image](/image)
|
||||
![./image](./image)
|
||||
`)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t,
|
||||
`<p>65f1bf27bc3bf70f64657658635e66094edbcb4d
|
||||
#1
|
||||
<a href="/base/user2" rel="nofollow">@user2</a></p>
|
||||
<p><a href="/base/test" rel="nofollow">/test</a>
|
||||
<a href="/base/test" rel="nofollow">./test</a>
|
||||
<a href="/base/image" target="_blank" rel="nofollow noopener"><img src="/base/image" alt="/image"/></a>
|
||||
<a href="/base/image" target="_blank" rel="nofollow noopener"><img src="/base/image" alt="./image"/></a></p>
|
||||
`, rendered)
|
||||
}
|
@ -617,10 +617,7 @@ func (repo *Repository) CanEnableEditor() bool {
|
||||
|
||||
// DescriptionHTML does special handles to description and return HTML string.
|
||||
func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
|
||||
desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
// Don't use Metas to speedup requests
|
||||
}, repo.Description)
|
||||
desc, err := markup.PostProcessDescriptionHTML(markup.NewRenderContext(ctx), repo.Description)
|
||||
if err != nil {
|
||||
log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err)
|
||||
return template.HTML(markup.SanitizeDescription(repo.Description))
|
||||
|
@ -144,8 +144,8 @@ func TestGetRepositoryByURL(t *testing.T) {
|
||||
assert.NotNil(t, repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, repo.ID, int64(2))
|
||||
assert.Equal(t, repo.OwnerID, int64(2))
|
||||
assert.Equal(t, int64(2), repo.ID)
|
||||
assert.Equal(t, int64(2), repo.OwnerID)
|
||||
}
|
||||
|
||||
test(t, "https://try.gitea.io/user2/repo2")
|
||||
@ -159,8 +159,8 @@ func TestGetRepositoryByURL(t *testing.T) {
|
||||
assert.NotNil(t, repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, repo.ID, int64(2))
|
||||
assert.Equal(t, repo.OwnerID, int64(2))
|
||||
assert.Equal(t, int64(2), repo.ID)
|
||||
assert.Equal(t, int64(2), repo.OwnerID)
|
||||
}
|
||||
|
||||
test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
|
||||
@ -177,8 +177,8 @@ func TestGetRepositoryByURL(t *testing.T) {
|
||||
assert.NotNil(t, repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, repo.ID, int64(2))
|
||||
assert.Equal(t, repo.OwnerID, int64(2))
|
||||
assert.Equal(t, int64(2), repo.ID)
|
||||
assert.Equal(t, int64(2), repo.OwnerID)
|
||||
}
|
||||
|
||||
test(t, "sshuser@try.gitea.io:user2/repo2")
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user