Compare commits
82 commits
327bcf710d
...
434e1b76a7
Author | SHA1 | Date | |
---|---|---|---|
Cat /dev/Nulo | 434e1b76a7 | ||
Cat /dev/Nulo | ce53cfe42a | ||
a3f403f438 | |||
8ee1ed877b | |||
2c2e00899d | |||
6cbb6f303a | |||
6af698fb81 | |||
94a05a492d | |||
6de862abdf | |||
b47482d58e | |||
74ab798033 | |||
97a0bf151a | |||
5e2bae7716 | |||
96d3fcf179 | |||
265f485295 | |||
f144521aea | |||
6f4d5c0b8c | |||
1ec622db24 | |||
40d51188c0 | |||
87db4a47c8 | |||
cd2dd5a67d | |||
46beb7f33f | |||
3107093394 | |||
272ae03341 | |||
b56a9f6ded | |||
c5c44d0951 | |||
8f2805f757 | |||
5eaf91e919 | |||
b7e3adc66c | |||
5b5f8aab19 | |||
fef34790bb | |||
8b590de186 | |||
5105d2093c | |||
08445d5d86 | |||
b71d4c3ec0 | |||
bf537adf8a | |||
8c8c24f8eb | |||
fee9c05ed3 | |||
e15fe85335 | |||
4f5122a7fe | |||
84e65afffd | |||
d2908b2794 | |||
24e03a125d | |||
76e892317b | |||
5001f63c07 | |||
6d22ca15ab | |||
ea9f5a57e4 | |||
96141e4e55 | |||
ca5f0c93c6 | |||
196100a07a | |||
bc3d8bff73 | |||
7f81110461 | |||
5ed0eefc9a | |||
4b89c0f996 | |||
7cae4dfc00 | |||
28b8e0b43e | |||
23838c2c2e | |||
f9763f1366 | |||
a2314ca9c5 | |||
994ba35f11 | |||
447422fe27 | |||
9bfee5014b | |||
7128929a0d | |||
efcbaf8fa8 | |||
c997e90738 | |||
ffab076b72 | |||
117d9a117f | |||
f8c5f202b7 | |||
7213506680 | |||
1f82be6604 | |||
56bedf2bcc | |||
f7567f798d | |||
93ede4bc83 | |||
9f63d27ec4 | |||
073d8c50dd | |||
bc6477b36b | |||
09efce9da2 | |||
124a9957d0 | |||
d72e20627d | |||
00cd5ba6f4 | |||
eef4148935 | |||
d4122712f7 |
9
.github/workflows/release-tag-rc.yml
vendored
9
.github/workflows/release-tag-rc.yml
vendored
|
@ -44,7 +44,7 @@ jobs:
|
|||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
run: |
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
|
@ -56,6 +56,10 @@ jobs:
|
|||
- name: upload binaries to s3
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
- name: Install GH CLI
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
run: |
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
||||
|
@ -74,6 +78,8 @@ jobs:
|
|||
id: meta
|
||||
with:
|
||||
images: gitea/gitea
|
||||
flavor: |
|
||||
latest=false
|
||||
# 1.2.3-rc0
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
|
@ -105,6 +111,7 @@ jobs:
|
|||
images: gitea/gitea
|
||||
# each tag below will have the suffix of -rootless
|
||||
flavor: |
|
||||
latest=false
|
||||
suffix=-rootless
|
||||
# 1.2.3-rc0
|
||||
tags: |
|
||||
|
|
12
.github/workflows/release-tag-version.yml
vendored
12
.github/workflows/release-tag-version.yml
vendored
|
@ -46,7 +46,7 @@ jobs:
|
|||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
run: |
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
|
@ -58,9 +58,13 @@ jobs:
|
|||
- name: upload binaries to s3
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
- name: Install GH CLI
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
run: |
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
|
@ -82,7 +86,6 @@ jobs:
|
|||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
|
@ -114,14 +117,13 @@ jobs:
|
|||
images: gitea/gitea
|
||||
# each tag below will have the suffix of -rootless
|
||||
flavor: |
|
||||
suffix=-rootless
|
||||
suffix=-rootless,onlatest=true
|
||||
# this will generate tags in the following format (with -rootless suffix added):
|
||||
# latest
|
||||
# 1
|
||||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
|
|
15
.woodpecker.yml
Normal file
15
.woodpecker.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
pipeline:
|
||||
nulo-container:
|
||||
image: docker.io/woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
repo: gitea.nulo.in/nulo/forgejo
|
||||
tag: v1.21.3-0
|
||||
registry: https://gitea.nulo.in
|
||||
username: Nulo
|
||||
password:
|
||||
from_secret: registry_secret
|
||||
secrets: [REGISTRY_SECRET]
|
||||
when:
|
||||
branch: "nulo/release/v1.21"
|
||||
event: "push"
|
||||
|
515
CHANGELOG.md
515
CHANGELOG.md
|
@ -4,6 +4,520 @@ This changelog goes through all the changes that have been made in each release
|
|||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.21.2](https://github.com/go-gitea/gitea/releases/tag/1.21.2) - 2023-12-12
|
||||
|
||||
* SECURITY
|
||||
* Rebuild with recently released golang version
|
||||
* Fix missing check (#28406) (#28411)
|
||||
* Do some missing checks (#28423) (#28432)
|
||||
* BUGFIXES
|
||||
* Fix margin in server signed signature verification view (#28379) (#28381)
|
||||
* Fix object does not exist error when checking citation file (#28314) (#28369)
|
||||
* Use `filepath` instead of `path` to create SQLite3 database file (#28374) (#28378)
|
||||
* Fix the runs will not be displayed bug when the main branch have no workflows but other branches have (#28359) (#28365)
|
||||
* Handle repository.size column being NULL in migration v263 (#28336) (#28363)
|
||||
* Convert git commit summary to valid UTF8. (#28356) (#28358)
|
||||
* Fix migration panic due to an empty review comment diff (#28334) (#28362)
|
||||
* Add `HEAD` support for rpm repo files (#28309) (#28360)
|
||||
* Fix RPM/Debian signature key creation (#28352) (#28353)
|
||||
* Keep profile tab when clicking on Language (#28320) (#28331)
|
||||
* Fix missing issue search index update when changing status (#28325) (#28330)
|
||||
* Fix wrong link in `protect_branch_name_pattern_desc` (#28313) (#28315)
|
||||
* Read `previous` info from git blame (#28306) (#28310)
|
||||
* Ignore "non-existing" errors when getDirectorySize calculates the size (#28276) (#28285)
|
||||
* Use appSubUrl for OAuth2 callback URL tip (#28266) (#28275)
|
||||
* Meilisearch: require all query terms to be matched (#28293) (#28296)
|
||||
* Fix required error for token name (#28267) (#28284)
|
||||
* Fix issue will be detected as pull request when checking `First-time contributor` (#28237) (#28271)
|
||||
* Use full width for project boards (#28225) (#28245)
|
||||
* Increase "version" when update the setting value to a same value as before (#28243) (#28244)
|
||||
* Also sync DB branches on push if necessary (#28361) (#28403)
|
||||
* Make gogit Repository.GetBranchNames consistent (#28348) (#28386)
|
||||
* Recover from panic in cron task (#28409) (#28425)
|
||||
* Deprecate query string auth tokens (#28390) (#28430)
|
||||
* ENHANCEMENTS
|
||||
* Improve doctor cli behavior (#28422) (#28424)
|
||||
* Fix margin in server signed signature verification view (#28379) (#28381)
|
||||
* Refactor template empty checks (#28351) (#28354)
|
||||
* Read `previous` info from git blame (#28306) (#28310)
|
||||
* Use full width for project boards (#28225) (#28245)
|
||||
* Enable system users search via the API (#28013) (#28018)
|
||||
|
||||
## [1.21.1](https://github.com/go-gitea/gitea/releases/tag/1.21.1) - 2023-11-26
|
||||
|
||||
* SECURITY
|
||||
* Fix comment permissions (#28213) (#28216)
|
||||
* BUGFIXES
|
||||
* Fix delete-orphaned-repos (#28200) (#28202)
|
||||
* Make CORS work for oauth2 handlers (#28184) (#28185)
|
||||
* Fix missing buttons (#28179) (#28181)
|
||||
* Fix no ActionTaskOutput table waring (#28149) (#28152)
|
||||
* Fix empty action run title (#28113) (#28148)
|
||||
* Use "is-loading" to avoid duplicate form submit for code comment (#28143) (#28147)
|
||||
* Fix Matrix and MSTeams nil dereference (#28089) (#28105)
|
||||
* Fix incorrect pgsql conn builder behavior (#28085) (#28098)
|
||||
* Fix system config cache expiration timing (#28072) (#28090)
|
||||
* Restricted users only see repos in orgs which their team was assigned to (#28025) (#28051)
|
||||
* API
|
||||
* Fix permissions for Token DELETE endpoint to match GET and POST (#27610) (#28099)
|
||||
* ENHANCEMENTS
|
||||
* Do not display search box when there's no packages yet (#28146) (#28159)
|
||||
* Add missing `packages.cleanup.success` (#28129) (#28132)
|
||||
* DOCS
|
||||
* Docs: Replace deprecated IS_TLS_ENABLED mailer setting in email setup (#28205) (#28208)
|
||||
* Fix the description about the default setting for action in quick start document (#28160) (#28168)
|
||||
* Add guide page to actions when there's no workflows (#28145) (#28153)
|
||||
* MISC
|
||||
* Use full width for PR comparison (#28182) (#28186)
|
||||
|
||||
## [1.21.0](https://github.com/go-gitea/gitea/releases/tag/v1.21.0) - 2023-11-14
|
||||
|
||||
* BREAKING
|
||||
* Restrict certificate type for builtin SSH server (#26789)
|
||||
* Refactor to use urfave/cli/v2 (#25959)
|
||||
* Move public asset files to the proper directory (#25907)
|
||||
* Remove commit status running and warning to align GitHub (#25839) (partially reverted: Restore warning commit status (#27504) (#27529))
|
||||
* Remove "CHARSET" config option for MySQL, always use "utf8mb4" (#25413)
|
||||
* Set SSH_AUTHORIZED_KEYS_BACKUP to false (#25412)
|
||||
* FEATURES
|
||||
* User details page (#26713)
|
||||
* Chore(actions): support cron schedule task (#26655)
|
||||
* Support rebuilding issue indexer manually (#26546)
|
||||
* Allow to archive labels (#26478)
|
||||
* Add disable workflow feature (#26413)
|
||||
* Support `.git-blame-ignore-revs` file (#26395)
|
||||
* Pre-register OAuth2 applications for git credential helpers (#26291)
|
||||
* Add `Retry` button when creating a mirror-repo fails (#26228)
|
||||
* Artifacts retention and auto clean up (#26131)
|
||||
* Serve pre-defined files in "public", add "security.txt", add CORS header for ".well-known" (#25974)
|
||||
* Implement auto-cancellation of concurrent jobs if the event is push (#25716)
|
||||
* Newly pushed branches hints on repository home page (#25715)
|
||||
* Display branch commit status (#25608)
|
||||
* Add direct serving of package content (#25543)
|
||||
* Add commits dropdown in PR files view and allow commit by commit review (#25528)
|
||||
* Allow package cleanup from admin page (#25307)
|
||||
* Batch delete issue and improve tippy opts (#25253)
|
||||
* Show branches and tags that contain a commit (#25180)
|
||||
* Add actor and status dropdowns to run list (#25118)
|
||||
* Allow Organisations to have a E-Mail (#25082)
|
||||
* Add codeowners feature (#24910)
|
||||
* Actions Artifacts support uploading multiple files and directories (#24874)
|
||||
* Support configuration variables on Gitea Actions (#24724)
|
||||
* Support downloading raw task logs (#24451)
|
||||
* API
|
||||
* Unify two factor check (#27915) (#27929)
|
||||
* Fix package webhook (#27839) (#27855)
|
||||
* Fix/upload artifact error windows (#27802) (#27840)
|
||||
* Fix bad method call when deleting user secrets via API (#27829) (#27831)
|
||||
* Do not force creation of _cargo-index repo on publish (#27266) (#27765)
|
||||
* Delete repos of org when purge delete user (#27273) (#27728)
|
||||
* Fix org team endpoint (#27721) (#27727)
|
||||
* Api: GetPullRequestCommits: return file list (#27483) (#27539)
|
||||
* Don't let API add 2 exclusive labels from same scope (#27433) (#27460)
|
||||
* Redefine the meaning of column is_active to make Actions Registration Token generation easier (#27143) (#27304)
|
||||
* Fix PushEvent NullPointerException jenkinsci/github-plugin (#27203) (#27251)
|
||||
* Fix organization field being null in POST /orgs/{orgid}/teams (#27150) (#27163)
|
||||
* Allow empty Conan files (#27092)
|
||||
* Fix token endpoints ignore specified account (#27080)
|
||||
* Reduce usage of `db.DefaultContext` (#27073) (#27083) (#27089) (#27103) (#27262) (#27265) (#27347) (#26076)
|
||||
* Make SSPI auth mockable (#27036)
|
||||
* Extract auth middleware from service (#27028)
|
||||
* Add `RemoteAddress` to mirrors (#26952)
|
||||
* Feat(API): add routes and functions for managing user's secrets (#26909)
|
||||
* Feat(API): add secret deletion functionality for repository (#26808)
|
||||
* Feat(API): add route and implementation for creating/updating repository secret (#26766)
|
||||
* Add Upload URL to release API (#26663)
|
||||
* Feat(API): update and delete secret for managing organization secrets (#26660)
|
||||
* Feat: implement organization secret creation API (#26566)
|
||||
* Add API route to list org secrets (#26485)
|
||||
* Set commit id when ref used explicitly (#26447)
|
||||
* PATCH branch-protection updates check list even when checks are disabled (#26351)
|
||||
* Add file status for API "Get a single commit from a repository" (#16205) (#25831)
|
||||
* Add API for changing Avatars (#25369)
|
||||
* BUGFIXES
|
||||
* Fix viewing wiki commit on empty repo (#28040) (#28044)
|
||||
* Enable system users for comment.LoadPoster (#28014) (#28032)
|
||||
* Fixed duplicate attachments on dump on windows (#28019) (#28031)
|
||||
* Fix wrong xorm Delete usage(backport for 1.21) (#28002)
|
||||
* Add word-break to repo description in home page (#27924) (#27957)
|
||||
* Fix rendering assignee changed comments without assignee (#27927) (#27952)
|
||||
* Add word break to release title (#27942) (#27947)
|
||||
* Fix JS NPE when viewing specific range of PR commits (#27912) (#27923)
|
||||
* Show correct commit sha when viewing single commit diff (#27916) (#27921)
|
||||
* Fix 500 when deleting a dismissed review (#27903) (#27910)
|
||||
* Fix DownloadFunc when migrating releases (#27887) (#27890)
|
||||
* Fix http protocol auth (#27875) (#27876)
|
||||
* Refactor postgres connection string building (#27723) (#27869)
|
||||
* Close all hashed buffers (#27787) (#27790)
|
||||
* Fix label render containing invalid HTML (#27752) (#27762)
|
||||
* Fix duplicate project board when hitting `enter` key (#27746) (#27751)
|
||||
* Fix `link-action` redirect network error (#27734) (#27749)
|
||||
* Fix sticky diff header background (#27697) (#27712)
|
||||
* Always delete existing scheduled action tasks (#27662) (#27688)
|
||||
* Support allowed hosts for webhook to work with proxy (#27655) (#27675)
|
||||
* Fix poster is not loaded in get default merge message (#27657) (#27666)
|
||||
* Improve dropdown button alignment and fix hover bug (#27632) (#27637)
|
||||
* Improve retrying index issues (#27554) (#27634)
|
||||
* Fix 404 when deleting Docker package with an internal version (#27615) (#27630)
|
||||
* Backport manually for a tmpl issue in v1.21 (#27612)
|
||||
* Don't show Link to TOTP if not set up (#27585) (#27588)
|
||||
* Fix data-race bug when accessing task.LastRun (#27584) (#27586)
|
||||
* Fix attachment download bug (#27486) (#27571)
|
||||
* Respect SSH.KeygenPath option when calculating ssh key fingerprints (#27536) (#27551)
|
||||
* Improve dropdown's behavior when there is a search input in menu (#27526) (#27534)
|
||||
* Fix panic in storageHandler (#27446) (#27479)
|
||||
* When comparing with an non-exist repository, return 404 but 500 (#27437) (#27442)
|
||||
* Fix pr template (#27436) (#27440)
|
||||
* Fix git 2.11 error when checking IsEmpty (#27393) (#27397)
|
||||
* Allow get release download files and lfs files with oauth2 token format (#26430) (#27379)
|
||||
* Fix missing ctx for GetRepoLink in dashboard (#27372) (#27375)
|
||||
* Absolute positioned checkboxes overlay floated elements (#26870) (#27366)
|
||||
* Introduce fixes and more rigorous tests for 'Show on a map' feature (#26803) (#27365)
|
||||
* Fix repo count in org action settings (#27245) (#27353)
|
||||
* Add logs for data broken of comment review (#27326) (#27345)
|
||||
* Fix the approval count of PR when there is no protection branch rule (#27272) (#27343)
|
||||
* Fix Bug in Issue Config when only contact links are set (#26521) (#27334)
|
||||
* Improve issue history dialog and make poster can delete their own history (#27323) (#27327)
|
||||
* Fix orphan check for deleted branch (#27310) (#27321)
|
||||
* Fix protected branch icon location (#26576) (#27317)
|
||||
* Fix yaml test (#27297) (#27303)
|
||||
* Fix some animation bugs (#27287) (#27294)
|
||||
* Fix incorrect change from #27231 (#27275) (#27282)
|
||||
* Add missing public user visibility in user details page (#27246) (#27250)
|
||||
* Fix EOL handling in web editor (#27141) (#27234)
|
||||
* Fix issues on action runners page (#27226) (#27233)
|
||||
* Quote table `release` in sql queries (#27205) (#27218)
|
||||
* Fix release URL in webhooks (#27182) (#27185)
|
||||
* Fix review request number and add more tests (#27104) (#27168)
|
||||
* Fix the variable regexp pattern on web page (#27161) (#27164)
|
||||
* Fix: treat tab "overview" as "repositories" in user profiles without readme (#27124)
|
||||
* Fix NPE when editing OAuth2 applications (#27078)
|
||||
* Fix the incorrect route path in the user edit page. (#27007)
|
||||
* Fix the secret regexp pattern on web page (#26910)
|
||||
* Allow users with write permissions for issues to add attachments with API (#26837)
|
||||
* Make "link-action" backend code respond correct JSON content (#26680)
|
||||
* Use line-height: normal by default (#26635)
|
||||
* Fix NPM packages name validation (#26595)
|
||||
* Rewrite the DiffFileTreeItem and fix misalignment (#26565)
|
||||
* Return empty when searching issues with no repos (#26545)
|
||||
* Explain SearchOptions and fix ToSearchOptions (#26542)
|
||||
* Add missing triggers to update issue indexer (#26539)
|
||||
* Handle base64 decoding correctly to avoid panic (#26483)
|
||||
* Avoiding accessing undefined mentionValues (#26461)
|
||||
* Fix incorrect redirection in new issue using references (#26440)
|
||||
* Fix the bug when getting files changed for `pull_request_target` event (#26320)
|
||||
* Remove IsWarning in tmpl (#26120)
|
||||
* Fix loading `LFS_JWT_SECRET` from wrong section (#26109)
|
||||
* Fixing redirection issue for logged-in users (#26105)
|
||||
* Improve "gitea doctor" sub-command and fix "help" commands (#26072)
|
||||
* Fix the truncate and alignment problem for some admin tables (#26042)
|
||||
* Update minimum password length requirements (#25946)
|
||||
* Do not "guess" the file encoding/BOM when using API to upload files (#25828)
|
||||
* Restructure issue list template, styles (#25750)
|
||||
* Fix `ref` for workflows triggered by `pull_request_target` (#25743)
|
||||
* Fix issues indexer document mapping (#25619)
|
||||
* Use JSON response for "user/logout" (#25522)
|
||||
* Fix migrate page layout on mobile (#25507)
|
||||
* Link to existing PR when trying to open a new PR on the same branches (#25494)
|
||||
* Do not publish docker release images on `-dev` tags (#25471)
|
||||
* Support `pull_request_target` event (#25229)
|
||||
* Modify the content format of the Feishu webhook (#25106)
|
||||
* ENHANCEMENTS
|
||||
* Render email addresses as such if followed by punctuation (#27987) (#27992)
|
||||
* Show error toast when file size exceeds the limits (#27985) (#27986)
|
||||
* Fix citation error when the file size is larger than 1024 bytes (#27958) (#27965)
|
||||
* Remove action runners on user deletion (#27902) (#27908)
|
||||
* Remove set tabindex on view issue (#27892) (#27896)
|
||||
* Reduce margin/padding on flex-list items and divider (#27872) (#27874)
|
||||
* Change katex limits (#27823) (#27868)
|
||||
* Clean up template locale usage (#27856) (#27857)
|
||||
* Add dedicated class for empty placeholders (#27788) (#27792)
|
||||
* Add gap between diff boxes (#27776) (#27781)
|
||||
* Fix incorrect "tab" parameter for repo search sub-template (#27755) (#27764)
|
||||
* Enable followCursor for language stats bar (#27713) (#27739)
|
||||
* Improve diff tree spacing (#27714) (#27719)
|
||||
* Feed UI Improvements (#27356) (#27717)
|
||||
* Improve feed icons and feed merge text color (#27498) (#27716)
|
||||
* [FIX] resolve confusing colors in languages stats by insert a gap (#27704) (#27715)
|
||||
* Add doctor dbconsistency fix to delete repos with no owner (#27290) (#27693)
|
||||
* Fix required checkboxes in issue forms (#27592) (#27692)
|
||||
* Hide archived labels by default from the suggestions when assigning labels for an issue (#27451) (#27661)
|
||||
* Cleanup repo details icons/labels (#27644) (#27654)
|
||||
* Keep filter when showing unfiltered results on explore page (#27192) (#27589)
|
||||
* Show manual cron run's last time (#27544) (#27577)
|
||||
* Revert "Fix pr template (#27436)" (#27567)
|
||||
* Increase queue length (#27555) (#27562)
|
||||
* Avoid run change title process when the title is same (#27467) (#27558)
|
||||
* Remove max-width and add hide text overflow (#27359) (#27550)
|
||||
* Add hover background to wiki list page (#27507) (#27521)
|
||||
* Fix mermaid flowchart margin issue (#27503) (#27516)
|
||||
* Refactor system setting (#27000) (#27452)
|
||||
* Fix missing `ctx` in new_form.tmpl (#27434) (#27438)
|
||||
* Add Index to `action.user_id` (#27403) (#27425)
|
||||
* Don't use subselect in `DeleteIssuesByRepoID` (#27332) (#27408)
|
||||
* Add support for HEAD ref in /src/branch and /src/commit routes (#27384) (#27407)
|
||||
* Make Actions tasks/jobs timeouts configurable by the user (#27400) (#27402)
|
||||
* Hide archived labels when filtering by labels on the issue list (#27115) (#27381)
|
||||
* Highlight user details link (#26998) (#27376)
|
||||
* Add protected branch name description (#27257) (#27351)
|
||||
* Improve tree not found page (#26570) (#27346)
|
||||
* Add Index to `comment.dependent_issue_id` (#27325) (#27340)
|
||||
* Improve branch list UI (#27319) (#27324)
|
||||
* Fix divider in subscription page (#27298) (#27301)
|
||||
* Add missed return to actions view fetch (#27289) (#27293)
|
||||
* Backport ctx locale refactoring manually (#27231) (#27259) (#27260)
|
||||
* Disable `Test Delivery` and `Replay` webhook buttons when webhook is inactive (#27211) (#27253)
|
||||
* Use mask-based fade-out effect for `.new-menu` (#27181) (#27243)
|
||||
* Cleanup locale function usage (#27227) (#27240)
|
||||
* Fix z-index on markdown completion (#27237) (#27239)
|
||||
* Fix Fomantic UI dropdown icon bug when there is a search input in menu (#27225) (#27228)
|
||||
* Allow copying issue comment link on archived repos and when not logged in (#27193) (#27210)
|
||||
* Fix: text decorator on issue sidebar menu label (#27206) (#27209)
|
||||
* Fix dropdown icon position (#27175) (#27177)
|
||||
* Add index to `issue_user.issue_id` (#27154) (#27158)
|
||||
* Increase auth provider icon size on login page (#27122)
|
||||
* Remove a `gt-float-right` and some unnecessary helpers (#27110)
|
||||
* Change green buttons to primary color (#27099)
|
||||
* Use db.WithTx for AddTeamMember to avoid ctx abuse (#27095)
|
||||
* Use `print` instead of `printf` (#27093)
|
||||
* Remove the useless function `GetUserIssueStats` and move relevant tests to `indexer_test.go` (#27067)
|
||||
* Search branches (#27055)
|
||||
* Display all user types and org types on admin management UI (#27050)
|
||||
* Ui correction in mobile view nav bar left aligned items. (#27046)
|
||||
* Chroma color tweaks (#26978)
|
||||
* Move some functions to service layer (#26969)
|
||||
* Improve "language stats" UI (#26968)
|
||||
* Replace `util.SliceXxx` with `slices.Xxx` (#26958)
|
||||
* Refactor dashboard/feed.tmpl (#26956)
|
||||
* Move repository deletion to service layer (#26948)
|
||||
* Fix the missing repo count (#26942)
|
||||
* Improve hint when uploading a too large avatar (#26935)
|
||||
* Extract common code to new template (#26933)
|
||||
* Move createrepository from module to service layer (#26927)
|
||||
* Move notification interface to services layer (#26915)
|
||||
* Move feed notification service layer (#26908)
|
||||
* Move ui notification to service layer (#26907)
|
||||
* Move indexer notification to service layer (#26906)
|
||||
* Move mail notification logic to service layer (#26905)
|
||||
* Extract common code to new template (#26903)
|
||||
* Show queue's active worker number (#26896)
|
||||
* Fix media description render for orgmode (#26895)
|
||||
* Remove CSS `has` selector and improve various styles (#26891)
|
||||
* Relocate the `RSS user feed` button (#26882)
|
||||
* Refactor "shortsha" (#26877)
|
||||
* Refactor `og:description` to limit the max length (#26876)
|
||||
* Move web/api context related testing function into a separate package (#26859)
|
||||
* Redable error on S3 storage connection failure (#26856)
|
||||
* Improve opengraph previews (#26851)
|
||||
* Add more descriptive error on forgot password page (#26848)
|
||||
* Show always repo count in header (#26842)
|
||||
* Remove "TODO" tasks from CSS file (#26835)
|
||||
* Render code blocks in repo description (#26830)
|
||||
* Minor dashboard tweaks, fix flex-list margins (#26829)
|
||||
* Remove polluted `.ui.right` (#26825)
|
||||
* Display archived labels specially when listing labels (#26820)
|
||||
* Remove polluted ".ui.left" style (#26809)
|
||||
* Make it posible to customize nav text color via css var (#26807)
|
||||
* Refactor lfs requests (#26783)
|
||||
* Improve flex list item padding (#26779)
|
||||
* Remove fomantic `text` module (#26777)
|
||||
* Remove fomantic `item` module (#26775)
|
||||
* Remove redundant nil check in `WalkGitLog` (#26773)
|
||||
* Reduce some allocations in type conversion (#26772)
|
||||
* Refactor some CSS styles and simplify code (#26771)
|
||||
* Unify `border-radius` behavior (#26770)
|
||||
* Improve modal dialog UI (#26764)
|
||||
* Allow "latest" to be used in release vTag when downloading file (#26748)
|
||||
* Adding hint `Archived` to archive label. (#26741)
|
||||
* Move `modules/mirror` to `services` (#26737)
|
||||
* Add "dir=auto" for input/textarea elements by default (#26735)
|
||||
* Add auth-required to config.json for Cargo http registry (#26729)
|
||||
* Simplify helper CSS classes and avoid abuse (#26728)
|
||||
* Make web context initialize correctly for different cases (#26726)
|
||||
* Focus editor on "Write" tab click (#26714)
|
||||
* Remove incorrect CSS helper classes (#26712)
|
||||
* Fix review bar misalignment (#26711)
|
||||
* Add reverseproxy auth for API back with default disabled (#26703)
|
||||
* Add default label in branch select list (#26697)
|
||||
* Improve Image Diff UI (#26696)
|
||||
* Fixed text overflow in dropdown menu (#26694)
|
||||
* [Refactor] getIssueStatsChunk to move inner function into own one (#26671)
|
||||
* Remove fomantic loader module (#26670)
|
||||
* Add `member`, `collaborator`, `contributor`, and `first-time contributor` roles and tooltips (#26658)
|
||||
* Improve some flex layouts (#26649)
|
||||
* Improve the branch selector tab UI (#26631)
|
||||
* Improve show role (#26621)
|
||||
* Remove avatarHTML from template helpers (#26598)
|
||||
* Allow text selection in actions step header (#26588)
|
||||
* Improve translation of milestone filters (#26569)
|
||||
* Add optimistic lock to ActionRun table (#26563)
|
||||
* Update team invitation email link (#26550)
|
||||
* Differentiate better between user settings and admin settings (#26538)
|
||||
* Check disabled workflow when rerun jobs (#26535)
|
||||
* Improve deadline icon location in milestone list page (#26532)
|
||||
* Improve repo sub menu (#26531)
|
||||
* Fix the display of org level badges (#26504)
|
||||
* Rename `Sync2` -> `Sync` (#26479)
|
||||
* Fix stderr usages (#26477)
|
||||
* Remove fomantic transition module (#26469)
|
||||
* Refactor tests (#26464)
|
||||
* Refactor project templates (#26448)
|
||||
* Fall back to esbuild for css minify (#26445)
|
||||
* Always show usernames in reaction tooltips (#26444)
|
||||
* Use correct pull request commit link instead of a generic commit link (#26434)
|
||||
* Refactor "editorconfig" (#26391)
|
||||
* Make `user-content-* ` consistent with github (#26388)
|
||||
* Remove unnecessary template helper repoAvatar (#26387)
|
||||
* Remove unnecessary template helper DisableGravatar (#26386)
|
||||
* Use template context function for avatar rendering (#26385)
|
||||
* Rename code_langauge.go to code_language.go (#26377)
|
||||
* Use more `IssueList` instead of `[]*Issue` (#26369)
|
||||
* Do not highlight `#number` in documents (#26365)
|
||||
* Fix display problems of members and teams unit (#26363)
|
||||
* Fix 404 error when remove self from an organization (#26362)
|
||||
* Improve CLI and messages (#26341)
|
||||
* Refactor backend SVG package and add tests (#26335)
|
||||
* Add link to job details and tooltip to commit status in repo list in dashboard (#26326)
|
||||
* Use yellow if an approved review is stale (#26312)
|
||||
* Remove commit load branches and tags in wiki repo (#26304)
|
||||
* Add highlight to selected repos in milestone dashboard (#26300)
|
||||
* Delete `issue_service.CreateComment` (#26298)
|
||||
* Do not show Profile README when repository is private (#26295)
|
||||
* Tweak actions menu (#26278)
|
||||
* Start using template context function (#26254)
|
||||
* Use calendar icon for `Joined on...` in profiles (#26215)
|
||||
* Add 'Show on a map' button to Location in profile, fix layout (#26214)
|
||||
* Render plaintext task list items for markdown files (#26186)
|
||||
* Add tooltip to describe LFS table column and color `delete LFS file` button red (#26181)
|
||||
* Release attachments duplicated check (#26176)
|
||||
* De-emphasize issue sidebar buttons (#26171)
|
||||
* Fixing the align of commit stats in commit_page template. (#26161)
|
||||
* Allow editing push mirrors after creation (#26151)
|
||||
* Move web JSON functions to web context and simplify code (#26132)
|
||||
* Refactor improve NoBetterThan (#26126)
|
||||
* Improve clickable area in repo action view page (#26115)
|
||||
* Add context parameter to some database functions (#26055)
|
||||
* Docusaurus-ify (#26051)
|
||||
* Improve text for empty issue/pr description (#26047)
|
||||
* Categorize admin settings sidebar panel (#26030)
|
||||
* Remove redundant "RouteMethods" method (#26024)
|
||||
* Refactor and enhance issue indexer to support both searching, filtering and paging (#26012)
|
||||
* Add a link to OpenID Issuer URL in WebFinger response (#26000)
|
||||
* Fix UI for release tag page / wiki page / subscription page (#25948)
|
||||
* Support copy protected branch from template repository (#25889)
|
||||
* Improve display of Labels/Projects/Assignees sort options (#25886)
|
||||
* Fix margin on the new/edit project page. (#25885)
|
||||
* Show image size on view page (#25884)
|
||||
* Remove ref name in PR commits page (#25876)
|
||||
* Allow the use of alternative net.Listener implementations by downstreams (#25855)
|
||||
* Refactor "Content" for file uploading (#25851)
|
||||
* Add error info if no user can fork the repo (#25820)
|
||||
* Show edit title button on commits tab of PR, too (#25791)
|
||||
* Introduce `flex-list` & `flex-item` elements for Gitea UI (#25790)
|
||||
* Don't stack PR tab menu on small screens (#25789)
|
||||
* Repository Archived text title center align (#25767)
|
||||
* Make route middleware/handler mockable (#25766)
|
||||
* Move issue filters to shared template (#25729)
|
||||
* Use frontend fetch for branch dropdown component (#25719)
|
||||
* Add open/closed field support for issue index (#25708)
|
||||
* Some less naked returns (#25682)
|
||||
* Fix inconsistent user profile layout across tabs (#25625)
|
||||
* Get latest commit statuses from database instead of git data on dashboard for repositories (#25605)
|
||||
* Adding branch-name copy to clipboard branches screen. (#25596)
|
||||
* Update emoji set to Unicode 15 (#25595)
|
||||
* Move some files under repo/setting (#25585)
|
||||
* Add custom ansi colors and CSS variables for them (#25546)
|
||||
* Add log line anchor for action logs (#25532)
|
||||
* Use flex instead of float for sort button and search input (#25519)
|
||||
* Update octicons and use `octicon-file-directory-symlink` (#25453)
|
||||
* Add toasts to UI (#25449)
|
||||
* Fine tune project board label colors and modal content background (#25419)
|
||||
* Import additional secrets via file uri (#25408)
|
||||
* Switch to ansi_up for ansi rendering in actions (#25401)
|
||||
* Store and use seconds for timeline time comments (#25392)
|
||||
* Support displaying diff stats in PR tab bar (#25387)
|
||||
* Use fetch form action for lock/unlock/pin/unpin on sidebar (#25380)
|
||||
* Refactor: TotalTimes return seconds (#25370)
|
||||
* Navbar styling rework (#25343)
|
||||
* Introduce shared template for search inputs (#25338)
|
||||
* Only show 'Manage Account Links' when necessary (#25311)
|
||||
* Improve 'Privacy' section in profile settings (#25309)
|
||||
* Substitute variables in path names of template repos too (#25294)
|
||||
* Fix tags line no margin see #25255 (#25280)
|
||||
* Use fetch to send requests to create issues/comments (#25258)
|
||||
* Change form actions to fetch for submit review box (#25219)
|
||||
* Improve AJAX link and modal confirm dialog (#25210)
|
||||
* Reduce unnecessary DB queries for Actions tasks (#25199)
|
||||
* Disable `Create column` button while the column name is empty (#25192)
|
||||
* Refactor indexer (#25174)
|
||||
* Adjust style for action run list (align icons, adjust padding) (#25170)
|
||||
* Remove duplicated functions when deleting a branch (#25128)
|
||||
* Make confusable character warning less jarring (#25069)
|
||||
* Highlight viewed files differently in the PR filetree (#24956)
|
||||
* Support changing labels of Actions runner without re-registration (#24806)
|
||||
* Fix duplicate Reviewed-by trailers (#24796)
|
||||
* Resolve issue with sort icons on admin/users and admin/runners (#24360)
|
||||
* Split lfs size from repository size (#22900)
|
||||
* Sync branches into databases (#22743)
|
||||
* Disable run user change in installation page (#22499)
|
||||
* Add merge files files to GetCommitFileStatus (#20515)
|
||||
* Show OpenID Connect and OAuth on signup page (#20242)
|
||||
* SECURITY
|
||||
* Dont leak private users via extensions (#28023) (#28029)
|
||||
* Expanded minimum RSA Keylength to 3072 (#26604)
|
||||
* TESTING
|
||||
* Add user secrets API integration tests (#27832) (#27852)
|
||||
* Add tests for db indexer in indexer_test.go (#27087)
|
||||
* Speed up TestEventSourceManagerRun (#26262)
|
||||
* Add unit test for user renaming (#26261)
|
||||
* Add some Wiki unit tests (#26260)
|
||||
* Improve unit test for caching (#26185)
|
||||
* Add unit test for `HashAvatar` (#25662)
|
||||
* TRANSLATION
|
||||
* Backport translations to v1.21 (#27899)
|
||||
* Fix issues in translation file (#27699) (#27737)
|
||||
* Add locale for deleted head branch (#26296)
|
||||
* Improve multiple strings in en-US locale (#26213)
|
||||
* Fix broken translations for package documantion (#25742)
|
||||
* Correct translation wrong format (#25643)
|
||||
* BUILD
|
||||
* Dockerfile small refactor (#27757) (#27826)
|
||||
* Fix build errors on BSD (in BSDMakefile) (#27594) (#27608)
|
||||
* Fully replace drone with actions (#27556) (#27575)
|
||||
* Enable markdownlint `no-duplicate-header` (#27500) (#27506)
|
||||
* Enable production source maps for index.js, fix CSS sourcemaps (#27291) (#27295)
|
||||
* Update snap package (#27021)
|
||||
* Bump go to 1.21 (#26608)
|
||||
* Bump xgo to go-1.21.x and node to 20 in release-version (#26589)
|
||||
* Add template linting via djlint (#25212)
|
||||
* DOCS
|
||||
* Change default size of issue/pr attachments and repo file (#27946) (#28017)
|
||||
* Remove `known issue` section in Gitea Actions Doc (#27930) (#27938)
|
||||
* Remove outdated paragraphs when comparing Gitea Actions to GitHub Actions (#27119)
|
||||
* Update brew installation documentation since gitea moved to brew core package (#27070)
|
||||
* Actions are no longer experimental, so enable them by default (#27054)
|
||||
* Add a documentation note for Windows Service (#26938)
|
||||
* Add sparse url in cargo package guide (#26937)
|
||||
* Update nginx recommendations (#26924)
|
||||
* Update backup instructions to align with archive structure (#26902)
|
||||
* Expanding documentation in queue.go (#26889)
|
||||
* Update info regarding internet connection for build (#26776)
|
||||
* Docs: template variables (#26547)
|
||||
* Update index doc (#26455)
|
||||
* Update zh-cn documentation (#26406)
|
||||
* Fix typos and grammer problems for actions documentation (#26328)
|
||||
* Update documentation for 1.21 actions (#26317)
|
||||
* Doc update swagger doc for POST /orgs/{org}/teams (#26155)
|
||||
* Doc sync authentication.md to zh-cn (#26117)
|
||||
* Doc guide the user to create the appropriate level runner (#26091)
|
||||
* Make organization redirect warning more clear (#26077)
|
||||
* Update blog links (#25843)
|
||||
* Fix default value for LocalURL (#25426)
|
||||
* Update `from-source.zh-cn.md` & `from-source.en-us.md` - Cross Compile Using Zig (#25194)
|
||||
* MISC
|
||||
* Replace deprecated `elliptic.Marshal` (#26800)
|
||||
* Add elapsed time on debug for slow git commands (#25642)
|
||||
|
||||
## [1.20.5](https://github.com/go-gitea/gitea/releases/tag/v1.20.5) - 2023-10-03
|
||||
|
||||
* ENHANCEMENTS
|
||||
|
@ -455,7 +969,6 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
|
|||
* Add option to search for users is active join a team (#24093)
|
||||
* Add PDF rendering via PDFObject (#24086)
|
||||
* Refactor web route (#24080)
|
||||
* Make more functions use ctx instead of db.DefaultContext (#24068)
|
||||
* Make HTML template functions support context (#24056)
|
||||
* Refactor rename user and rename organization (#24052)
|
||||
* Localize milestone related time strings (#24051)
|
||||
|
|
|
@ -69,10 +69,10 @@ RUN addgroup \
|
|||
-s /bin/bash \
|
||||
-u 1000 \
|
||||
-G git \
|
||||
git && \
|
||||
echo "git:*" | chpasswd -e
|
||||
_gitea && \
|
||||
echo "_gitea:*" | chpasswd -e
|
||||
|
||||
ENV USER git
|
||||
ENV USER _gitea
|
||||
ENV GITEA_CUSTOM /data/gitea
|
||||
|
||||
VOLUME ["/data"]
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
migrate_base "code.gitea.io/gitea/models/migrations/base"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/doctor"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -22,6 +23,19 @@ import (
|
|||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = &cli.Command{
|
||||
Name: "doctor",
|
||||
Usage: "Diagnose and optionally fix problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
||||
|
||||
Subcommands: []*cli.Command{
|
||||
cmdDoctorCheck,
|
||||
cmdRecreateTable,
|
||||
cmdDoctorConvert,
|
||||
},
|
||||
}
|
||||
|
||||
var cmdDoctorCheck = &cli.Command{
|
||||
Name: "check",
|
||||
Usage: "Diagnose and optionally fix problems",
|
||||
|
@ -60,19 +74,6 @@ var cmdDoctorCheck = &cli.Command{
|
|||
},
|
||||
}
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = &cli.Command{
|
||||
Name: "doctor",
|
||||
Usage: "Diagnose and optionally fix problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
||||
|
||||
Subcommands: []*cli.Command{
|
||||
cmdDoctorCheck,
|
||||
cmdRecreateTable,
|
||||
cmdDoctorConvert,
|
||||
},
|
||||
}
|
||||
|
||||
var cmdRecreateTable = &cli.Command{
|
||||
Name: "recreate-table",
|
||||
Usage: "Recreate tables from XORM definitions and copy the data.",
|
||||
|
@ -177,6 +178,7 @@ func runDoctorCheck(ctx *cli.Context) error {
|
|||
if ctx.IsSet("list") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
||||
doctor.SortChecks(doctor.Checks)
|
||||
for _, check := range doctor.Checks {
|
||||
if check.IsDefault {
|
||||
_, _ = w.Write([]byte{'*'})
|
||||
|
@ -192,26 +194,20 @@ func runDoctorCheck(ctx *cli.Context) error {
|
|||
|
||||
var checks []*doctor.Check
|
||||
if ctx.Bool("all") {
|
||||
checks = doctor.Checks
|
||||
checks = make([]*doctor.Check, len(doctor.Checks))
|
||||
copy(checks, doctor.Checks)
|
||||
} else if ctx.IsSet("run") {
|
||||
addDefault := ctx.Bool("default")
|
||||
names := ctx.StringSlice("run")
|
||||
for i, name := range names {
|
||||
names[i] = strings.ToLower(strings.TrimSpace(name))
|
||||
}
|
||||
|
||||
runNamesSet := container.SetOf(ctx.StringSlice("run")...)
|
||||
for _, check := range doctor.Checks {
|
||||
if addDefault && check.IsDefault {
|
||||
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
|
||||
checks = append(checks, check)
|
||||
continue
|
||||
}
|
||||
for _, name := range names {
|
||||
if name == check.Name {
|
||||
checks = append(checks, check)
|
||||
break
|
||||
}
|
||||
runNamesSet.Remove(check.Name)
|
||||
}
|
||||
}
|
||||
if len(runNamesSet) > 0 {
|
||||
return fmt.Errorf("unknown checks: %q", strings.Join(runNamesSet.Values(), ","))
|
||||
}
|
||||
} else {
|
||||
for _, check := range doctor.Checks {
|
||||
if check.IsDefault {
|
||||
|
@ -219,6 +215,5 @@ func runDoctorCheck(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
|
||||
}
|
||||
|
|
33
cmd/doctor_test.go
Normal file
33
cmd/doctor_test.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/doctor"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func TestDoctorRun(t *testing.T) {
|
||||
doctor.Register(&doctor.Check{
|
||||
Title: "Test Check",
|
||||
Name: "test-check",
|
||||
Run: func(ctx context.Context, logger log.Logger, autofix bool) error { return nil },
|
||||
|
||||
SkipDatabaseInitialization: true,
|
||||
})
|
||||
app := cli.NewApp()
|
||||
app.Commands = []*cli.Command{cmdDoctorCheck}
|
||||
err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
|
||||
assert.NoError(t, err)
|
||||
err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
|
||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
||||
err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
|
||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
||||
}
|
|
@ -452,7 +452,7 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
|
|||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
currentAbsPath := path.Join(absPath, file.Name())
|
||||
currentAbsPath := filepath.Join(absPath, file.Name())
|
||||
currentInsidePath := path.Join(insidePath, file.Name())
|
||||
if file.IsDir() {
|
||||
if !util.SliceContainsString(excludeAbsPath, currentAbsPath) {
|
||||
|
|
|
@ -491,6 +491,11 @@ INTERNAL_TOKEN=
|
|||
;; Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations.
|
||||
;; This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
|
||||
;SUCCESSFUL_TOKENS_CACHE_SIZE = 20
|
||||
;;
|
||||
;; Reject API tokens sent in URL query string (Accept Header-based API tokens only). This avoids security vulnerabilities
|
||||
;; stemming from cached/logged plain-text API tokens.
|
||||
;; In future releases, this will become the default behavior
|
||||
;DISABLE_QUERY_AUTH_TOKEN = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -1014,8 +1019,8 @@ LEVEL = Info
|
|||
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||
;ALLOWED_TYPES =
|
||||
;;
|
||||
;; Max size of each file in megabytes. Defaults to 3MB
|
||||
;FILE_MAX_SIZE = 3
|
||||
;; Max size of each file in megabytes. Defaults to 50MB
|
||||
;FILE_MAX_SIZE = 50
|
||||
;;
|
||||
;; Max number of files per upload. Defaults to 5
|
||||
;MAX_FILES = 5
|
||||
|
@ -1205,6 +1210,9 @@ LEVEL = Info
|
|||
;; Max size of files to be displayed (default is 8MiB)
|
||||
;MAX_DISPLAY_FILE_SIZE = 8388608
|
||||
;;
|
||||
;; Detect ambiguous unicode characters in file contents and show warnings on the UI
|
||||
;AMBIGUOUS_UNICODE_DETECTION = true
|
||||
;;
|
||||
;; Whether the email of the user should be shown in the Explore Users page
|
||||
;SHOW_USER_EMAIL = true
|
||||
;;
|
||||
|
@ -1815,8 +1823,8 @@ LEVEL = Info
|
|||
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||
;ALLOWED_TYPES = .csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip
|
||||
;;
|
||||
;; Max size of each file. Defaults to 4MB
|
||||
;MAX_SIZE = 4
|
||||
;; Max size of each file. Defaults to 2048MB
|
||||
;MAX_SIZE = 2048
|
||||
;;
|
||||
;; Max number of files per upload. Defaults to 5
|
||||
;MAX_FILES = 5
|
||||
|
|
|
@ -19,10 +19,10 @@ Some jurisdictions (such as EU), requires certain legal pages (e.g. Privacy Poli
|
|||
|
||||
## Getting Pages
|
||||
|
||||
Gitea source code ships with sample pages, available in `contrib/legal` directory. Copy them to `custom/public/`. For example, to add Privacy Policy:
|
||||
Gitea source code ships with sample pages, available in `contrib/legal` directory. Copy them to `custom/public/assets/`. For example, to add Privacy Policy:
|
||||
|
||||
```
|
||||
wget -O /path/to/custom/public/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
|
||||
wget -O /path/to/custom/public/assets/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
|
||||
```
|
||||
|
||||
Now you need to edit the page to meet your requirements. In particular you must change the email addresses, web addresses and references to "Your Gitea Instance" to match your situation.
|
||||
|
|
|
@ -19,10 +19,10 @@ menu:
|
|||
|
||||
## 获取页面
|
||||
|
||||
Gitea 源代码附带了示例页面,位于 `contrib/legal` 目录中。将它们复制到 `custom/public/` 目录下。例如,如果要添加隐私政策:
|
||||
Gitea 源代码附带了示例页面,位于 `contrib/legal` 目录中。将它们复制到 `custom/public/assets/` 目录下。例如,如果要添加隐私政策:
|
||||
|
||||
```
|
||||
wget -O /path/to/custom/public/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
|
||||
wget -O /path/to/custom/public/assets/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/main/contrib/legal/privacy.html.sample
|
||||
```
|
||||
|
||||
现在,你需要编辑该页面以满足你的需求。特别是,你必须更改电子邮件地址、网址以及与 "Your Gitea Instance" 相关的引用,以匹配你的情况。
|
||||
|
|
|
@ -146,7 +146,7 @@ In addition, there is _`StaticRootPath`_ which can be set as a built-in at build
|
|||
- `ENABLED`: **true**: Whether repository file uploads are enabled
|
||||
- `TEMP_PATH`: **data/tmp/uploads**: Path for uploads (content gets deleted on Gitea restart)
|
||||
- `ALLOWED_TYPES`: **_empty_**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||
- `FILE_MAX_SIZE`: **3**: Max size of each file in megabytes.
|
||||
- `FILE_MAX_SIZE`: **50**: Max size of each file in megabytes.
|
||||
- `MAX_FILES`: **5**: Max number of files per upload
|
||||
|
||||
### Repository - Release (`repository.release`)
|
||||
|
@ -220,6 +220,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
- `THEMES`: **auto,gitea,arc-green**: All available themes. Allow users select personalized themes.
|
||||
regardless of the value of `DEFAULT_THEME`.
|
||||
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
|
||||
- `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
|
||||
- `REACTIONS`: All available reactions users can choose on issues/prs and comments
|
||||
Values can be emoji alias (:smile:) or a unicode emoji.
|
||||
For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
|
||||
|
@ -341,7 +342,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
- `SSH_AUTHORIZED_PRINCIPALS_ALLOW`: **off** or **username, email**: \[off, username, email, anything\]: Specify the principals values that users are allowed to use as principal. When set to `anything` no checks are done on the principal string. When set to `off` authorized principal are not allowed to be set.
|
||||
- `SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE`: **false/true**: Gitea will create a authorized_principals file by default when it is not using the internal ssh server and `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`.
|
||||
- `SSH_AUTHORIZED_PRINCIPALS_BACKUP`: **false/true**: Enable SSH Authorized Principals Backup when rewriting all keys, default is true if `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`.
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}**: Set the template for the command to passed on authorized keys. Possible keys are: AppPath, AppWorkPath, CustomConf, CustomPath, Key - where Key is a `models/asymkey.PublicKey` and the others are strings which are shellquoted.
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **`{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}`**: Set the template for the command to passed on authorized keys. Possible keys are: AppPath, AppWorkPath, CustomConf, CustomPath, Key - where Key is a `models/asymkey.PublicKey` and the others are strings which are shellquoted.
|
||||
- `SSH_SERVER_CIPHERS`: **chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect.
|
||||
- `SSH_SERVER_KEY_EXCHANGES`: **curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect.
|
||||
- `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect
|
||||
|
@ -571,6 +572,7 @@ And the following unique queues:
|
|||
- off - do not check password complexity
|
||||
- `PASSWORD_CHECK_PWN`: **false**: Check [HaveIBeenPwned](https://haveibeenpwned.com/Passwords) to see if a password has been exposed.
|
||||
- `SUCCESSFUL_TOKENS_CACHE_SIZE`: **20**: Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations. This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
|
||||
- `DISABLE_QUERY_AUTH_TOKEN`: **false**: Reject API tokens sent in URL query string (Accept Header-based API tokens only). This setting will default to `true` in Gitea 1.23 and be deprecated in Gitea 1.24.
|
||||
|
||||
## Camo (`camo`)
|
||||
|
||||
|
@ -822,7 +824,7 @@ Default templates for project boards:
|
|||
|
||||
- `ENABLED`: **true**: Whether issue and pull request attachments are enabled.
|
||||
- `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||
- `MAX_SIZE`: **4**: Maximum size (MB).
|
||||
- `MAX_SIZE`: **2048**: Maximum size (MB).
|
||||
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once.
|
||||
- `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
|
||||
- `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing.
|
||||
|
@ -1404,7 +1406,7 @@ Please note that using `self` is not recommended for most cases, as it could mak
|
|||
Additionally, it requires you to mirror all the actions you need to your Gitea instance, which may not be worth it.
|
||||
Therefore, please use `self` only if you understand what you are doing.
|
||||
|
||||
In earlier versions (<= 1.19), `DEFAULT_ACTIONS_URL` cound be set to any custom URLs like `https://gitea.com` or `http://your-git-server,https://gitea.com`, and the default value was `https://gitea.com`.
|
||||
In earlier versions (`<= 1.19`), `DEFAULT_ACTIONS_URL` cound be set to any custom URLs like `https://gitea.com` or `http://your-git-server,https://gitea.com`, and the default value was `https://gitea.com`.
|
||||
However, later updates removed those options, and now the only options are `github` and `self`, with the default value being `github`.
|
||||
However, if you want to use actions from other git server, you can use a complete URL in `uses` field, it's supported by Gitea (but not GitHub).
|
||||
Like `uses: https://gitea.com/actions/checkout@v3` or `uses: http://your-git-server/actions/checkout@v3`.
|
||||
|
|
|
@ -145,7 +145,7 @@ menu:
|
|||
- `ENABLED`: **true**: 是否启用仓库文件上传。
|
||||
- `TEMP_PATH`: **data/tmp/uploads**: 文件上传的临时保存路径(在Gitea重启的时候该目录会被清空)。
|
||||
- `ALLOWED_TYPES`: **_empty_**: 以逗号分割的列表,代表支持上传的文件类型。(`.zip`), mime类型 (`text/plain`) or 通配符类型 (`image/*`, `audio/*`, `video/*`). 为空或者 `*/*`代表允许所有类型文件。
|
||||
- `FILE_MAX_SIZE`: **3**: 每个文件的最大大小(MB)。
|
||||
- `FILE_MAX_SIZE`: **50**: 每个文件的最大大小(MB)。
|
||||
- `MAX_FILES`: **5**: 每次上传的最大文件数。
|
||||
|
||||
### 仓库 - 版本发布 (`repository.release`)
|
||||
|
@ -335,7 +335,7 @@ menu:
|
|||
- `SSH_AUTHORIZED_PRINCIPALS_ALLOW`: **off** 或 **username, email**:\[off, username, email, anything\]:指定允许用户用作 principal 的值。当设置为 `anything` 时,对 principal 字符串不执行任何检查。当设置为 `off` 时,不允许设置授权的 principal。
|
||||
- `SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE`: **false/true**:当 Gitea 不使用内置 SSH 服务器且 `SSH_AUTHORIZED_PRINCIPALS_ALLOW` 不为 `off` 时,默认情况下 Gitea 会创建一个 authorized_principals 文件。
|
||||
- `SSH_AUTHORIZED_PRINCIPALS_BACKUP`: **false/true**:在重写所有密钥时启用 SSH 授权 principal 备份,默认值为 true(如果 `SSH_AUTHORIZED_PRINCIPALS_ALLOW` 不为 `off`)。
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}**:设置用于传递授权密钥的命令模板。可能的密钥是:AppPath、AppWorkPath、CustomConf、CustomPath、Key,其中 Key 是 `models/asymkey.PublicKey`,其他是 shellquoted 字符串。
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **`{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}`**:设置用于传递授权密钥的命令模板。可能的密钥是:AppPath、AppWorkPath、CustomConf、CustomPath、Key,其中 Key 是 `models/asymkey.PublicKey`,其他是 shellquoted 字符串。
|
||||
- `SSH_SERVER_CIPHERS`: **chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com**:对于内置的 SSH 服务器,选择支持的 SSH 连接的加密方法,对于系统 SSH,此设置无效。
|
||||
- `SSH_SERVER_KEY_EXCHANGES`: **curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1**:对于内置 SSH 服务器,选择支持的 SSH 连接的密钥交换算法,对于系统 SSH,此设置无效。
|
||||
- `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1**:对于内置 SSH 服务器,选择支持的 SSH 连接的 MAC 算法,对于系统 SSH,此设置无效。
|
||||
|
@ -784,7 +784,7 @@ Gitea 创建以下非唯一队列:
|
|||
|
||||
- `ENABLED`: **true**: 是否允许用户上传附件。
|
||||
- `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: 允许的文件扩展名(`.zip`)、mime 类型(`text/plain`)或通配符类型(`image/*`、`audio/*`、`video/*`)的逗号分隔列表。空值或 `*/*` 允许所有类型。
|
||||
- `MAX_SIZE`: **4**: 附件的最大限制(MB)。
|
||||
- `MAX_SIZE`: **2048**: 附件的最大限制(MB)。
|
||||
- `MAX_FILES`: **5**: 一次最多上传的附件数量。
|
||||
- `STORAGE_TYPE`: **local**: 附件的存储类型,`local` 表示本地磁盘,`minio` 表示兼容 S3 的对象存储服务,如果未设置将使用默认值 `local` 或其他在 `[storage.xxx]` 中定义的名称。
|
||||
- `SERVE_DIRECT`: **false**: 允许存储驱动器重定向到经过身份验证的 URL 以直接提供文件。目前,只支持 Minio/S3 通过签名 URL 提供支持,local 不会执行任何操作。
|
||||
|
@ -1040,10 +1040,11 @@ Gitea 创建以下非唯一队列:
|
|||
|
||||
## API (`api`)
|
||||
|
||||
- `ENABLE_SWAGGER`: **true**: 是否启用swagger路由 (`/api/swagger`, `/api/v1/swagger`, …)。
|
||||
- `MAX_RESPONSE_ITEMS`: **50**: 单个页面的最大 Feed.
|
||||
- `ENABLE_OPENID_SIGNIN`: **false**: 允许使用OpenID登录,当设置为`true`时可以通过 `/user/login` 页面进行OpenID登录。
|
||||
- `DISABLE_REGISTRATION`: **false**: 关闭用户注册。
|
||||
- `ENABLE_SWAGGER`: **true**: 启用API文档接口 (`/api/swagger`, `/api/v1/swagger`, …). True or false。
|
||||
- `MAX_RESPONSE_ITEMS`: **50**: API分页的最大单页项目数。
|
||||
- `DEFAULT_PAGING_NUM`: **30**: API分页的默认分页数。
|
||||
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: Git trees API的默认单页项目数。
|
||||
- `DEFAULT_MAX_BLOB_SIZE`: **10485760** (10MiB): blobs API的默认最大文件大小。
|
||||
|
||||
## OAuth2 (`oauth2`)
|
||||
|
||||
|
@ -1344,7 +1345,7 @@ PROXY_HOSTS = *.github.com
|
|||
此外,它要求您将所有所需的操作镜像到您的 Gitea 实例,这可能不值得。
|
||||
因此,请仅在您了解自己在做什么的情况下使用 `self`。
|
||||
|
||||
在早期版本(<= 1.19)中,`DEFAULT_ACTIONS_URL` 可以设置为任何自定义 URL,例如 `https://gitea.com` 或 `http://your-git-server,https://gitea.com`,默认值为 `https://gitea.com`。
|
||||
在早期版本(`<= 1.19`)中,`DEFAULT_ACTIONS_URL` 可以设置为任何自定义 URL,例如 `https://gitea.com` 或 `http://your-git-server,https://gitea.com`,默认值为 `https://gitea.com`。
|
||||
然而,后来的更新删除了这些选项,现在唯一的选项是 `github` 和 `self`,默认值为 `github`。
|
||||
但是,如果您想要使用其他 Git 服务器中的操作,您可以在 `uses` 字段中使用完整的 URL,Gitea 支持此功能(GitHub 不支持)。
|
||||
例如 `uses: https://gitea.com/actions/checkout@v3` 或 `uses: http://your-git-server/actions/checkout@v3`。
|
||||
|
|
|
@ -42,11 +42,11 @@ Gitea 引用 `custom` 目录中的自定义配置文件来覆盖配置、模板
|
|||
|
||||
将自定义的公共文件(比如页面和图片)作为 webroot 放在 `custom/public/` 中来让 Gitea 提供这些自定义内容(符号链接将被追踪)。
|
||||
|
||||
举例说明:`image.png` 存放在 `custom/public/`中,那么它可以通过链接 http://gitea.domain.tld/assets/image.png 访问。
|
||||
举例说明:`image.png` 存放在 `custom/public/assets/`中,那么它可以通过链接 http://gitea.domain.tld/assets/image.png 访问。
|
||||
|
||||
## 修改默认头像
|
||||
|
||||
替换以下目录中的 png 图片: `custom/public/img/avatar\_default.png`
|
||||
替换以下目录中的 png 图片: `custom/public/assets/img/avatar\_default.png`
|
||||
|
||||
## 自定义 Gitea 页面
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ Please note: authentication is only supported when the SMTP server communication
|
|||
|
||||
- STARTTLS (also known as Opportunistic TLS) via port 587. Initial connection is done over cleartext, but then be upgraded over TLS if the server supports it.
|
||||
- SMTPS connection (SMTP over TLS) via the default port 465. Connection to the server use TLS from the beginning.
|
||||
- Forced SMTPS connection with `IS_TLS_ENABLED=true`. (These are both known as Implicit TLS.)
|
||||
- Forced SMTPS connection with `PROTOCOL=smtps`. (These are both known as Implicit TLS.)
|
||||
This is due to protections imposed by the Go internal libraries against STRIPTLS attacks.
|
||||
|
||||
Note that Implicit TLS is recommended by [RFC8314](https://tools.ietf.org/html/rfc8314#section-3) since 2018.
|
||||
|
|
|
@ -55,13 +55,13 @@ PASSWD = `password`
|
|||
|
||||
要发送测试邮件以验证设置,请转到 Gitea > 站点管理 > 配置 > SMTP 邮件配置。
|
||||
|
||||
有关所有选项的完整列表,请查看[配置速查表](doc/administration/config-cheat-sheet.zh-cn.md)。
|
||||
有关所有选项的完整列表,请查看[配置速查表](administration/config-cheat-sheet.md)。
|
||||
|
||||
请注意:只有在使用 TLS 或 `HOST=localhost` 加密 SMTP 服务器通信时才支持身份验证。TLS 加密可以通过以下方式进行:
|
||||
|
||||
- 通过端口 587 的 STARTTLS(也称为 Opportunistic TLS)。初始连接是明文的,但如果服务器支持,则可以升级为 TLS。
|
||||
- 通过默认端口 465 的 SMTPS 连接。连接到服务器从一开始就使用 TLS。
|
||||
- 使用 `IS_TLS_ENABLED=true` 进行强制的 SMTPS 连接。(这两种方式都被称为 Implicit TLS)
|
||||
- 使用 `PROTOCOL=smtps` 进行强制的 SMTPS 连接。(这两种方式都被称为 Implicit TLS)
|
||||
这是由于 Go 内部库对 STRIPTLS 攻击的保护机制。
|
||||
|
||||
请注意,自2018年起,[RFC8314](https://tools.ietf.org/html/rfc8314#section-3) 推荐使用 Implicit TLS。
|
||||
|
|
|
@ -194,7 +194,7 @@ ALLOW_DATA_URI_IMAGES = true
|
|||
}
|
||||
```
|
||||
|
||||
将您的样式表添加到自定义目录中,例如 `custom/public/css/my-style-XXXXX.css`,并使用自定义的头文件 `custom/templates/custom/header.tmpl` 进行导入:
|
||||
将您的样式表添加到自定义目录中,例如 `custom/public/assets/css/my-style-XXXXX.css`,并使用自定义的头文件 `custom/templates/custom/header.tmpl` 进行导入:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="{{AppSubUrl}}/assets/css/my-style-XXXXX.css" />
|
||||
|
|
|
@ -33,7 +33,7 @@ CERT_FILE = cert.pem
|
|||
KEY_FILE = key.pem
|
||||
```
|
||||
|
||||
请注意,如果您的证书由第三方证书颁发机构签名(即不是自签名的),则 cert.pem 应包含证书链。服务器证书必须是 cert.pem 中的第一个条目,后跟中介(如果有)。不必包含根证书,因为连接客户端必须已经拥有根证书才能建立信任关系。要了解有关配置值的更多信息,请查看 [配置备忘单](../config-cheat-sheet#server-server)。
|
||||
请注意,如果您的证书由第三方证书颁发机构签名(即不是自签名的),则 cert.pem 应包含证书链。服务器证书必须是 cert.pem 中的第一个条目,后跟中介(如果有)。不必包含根证书,因为连接客户端必须已经拥有根证书才能建立信任关系。要了解有关配置值的更多信息,请查看 [配置备忘单](administration/config-cheat-sheet#server-server)。
|
||||
|
||||
对于“CERT_FILE”或“KEY_FILE”字段,当文件路径是相对路径时,文件路径相对于“GITEA_CUSTOM”环境变量。它也可以是绝对路径。
|
||||
|
||||
|
|
|
@ -19,10 +19,7 @@ menu:
|
|||
|
||||
## Enabling/configuring API access
|
||||
|
||||
By default, `ENABLE_SWAGGER` is true, and
|
||||
`MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat
|
||||
Sheet](administration/config-cheat-sheet.md) for more
|
||||
information.
|
||||
By default, `ENABLE_SWAGGER` is true, and `MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat Sheet](administration/config-cheat-sheet.md) for more information.
|
||||
|
||||
## Authentication
|
||||
|
||||
|
|
|
@ -19,8 +19,7 @@ menu:
|
|||
|
||||
## 开启/配置 API 访问
|
||||
|
||||
通常情况下, `ENABLE_SWAGGER` 默认开启并且参数 `MAX_RESPONSE_ITEMS` 默认为 50。您可以从 [Config Cheat
|
||||
Sheet](administration/config-cheat-sheet.md) 中获取更多配置相关信息。
|
||||
通常情况下, `ENABLE_SWAGGER` 默认开启并且参数 `MAX_RESPONSE_ITEMS` 默认为 50。您可以从 [Config Cheat Sheet](administration/config-cheat-sheet.md) 中获取更多配置相关信息。
|
||||
|
||||
## 通过 API 认证
|
||||
|
||||
|
|
|
@ -138,9 +138,9 @@ All Gitea instances have the built-in API and there is no way to disable it comp
|
|||
You can, however, disable showing its documentation by setting `ENABLE_SWAGGER` to `false` in the `api` section of your `app.ini`.
|
||||
For more information, refer to Gitea's [API docs](development/api-usage.md).
|
||||
|
||||
You can see the latest API (for example) on <https://try.gitea.io/api/swagger>.
|
||||
You can see the latest API (for example) on https://try.gitea.io/api/swagger
|
||||
|
||||
You can also see an example of the `swagger.json` file at <https://try.gitea.io/swagger.v1.json>.
|
||||
You can also see an example of the `swagger.json` file at https://try.gitea.io/swagger.v1.json
|
||||
|
||||
## Adjusting your server for public/private use
|
||||
|
||||
|
|
|
@ -142,9 +142,9 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供
|
|||
但是,您可以在app.ini的api部分将ENABLE_SWAGGER设置为false,以禁用其文档显示。
|
||||
有关更多信息,请参阅Gitea的[API文档](development/api-usage.md)。
|
||||
|
||||
您可以在上查看最新的API(例如)<https://try.gitea.io/api/swagger>。
|
||||
您可以在上查看最新的API(例如)https://try.gitea.io/api/swagger
|
||||
|
||||
您还可以在上查看`swagger.json`文件的示例 <https://try.gitea.io/swagger.v1.json>。
|
||||
您还可以在上查看`swagger.json`文件的示例 https://try.gitea.io/swagger.v1.json
|
||||
|
||||
## 调整服务器用于公共/私有使用
|
||||
|
||||
|
@ -190,7 +190,7 @@ Gitea 目前支持三个官方主题,分别是 `gitea`(亮色)、`arc-gree
|
|||
|
||||
假设我们的主题是 `arc-blue`(这是一个真实的主题,可以在[此问题](https://github.com/go-gitea/gitea/issues/6011)中找到)
|
||||
|
||||
将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/css`文件夹中
|
||||
将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/assets/css`文件夹中
|
||||
|
||||
通过将`arc-blue`添加到`app.ini`中的`THEMES`列表中,允许用户使用该主题
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ chmod 770 /etc/gitea
|
|||
- 使用 `gitea generate secret` 创建 `SECRET_KEY` 和 `INTERNAL_TOKEN`
|
||||
- 提供所有必要的密钥
|
||||
|
||||
详情参考 [命令行文档](/zh-cn/command-line/) 中有关 `gitea generate secret` 的内容。
|
||||
详情参考 [命令行文档](administration/command-line.md) 中有关 `gitea generate secret` 的内容。
|
||||
|
||||
### 配置 Gitea 工作路径
|
||||
|
||||
|
@ -209,6 +209,6 @@ remote: ./hooks/pre-receive.d/gitea: line 2: [...]: No such file or directory
|
|||
|
||||
如果您没有使用 Gitea 内置的 SSH 服务器,您还需要通过在管理选项中运行任务 `Update the '.ssh/authorized_keys' file with Gitea SSH keys.` 来重新编写授权密钥文件。
|
||||
|
||||
> 更多经验总结,请参考英文版 [Troubleshooting](/en-us/install-from-binary/#troubleshooting)
|
||||
> 更多经验总结,请参考英文版 [Troubleshooting](https://docs.gitea.com/installation/install-from-binary#troubleshooting)
|
||||
|
||||
如果从本页中没有找到你需要的内容,请访问 [帮助页面](help/support.md)
|
||||
|
|
|
@ -64,7 +64,7 @@ git checkout v@version@ # or git checkout pr-xyz
|
|||
|
||||
- `go` @minGoVersion@ 或更高版本,请参阅 [这里](https://golang.org/dl/)
|
||||
- `node` @minNodeVersion@ 或更高版本,并且安装 `npm`, 请参阅 [这里](https://nodejs.org/zh-cn/download/)
|
||||
- `make`, 请参阅 [这里](/zh-cn/hacking-on-gitea/)
|
||||
- `make`, 请参阅 [这里](development/hacking-on-gitea.md)
|
||||
|
||||
为了尽可能简化编译过程,提供了各种 [make任务](https://github.com/go-gitea/gitea/blob/main/Makefile)。
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ If you cannot see the settings page, please make sure that you have the right pe
|
|||
|
||||
The format of the registration token is a random string `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx`.
|
||||
|
||||
A registration token can also be obtained from the gitea [command-line interface](../../administration/command-line.en-us.md#actions-generate-runner-token):
|
||||
A registration token can also be obtained from the gitea [command-line interface](administration/command-line.md#actions-generate-runner-token):
|
||||
|
||||
```
|
||||
gitea --config /etc/gitea/app.ini actions generate-runner-token
|
||||
|
|
|
@ -113,7 +113,7 @@ Runner级别决定了从哪里获取注册令牌。
|
|||
|
||||
注册令牌的格式是一个随机字符串 `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx`。
|
||||
|
||||
注册令牌也可以通过 Gitea 的 [命令行](../../administration/command-line.en-us.md#actions-generate-runner-token) 获得:
|
||||
注册令牌也可以通过 Gitea 的 [命令行](administration/command-line.md#actions-generate-runner-token) 获得:
|
||||
|
||||
### 注册Runner
|
||||
|
||||
|
|
|
@ -116,8 +116,8 @@ Pre and Post steps don't have their own section in the job log user interface.
|
|||
|
||||
Previously (Pre 1.21.0), `[actions].DEFAULT_ACTIONS_URL` defaulted to `https://gitea.com`.
|
||||
We have since restricted this option to only allow two values (`github` and `self`).
|
||||
When set to `github`, the new default, Gitea will download non-fully-qualified actions from <https://github.com>.
|
||||
For example, if you use `uses: actions/checkout@v3`, it will download the checkout repository from <https://github.com/actions/checkout.git>.
|
||||
When set to `github`, the new default, Gitea will download non-fully-qualified actions from `https://github.com`.
|
||||
For example, if you use `uses: actions/checkout@v3`, it will download the checkout repository from `https://github.com/actions/checkout.git`.
|
||||
|
||||
If you want to download an action from another git hoster, you can use an absolute URL, e.g. `uses: https://gitea.com/actions/checkout@v3`.
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ First of all, you need a Gitea instance.
|
|||
You can follow the [documentation](installation/from-package.md) to set up a new instance or upgrade your existing one.
|
||||
It doesn't matter how you install or run Gitea, as long as its version is 1.19.0 or higher.
|
||||
|
||||
Actions are disabled by default, so you need to add the following to the configuration file to enable it:
|
||||
Since 1.21.0, Actions are enabled by default. If you are using versions before 1.21.0, you need to add the following to the configuration file to enable it:
|
||||
|
||||
```ini
|
||||
[actions]
|
||||
|
|
|
@ -23,7 +23,7 @@ menu:
|
|||
您可以按照[文档](installation/from-package.md) 来设置一个新实例或升级现有实例。
|
||||
无论您如何安装或运行Gitea,只要版本号是1.19.0或更高即可。
|
||||
|
||||
默认情况下,Actions是禁用的,因此您需要将以下内容添加到配置文件中以启用它:
|
||||
从1.21.0开始,默认情况下,Actions是启用的。如果您正在使用1.21.0之前的版本,您需要将以下内容添加到配置文件中以启用它:
|
||||
|
||||
```ini
|
||||
[actions]
|
||||
|
|
|
@ -198,7 +198,7 @@ administrative user.
|
|||
field is set to `mail.com`, then Gitea will expect the `user email` field
|
||||
for an authenticated GIT instance to be `gituser@mail.com`.[^2]
|
||||
|
||||
**Note**: PAM support is added via [build-time flags](installation/install-from-source.md#build),
|
||||
**Note**: PAM support is added via [build-time flags](installation/from-source.md#build),
|
||||
and the official binaries provided do not have this enabled. PAM requires that
|
||||
the necessary libpam dynamic library be available and the necessary PAM
|
||||
development headers be accessible to the compiler.
|
||||
|
|
|
@ -162,7 +162,7 @@ PAM提供了一种机制,通过对用户进行PAM认证来自动将其添加
|
|||
- PAM电子邮件域:用户认证时要附加的电子邮件后缀。例如,如果登录系统期望一个名为gituse的用户,
|
||||
并且将此字段设置为mail.com,那么Gitea在验证一个GIT实例的用户时将期望user emai字段为gituser@mail.com[^2]。
|
||||
|
||||
**Note**: PAM 支持通过[build-time flags](installation/install-from-source.md#build)添加,
|
||||
**Note**: PAM 支持通过[build-time flags](installation/from-source.md#build)添加,
|
||||
而官方提供的二进制文件通常不会默认启用此功能。PAM需要确保系统上有必要的libpam动态库,并且编译器可以访问必要的PAM开发头文件。
|
||||
|
||||
[^1]: 例如,在Debian "Bullseye"上使用标准Linux登录,可以使用`common-session-noninteractive`。这个值对于其他版本的Debian,
|
||||
|
|
|
@ -20,6 +20,10 @@ type ActionTaskOutput struct {
|
|||
OutputValue string `xorm:"MEDIUMTEXT"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterModel(new(ActionTaskOutput))
|
||||
}
|
||||
|
||||
// FindTaskOutputByTaskID returns the outputs of the task.
|
||||
func FindTaskOutputByTaskID(ctx context.Context, taskID int64) ([]*ActionTaskOutput, error) {
|
||||
var outputs []*ActionTaskOutput
|
||||
|
|
|
@ -92,10 +92,9 @@ func CountUserGPGKeys(ctx context.Context, userID int64) (int64, error) {
|
|||
return db.GetEngine(ctx).Where("owner_id=? AND primary_key_id=''", userID).Count(&GPGKey{})
|
||||
}
|
||||
|
||||
// GetGPGKeyByID returns public key by given ID.
|
||||
func GetGPGKeyByID(ctx context.Context, keyID int64) (*GPGKey, error) {
|
||||
func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, error) {
|
||||
key := new(GPGKey)
|
||||
has, err := db.GetEngine(ctx).ID(keyID).Get(key)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND owner_id=?", keyID, ownerID).Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
|
@ -225,7 +224,7 @@ func deleteGPGKey(ctx context.Context, keyID string) (int64, error) {
|
|||
|
||||
// DeleteGPGKey deletes GPG key information in database.
|
||||
func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err error) {
|
||||
key, err := GetGPGKeyByID(ctx, id)
|
||||
key, err := GetGPGKeyForUserByID(ctx, doer.ID, id)
|
||||
if err != nil {
|
||||
if IsErrGPGKeyNotExist(err) {
|
||||
return nil
|
||||
|
@ -233,11 +232,6 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
|
|||
return fmt.Errorf("GetPublicKeyByID: %w", err)
|
||||
}
|
||||
|
||||
// Check if user has access to delete this key.
|
||||
if !doer.IsAdmin && doer.ID != key.OwnerID {
|
||||
return ErrGPGKeyAccessDenied{doer.ID, key.ID}
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -29,10 +29,15 @@ func VerifySSHKey(ownerID int64, fingerprint, token, signature string) (string,
|
|||
return "", ErrKeyNotExist{}
|
||||
}
|
||||
|
||||
if err := sshsig.Verify(bytes.NewBuffer([]byte(token)), []byte(signature), []byte(key.Content), "gitea"); err != nil {
|
||||
log.Error("Unable to validate token signature. Error: %v", err)
|
||||
return "", ErrSSHInvalidTokenSignature{
|
||||
Fingerprint: key.Fingerprint,
|
||||
err = sshsig.Verify(bytes.NewBuffer([]byte(token)), []byte(signature), []byte(key.Content), "gitea")
|
||||
if err != nil {
|
||||
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
|
||||
// see https://github.com/PowerShell/PowerShell/issues/5974
|
||||
if sshsig.Verify(bytes.NewBuffer([]byte(token+"\r\n")), []byte(signature), []byte(key.Content), "gitea") != nil {
|
||||
log.Error("Unable to validate token signature. Error: %v", err)
|
||||
return "", ErrSSHInvalidTokenSignature{
|
||||
Fingerprint: key.Fingerprint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -178,6 +178,15 @@ func GetByBean(ctx context.Context, bean any) (bool, error) {
|
|||
return GetEngine(ctx).Get(bean)
|
||||
}
|
||||
|
||||
func Exist[T any](ctx context.Context, cond builder.Cond) (bool, error) {
|
||||
if !cond.IsValid() {
|
||||
return false, ErrConditionRequired{}
|
||||
}
|
||||
|
||||
var bean T
|
||||
return GetEngine(ctx).Where(cond).NoAutoCondition().Exist(&bean)
|
||||
}
|
||||
|
||||
// DeleteByBean deletes all records according non-empty fields of the bean as conditions.
|
||||
func DeleteByBean(ctx context.Context, bean any) (int64, error) {
|
||||
return GetEngine(ctx).Delete(bean)
|
||||
|
|
|
@ -72,3 +72,21 @@ func (err ErrNotExist) Error() string {
|
|||
func (err ErrNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrConditionRequired represents an error which require condition.
|
||||
type ErrConditionRequired struct{}
|
||||
|
||||
// IsErrConditionRequired checks if an error is an ErrConditionRequired
|
||||
func IsErrConditionRequired(err error) bool {
|
||||
_, ok := err.(ErrConditionRequired)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrConditionRequired) Error() string {
|
||||
return "condition is required"
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrConditionRequired) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
|
|
@ -66,3 +66,12 @@
|
|||
tree_path: "README.md"
|
||||
created_unix: 946684812
|
||||
invalidated: true
|
||||
|
||||
-
|
||||
id: 8
|
||||
type: 0 # comment
|
||||
poster_id: 2
|
||||
issue_id: 4 # in repo_id 2
|
||||
content: "comment in private pository"
|
||||
created_unix: 946684811
|
||||
updated_unix: 946684811
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
priority: 0
|
||||
is_closed: true
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
num_comments: 1
|
||||
created_unix: 946684830
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
|
|
@ -205,10 +205,9 @@ func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64
|
|||
})
|
||||
}
|
||||
|
||||
// UpdateBranch updates the branch information in the database. If the branch exist, it will update latest commit of this branch information
|
||||
// If it doest not exist, insert a new record into database
|
||||
func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) error {
|
||||
cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
|
||||
// UpdateBranch updates the branch information in the database.
|
||||
func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) (int64, error) {
|
||||
return db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
|
||||
Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted, updated_unix").
|
||||
Update(&Branch{
|
||||
CommitID: commit.ID.String(),
|
||||
|
@ -217,21 +216,6 @@ func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string
|
|||
CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
|
||||
IsDeleted: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return db.Insert(ctx, &Branch{
|
||||
RepoID: repoID,
|
||||
Name: branchName,
|
||||
CommitID: commit.ID.String(),
|
||||
CommitMessage: commit.Summary(),
|
||||
PusherID: pusherID,
|
||||
CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
|
||||
})
|
||||
}
|
||||
|
||||
// AddDeletedBranch adds a deleted branch to the database
|
||||
|
@ -308,6 +292,17 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
|||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
var branch Branch
|
||||
exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !exist || branch.IsDeleted {
|
||||
return ErrBranchNotExist{
|
||||
RepoID: repo.ID,
|
||||
BranchName: from,
|
||||
}
|
||||
}
|
||||
|
||||
// 1. update branch in database
|
||||
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
|
||||
Name: to,
|
||||
|
|
|
@ -73,7 +73,7 @@ type FindBranchOptions struct {
|
|||
Keyword string
|
||||
}
|
||||
|
||||
func (opts *FindBranchOptions) Cond() builder.Cond {
|
||||
func (opts FindBranchOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
|
@ -92,7 +92,7 @@ func (opts *FindBranchOptions) Cond() builder.Cond {
|
|||
}
|
||||
|
||||
func CountBranches(ctx context.Context, opts FindBranchOptions) (int64, error) {
|
||||
return db.GetEngine(ctx).Where(opts.Cond()).Count(&Branch{})
|
||||
return db.GetEngine(ctx).Where(opts.ToConds()).Count(&Branch{})
|
||||
}
|
||||
|
||||
func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
|
||||
|
@ -108,7 +108,7 @@ func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
|
|||
}
|
||||
|
||||
func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, error) {
|
||||
sess := db.GetEngine(ctx).Where(opts.Cond())
|
||||
sess := db.GetEngine(ctx).Where(opts.ToConds())
|
||||
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, erro
|
|||
}
|
||||
|
||||
func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, error) {
|
||||
sess := db.GetEngine(ctx).Select("name").Where(opts.Cond())
|
||||
sess := db.GetEngine(ctx).Select("name").Where(opts.ToConds())
|
||||
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestAddDeletedBranch(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
|
||||
_, err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -342,7 +342,7 @@ func (c *Comment) AfterLoad(session *xorm.Session) {
|
|||
|
||||
// LoadPoster loads comment poster
|
||||
func (c *Comment) LoadPoster(ctx context.Context) (err error) {
|
||||
if c.PosterID <= 0 || c.Poster != nil {
|
||||
if c.Poster != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1016,6 +1016,7 @@ type FindCommentsOptions struct {
|
|||
Type CommentType
|
||||
IssueIDs []int64
|
||||
Invalidated util.OptionalBool
|
||||
IsPull util.OptionalBool
|
||||
}
|
||||
|
||||
// ToConds implements FindOptions interface
|
||||
|
@ -1050,6 +1051,9 @@ func (opts *FindCommentsOptions) ToConds() builder.Cond {
|
|||
if !opts.Invalidated.IsNone() {
|
||||
cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.IsTrue()})
|
||||
}
|
||||
if opts.IsPull != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.IsTrue()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
@ -1057,7 +1061,7 @@ func (opts *FindCommentsOptions) ToConds() builder.Cond {
|
|||
func FindComments(ctx context.Context, opts *FindCommentsOptions) (CommentList, error) {
|
||||
comments := make([]*Comment, 0, 10)
|
||||
sess := db.GetEngine(ctx).Where(opts.ToConds())
|
||||
if opts.RepoID > 0 {
|
||||
if opts.RepoID > 0 || opts.IsPull != util.OptionalBoolNone {
|
||||
sess.Join("INNER", "issue", "issue.id = comment.issue_id")
|
||||
}
|
||||
|
||||
|
|
|
@ -218,9 +218,9 @@ func GetIssueContentHistoryByID(dbCtx context.Context, id int64) (*ContentHistor
|
|||
}
|
||||
|
||||
// GetIssueContentHistoryAndPrev get a history and the previous non-deleted history (to compare)
|
||||
func GetIssueContentHistoryAndPrev(dbCtx context.Context, id int64) (history, prevHistory *ContentHistory, err error) {
|
||||
func GetIssueContentHistoryAndPrev(dbCtx context.Context, issueID, id int64) (history, prevHistory *ContentHistory, err error) {
|
||||
history = &ContentHistory{}
|
||||
has, err := db.GetEngine(dbCtx).ID(id).Get(history)
|
||||
has, err := db.GetEngine(dbCtx).Where("id=? AND issue_id=?", id, issueID).Get(history)
|
||||
if err != nil {
|
||||
log.Error("failed to get issue content history %v. err=%v", id, err)
|
||||
return nil, nil, err
|
||||
|
|
|
@ -58,13 +58,13 @@ func TestContentHistory(t *testing.T) {
|
|||
hasHistory2, _ := issues_model.HasIssueContentHistory(dbCtx, 10, 1)
|
||||
assert.False(t, hasHistory2)
|
||||
|
||||
h6, h6Prev, _ := issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6)
|
||||
h6, h6Prev, _ := issues_model.GetIssueContentHistoryAndPrev(dbCtx, 10, 6)
|
||||
assert.EqualValues(t, 6, h6.ID)
|
||||
assert.EqualValues(t, 5, h6Prev.ID)
|
||||
|
||||
// soft-delete
|
||||
_ = issues_model.SoftDeleteIssueContentHistory(dbCtx, 5)
|
||||
h6, h6Prev, _ = issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6)
|
||||
h6, h6Prev, _ = issues_model.GetIssueContentHistoryAndPrev(dbCtx, 10, 6)
|
||||
assert.EqualValues(t, 6, h6.ID)
|
||||
assert.EqualValues(t, 4, h6Prev.ID)
|
||||
|
||||
|
|
|
@ -32,7 +32,12 @@ func AddGitSizeAndLFSSizeToRepositoryTable(x *xorm.Engine) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = sess.Exec(`UPDATE repository SET git_size = size - lfs_size`)
|
||||
_, err = sess.Exec(`UPDATE repository SET size = 0 WHERE size IS NULL`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = sess.Exec(`UPDATE repository SET git_size = size - lfs_size WHERE size > lfs_size`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -311,6 +311,18 @@ func GetProjectByID(ctx context.Context, id int64) (*Project, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
// GetProjectForRepoByID returns the projects in a repository
|
||||
func GetProjectForRepoByID(ctx context.Context, repoID, id int64) (*Project, error) {
|
||||
p := new(Project)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", id, repoID).Get(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrProjectNotExist{ID: id}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// UpdateProject updates project properties
|
||||
func UpdateProject(ctx context.Context, p *Project) error {
|
||||
if !IsCardTypeValid(p.CardType) {
|
||||
|
|
|
@ -207,6 +207,21 @@ func GetReleaseByID(ctx context.Context, id int64) (*Release, error) {
|
|||
return rel, nil
|
||||
}
|
||||
|
||||
// GetReleaseForRepoByID returns release with given ID.
|
||||
func GetReleaseForRepoByID(ctx context.Context, repoID, id int64) (*Release, error) {
|
||||
rel := new(Release)
|
||||
has, err := db.GetEngine(ctx).
|
||||
Where("id=? AND repo_id=?", id, repoID).
|
||||
Get(rel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrReleaseNotExist{id, ""}
|
||||
}
|
||||
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// FindReleasesOptions describes the conditions to Find releases
|
||||
type FindReleasesOptions struct {
|
||||
db.ListOptions
|
||||
|
|
|
@ -47,6 +47,14 @@ func (err ErrUserDoesNotHaveAccessToRepo) Unwrap() error {
|
|||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
type ErrRepoIsArchived struct {
|
||||
Repo *Repository
|
||||
}
|
||||
|
||||
func (err ErrRepoIsArchived) Error() string {
|
||||
return fmt.Sprintf("%s is archived", err.Repo.LogString())
|
||||
}
|
||||
|
||||
var (
|
||||
reservedRepoNames = []string{".", "..", "-"}
|
||||
reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
|
||||
|
@ -594,25 +602,23 @@ func ComposeHTTPSCloneURL(owner, repo string) string {
|
|||
|
||||
func ComposeSSHCloneURL(ownerName, repoName string) string {
|
||||
sshUser := setting.SSH.User
|
||||
|
||||
// if we have a ipv6 literal we need to put brackets around it
|
||||
// for the git cloning to work.
|
||||
sshDomain := setting.SSH.Domain
|
||||
ip := net.ParseIP(setting.SSH.Domain)
|
||||
if ip != nil && ip.To4() == nil {
|
||||
sshDomain = "[" + setting.SSH.Domain + "]"
|
||||
|
||||
// non-standard port, it must use full URI
|
||||
if setting.SSH.Port != 22 {
|
||||
sshHost := net.JoinHostPort(sshDomain, strconv.Itoa(setting.SSH.Port))
|
||||
return fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName))
|
||||
}
|
||||
|
||||
if setting.SSH.Port != 22 {
|
||||
return fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser,
|
||||
net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)),
|
||||
url.PathEscape(ownerName),
|
||||
url.PathEscape(repoName))
|
||||
// for standard port, it can use a shorter URI (without the port)
|
||||
sshHost := sshDomain
|
||||
if ip := net.ParseIP(sshHost); ip != nil && ip.To4() == nil {
|
||||
sshHost = "[" + sshHost + "]" // for IPv6 address, wrap it with brackets
|
||||
}
|
||||
if setting.Repository.UseCompatSSHURI {
|
||||
return fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, url.PathEscape(ownerName), url.PathEscape(repoName))
|
||||
return fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName))
|
||||
}
|
||||
return fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, url.PathEscape(ownerName), url.PathEscape(repoName))
|
||||
return fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName))
|
||||
}
|
||||
|
||||
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
|
||||
|
@ -654,6 +660,14 @@ func (repo *Repository) GetTrustModel() TrustModelType {
|
|||
return trustModel
|
||||
}
|
||||
|
||||
// MustNotBeArchived returns ErrRepoIsArchived if the repo is archived
|
||||
func (repo *Repository) MustNotBeArchived() error {
|
||||
if repo.IsArchived {
|
||||
return ErrRepoIsArchived{Repo: repo}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// __________ .__ __
|
||||
// \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
|
||||
// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
|
||||
|
|
|
@ -652,12 +652,12 @@ func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) bu
|
|||
userOrgTeamUnitRepoCond("`repository`.id", user.ID, unitType),
|
||||
)
|
||||
}
|
||||
cond = cond.Or(
|
||||
// 4. Repositories that we directly own
|
||||
builder.Eq{"`repository`.owner_id": user.ID},
|
||||
// 4. Repositories that we directly own
|
||||
cond = cond.Or(builder.Eq{"`repository`.owner_id": user.ID})
|
||||
if !user.IsRestricted {
|
||||
// 5. Be able to see all public repos in private organizations that we are an org_user of
|
||||
userOrgPublicRepoCond(user.ID),
|
||||
)
|
||||
cond = cond.Or(userOrgPublicRepoCond(user.ID))
|
||||
}
|
||||
}
|
||||
|
||||
return cond
|
||||
|
|
|
@ -12,6 +12,8 @@ import (
|
|||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -186,3 +188,32 @@ func TestGetRepositoryByURL(t *testing.T) {
|
|||
test(t, "try.gitea.io:user2/repo2.git")
|
||||
})
|
||||
}
|
||||
|
||||
func TestComposeSSHCloneURL(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.SSH, setting.SSH)()
|
||||
defer test.MockVariableValue(&setting.Repository, setting.Repository)()
|
||||
|
||||
setting.SSH.User = "git"
|
||||
|
||||
// test SSH_DOMAIN
|
||||
setting.SSH.Domain = "domain"
|
||||
setting.SSH.Port = 22
|
||||
setting.Repository.UseCompatSSHURI = false
|
||||
assert.Equal(t, "git@domain:user/repo.git", repo_model.ComposeSSHCloneURL("user", "repo"))
|
||||
setting.Repository.UseCompatSSHURI = true
|
||||
assert.Equal(t, "ssh://git@domain/user/repo.git", repo_model.ComposeSSHCloneURL("user", "repo"))
|
||||
// test SSH_DOMAIN while use non-standard SSH port
|
||||
setting.SSH.Port = 123
|
||||
setting.Repository.UseCompatSSHURI = false
|
||||
assert.Equal(t, "ssh://git@domain:123/user/repo.git", repo_model.ComposeSSHCloneURL("user", "repo"))
|
||||
setting.Repository.UseCompatSSHURI = true
|
||||
assert.Equal(t, "ssh://git@domain:123/user/repo.git", repo_model.ComposeSSHCloneURL("user", "repo"))
|
||||
|
||||
// test IPv6 SSH_DOMAIN
|
||||
setting.Repository.UseCompatSSHURI = false
|
||||
setting.SSH.Domain = "::1"
|
||||
setting.SSH.Port = 22
|
||||
assert.Equal(t, "git@[::1]:user/repo.git", repo_model.ComposeSSHCloneURL("user", "repo"))
|
||||
setting.SSH.Port = 123
|
||||
assert.Equal(t, "ssh://git@[::1]:123/user/repo.git", repo_model.ComposeSSHCloneURL("user", "repo"))
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ func SetSettings(ctx context.Context, settings map[string]string) error {
|
|||
return err
|
||||
}
|
||||
for k, v := range settings {
|
||||
res, err := e.Exec("UPDATE system_setting SET setting_value=? WHERE setting_key=?", v, k)
|
||||
res, err := e.Exec("UPDATE system_setting SET version=version+1, setting_value=? WHERE setting_key=?", v, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -115,24 +115,26 @@ func (d *dbConfigCachedGetter) GetValue(ctx context.Context, key string) (v stri
|
|||
|
||||
func (d *dbConfigCachedGetter) GetRevision(ctx context.Context) int {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
if time.Since(d.cacheTime) < time.Second {
|
||||
return d.revision
|
||||
cachedDuration := time.Since(d.cacheTime)
|
||||
cachedRevision := d.revision
|
||||
d.mu.RUnlock()
|
||||
|
||||
if cachedDuration < time.Second {
|
||||
return cachedRevision
|
||||
}
|
||||
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if GetRevision(ctx) != d.revision {
|
||||
d.mu.RUnlock()
|
||||
d.mu.Lock()
|
||||
rev, set, err := GetAllSettings(ctx)
|
||||
if err != nil {
|
||||
log.Error("Unable to get all settings: %v", err)
|
||||
} else {
|
||||
d.cacheTime = time.Now()
|
||||
d.revision = rev
|
||||
d.settings = set
|
||||
}
|
||||
d.mu.Unlock()
|
||||
d.mu.RLock()
|
||||
}
|
||||
d.cacheTime = time.Now()
|
||||
return d.revision
|
||||
}
|
||||
|
||||
|
|
|
@ -39,4 +39,16 @@ func TestSettings(t *testing.T) {
|
|||
assert.EqualValues(t, 3, rev)
|
||||
assert.Len(t, settings, 2)
|
||||
assert.EqualValues(t, "false", settings[keyName])
|
||||
|
||||
// setting the same value should not trigger DuplicateKey error, and the "version" should be increased
|
||||
setting := &system.Setting{SettingKey: keyName}
|
||||
_, err = db.GetByBean(db.DefaultContext, setting)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, setting.Version)
|
||||
err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"})
|
||||
assert.NoError(t, err)
|
||||
setting = &system.Setting{SettingKey: keyName}
|
||||
_, err = db.GetByBean(db.DefaultContext, setting)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, setting.Version)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ func NewReplaceUser(name string) *User {
|
|||
}
|
||||
|
||||
const (
|
||||
GhostUserID = -1
|
||||
ActionsUserID = -2
|
||||
ActionsUserName = "gitea-actions"
|
||||
ActionsFullName = "Gitea Actions"
|
||||
|
|
|
@ -392,39 +392,40 @@ func CreateWebhooks(ctx context.Context, ws []*Webhook) error {
|
|||
return db.Insert(ctx, ws)
|
||||
}
|
||||
|
||||
// getWebhook uses argument bean as query condition,
|
||||
// ID must be specified and do not assign unnecessary fields.
|
||||
func getWebhook(bean *Webhook) (*Webhook, error) {
|
||||
has, err := db.GetEngine(db.DefaultContext).Get(bean)
|
||||
// GetWebhookByID returns webhook of repository by given ID.
|
||||
func GetWebhookByID(ctx context.Context, id int64) (*Webhook, error) {
|
||||
bean := new(Webhook)
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(bean)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrWebhookNotExist{ID: bean.ID}
|
||||
return nil, ErrWebhookNotExist{ID: id}
|
||||
}
|
||||
return bean, nil
|
||||
}
|
||||
|
||||
// GetWebhookByID returns webhook of repository by given ID.
|
||||
func GetWebhookByID(id int64) (*Webhook, error) {
|
||||
return getWebhook(&Webhook{
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
|
||||
// GetWebhookByRepoID returns webhook of repository by given ID.
|
||||
func GetWebhookByRepoID(repoID, id int64) (*Webhook, error) {
|
||||
return getWebhook(&Webhook{
|
||||
ID: id,
|
||||
RepoID: repoID,
|
||||
})
|
||||
func GetWebhookByRepoID(ctx context.Context, repoID, id int64) (*Webhook, error) {
|
||||
webhook := new(Webhook)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", id, repoID).Get(webhook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrWebhookNotExist{ID: id}
|
||||
}
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
// GetWebhookByOwnerID returns webhook of a user or organization by given ID.
|
||||
func GetWebhookByOwnerID(ownerID, id int64) (*Webhook, error) {
|
||||
return getWebhook(&Webhook{
|
||||
ID: id,
|
||||
OwnerID: ownerID,
|
||||
})
|
||||
func GetWebhookByOwnerID(ctx context.Context, ownerID, id int64) (*Webhook, error) {
|
||||
webhook := new(Webhook)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND owner_id=?", id, ownerID).Get(webhook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrWebhookNotExist{ID: id}
|
||||
}
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
// ListWebhookOptions are options to filter webhooks on ListWebhooksByOpts
|
||||
|
@ -482,20 +483,20 @@ func UpdateWebhookLastStatus(w *Webhook) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// deleteWebhook uses argument bean as query condition,
|
||||
// DeleteWebhookByID uses argument bean as query condition,
|
||||
// ID must be specified and do not assign unnecessary fields.
|
||||
func deleteWebhook(bean *Webhook) (err error) {
|
||||
ctx, committer, err := db.TxContext(db.DefaultContext)
|
||||
func DeleteWebhookByID(ctx context.Context, id int64) (err error) {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
if count, err := db.DeleteByBean(ctx, bean); err != nil {
|
||||
if count, err := db.DeleteByID(ctx, id, new(Webhook)); err != nil {
|
||||
return err
|
||||
} else if count == 0 {
|
||||
return ErrWebhookNotExist{ID: bean.ID}
|
||||
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: bean.ID}); err != nil {
|
||||
return ErrWebhookNotExist{ID: id}
|
||||
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: id}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -503,17 +504,17 @@ func deleteWebhook(bean *Webhook) (err error) {
|
|||
}
|
||||
|
||||
// DeleteWebhookByRepoID deletes webhook of repository by given ID.
|
||||
func DeleteWebhookByRepoID(repoID, id int64) error {
|
||||
return deleteWebhook(&Webhook{
|
||||
ID: id,
|
||||
RepoID: repoID,
|
||||
})
|
||||
func DeleteWebhookByRepoID(ctx context.Context, repoID, id int64) error {
|
||||
if _, err := GetWebhookByRepoID(ctx, repoID, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return DeleteWebhookByID(ctx, id)
|
||||
}
|
||||
|
||||
// DeleteWebhookByOwnerID deletes webhook of a user or organization by given ID.
|
||||
func DeleteWebhookByOwnerID(ownerID, id int64) error {
|
||||
return deleteWebhook(&Webhook{
|
||||
ID: id,
|
||||
OwnerID: ownerID,
|
||||
})
|
||||
func DeleteWebhookByOwnerID(ctx context.Context, ownerID, id int64) error {
|
||||
if _, err := GetWebhookByOwnerID(ctx, ownerID, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return DeleteWebhookByID(ctx, id)
|
||||
}
|
||||
|
|
|
@ -101,22 +101,22 @@ func TestCreateWebhook(t *testing.T) {
|
|||
|
||||
func TestGetWebhookByRepoID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hook, err := GetWebhookByRepoID(1, 1)
|
||||
hook, err := GetWebhookByRepoID(db.DefaultContext, 1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), hook.ID)
|
||||
|
||||
_, err = GetWebhookByRepoID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
_, err = GetWebhookByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
|
||||
func TestGetWebhookByOwnerID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hook, err := GetWebhookByOwnerID(3, 3)
|
||||
hook, err := GetWebhookByOwnerID(db.DefaultContext, 3, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), hook.ID)
|
||||
|
||||
_, err = GetWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
_, err = GetWebhookByOwnerID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
|
@ -174,10 +174,10 @@ func TestUpdateWebhook(t *testing.T) {
|
|||
func TestDeleteWebhookByRepoID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2, RepoID: 1})
|
||||
assert.NoError(t, DeleteWebhookByRepoID(1, 2))
|
||||
assert.NoError(t, DeleteWebhookByRepoID(db.DefaultContext, 1, 2))
|
||||
unittest.AssertNotExistsBean(t, &Webhook{ID: 2, RepoID: 1})
|
||||
|
||||
err := DeleteWebhookByRepoID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
err := DeleteWebhookByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
|
@ -185,10 +185,10 @@ func TestDeleteWebhookByRepoID(t *testing.T) {
|
|||
func TestDeleteWebhookByOwnerID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OwnerID: 3})
|
||||
assert.NoError(t, DeleteWebhookByOwnerID(3, 3))
|
||||
assert.NoError(t, DeleteWebhookByOwnerID(db.DefaultContext, 3, 3))
|
||||
unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OwnerID: 3})
|
||||
|
||||
err := DeleteWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
err := DeleteWebhookByOwnerID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
package charset
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"html/template"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
)
|
||||
|
||||
|
@ -20,20 +21,18 @@ import (
|
|||
const RuneNBSP = 0xa0
|
||||
|
||||
// EscapeControlHTML escapes the unicode control sequences in a provided html document
|
||||
func EscapeControlHTML(text string, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output string) {
|
||||
func EscapeControlHTML(html template.HTML, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output template.HTML) {
|
||||
sb := &strings.Builder{}
|
||||
outputStream := &HTMLStreamerWriter{Writer: sb}
|
||||
streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
|
||||
|
||||
if err := StreamHTML(strings.NewReader(text), streamer); err != nil {
|
||||
streamer.escaped.HasError = true
|
||||
log.Error("Error whilst escaping: %v", err)
|
||||
}
|
||||
return streamer.escaped, sb.String()
|
||||
escaped, _ = EscapeControlReader(strings.NewReader(string(html)), sb, locale, allowed...) // err has been handled in EscapeControlReader
|
||||
return escaped, template.HTML(sb.String())
|
||||
}
|
||||
|
||||
// EscapeControlReaders escapes the unicode control sequences in a provided reader of HTML content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte
|
||||
// EscapeControlReader escapes the unicode control sequences in a provided reader of HTML content and writer in a locale and returns the findings as an EscapeStatus
|
||||
func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
|
||||
if !setting.UI.AmbiguousUnicodeDetection {
|
||||
_, err = io.Copy(writer, reader)
|
||||
return &EscapeStatus{}, err
|
||||
}
|
||||
outputStream := &HTMLStreamerWriter{Writer: writer}
|
||||
streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
|
||||
|
||||
|
@ -43,41 +42,3 @@ func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.
|
|||
}
|
||||
return streamer.escaped, err
|
||||
}
|
||||
|
||||
// EscapeControlStringReader escapes the unicode control sequences in a provided reader of string content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte. HTML line breaks are not inserted after every newline by this method.
|
||||
func EscapeControlStringReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
|
||||
bufRd := bufio.NewReader(reader)
|
||||
outputStream := &HTMLStreamerWriter{Writer: writer}
|
||||
streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
|
||||
|
||||
for {
|
||||
line, rdErr := bufRd.ReadString('\n')
|
||||
if len(line) > 0 {
|
||||
if err := streamer.Text(line); err != nil {
|
||||
streamer.escaped.HasError = true
|
||||
log.Error("Error whilst escaping: %v", err)
|
||||
return streamer.escaped, err
|
||||
}
|
||||
}
|
||||
if rdErr != nil {
|
||||
if rdErr != io.EOF {
|
||||
err = rdErr
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return streamer.escaped, err
|
||||
}
|
||||
|
||||
// EscapeControlString escapes the unicode control sequences in a provided string and returns the findings as an EscapeStatus and the escaped string
|
||||
func EscapeControlString(text string, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output string) {
|
||||
sb := &strings.Builder{}
|
||||
outputStream := &HTMLStreamerWriter{Writer: sb}
|
||||
streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
|
||||
|
||||
if err := streamer.Text(text); err != nil {
|
||||
streamer.escaped.HasError = true
|
||||
log.Error("Error whilst escaping: %v", err)
|
||||
}
|
||||
return streamer.escaped, sb.String()
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ func (e *escapeStreamer) Text(data string) error {
|
|||
until, next = nextIdxs[0]+pos, nextIdxs[1]+pos
|
||||
}
|
||||
|
||||
// from pos until until we know that the runes are not \r\t\n or even ' '
|
||||
// from pos until we know that the runes are not \r\t\n or even ' '
|
||||
runes := make([]rune, 0, next-until)
|
||||
positions := make([]int, 0, next-until+1)
|
||||
|
||||
|
|
|
@ -4,11 +4,14 @@
|
|||
package charset
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type escapeControlTest struct {
|
||||
|
@ -132,22 +135,8 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`,
|
|||
},
|
||||
}
|
||||
|
||||
func TestEscapeControlString(t *testing.T) {
|
||||
for _, tt := range escapeControlTests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
status, result := EscapeControlString(tt.text, &translation.MockLocale{})
|
||||
if !reflect.DeepEqual(*status, tt.status) {
|
||||
t.Errorf("EscapeControlString() status = %v, wanted= %v", status, tt.status)
|
||||
}
|
||||
if result != tt.result {
|
||||
t.Errorf("EscapeControlString()\nresult= %v,\nwanted= %v", result, tt.result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapeControlReader(t *testing.T) {
|
||||
// lets add some control characters to the tests
|
||||
// add some control characters to the tests
|
||||
tests := make([]escapeControlTest, 0, len(escapeControlTests)*3)
|
||||
copy(tests, escapeControlTests)
|
||||
|
||||
|
@ -169,29 +158,20 @@ func TestEscapeControlReader(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
input := strings.NewReader(tt.text)
|
||||
output := &strings.Builder{}
|
||||
status, err := EscapeControlReader(input, output, &translation.MockLocale{})
|
||||
result := output.String()
|
||||
if err != nil {
|
||||
t.Errorf("EscapeControlReader(): err = %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*status, tt.status) {
|
||||
t.Errorf("EscapeControlReader() status = %v, wanted= %v", status, tt.status)
|
||||
}
|
||||
if result != tt.result {
|
||||
t.Errorf("EscapeControlReader()\nresult= %v,\nwanted= %v", result, tt.result)
|
||||
}
|
||||
status, err := EscapeControlReader(strings.NewReader(tt.text), output, &translation.MockLocale{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.status, *status)
|
||||
assert.Equal(t, tt.result, output.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapeControlReader_panic(t *testing.T) {
|
||||
bs := make([]byte, 0, 20479)
|
||||
bs = append(bs, 'A')
|
||||
for i := 0; i < 6826; i++ {
|
||||
bs = append(bs, []byte("—")...)
|
||||
}
|
||||
_, _ = EscapeControlString(string(bs), &translation.MockLocale{})
|
||||
func TestSettingAmbiguousUnicodeDetection(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.UI.AmbiguousUnicodeDetection, true)()
|
||||
_, out := EscapeControlHTML("a test", &translation.MockLocale{})
|
||||
assert.EqualValues(t, `a<span class="escaped-code-point" data-escaped="[U+00A0]"><span class="char"> </span></span>test`, out)
|
||||
setting.UI.AmbiguousUnicodeDetection = false
|
||||
_, out = EscapeControlHTML("a test", &translation.MockLocale{})
|
||||
assert.EqualValues(t, `a test`, out)
|
||||
}
|
||||
|
|
|
@ -560,6 +560,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
|
|||
ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
|
||||
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
|
||||
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
|
||||
ctx.Data["CanWriteActions"] = ctx.Repo.CanWrite(unit_model.TypeActions)
|
||||
|
||||
canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository)
|
||||
if err != nil {
|
||||
|
|
|
@ -79,6 +79,7 @@ var Checks []*Check
|
|||
|
||||
// RunChecks runs the doctor checks for the provided list
|
||||
func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) error {
|
||||
SortChecks(checks)
|
||||
// the checks output logs by a special logger, they do not use the default logger
|
||||
logger := log.BaseLoggerToGeneralLogger(&doctorCheckLogger{colorize: colorize})
|
||||
loggerStep := log.BaseLoggerToGeneralLogger(&doctorCheckStepLogger{colorize: colorize})
|
||||
|
@ -104,20 +105,23 @@ func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) err
|
|||
logger.Info("OK")
|
||||
}
|
||||
}
|
||||
logger.Info("\nAll done.")
|
||||
logger.Info("\nAll done (checks: %d).", len(checks))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Register registers a command with the list
|
||||
func Register(command *Check) {
|
||||
Checks = append(Checks, command)
|
||||
sort.SliceStable(Checks, func(i, j int) bool {
|
||||
if Checks[i].Priority == Checks[j].Priority {
|
||||
return Checks[i].Name < Checks[j].Name
|
||||
}
|
||||
|
||||
func SortChecks(checks []*Check) {
|
||||
sort.SliceStable(checks, func(i, j int) bool {
|
||||
if checks[i].Priority == checks[j].Priority {
|
||||
return checks[i].Name < checks[j].Name
|
||||
}
|
||||
if Checks[i].Priority == 0 {
|
||||
if checks[i].Priority == 0 {
|
||||
return false
|
||||
}
|
||||
return Checks[i].Priority < Checks[j].Priority
|
||||
return checks[i].Priority < checks[j].Priority
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
|
||||
"xorm.io/builder"
|
||||
|
@ -26,11 +27,15 @@ func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix b
|
|||
|
||||
// countOrphanedRepos count repository where user of owner_id do not exist
|
||||
func countOrphanedRepos(ctx context.Context) (int64, error) {
|
||||
return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=user.id")
|
||||
return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=`user`.id")
|
||||
}
|
||||
|
||||
// deleteOrphanedRepos delete repository where user of owner_id do not exist
|
||||
func deleteOrphanedRepos(ctx context.Context) (int64, error) {
|
||||
if err := storage.Init(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
batchSize := db.MaxBatchInsertSize("repository")
|
||||
e := db.GetEngine(ctx)
|
||||
var deleted int64
|
||||
|
@ -39,7 +44,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) {
|
|||
for {
|
||||
var ids []int64
|
||||
if err := e.Table("`repository`").
|
||||
Join("LEFT", "`user`", "repository.owner_id=user.id").
|
||||
Join("LEFT", "`user`", "repository.owner_id=`user`.id").
|
||||
Where(builder.IsNull{"`user`.id"}).
|
||||
Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
|
||||
return deleted, err
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -18,8 +19,10 @@ import (
|
|||
|
||||
// BlamePart represents block of blame - continuous lines with one sha
|
||||
type BlamePart struct {
|
||||
Sha string
|
||||
Lines []string
|
||||
Sha string
|
||||
Lines []string
|
||||
PreviousSha string
|
||||
PreviousPath string
|
||||
}
|
||||
|
||||
// BlameReader returns part of file blame one by one
|
||||
|
@ -43,30 +46,38 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
|
|||
var blamePart *BlamePart
|
||||
|
||||
if r.lastSha != nil {
|
||||
blamePart = &BlamePart{*r.lastSha, make([]string, 0)}
|
||||
blamePart = &BlamePart{
|
||||
Sha: *r.lastSha,
|
||||
Lines: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
var line []byte
|
||||
var lineBytes []byte
|
||||
var isPrefix bool
|
||||
var err error
|
||||
|
||||
for err != io.EOF {
|
||||
line, isPrefix, err = r.bufferedReader.ReadLine()
|
||||
lineBytes, isPrefix, err = r.bufferedReader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
|
||||
if len(line) == 0 {
|
||||
if len(lineBytes) == 0 {
|
||||
// isPrefix will be false
|
||||
continue
|
||||
}
|
||||
|
||||
lines := shaLineRegex.FindSubmatch(line)
|
||||
line := string(lineBytes)
|
||||
|
||||
lines := shaLineRegex.FindStringSubmatch(line)
|
||||
if lines != nil {
|
||||
sha1 := string(lines[1])
|
||||
sha1 := lines[1]
|
||||
|
||||
if blamePart == nil {
|
||||
blamePart = &BlamePart{sha1, make([]string, 0)}
|
||||
blamePart = &BlamePart{
|
||||
Sha: sha1,
|
||||
Lines: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
if blamePart.Sha != sha1 {
|
||||
|
@ -81,9 +92,11 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
|
|||
return blamePart, nil
|
||||
}
|
||||
} else if line[0] == '\t' {
|
||||
code := line[1:]
|
||||
|
||||
blamePart.Lines = append(blamePart.Lines, string(code))
|
||||
blamePart.Lines = append(blamePart.Lines, line[1:])
|
||||
} else if strings.HasPrefix(line, "previous ") {
|
||||
parts := strings.SplitN(line[len("previous "):], " ", 2)
|
||||
blamePart.PreviousSha = parts[0]
|
||||
blamePart.PreviousPath = parts[1]
|
||||
}
|
||||
|
||||
// need to munch to end of line...
|
||||
|
|
|
@ -24,15 +24,17 @@ func TestReadingBlameOutput(t *testing.T) {
|
|||
|
||||
parts := []*BlamePart{
|
||||
{
|
||||
"72866af952e98d02a73003501836074b286a78f6",
|
||||
[]string{
|
||||
Sha: "72866af952e98d02a73003501836074b286a78f6",
|
||||
Lines: []string{
|
||||
"# test_repo",
|
||||
"Test repository for testing migration from github to gitea",
|
||||
},
|
||||
},
|
||||
{
|
||||
"f32b0a9dfd09a60f616f29158f772cedd89942d2",
|
||||
[]string{"", "Do not make any changes to this repo it is used for unit testing"},
|
||||
Sha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
|
||||
Lines: []string{"", "Do not make any changes to this repo it is used for unit testing"},
|
||||
PreviousSha: "72866af952e98d02a73003501836074b286a78f6",
|
||||
PreviousPath: "README.md",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -64,16 +66,18 @@ func TestReadingBlameOutput(t *testing.T) {
|
|||
|
||||
full := []*BlamePart{
|
||||
{
|
||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
[]string{"line", "line"},
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line"},
|
||||
},
|
||||
{
|
||||
"45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||
[]string{"changed line"},
|
||||
Sha: "45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||
Lines: []string{"changed line"},
|
||||
PreviousSha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
PreviousPath: "blame.txt",
|
||||
},
|
||||
{
|
||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
[]string{"line", "line", ""},
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line", ""},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -89,8 +93,8 @@ func TestReadingBlameOutput(t *testing.T) {
|
|||
Bypass: false,
|
||||
Parts: []*BlamePart{
|
||||
{
|
||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
[]string{"line", "line", "changed line", "line", "line", ""},
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line", "changed line", "line", "line", ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -389,15 +388,11 @@ func (r *runStdError) IsExitCode(code int) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func bytesToString(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b)) // that's what Golang's strings.Builder.String() does (go/src/strings/builder.go)
|
||||
}
|
||||
|
||||
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
|
||||
func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) {
|
||||
stdoutBytes, stderrBytes, err := c.RunStdBytes(opts)
|
||||
stdout = bytesToString(stdoutBytes)
|
||||
stderr = bytesToString(stderrBytes)
|
||||
stdout = util.UnsafeBytesToString(stdoutBytes)
|
||||
stderr = util.UnsafeBytesToString(stderrBytes)
|
||||
if err != nil {
|
||||
return stdout, stderr, &runStdError{err: err, stderr: stderr}
|
||||
}
|
||||
|
@ -432,7 +427,7 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
|
|||
err := c.Run(newOpts)
|
||||
stderr = stderrBuf.Bytes()
|
||||
if err != nil {
|
||||
return nil, stderr, &runStdError{err: err, stderr: bytesToString(stderr)}
|
||||
return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)}
|
||||
}
|
||||
// even if there is no err, there could still be some stderr output
|
||||
return stdoutBuf.Bytes(), stderr, nil
|
||||
|
|
|
@ -43,8 +43,9 @@ func (c *Commit) Message() string {
|
|||
}
|
||||
|
||||
// Summary returns first line of commit message.
|
||||
// The string is forced to be valid UTF8
|
||||
func (c *Commit) Summary() string {
|
||||
return strings.Split(strings.TrimSpace(c.CommitMessage), "\n")[0]
|
||||
return strings.ToValidUTF8(strings.Split(strings.TrimSpace(c.CommitMessage), "\n")[0], "?")
|
||||
}
|
||||
|
||||
// ParentID returns oid of n-th parent (0-based index).
|
||||
|
|
|
@ -8,6 +8,7 @@ package git
|
|||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
|
@ -52,32 +53,46 @@ func (repo *Repository) IsBranchExist(name string) bool {
|
|||
|
||||
// GetBranches returns branches from the repository, skipping "skip" initial branches and
|
||||
// returning at most "limit" branches, or all branches if "limit" is 0.
|
||||
// Branches are returned with sort of `-commiterdate` as the nogogit
|
||||
// implementation. This requires full fetch, sort and then the
|
||||
// skip/limit applies later as gogit returns in undefined order.
|
||||
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
|
||||
var branchNames []string
|
||||
type BranchData struct {
|
||||
name string
|
||||
committerDate int64
|
||||
}
|
||||
var branchData []BranchData
|
||||
|
||||
branches, err := repo.gogitRepo.Branches()
|
||||
branchIter, err := repo.gogitRepo.Branches()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
i := 0
|
||||
count := 0
|
||||
_ = branches.ForEach(func(branch *plumbing.Reference) error {
|
||||
count++
|
||||
if i < skip {
|
||||
i++
|
||||
return nil
|
||||
} else if limit != 0 && count > skip+limit {
|
||||
_ = branchIter.ForEach(func(branch *plumbing.Reference) error {
|
||||
obj, err := repo.gogitRepo.CommitObject(branch.Hash())
|
||||
if err != nil {
|
||||
// skip branch if can't find commit
|
||||
return nil
|
||||
}
|
||||
|
||||
branchNames = append(branchNames, strings.TrimPrefix(branch.Name().String(), BranchPrefix))
|
||||
branchData = append(branchData, BranchData{strings.TrimPrefix(branch.Name().String(), BranchPrefix), obj.Committer.When.Unix()})
|
||||
return nil
|
||||
})
|
||||
|
||||
// TODO: Sort?
|
||||
sort.Slice(branchData, func(i, j int) bool {
|
||||
return !(branchData[i].committerDate < branchData[j].committerDate)
|
||||
})
|
||||
|
||||
return branchNames, count, nil
|
||||
var branchNames []string
|
||||
maxPos := len(branchData)
|
||||
if limit > 0 {
|
||||
maxPos = min(skip+limit, maxPos)
|
||||
}
|
||||
for i := skip; i < maxPos; i++ {
|
||||
branchNames = append(branchNames, branchData[i].name)
|
||||
}
|
||||
|
||||
return branchNames, len(branchData), nil
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
gohtml "html"
|
||||
"html/template"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -55,7 +56,7 @@ func NewContext() {
|
|||
}
|
||||
|
||||
// Code returns a HTML version of code string with chroma syntax highlighting classes and the matched lexer name
|
||||
func Code(fileName, language, code string) (string, string) {
|
||||
func Code(fileName, language, code string) (output template.HTML, lexerName string) {
|
||||
NewContext()
|
||||
|
||||
// diff view newline will be passed as empty, change to literal '\n' so it can be copied
|
||||
|
@ -65,7 +66,7 @@ func Code(fileName, language, code string) (string, string) {
|
|||
}
|
||||
|
||||
if len(code) > sizeLimit {
|
||||
return code, ""
|
||||
return template.HTML(template.HTMLEscapeString(code)), ""
|
||||
}
|
||||
|
||||
var lexer chroma.Lexer
|
||||
|
@ -102,13 +103,11 @@ func Code(fileName, language, code string) (string, string) {
|
|||
cache.Add(fileName, lexer)
|
||||
}
|
||||
|
||||
lexerName := formatLexerName(lexer.Config().Name)
|
||||
|
||||
return CodeFromLexer(lexer, code), lexerName
|
||||
return CodeFromLexer(lexer, code), formatLexerName(lexer.Config().Name)
|
||||
}
|
||||
|
||||
// CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes
|
||||
func CodeFromLexer(lexer chroma.Lexer, code string) string {
|
||||
func CodeFromLexer(lexer chroma.Lexer, code string) template.HTML {
|
||||
formatter := html.New(html.WithClasses(true),
|
||||
html.WithLineNumbers(false),
|
||||
html.PreventSurroundingPre(true),
|
||||
|
@ -120,23 +119,23 @@ func CodeFromLexer(lexer chroma.Lexer, code string) string {
|
|||
iterator, err := lexer.Tokenise(nil, code)
|
||||
if err != nil {
|
||||
log.Error("Can't tokenize code: %v", err)
|
||||
return code
|
||||
return template.HTML(template.HTMLEscapeString(code))
|
||||
}
|
||||
// style not used for live site but need to pass something
|
||||
err = formatter.Format(htmlw, githubStyles, iterator)
|
||||
if err != nil {
|
||||
log.Error("Can't format code: %v", err)
|
||||
return code
|
||||
return template.HTML(template.HTMLEscapeString(code))
|
||||
}
|
||||
|
||||
_ = htmlw.Flush()
|
||||
// Chroma will add newlines for certain lexers in order to highlight them properly
|
||||
// Once highlighted, strip them here, so they don't cause copy/paste trouble in HTML output
|
||||
return strings.TrimSuffix(htmlbuf.String(), "\n")
|
||||
return template.HTML(strings.TrimSuffix(htmlbuf.String(), "\n"))
|
||||
}
|
||||
|
||||
// File returns a slice of chroma syntax highlighted HTML lines of code and the matched lexer name
|
||||
func File(fileName, language string, code []byte) ([]string, string, error) {
|
||||
func File(fileName, language string, code []byte) ([]template.HTML, string, error) {
|
||||
NewContext()
|
||||
|
||||
if len(code) > sizeLimit {
|
||||
|
@ -183,14 +182,14 @@ func File(fileName, language string, code []byte) ([]string, string, error) {
|
|||
tokensLines := chroma.SplitTokensIntoLines(iterator.Tokens())
|
||||
htmlBuf := &bytes.Buffer{}
|
||||
|
||||
lines := make([]string, 0, len(tokensLines))
|
||||
lines := make([]template.HTML, 0, len(tokensLines))
|
||||
for _, tokens := range tokensLines {
|
||||
iterator = chroma.Literator(tokens...)
|
||||
err = formatter.Format(htmlBuf, githubStyles, iterator)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("can't format code: %w", err)
|
||||
}
|
||||
lines = append(lines, htmlBuf.String())
|
||||
lines = append(lines, template.HTML(htmlBuf.String()))
|
||||
htmlBuf.Reset()
|
||||
}
|
||||
|
||||
|
@ -198,9 +197,9 @@ func File(fileName, language string, code []byte) ([]string, string, error) {
|
|||
}
|
||||
|
||||
// PlainText returns non-highlighted HTML for code
|
||||
func PlainText(code []byte) []string {
|
||||
func PlainText(code []byte) []template.HTML {
|
||||
r := bufio.NewReader(bytes.NewReader(code))
|
||||
m := make([]string, 0, bytes.Count(code, []byte{'\n'})+1)
|
||||
m := make([]template.HTML, 0, bytes.Count(code, []byte{'\n'})+1)
|
||||
for {
|
||||
content, err := r.ReadString('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
|
@ -210,7 +209,7 @@ func PlainText(code []byte) []string {
|
|||
if content == "" && err == io.EOF {
|
||||
break
|
||||
}
|
||||
s := gohtml.EscapeString(content)
|
||||
s := template.HTML(gohtml.EscapeString(content))
|
||||
m = append(m, s)
|
||||
}
|
||||
return m
|
||||
|
|
|
@ -4,21 +4,36 @@
|
|||
package highlight
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func lines(s string) []string {
|
||||
return strings.Split(strings.ReplaceAll(strings.TrimSpace(s), `\n`, "\n"), "\n")
|
||||
func lines(s string) (out []template.HTML) {
|
||||
// "" => [], "a" => ["a"], "a\n" => ["a\n"], "a\nb" => ["a\n", "b"] (each line always includes EOL "\n" if it exists)
|
||||
out = make([]template.HTML, 0)
|
||||
s = strings.ReplaceAll(strings.ReplaceAll(strings.TrimSpace(s), "\n", ""), `\n`, "\n")
|
||||
for {
|
||||
if p := strings.IndexByte(s, '\n'); p != -1 {
|
||||
out = append(out, template.HTML(s[:p+1]))
|
||||
s = s[p+1:]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if s != "" {
|
||||
out = append(out, template.HTML(s))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
code string
|
||||
want []string
|
||||
want []template.HTML
|
||||
lexerName string
|
||||
}{
|
||||
{
|
||||
|
@ -99,10 +114,7 @@ c=2
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
out, lexerName, err := File(tt.name, "", []byte(tt.code))
|
||||
assert.NoError(t, err)
|
||||
expected := strings.Join(tt.want, "\n")
|
||||
actual := strings.Join(out, "\n")
|
||||
assert.Equal(t, strings.Count(actual, "<span"), strings.Count(actual, "</span>"))
|
||||
assert.EqualValues(t, expected, actual)
|
||||
assert.EqualValues(t, tt.want, out)
|
||||
assert.Equal(t, tt.lexerName, lexerName)
|
||||
})
|
||||
}
|
||||
|
@ -112,7 +124,7 @@ func TestPlainText(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
code string
|
||||
want []string
|
||||
want []template.HTML
|
||||
}{
|
||||
{
|
||||
name: "empty.py",
|
||||
|
@ -165,9 +177,7 @@ c=2`),
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
out := PlainText([]byte(tt.code))
|
||||
expected := strings.Join(tt.want, "\n")
|
||||
actual := strings.Join(out, "\n")
|
||||
assert.EqualValues(t, expected, actual)
|
||||
assert.EqualValues(t, tt.want, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ package code
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"html/template"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/highlight"
|
||||
|
@ -22,7 +23,7 @@ type Result struct {
|
|||
Language string
|
||||
Color string
|
||||
LineNumbers []int
|
||||
FormattedLines string
|
||||
FormattedLines template.HTML
|
||||
}
|
||||
|
||||
type SearchResultLanguages = internal.SearchResultLanguages
|
||||
|
|
|
@ -211,10 +211,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits)
|
||||
|
||||
searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(options.Keyword, &meilisearch.SearchRequest{
|
||||
Filter: query.Statement(),
|
||||
Limit: int64(limit),
|
||||
Offset: int64(skip),
|
||||
Sort: sortBy,
|
||||
Filter: query.Statement(),
|
||||
Limit: int64(limit),
|
||||
Offset: int64(skip),
|
||||
Sort: sortBy,
|
||||
MatchingStrategy: "all",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -87,7 +87,7 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
|
|||
}
|
||||
lexer = chroma.Coalesce(lexer)
|
||||
|
||||
if _, err := w.WriteString(highlight.CodeFromLexer(lexer, source)); err != nil {
|
||||
if _, err := w.WriteString(string(highlight.CodeFromLexer(lexer, source))); err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,24 +160,25 @@ const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | o
|
|||
// getDirectorySize returns the disk consumption for a given path
|
||||
func getDirectorySize(path string) (int64, error) {
|
||||
var size int64
|
||||
err := filepath.WalkDir(path, func(_ string, info os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) { // ignore the error because the file maybe deleted during traversing.
|
||||
return nil
|
||||
}
|
||||
err := filepath.WalkDir(path, func(_ string, entry os.DirEntry, err error) error {
|
||||
if os.IsNotExist(err) { // ignore the error because some files (like temp/lock file) may be deleted during traversing.
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
if entry.IsDir() {
|
||||
return nil
|
||||
}
|
||||
f, err := info.Info()
|
||||
if err != nil {
|
||||
info, err := entry.Info()
|
||||
if os.IsNotExist(err) { // ignore the error as above
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if (f.Mode() & notRegularFileMode) == 0 {
|
||||
size += f.Size()
|
||||
if (info.Mode() & notRegularFileMode) == 0 {
|
||||
size += info.Size()
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
})
|
||||
return size, err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ var Attachment = struct {
|
|||
}{
|
||||
Storage: &Storage{},
|
||||
AllowedTypes: ".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip",
|
||||
MaxSize: 4,
|
||||
MaxSize: 2048,
|
||||
MaxFiles: 5,
|
||||
Enabled: true,
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ func loadAttachmentFrom(rootCfg ConfigProvider) (err error) {
|
|||
}
|
||||
|
||||
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip")
|
||||
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
|
||||
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(2048)
|
||||
Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5)
|
||||
Attachment.Enabled = sec.Key("ENABLED").MustBool(true)
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -109,7 +108,7 @@ func DBConnStr() (string, error) {
|
|||
connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%scharset=%s&parseTime=true&tls=%s",
|
||||
Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, Database.MysqlCharset, tls)
|
||||
case "postgres":
|
||||
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, paramSep, Database.SSLMode)
|
||||
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Database.SSLMode)
|
||||
case "mssql":
|
||||
host, port := ParseMSSQLHostPort(Database.Host)
|
||||
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd)
|
||||
|
@ -117,7 +116,7 @@ func DBConnStr() (string, error) {
|
|||
if !EnableSQLite3 {
|
||||
return "", errors.New("this Gitea binary was not built with SQLite3 support")
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(Database.Path), os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("Failed to create directories: %w", err)
|
||||
}
|
||||
journalMode := ""
|
||||
|
@ -157,7 +156,8 @@ func parsePostgreSQLHostPort(info string) (host, port string) {
|
|||
return host, port
|
||||
}
|
||||
|
||||
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbParam, dbsslMode string) (connStr string) {
|
||||
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbsslMode string) (connStr string) {
|
||||
dbName, dbParam, _ := strings.Cut(dbName, "?")
|
||||
host, port := parsePostgreSQLHostPort(dbHost)
|
||||
connURL := url.URL{
|
||||
Scheme: "postgres",
|
||||
|
|
|
@ -59,38 +59,39 @@ func Test_parsePostgreSQLHostPort(t *testing.T) {
|
|||
func Test_getPostgreSQLConnectionString(t *testing.T) {
|
||||
tests := []struct {
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Passwd string
|
||||
Name string
|
||||
Param string
|
||||
SSLMode string
|
||||
Output string
|
||||
}{
|
||||
{
|
||||
Host: "/tmp/pg.sock",
|
||||
Port: "4321",
|
||||
User: "testuser",
|
||||
Passwd: "space space !#$%^^%^```-=?=",
|
||||
Name: "gitea",
|
||||
Param: "",
|
||||
SSLMode: "false",
|
||||
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
||||
},
|
||||
{
|
||||
Host: "localhost",
|
||||
Port: "1234",
|
||||
User: "pgsqlusername",
|
||||
Passwd: "I love Gitea!",
|
||||
Name: "gitea",
|
||||
Param: "",
|
||||
SSLMode: "true",
|
||||
Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/gitea?sslmode=true",
|
||||
},
|
||||
{
|
||||
Host: "localhost:1234",
|
||||
User: "user",
|
||||
Passwd: "pass",
|
||||
Name: "gitea?param=1",
|
||||
Output: "postgres://user:pass@localhost:1234/gitea?param=1&sslmode=",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.Param, test.SSLMode)
|
||||
connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.SSLMode)
|
||||
assert.Equal(t, test.Output, connStr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ var (
|
|||
Enabled: true,
|
||||
TempPath: "data/tmp/uploads",
|
||||
AllowedTypes: "",
|
||||
FileMaxSize: 3,
|
||||
FileMaxSize: 50,
|
||||
MaxFiles: 5,
|
||||
},
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ var (
|
|||
PasswordHashAlgo string
|
||||
PasswordCheckPwn bool
|
||||
SuccessfulTokensCacheSize int
|
||||
DisableQueryAuthToken bool
|
||||
CSRFCookieName = "_csrf"
|
||||
CSRFCookieHTTPOnly = true
|
||||
)
|
||||
|
@ -159,4 +160,11 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
|||
PasswordComplexity = append(PasswordComplexity, name)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: default value should be true in future releases
|
||||
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
||||
|
||||
if !DisableQueryAuthToken {
|
||||
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ var UI = struct {
|
|||
SearchRepoDescription bool
|
||||
OnlyShowRelevantRepos bool
|
||||
|
||||
AmbiguousUnicodeDetection bool
|
||||
|
||||
Notification struct {
|
||||
MinTimeout time.Duration
|
||||
TimeoutStep time.Duration
|
||||
|
@ -81,6 +83,9 @@ var UI = struct {
|
|||
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
|
||||
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
|
||||
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},
|
||||
|
||||
AmbiguousUnicodeDetection: true,
|
||||
|
||||
Notification: struct {
|
||||
MinTimeout time.Duration
|
||||
TimeoutStep time.Duration
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
package util
|
||||
|
||||
import "github.com/yuin/goldmark/util"
|
||||
import "unsafe"
|
||||
|
||||
func isSnakeCaseUpper(c byte) bool {
|
||||
return 'A' <= c && c <= 'Z'
|
||||
|
@ -83,5 +83,15 @@ func ToSnakeCase(input string) string {
|
|||
}
|
||||
}
|
||||
}
|
||||
return util.BytesToReadOnlyString(res)
|
||||
return UnsafeBytesToString(res)
|
||||
}
|
||||
|
||||
// UnsafeBytesToString uses Go's unsafe package to convert a byte slice to a string.
|
||||
// TODO: replace all "goldmark/util.BytesToReadOnlyString" with this official approach
|
||||
func UnsafeBytesToString(b []byte) string {
|
||||
return unsafe.String(unsafe.SliceData(b), len(b))
|
||||
}
|
||||
|
||||
func UnsafeStringToBytes(s string) []byte {
|
||||
return unsafe.Slice(unsafe.StringData(s), len(s))
|
||||
}
|
||||
|
|
|
@ -136,6 +136,10 @@ func (r *Route) Get(pattern string, h ...any) {
|
|||
r.Methods("GET", pattern, h...)
|
||||
}
|
||||
|
||||
func (r *Route) Options(pattern string, h ...any) {
|
||||
r.Methods("OPTIONS", pattern, h...)
|
||||
}
|
||||
|
||||
// GetOptions delegate get and options method
|
||||
func (r *Route) GetOptions(pattern string, h ...any) {
|
||||
r.Methods("GET,OPTIONS", pattern, h...)
|
||||
|
|
|
@ -2303,7 +2303,7 @@ settings.dismiss_stale_approvals_desc = When new commits that change the content
|
|||
settings.require_signed_commits = Require Signed Commits
|
||||
settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable.
|
||||
settings.protect_branch_name_pattern = Protected Branch Name Pattern
|
||||
settings.protect_branch_name_pattern_desc = "Protected branch name patterns. See <a href="github.com/gobwas/glob">the documentation</a> for pattern syntax. Examples: main, release/**"
|
||||
settings.protect_branch_name_pattern_desc = "Protected branch name patterns. See <a href="https://github.com/gobwas/glob">the documentation</a> for pattern syntax. Examples: main, release/**"
|
||||
settings.protect_patterns = Patterns
|
||||
settings.protect_protected_file_patterns = "Protected file patterns (separated using semicolon ';'):"
|
||||
settings.protect_protected_file_patterns_desc = "Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (';'). See <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> documentation for pattern syntax. Examples: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>."
|
||||
|
@ -2876,6 +2876,7 @@ packages.package_manage_panel = Package Management
|
|||
packages.total_size = Total Size: %s
|
||||
packages.unreferenced_size = Unreferenced Size: %s
|
||||
packages.cleanup = Clean up expired data
|
||||
packages.cleanup.success = Cleaned up expired data successfully
|
||||
packages.owner = Owner
|
||||
packages.creator = Creator
|
||||
packages.name = Name
|
||||
|
@ -3525,7 +3526,11 @@ runs.status = Status
|
|||
runs.actors_no_select = All actors
|
||||
runs.status_no_select = All status
|
||||
runs.no_results = No results matched.
|
||||
runs.no_workflows = There are no workflows yet.
|
||||
runs.no_workflows.quick_start = Don't know how to start with Gitea Action? See <a target="_blank" rel="noopener noreferrer" href="%s">the quick start guide</a>.
|
||||
runs.no_workflows.documentation = For more information on the Gitea Action, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
|
||||
runs.no_runs = The workflow has no runs yet.
|
||||
runs.empty_commit_message = (empty commit message)
|
||||
|
||||
workflow.disable = Disable Workflow
|
||||
workflow.disable_success = Workflow '%s' disabled successfully.
|
||||
|
|
|
@ -520,7 +520,10 @@ func CommonRoutes() *web.Route {
|
|||
r.Get("", rpm.DownloadPackageFile)
|
||||
r.Delete("", reqPackageAccess(perm.AccessModeWrite), rpm.DeletePackageFile)
|
||||
})
|
||||
r.Get("/repodata/{filename}", rpm.GetRepositoryFile)
|
||||
r.Group("/repodata/{filename}", func() {
|
||||
r.Head("", rpm.CheckRepositoryFileExistence)
|
||||
r.Get("", rpm.GetRepositoryFile)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/rubygems", func() {
|
||||
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
|
||||
|
@ -600,7 +603,10 @@ func ContainerRoutes() *web.Route {
|
|||
})
|
||||
|
||||
r.Get("", container.ReqContainerAccess, container.DetermineSupport)
|
||||
r.Get("/token", container.Authenticate)
|
||||
r.Group("/token", func() {
|
||||
r.Get("", container.Authenticate)
|
||||
r.Post("", container.AuthenticateNotImplemented)
|
||||
})
|
||||
r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
|
||||
r.Group("/{username}", func() {
|
||||
r.Group("/{image}", func() {
|
||||
|
|
|
@ -156,6 +156,17 @@ func Authenticate(ctx *context.Context) {
|
|||
})
|
||||
}
|
||||
|
||||
// https://distribution.github.io/distribution/spec/auth/oauth/
|
||||
func AuthenticateNotImplemented(ctx *context.Context) {
|
||||
// This optional endpoint can be used to authenticate a client.
|
||||
// It must implement the specification described in:
|
||||
// https://datatracker.ietf.org/doc/html/rfc6749
|
||||
// https://distribution.github.io/distribution/spec/auth/oauth/
|
||||
// Purpose of this stub is to respond with 404 Not Found instead of 405 Method Not Allowed.
|
||||
|
||||
ctx.Status(http.StatusNotFound)
|
||||
}
|
||||
|
||||
// https://docs.docker.com/registry/spec/api/#listing-repositories
|
||||
func GetRepositoryList(ctx *context.Context) {
|
||||
n := ctx.FormInt("n")
|
||||
|
|
|
@ -57,6 +57,30 @@ func GetRepositoryKey(ctx *context.Context) {
|
|||
})
|
||||
}
|
||||
|
||||
func CheckRepositoryFileExistence(ctx *context.Context) {
|
||||
pv, err := rpm_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), packages_model.EmptyFileKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetServeHeaders(&context.ServeHeaderOptions{
|
||||
Filename: pf.Name,
|
||||
LastModified: pf.CreatedUnix.AsLocalTime(),
|
||||
})
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// Gets a pre-generated repository metadata file
|
||||
func GetRepositoryFile(ctx *context.Context) {
|
||||
pv, err := rpm_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package v1 Gitea API.
|
||||
// Package v1 Gitea API
|
||||
//
|
||||
// This documentation describes the Gitea API.
|
||||
//
|
||||
|
@ -35,10 +35,12 @@
|
|||
// type: apiKey
|
||||
// name: token
|
||||
// in: query
|
||||
// description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.
|
||||
// AccessToken:
|
||||
// type: apiKey
|
||||
// name: access_token
|
||||
// in: query
|
||||
// description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.
|
||||
// AuthorizationHeaderToken:
|
||||
// type: apiKey
|
||||
// name: Authorization
|
||||
|
@ -787,6 +789,31 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC
|
|||
}
|
||||
}
|
||||
|
||||
func individualPermsChecker(ctx *context.APIContext) {
|
||||
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
|
||||
if ctx.ContextUser.IsIndividual() {
|
||||
switch {
|
||||
case ctx.ContextUser.Visibility == api.VisibleTypePrivate:
|
||||
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
case ctx.ContextUser.Visibility == api.VisibleTypeLimited:
|
||||
if ctx.Doer == nil {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for and warn against deprecated authentication options
|
||||
func checkDeprecatedAuthMethods(ctx *context.APIContext) {
|
||||
if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" {
|
||||
ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.")
|
||||
}
|
||||
}
|
||||
|
||||
// Routes registers all v1 APIs routes to web application.
|
||||
func Routes() *web.Route {
|
||||
m := web.NewRoute()
|
||||
|
@ -805,6 +832,8 @@ func Routes() *web.Route {
|
|||
}
|
||||
m.Use(context.APIContexter())
|
||||
|
||||
m.Use(checkDeprecatedAuthMethods)
|
||||
|
||||
// Get user from session if logged in.
|
||||
m.Use(apiAuth(buildAuthGroup()))
|
||||
|
||||
|
@ -887,7 +916,7 @@ func Routes() *web.Route {
|
|||
}, reqSelfOrAdmin(), reqBasicOrRevProxyAuth())
|
||||
|
||||
m.Get("/activities/feeds", user.ListUserActivityFeeds)
|
||||
}, context_service.UserAssignmentAPI())
|
||||
}, context_service.UserAssignmentAPI(), individualPermsChecker)
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser))
|
||||
|
||||
// Users (requires user scope)
|
||||
|
@ -1258,8 +1287,8 @@ func Routes() *web.Route {
|
|||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/issues", func() {
|
||||
m.Combo("").Get(repo.ListIssues).
|
||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
|
||||
m.Get("/pinned", repo.ListPinnedIssues)
|
||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), reqRepoReader(unit.TypeIssues), repo.CreateIssue)
|
||||
m.Get("/pinned", reqRepoReader(unit.TypeIssues), repo.ListPinnedIssues)
|
||||
m.Group("/comments", func() {
|
||||
m.Get("", repo.ListRepoIssueComments)
|
||||
m.Group("/{id}", func() {
|
||||
|
|
|
@ -262,12 +262,11 @@ func CreateBranch(ctx *context.APIContext) {
|
|||
}
|
||||
}
|
||||
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName)
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, oldCommit.ID.String(), opt.BranchName)
|
||||
if err != nil {
|
||||
if git_model.IsErrBranchNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
||||
}
|
||||
if models.IsErrTagAlreadyExists(err) {
|
||||
} else if models.IsErrTagAlreadyExists(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
||||
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
||||
|
|
|
@ -301,7 +301,7 @@ func DeleteHook(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
|
||||
if webhook.IsErrWebhookNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
|
|
|
@ -462,6 +462,24 @@ func ListIssues(ctx *context.APIContext) {
|
|||
isPull = util.OptionalBoolNone
|
||||
}
|
||||
|
||||
if isPull != util.OptionalBoolNone && !ctx.Repo.CanReadIssuesOrPulls(isPull.IsTrue()) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if isPull == util.OptionalBoolNone {
|
||||
canReadIssues := ctx.Repo.CanRead(unit.TypeIssues)
|
||||
canReadPulls := ctx.Repo.CanRead(unit.TypePullRequests)
|
||||
if !canReadIssues && !canReadPulls {
|
||||
ctx.NotFound()
|
||||
return
|
||||
} else if !canReadIssues {
|
||||
isPull = util.OptionalBoolTrue
|
||||
} else if !canReadPulls {
|
||||
isPull = util.OptionalBoolFalse
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: we should be more efficient here
|
||||
createdByID := getUserIDForFilter(ctx, "created_by")
|
||||
if ctx.Written() {
|
||||
|
@ -593,6 +611,10 @@ func GetIssue(ctx *context.APIContext) {
|
|||
}
|
||||
return
|
||||
}
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue))
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,11 @@ import (
|
|||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
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/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
|
@ -71,6 +73,11 @@ func ListIssueComments(ctx *context.APIContext) {
|
|||
ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
|
||||
return
|
||||
}
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
issue.Repo = ctx.Repo.Repository
|
||||
|
||||
opts := &issues_model.FindCommentsOptions{
|
||||
|
@ -271,12 +278,27 @@ func ListRepoIssueComments(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
var isPull util.OptionalBool
|
||||
canReadIssue := ctx.Repo.CanRead(unit.TypeIssues)
|
||||
canReadPull := ctx.Repo.CanRead(unit.TypePullRequests)
|
||||
if canReadIssue && canReadPull {
|
||||
isPull = util.OptionalBoolNone
|
||||
} else if canReadIssue {
|
||||
isPull = util.OptionalBoolFalse
|
||||
} else if canReadPull {
|
||||
isPull = util.OptionalBoolTrue
|
||||
} else {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
opts := &issues_model.FindCommentsOptions{
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Type: issues_model.CommentTypeComment,
|
||||
Since: since,
|
||||
Before: before,
|
||||
IsPull: isPull,
|
||||
}
|
||||
|
||||
comments, err := issues_model.FindComments(ctx, opts)
|
||||
|
@ -365,6 +387,11 @@ func CreateIssueComment(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
|
||||
ctx.Error(http.StatusForbidden, "CreateIssueComment", errors.New(ctx.Tr("repo.issues.comment_on_locked")))
|
||||
return
|
||||
|
@ -434,6 +461,11 @@ func GetIssueComment(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Type != issues_model.CommentTypeComment {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
return
|
||||
|
@ -552,7 +584,17 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
@ -655,7 +697,17 @@ func deleteIssueComment(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
} else if comment.Type != issues_model.CommentTypeComment {
|
||||
|
|
|
@ -325,6 +325,10 @@ func getIssueCommentSafe(ctx *context.APIContext) *issues_model.Comment {
|
|||
return nil
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
return nil
|
||||
}
|
||||
|
||||
comment.Issue.Repo = ctx.Repo.Repository
|
||||
|
||||
return comment
|
||||
|
|
|
@ -61,6 +61,12 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
|||
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
|
@ -190,9 +196,19 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
|
|||
return
|
||||
}
|
||||
|
||||
err = comment.LoadIssue(ctx)
|
||||
if err != nil {
|
||||
if err = comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue() failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull) {
|
||||
|
|
|
@ -159,6 +159,12 @@ func GetDeployKey(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
// this check make it more consistent
|
||||
if key.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if err = key.GetContent(); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetContent", err)
|
||||
return
|
||||
|
|
|
@ -49,13 +49,12 @@ func GetRelease(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/notFound"
|
||||
|
||||
id := ctx.ParamsInt64(":id")
|
||||
release, err := repo_model.GetReleaseByID(ctx, id)
|
||||
release, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
|
||||
return
|
||||
}
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
|
||||
release.IsTag || release.RepoID != ctx.Repo.Repository.ID {
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) || release.IsTag {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
@ -315,13 +314,12 @@ func EditRelease(ctx *context.APIContext) {
|
|||
|
||||
form := web.GetForm(ctx).(*api.EditReleaseOption)
|
||||
id := ctx.ParamsInt64(":id")
|
||||
rel, err := repo_model.GetReleaseByID(ctx, id)
|
||||
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
|
||||
return
|
||||
}
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
|
||||
rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
@ -393,17 +391,16 @@ func DeleteRelease(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/empty"
|
||||
|
||||
id := ctx.ParamsInt64(":id")
|
||||
rel, err := repo_model.GetReleaseByID(ctx, id)
|
||||
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
|
||||
return
|
||||
}
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
|
||||
rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
if err := release_service.DeleteReleaseByID(ctx, id, ctx.Doer, false); err != nil {
|
||||
if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
|
|
|
@ -17,6 +17,23 @@ import (
|
|||
"code.gitea.io/gitea/services/convert"
|
||||
)
|
||||
|
||||
func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool {
|
||||
release, err := repo_model.GetReleaseByID(ctx, releaseID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.NotFound()
|
||||
return false
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
return false
|
||||
}
|
||||
if release.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetReleaseAttachment gets a single attachment of the release
|
||||
func GetReleaseAttachment(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoGetReleaseAttachment
|
||||
|
@ -54,6 +71,10 @@ func GetReleaseAttachment(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/notFound"
|
||||
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
|
@ -176,13 +197,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
|
|||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
release, err := repo_model.GetReleaseByID(ctx, releaseID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -203,7 +218,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
|
|||
attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, header.Size, &repo_model.Attachment{
|
||||
Name: filename,
|
||||
UploaderID: ctx.Doer.ID,
|
||||
RepoID: release.RepoID,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
ReleaseID: releaseID,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -264,6 +279,10 @@ func EditReleaseAttachment(ctx *context.APIContext) {
|
|||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
|
@ -328,6 +347,10 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
|
|||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
|
|
|
@ -112,7 +112,7 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, release.ID, ctx.Doer, false); err != nil {
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
|
|
|
@ -268,7 +268,7 @@ func DeleteTag(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, tag.ID, ctx.Doer, true); err != nil {
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
|
|
|
@ -193,7 +193,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := auth_model.DeleteAccessTokenByID(ctx, tokenID, ctx.Doer.ID); err != nil {
|
||||
if err := auth_model.DeleteAccessTokenByID(ctx, tokenID, ctx.ContextUser.ID); err != nil {
|
||||
if auth_model.IsErrAccessTokenNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
|
@ -343,6 +343,10 @@ func GetOauth2Application(ctx *context.APIContext) {
|
|||
}
|
||||
return
|
||||
}
|
||||
if app.UID != ctx.Doer.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
app.ClientSecret = ""
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ func GetGPGKey(ctx *context.APIContext) {
|
|||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
key, err := asymkey_model.GetGPGKeyByID(ctx, ctx.ParamsInt64(":id"))
|
||||
key, err := asymkey_model.GetGPGKeyForUserByID(ctx, ctx.Doer.ID, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrGPGKeyNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
|
|
@ -62,6 +62,11 @@ func GetHook(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
if !ctx.Doer.IsAdmin && hook.OwnerID != ctx.Doer.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
apiHook, err := webhook_service.ToHook(ctx.Doer.HomeLink(), hook)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
|
|
|
@ -54,19 +54,33 @@ func Search(ctx *context.APIContext) {
|
|||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
UID: ctx.FormInt64("uid"),
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: listOptions,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]any{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
uid := ctx.FormInt64("uid")
|
||||
var users []*user_model.User
|
||||
var maxResults int64
|
||||
var err error
|
||||
|
||||
switch uid {
|
||||
case user_model.GhostUserID:
|
||||
maxResults = 1
|
||||
users = []*user_model.User{user_model.NewGhostUser()}
|
||||
case user_model.ActionsUserID:
|
||||
maxResults = 1
|
||||
users = []*user_model.User{user_model.NewActionsUser()}
|
||||
default:
|
||||
users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
UID: uid,
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: listOptions,
|
||||
})
|
||||
return
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]any{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue