diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 4159f021889..18e8138601b 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -128,7 +128,7 @@ rules: "@stylistic/js/computed-property-spacing": [2, never] "@stylistic/js/dot-location": [2, property] "@stylistic/js/eol-last": [2] - "@stylistic/js/func-call-spacing": [2, never] + "@stylistic/js/function-call-spacing": [2, never] "@stylistic/js/function-call-argument-newline": [0] "@stylistic/js/function-paren-newline": [0] "@stylistic/js/generator-star-spacing": [0] diff --git a/.github/workflows/disk-clean.yml b/.github/workflows/disk-clean.yml index 78cd251354a..8abe8891c79 100644 --- a/.github/workflows/disk-clean.yml +++ b/.github/workflows/disk-clean.yml @@ -8,21 +8,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - # FIXME: https://github.com/jlumbroso/free-disk-space/issues/17 - - name: same as 'large-packages' but without 'google-cloud-sdk' - shell: bash - run: | - sudo apt-get update - sudo apt-get remove -y '^dotnet-.*' || true - sudo apt-get remove -y '^llvm-.*' || true - sudo apt-get remove -y 'php.*' || true - sudo apt-get remove -y '^mongodb-.*' || true - sudo apt-get remove -y '^mysql-.*' || true - sudo apt-get remove -y azure-cli google-chrome-stable firefox powershell mono-devel libgl1-mesa-dri || true - sudo apt-get autoremove -y - sudo apt-get clean - env: - DEBIAN_FRONTEND: noninteractive - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml index 861179d9c86..02ec1a1b2dd 100644 --- a/.github/workflows/release-tag-rc.yml +++ b/.github/workflows/release-tag-rc.yml @@ -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/* diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml index c3fce7e2a7c..635507c48d0 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -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: diff --git a/CHANGELOG.md b/CHANGELOG.md index ea0f80c0c91..ae87638f1cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,454 @@ 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.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 +903,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) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 75d2cd22e6f..b43ce01ff24 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -203,10 +203,20 @@ Some of the key points: In the PR title, describe the problem you are fixing, not how you are fixing it. \ Use the first comment as a summary of your PR. \ -In the PR summary, you can describe exactly how you are fixing this problem. \ +In the PR summary, you can describe exactly how you are fixing this problem. + Keep this summary up-to-date as the PR evolves. \ If your PR changes the UI, you must add **after** screenshots in the PR summary. \ -If you are not implementing a new feature, you should also post **before** screenshots for comparison. \ +If you are not implementing a new feature, you should also post **before** screenshots for comparison. + +If you are implementing a new feature, your PR will only be merged if your screenshots are up to date.\ +Furthermore, feature PRs will only be merged if their summary contains a clear usage description (understandable for users) and testing description (understandable for reviewers). +You should strive to combine both into a single description. + +Another requirement for merging PRs is that the PR is labeled correctly.\ +However, this is not your job as a contributor, but the job of the person merging your PR.\ +If you think that your PR was labeled incorrectly, or notice that it was merged without labels, please let us know. + If your PR closes some issues, you must note that in a way that both GitHub and Gitea understand, i.e. by appending a paragraph like ```text @@ -255,13 +265,16 @@ Changing the default value of a setting or replacing the setting with another on #### How to handle breaking PRs? -If your PR has a breaking change, you must add a `BREAKING` section to your PR summary, e.g. +If your PR has a breaking change, you must add two things to the summary of your PR: -``` +1. A reasoning why this breaking change is necessary +2. A `BREAKING` section explaining in simple terms (understandable for a typical user) how this PR affects users and how to mitigate these changes. This section can look for example like + +```md ## :warning: BREAKING :warning: ``` -To explain how this will affect users and how to mitigate these changes. +Breaking PRs will not be merged as long as not both of these requirements are met. ### Maintaining open PRs diff --git a/README.md b/README.md index aac793efe80..dc396465a7e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - + diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index 014ddf329f9..ec92e342d4d 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -9,6 +9,7 @@ import ( "text/tabwriter" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" auth_service "code.gitea.io/gitea/services/auth" "github.com/urfave/cli/v2" @@ -62,7 +63,7 @@ func runListAuth(c *cli.Context) error { return err } - authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{}) + authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) if err != nil { return err } diff --git a/cmd/dump.go b/cmd/dump.go index 97f292ae096..69ecdcec124 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -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) { diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 0e8123526c1..cf1895c348c 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1025,8 +1025,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 @@ -1247,6 +1247,10 @@ LEVEL = Info ;; Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used. ;; A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic). ;ONLY_SHOW_RELEVANT_REPOS = false +;; +;; Change the sort type of the explore pages. +;; Default is "recentupdate", but you also have "alphabetically", "reverselastlogin", "newest", "oldest". +;EXPLORE_PAGING_DEFAULT_SORT = recentupdate ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1826,8 +1830,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 @@ -2588,6 +2592,8 @@ LEVEL = Info ;ENDLESS_TASK_TIMEOUT = 3h ;; Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time ;ABANDONED_JOB_TIMEOUT = 24h +;; Strings committers can place inside a commit message to skip executing the corresponding actions workflow +;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 6f9a472052c..967890b4348 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -149,7 +149,7 @@ limit defined in `REPO_SIZE_LIMIT` or at the repository itself. - `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`) @@ -232,8 +232,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a add it to this config. - `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used. - `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page. -- `ONLY_SHOW_RELEVANT_REPOS`: **false** Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used. +- `ONLY_SHOW_RELEVANT_REPOS`: **false**: Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used. A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic). +- `EXPLORE_PAGING_DEFAULT_SORT`: **recentupdate**: Change the sort type of the explore pages. Valid values are "recentupdate", "alphabetically", "reverselastlogin", "newest" and "oldest" ### UI - Admin (`ui.admin`) @@ -345,7 +346,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 @@ -825,7 +826,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. @@ -1398,6 +1399,7 @@ PROXY_HOSTS = *.github.com - `ZOMBIE_TASK_TIMEOUT`: **10m**: Timeout to stop the task which have running status, but haven't been updated for a long time - `ENDLESS_TASK_TIMEOUT`: **3h**: Timeout to stop the tasks which have running status and continuous updates, but don't end for a long time - `ABANDONED_JOB_TIMEOUT`: **24h**: Timeout to cancel the jobs which have waiting status, but haven't been picked by a runner for a long time +- `SKIP_WORKFLOW_STRINGS`: **[skip ci],[ci skip],[no ci],[skip actions],[actions skip]**: Strings committers can place inside a commit message to skip executing the corresponding actions workflow `DEFAULT_ACTIONS_URL` indicates where the Gitea Actions runners should find the actions with relative path. For example, `uses: actions/checkout@v3` means `https://github.com/actions/checkout@v3` since the value of `DEFAULT_ACTIONS_URL` is `github`. @@ -1407,7 +1409,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` could 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` could 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`. diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md index d541013159f..c4e3876c1fe 100644 --- a/docs/content/administration/config-cheat-sheet.zh-cn.md +++ b/docs/content/administration/config-cheat-sheet.zh-cn.md @@ -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,此设置无效。 @@ -783,7 +783,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 不会执行任何操作。 @@ -1343,7 +1343,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`。 diff --git a/docs/content/administration/email-setup.en-us.md b/docs/content/administration/email-setup.en-us.md index 645a7a3f434..f9621e6075c 100644 --- a/docs/content/administration/email-setup.en-us.md +++ b/docs/content/administration/email-setup.en-us.md @@ -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. diff --git a/docs/content/administration/email-setup.zh-cn.md b/docs/content/administration/email-setup.zh-cn.md index 0a7ac3378f9..2e670be85b3 100644 --- a/docs/content/administration/email-setup.zh-cn.md +++ b/docs/content/administration/email-setup.zh-cn.md @@ -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。 diff --git a/docs/content/administration/https-support.zh-cn.md b/docs/content/administration/https-support.zh-cn.md index add7906cc62..8beb06e80f8 100644 --- a/docs/content/administration/https-support.zh-cn.md +++ b/docs/content/administration/https-support.zh-cn.md @@ -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”环境变量。它也可以是绝对路径。 diff --git a/docs/content/development/api-usage.en-us.md b/docs/content/development/api-usage.en-us.md index 465f4d380c8..94dac70b88e 100644 --- a/docs/content/development/api-usage.en-us.md +++ b/docs/content/development/api-usage.en-us.md @@ -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 diff --git a/docs/content/development/api-usage.zh-cn.md b/docs/content/development/api-usage.zh-cn.md index c7eeceeb7d7..96c19972940 100644 --- a/docs/content/development/api-usage.zh-cn.md +++ b/docs/content/development/api-usage.zh-cn.md @@ -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 认证 diff --git a/docs/content/help/faq.en-us.md b/docs/content/help/faq.en-us.md index b4f5171bcfd..93b3434f476 100644 --- a/docs/content/help/faq.en-us.md +++ b/docs/content/help/faq.en-us.md @@ -39,7 +39,6 @@ If a bug fix is targeted on 1.20.1 but 1.20.1 is not released yet, you can get t To migrate from Gogs to Gitea: -- [Gogs version 0.9.146 or less](installation/upgrade-from-gogs.md) - [Gogs version 0.11.46.0418](https://github.com/go-gitea/gitea/issues/4286) To migrate from GitHub to Gitea, you can use Gitea's built-in migration form. @@ -138,9 +137,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 . +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 . +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 diff --git a/docs/content/help/faq.zh-cn.md b/docs/content/help/faq.zh-cn.md index ff53fc1d039..096ac05f48b 100644 --- a/docs/content/help/faq.zh-cn.md +++ b/docs/content/help/faq.zh-cn.md @@ -41,7 +41,6 @@ menu: 要从Gogs迁移到Gitea: -- [Gogs版本0.9.146或更低](installation/upgrade-from-gogs.md) - [Gogs版本0.11.46.0418](https://github.com/go-gitea/gitea/issues/4286) 要从GitHub迁移到Gitea,您可以使用Gitea内置的迁移表单。 @@ -142,9 +141,9 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供 但是,您可以在app.ini的api部分将ENABLE_SWAGGER设置为false,以禁用其文档显示。 有关更多信息,请参阅Gitea的[API文档](development/api-usage.md)。 -您可以在上查看最新的API(例如)。 +您可以在上查看最新的API(例如)https://try.gitea.io/api/swagger -您还可以在上查看`swagger.json`文件的示例 。 +您还可以在上查看`swagger.json`文件的示例 https://try.gitea.io/swagger.v1.json ## 调整服务器用于公共/私有使用 diff --git a/docs/content/installation/from-binary.zh-cn.md b/docs/content/installation/from-binary.zh-cn.md index 56c7cc0ae0b..216a6be51e3 100644 --- a/docs/content/installation/from-binary.zh-cn.md +++ b/docs/content/installation/from-binary.zh-cn.md @@ -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) diff --git a/docs/content/installation/from-source.zh-cn.md b/docs/content/installation/from-source.zh-cn.md index 40a314340c6..4f3464588a9 100644 --- a/docs/content/installation/from-source.zh-cn.md +++ b/docs/content/installation/from-source.zh-cn.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)。 diff --git a/docs/content/usage/actions/act-runner.en-us.md b/docs/content/usage/actions/act-runner.en-us.md index 6e99beb870a..3fad9cbfe88 100644 --- a/docs/content/usage/actions/act-runner.en-us.md +++ b/docs/content/usage/actions/act-runner.en-us.md @@ -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 diff --git a/docs/content/usage/actions/act-runner.zh-cn.md b/docs/content/usage/actions/act-runner.zh-cn.md index 39d5c238c79..274b0f06922 100644 --- a/docs/content/usage/actions/act-runner.zh-cn.md +++ b/docs/content/usage/actions/act-runner.zh-cn.md @@ -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 diff --git a/docs/content/usage/actions/comparison.en-us.md b/docs/content/usage/actions/comparison.en-us.md index 56874c2a9fd..be40657bed7 100644 --- a/docs/content/usage/actions/comparison.en-us.md +++ b/docs/content/usage/actions/comparison.en-us.md @@ -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 . -For example, if you use `uses: actions/checkout@v3`, it will download the checkout repository from . +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`. diff --git a/docs/content/usage/actions/quickstart.en-us.md b/docs/content/usage/actions/quickstart.en-us.md index 73d90e2e68a..f7f4ee2c366 100644 --- a/docs/content/usage/actions/quickstart.en-us.md +++ b/docs/content/usage/actions/quickstart.en-us.md @@ -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] diff --git a/docs/content/usage/actions/quickstart.zh-cn.md b/docs/content/usage/actions/quickstart.zh-cn.md index c5b67ce1014..ffc1f07df25 100644 --- a/docs/content/usage/actions/quickstart.zh-cn.md +++ b/docs/content/usage/actions/quickstart.zh-cn.md @@ -23,7 +23,7 @@ menu: 您可以按照[文档](installation/from-package.md) 来设置一个新实例或升级现有实例。 无论您如何安装或运行Gitea,只要版本号是1.19.0或更高即可。 -默认情况下,Actions是禁用的,因此您需要将以下内容添加到配置文件中以启用它: +从1.21.0开始,默认情况下,Actions是启用的。如果您正在使用1.21.0之前的版本,您需要将以下内容添加到配置文件中以启用它: ```ini [actions] diff --git a/docs/content/usage/authentication.en-us.md b/docs/content/usage/authentication.en-us.md index 6e4ede0be6c..6a6de08d6c3 100644 --- a/docs/content/usage/authentication.en-us.md +++ b/docs/content/usage/authentication.en-us.md @@ -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. diff --git a/docs/content/usage/authentication.zh-cn.md b/docs/content/usage/authentication.zh-cn.md index e465bc3f23c..d1cfeeb8000 100644 --- a/docs/content/usage/authentication.zh-cn.md +++ b/docs/content/usage/authentication.zh-cn.md @@ -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, diff --git a/docs/content/usage/profile-readme.en-us.md b/docs/content/usage/profile-readme.en-us.md index 045d33d1c17..339176f7d4a 100644 --- a/docs/content/usage/profile-readme.en-us.md +++ b/docs/content/usage/profile-readme.en-us.md @@ -15,6 +15,7 @@ menu: # Profile READMEs -To display a Markdown file in your Gitea profile page, simply create a repository named `.profile` and add a new file called `README.md`. Gitea will automatically display the contents of the file on your profile, above your repositories. +To display a Markdown file in your Gitea user or organization profile page, create a repository named `.profile` and add a new file named `README.md` to it. +Gitea will automatically display the contents of the file on your profile, in a new "Overview" above your repositories. Making the `.profile` repository private will hide the Profile README. diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 849a90fd10a..42bd9c23cb4 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -14,6 +14,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) // ArtifactStatus is the status of an artifact, uploading, expired or need-delete @@ -108,29 +110,37 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro return err } -// ListArtifactsByRunID returns all artifacts of a run -func ListArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=?", runID).Find(&arts) +type FindArtifactsOptions struct { + db.ListOptions + RepoID int64 + RunID int64 + ArtifactName string + Status int } -// ListArtifactsByRunIDAndArtifactName returns an artifacts of a run by artifact name -func ListArtifactsByRunIDAndArtifactName(ctx context.Context, runID int64, artifactName string) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, artifactName).Find(&arts) -} +func (opts FindArtifactsOptions) ToConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + if opts.RunID > 0 { + cond = cond.And(builder.Eq{"run_id": opts.RunID}) + } + if opts.ArtifactName != "" { + cond = cond.And(builder.Eq{"artifact_name": opts.ArtifactName}) + } + if opts.Status > 0 { + cond = cond.And(builder.Eq{"status": opts.Status}) + } -// ListUploadedArtifactsByRunID returns all uploaded artifacts of a run -func ListUploadedArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=? AND status=?", runID, ArtifactStatusUploadConfirmed).Find(&arts) + return cond } // ActionArtifactMeta is the meta data of an artifact type ActionArtifactMeta struct { ArtifactName string FileSize int64 - Status int64 + Status ArtifactStatus } // ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run @@ -143,18 +153,6 @@ func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtif Find(&arts) } -// ListArtifactsByRepoID returns all artifacts of a repo -func ListArtifactsByRepoID(ctx context.Context, repoID int64) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("repo_id=?", repoID).Find(&arts) -} - -// ListArtifactsByRunIDAndName returns artifacts by name of a run -func ListArtifactsByRunIDAndName(ctx context.Context, runID int64, name string) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, name).Find(&arts) -} - // ListNeedExpiredArtifacts returns all need expired artifacts but not deleted func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { arts := make([]*ActionArtifact, 0, 10) diff --git a/models/actions/run.go b/models/actions/run.go index 8078613fb8f..4656aa22a29 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -170,7 +170,7 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err // CancelRunningJobs cancels all running and waiting jobs associated with a specific workflow. func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string) error { // Find all runs in the specified repository, reference, and workflow with statuses 'Running' or 'Waiting'. - runs, total, err := FindRuns(ctx, FindRunOptions{ + runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{ RepoID: repoID, Ref: ref, WorkflowID: workflowID, @@ -188,7 +188,7 @@ func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string // Iterate over each found run and cancel its associated jobs. for _, run := range runs { // Find all jobs associated with the current run. - jobs, _, err := FindRunJobs(ctx, FindRunJobOptions{ + jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{ RunID: run.ID, }) if err != nil { diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go index a166396694d..6ea6cb9d3b3 100644 --- a/models/actions/run_job_list.go +++ b/models/actions/run_job_list.go @@ -61,7 +61,7 @@ type FindRunJobOptions struct { UpdatedBefore timeutil.TimeStamp } -func (opts FindRunJobOptions) toConds() builder.Cond { +func (opts FindRunJobOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RunID > 0 { cond = cond.And(builder.Eq{"run_id": opts.RunID}) @@ -83,17 +83,3 @@ func (opts FindRunJobOptions) toConds() builder.Cond { } return cond } - -func FindRunJobs(ctx context.Context, opts FindRunJobOptions) (ActionJobList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var tasks ActionJobList - total, err := e.FindAndCount(&tasks) - return tasks, total, err -} - -func CountRunJobs(ctx context.Context, opts FindRunJobOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionRunJob)) -} diff --git a/models/actions/run_list.go b/models/actions/run_list.go index cd053ea7b54..375c46221b0 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -75,7 +75,7 @@ type FindRunOptions struct { Status []Status } -func (opts FindRunOptions) toConds() builder.Cond { +func (opts FindRunOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -101,18 +101,8 @@ func (opts FindRunOptions) toConds() builder.Cond { return cond } -func FindRuns(ctx context.Context, opts FindRunOptions) (RunList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var runs RunList - total, err := e.Desc("id").FindAndCount(&runs) - return runs, total, err -} - -func CountRuns(ctx context.Context, opts FindRunOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionRun)) +func (opts FindRunOptions) ToOrders() string { + return "`id` DESC" } type StatusInfo struct { diff --git a/models/actions/runner.go b/models/actions/runner.go index 2c092c2b4a3..717b770800d 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -156,7 +156,7 @@ type FindRunnerOptions struct { WithAvailable bool // not only runners belong to, but also runners can be used } -func (opts FindRunnerOptions) toCond() builder.Cond { +func (opts FindRunnerOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { @@ -181,7 +181,7 @@ func (opts FindRunnerOptions) toCond() builder.Cond { return cond } -func (opts FindRunnerOptions) toOrder() string { +func (opts FindRunnerOptions) ToOrders() string { switch opts.Sort { case "online": return "last_online DESC" @@ -199,22 +199,6 @@ func (opts FindRunnerOptions) toOrder() string { return "last_online DESC" } -func CountRunners(ctx context.Context, opts FindRunnerOptions) (int64, error) { - return db.GetEngine(ctx). - Where(opts.toCond()). - Count(ActionRunner{}) -} - -func FindRunners(ctx context.Context, opts FindRunnerOptions) (runners RunnerList, err error) { - sess := db.GetEngine(ctx). - Where(opts.toCond()). - OrderBy(opts.toOrder()) - if opts.Page > 0 { - sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - return runners, sess.Find(&runners) -} - // GetRunnerByUUID returns a runner via uuid func GetRunnerByUUID(ctx context.Context, uuid string) (*ActionRunner, error) { var runner ActionRunner @@ -263,8 +247,7 @@ func DeleteRunner(ctx context.Context, id int64) error { // CreateRunner creates new runner. func CreateRunner(ctx context.Context, t *ActionRunner) error { - _, err := db.GetEngine(ctx).Insert(t) - return err + return db.Insert(ctx, t) } func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go index ddd9a1321e5..b806550b87d 100644 --- a/models/actions/schedule_list.go +++ b/models/actions/schedule_list.go @@ -67,7 +67,7 @@ type FindScheduleOptions struct { OwnerID int64 } -func (opts FindScheduleOptions) toConds() builder.Cond { +func (opts FindScheduleOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -79,16 +79,6 @@ func (opts FindScheduleOptions) toConds() builder.Cond { return cond } -func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if !opts.ListAll && opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var schedules ScheduleList - total, err := e.Desc("id").FindAndCount(&schedules) - return schedules, total, err -} - -func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule)) +func (opts FindScheduleOptions) ToOrders() string { + return "`id` DESC" } diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go index 6bf91cf8196..e9ae268a6e4 100644 --- a/models/actions/schedule_spec_list.go +++ b/models/actions/schedule_spec_list.go @@ -71,7 +71,7 @@ type FindSpecOptions struct { Next int64 } -func (opts FindSpecOptions) toConds() builder.Cond { +func (opts FindSpecOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -84,23 +84,18 @@ func (opts FindSpecOptions) toConds() builder.Cond { return cond } +func (opts FindSpecOptions) ToOrders() string { + return "`id` DESC" +} + func FindSpecs(ctx context.Context, opts FindSpecOptions) (SpecList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var specs SpecList - total, err := e.Desc("id").FindAndCount(&specs) + specs, total, err := db.FindAndCount[ActionScheduleSpec](ctx, opts) if err != nil { return nil, 0, err } - if err := specs.LoadSchedules(ctx); err != nil { + if err := SpecList(specs).LoadSchedules(ctx); err != nil { return nil, 0, err } return specs, total, nil } - -func CountSpecs(ctx context.Context, opts FindSpecOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionScheduleSpec)) -} diff --git a/models/actions/task_list.go b/models/actions/task_list.go index 1f6b16772b8..b07d00b8dbd 100644 --- a/models/actions/task_list.go +++ b/models/actions/task_list.go @@ -62,7 +62,7 @@ type FindTaskOptions struct { IDOrderDesc bool } -func (opts FindTaskOptions) toConds() builder.Cond { +func (opts FindTaskOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -88,18 +88,9 @@ func (opts FindTaskOptions) toConds() builder.Cond { return cond } -func FindTasks(ctx context.Context, opts FindTaskOptions) (TaskList, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } +func (opts FindTaskOptions) ToOrders() string { if opts.IDOrderDesc { - e.OrderBy("id DESC") + return "`id` DESC" } - var tasks TaskList - return tasks, e.Find(&tasks) -} - -func CountTasks(ctx context.Context, opts FindTaskOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionTask)) + return "" } diff --git a/models/actions/task_output.go b/models/actions/task_output.go index 5291a783d66..eab5b931183 100644 --- a/models/actions/task_output.go +++ b/models/actions/task_output.go @@ -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 diff --git a/models/actions/variable.go b/models/actions/variable.go index e0bb59ccbe6..030b7bae92a 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -56,7 +56,7 @@ type FindVariablesOpts struct { RepoID int64 } -func (opts *FindVariablesOpts) toConds() builder.Cond { +func (opts FindVariablesOpts) ToConds() builder.Cond { cond := builder.NewCond() if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) @@ -67,15 +67,6 @@ func (opts *FindVariablesOpts) toConds() builder.Cond { return cond } -func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariable, error) { - var variables []*ActionVariable - sess := db.GetEngine(ctx) - if opts.PageSize != 0 { - sess = db.SetSessionPagination(sess, &opts.ListOptions) - } - return variables, sess.Where(opts.toConds()).Find(&variables) -} - func GetVariableByID(ctx context.Context, variableID int64) (*ActionVariable, error) { var variable ActionVariable has, err := db.GetEngine(ctx).Where("id=?", variableID).Get(&variable) diff --git a/models/activities/notification.go b/models/activities/notification.go index 7c794564b67..230bcdd6e80 100644 --- a/models/activities/notification.go +++ b/models/activities/notification.go @@ -22,7 +22,6 @@ import ( "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" - "xorm.io/xorm" ) type ( @@ -93,7 +92,7 @@ type FindNotificationOptions struct { } // ToCond will convert each condition into a xorm-Cond -func (opts *FindNotificationOptions) ToCond() builder.Cond { +func (opts FindNotificationOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.UserID != 0 { cond = cond.And(builder.Eq{"notification.user_id": opts.UserID}) @@ -105,7 +104,11 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond { cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID}) } if len(opts.Status) > 0 { - cond = cond.And(builder.In("notification.status", opts.Status)) + if len(opts.Status) == 1 { + cond = cond.And(builder.Eq{"notification.status": opts.Status[0]}) + } else { + cond = cond.And(builder.In("notification.status", opts.Status)) + } } if len(opts.Source) > 0 { cond = cond.And(builder.In("notification.source", opts.Source)) @@ -119,24 +122,8 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond { return cond } -// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required -func (opts *FindNotificationOptions) ToSession(ctx context.Context) *xorm.Session { - sess := db.GetEngine(ctx).Where(opts.ToCond()) - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, opts) - } - return sess -} - -// GetNotifications returns all notifications that fit to the given options. -func GetNotifications(ctx context.Context, options *FindNotificationOptions) (nl NotificationList, err error) { - err = options.ToSession(ctx).OrderBy("notification.updated_unix DESC").Find(&nl) - return nl, err -} - -// CountNotifications count all notifications that fit to the given options and ignore pagination. -func CountNotifications(ctx context.Context, opts *FindNotificationOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.ToCond()).Count(&Notification{}) +func (opts FindNotificationOptions) ToOrders() string { + return "notification.updated_unix DESC" } // CreateRepoTransferNotification creates notification for the user a repository was transferred to @@ -192,7 +179,9 @@ func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error { // init var toNotify container.Set[int64] - notifications, err := getNotificationsByIssueID(ctx, issueID) + notifications, err := db.Find[Notification](ctx, FindNotificationOptions{ + IssueID: issueID, + }) if err != nil { return err } @@ -273,23 +262,6 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n return nil } -func getNotificationsByIssueID(ctx context.Context, issueID int64) (notifications []*Notification, err error) { - err = db.GetEngine(ctx). - Where("issue_id = ?", issueID). - Find(¬ifications) - return notifications, err -} - -func notificationExists(notifications []*Notification, issueID, userID int64) bool { - for _, notification := range notifications { - if notification.IssueID == issueID && notification.UserID == userID { - return true - } - } - - return false -} - func createIssueNotification(ctx context.Context, userID int64, issue *issues_model.Issue, commentID, updatedByID int64) error { notification := &Notification{ UserID: userID, @@ -341,35 +313,6 @@ func GetIssueNotification(ctx context.Context, userID, issueID int64) (*Notifica return notification, err } -// NotificationsForUser returns notifications for a given user and status -func NotificationsForUser(ctx context.Context, user *user_model.User, statuses []NotificationStatus, page, perPage int) (notifications NotificationList, err error) { - if len(statuses) == 0 { - return nil, nil - } - - sess := db.GetEngine(ctx). - Where("user_id = ?", user.ID). - In("status", statuses). - OrderBy("updated_unix DESC") - - if page > 0 && perPage > 0 { - sess.Limit(perPage, (page-1)*perPage) - } - - err = sess.Find(¬ifications) - return notifications, err -} - -// CountUnread count unread notifications for a user -func CountUnread(ctx context.Context, userID int64) int64 { - exist, err := db.GetEngine(ctx).Where("user_id = ?", userID).And("status = ?", NotificationStatusUnread).Count(new(Notification)) - if err != nil { - log.Error("countUnread", err) - return 0 - } - return exist -} - // LoadAttributes load Repo Issue User and Comment if not loaded func (n *Notification) LoadAttributes(ctx context.Context) (err error) { if err = n.loadRepo(ctx); err != nil { @@ -481,17 +424,47 @@ func (n *Notification) APIURL() string { return setting.AppURL + "api/v1/notifications/threads/" + strconv.FormatInt(n.ID, 10) } +func notificationExists(notifications []*Notification, issueID, userID int64) bool { + for _, notification := range notifications { + if notification.IssueID == issueID && notification.UserID == userID { + return true + } + } + + return false +} + +// UserIDCount is a simple coalition of UserID and Count +type UserIDCount struct { + UserID int64 + Count int64 +} + +// GetUIDsAndNotificationCounts between the two provided times +func GetUIDsAndNotificationCounts(ctx context.Context, since, until timeutil.TimeStamp) ([]UserIDCount, error) { + sql := `SELECT user_id, count(*) AS count FROM notification ` + + `WHERE user_id IN (SELECT user_id FROM notification WHERE updated_unix >= ? AND ` + + `updated_unix < ?) AND status = ? GROUP BY user_id` + var res []UserIDCount + return res, db.GetEngine(ctx).SQL(sql, since, until, NotificationStatusUnread).Find(&res) +} + // NotificationList contains a list of notifications type NotificationList []*Notification // LoadAttributes load Repo Issue User and Comment if not loaded func (nl NotificationList) LoadAttributes(ctx context.Context) error { - var err error - for i := 0; i < len(nl); i++ { - err = nl[i].LoadAttributes(ctx) - if err != nil && !issues_model.IsErrCommentNotExist(err) { - return err - } + if _, _, err := nl.LoadRepos(ctx); err != nil { + return err + } + if _, err := nl.LoadIssues(ctx); err != nil { + return err + } + if _, err := nl.LoadUsers(ctx); err != nil { + return err + } + if _, err := nl.LoadComments(ctx); err != nil { + return err } return nil } @@ -665,6 +638,68 @@ func (nl NotificationList) getPendingCommentIDs() []int64 { return ids.Values() } +func (nl NotificationList) getUserIDs() []int64 { + ids := make(container.Set[int64], len(nl)) + for _, notification := range nl { + if notification.UserID == 0 || notification.User != nil { + continue + } + ids.Add(notification.UserID) + } + return ids.Values() +} + +// LoadUsers loads users from database +func (nl NotificationList) LoadUsers(ctx context.Context) ([]int, error) { + if len(nl) == 0 { + return []int{}, nil + } + + userIDs := nl.getUserIDs() + users := make(map[int64]*user_model.User, len(userIDs)) + left := len(userIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", userIDs[:limit]). + Rows(new(user_model.User)) + if err != nil { + return nil, err + } + + for rows.Next() { + var user user_model.User + err = rows.Scan(&user) + if err != nil { + rows.Close() + return nil, err + } + + users[user.ID] = &user + } + _ = rows.Close() + + left -= limit + userIDs = userIDs[limit:] + } + + failures := []int{} + for i, notification := range nl { + if notification.UserID > 0 && notification.User == nil && users[notification.UserID] != nil { + notification.User = users[notification.UserID] + if notification.User == nil { + log.Error("Notification[%d]: UserID[%d] failed to load", notification.ID, notification.UserID) + failures = append(failures, i) + continue + } + } + } + return failures, nil +} + // LoadComments loads comments from database func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) { if len(nl) == 0 { @@ -717,30 +752,6 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) { return failures, nil } -// GetNotificationCount returns the notification count for user -func GetNotificationCount(ctx context.Context, user *user_model.User, status NotificationStatus) (count int64, err error) { - count, err = db.GetEngine(ctx). - Where("user_id = ?", user.ID). - And("status = ?", status). - Count(&Notification{}) - return count, err -} - -// UserIDCount is a simple coalition of UserID and Count -type UserIDCount struct { - UserID int64 - Count int64 -} - -// GetUIDsAndNotificationCounts between the two provided times -func GetUIDsAndNotificationCounts(ctx context.Context, since, until timeutil.TimeStamp) ([]UserIDCount, error) { - sql := `SELECT user_id, count(*) AS count FROM notification ` + - `WHERE user_id IN (SELECT user_id FROM notification WHERE updated_unix >= ? AND ` + - `updated_unix < ?) AND status = ? GROUP BY user_id` - var res []UserIDCount - return res, db.GetEngine(ctx).SQL(sql, since, until, NotificationStatusUnread).Find(&res) -} - // SetIssueReadBy sets issue to be read by given user. func SetIssueReadBy(ctx context.Context, issueID, userID int64) error { if err := issues_model.UpdateIssueUserByRead(ctx, userID, issueID); err != nil { diff --git a/models/activities/notification_test.go b/models/activities/notification_test.go index b90ce70536f..52f0eacba12 100644 --- a/models/activities/notification_test.go +++ b/models/activities/notification_test.go @@ -34,8 +34,13 @@ func TestCreateOrUpdateIssueNotifications(t *testing.T) { func TestNotificationsForUser(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - statuses := []activities_model.NotificationStatus{activities_model.NotificationStatusRead, activities_model.NotificationStatusUnread} - notfs, err := activities_model.NotificationsForUser(db.DefaultContext, user, statuses, 1, 10) + notfs, err := db.Find[activities_model.Notification](db.DefaultContext, activities_model.FindNotificationOptions{ + UserID: user.ID, + Status: []activities_model.NotificationStatus{ + activities_model.NotificationStatusRead, + activities_model.NotificationStatusUnread, + }, + }) assert.NoError(t, err) if assert.Len(t, notfs, 3) { assert.EqualValues(t, 5, notfs[0].ID) @@ -68,11 +73,21 @@ func TestNotification_GetIssue(t *testing.T) { func TestGetNotificationCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - cnt, err := activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusRead) + cnt, err := db.Count[activities_model.Notification](db.DefaultContext, activities_model.FindNotificationOptions{ + UserID: user.ID, + Status: []activities_model.NotificationStatus{ + activities_model.NotificationStatusRead, + }, + }) assert.NoError(t, err) assert.EqualValues(t, 0, cnt) - cnt, err = activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusUnread) + cnt, err = db.Count[activities_model.Notification](db.DefaultContext, activities_model.FindNotificationOptions{ + UserID: user.ID, + Status: []activities_model.NotificationStatus{ + activities_model.NotificationStatusUnread, + }, + }) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) } diff --git a/models/activities/statistic.go b/models/activities/statistic.go index e9dab6fc10b..fe5f7d08725 100644 --- a/models/activities/statistic.go +++ b/models/activities/statistic.go @@ -52,7 +52,7 @@ type IssueByRepositoryCount struct { func GetStatistic(ctx context.Context) (stats Statistic) { e := db.GetEngine(ctx) stats.Counter.User = user_model.CountUsers(ctx, nil) - stats.Counter.Org, _ = organization.CountOrgs(ctx, organization.FindOrgOptions{IncludePrivate: true}) + stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludePrivate: true}) stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey)) stats.Counter.Repo, _ = repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{}) stats.Counter.Watch, _ = e.Count(new(repo_model.Watch)) @@ -102,7 +102,7 @@ func GetStatistic(ctx context.Context) (stats Statistic) { stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) stats.Counter.Release, _ = e.Count(new(repo_model.Release)) - stats.Counter.AuthSource = auth.CountSources(ctx, auth.FindSourcesOptions{}) + stats.Counter.AuthSource, _ = db.Count[auth.Source](ctx, auth.FindSourcesOptions{}) stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone)) stats.Counter.Label, _ = e.Count(new(issues_model.Label)) diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go index 1da7c346def..421f24d4de9 100644 --- a/models/asymkey/gpg_key.go +++ b/models/asymkey/gpg_key.go @@ -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 diff --git a/models/asymkey/ssh_key.go b/models/asymkey/ssh_key.go index f36738fb3d5..552f2ffd69e 100644 --- a/models/asymkey/ssh_key.go +++ b/models/asymkey/ssh_key.go @@ -179,45 +179,33 @@ func SearchPublicKeyByContentExact(ctx context.Context, content string) (*Public return key, nil } -// SearchPublicKey returns a list of public keys matching the provided arguments. -func SearchPublicKey(ctx context.Context, uid int64, fingerprint string) ([]*PublicKey, error) { - keys := make([]*PublicKey, 0, 5) +type FindPublicKeyOptions struct { + db.ListOptions + OwnerID int64 + Fingerprint string + KeyTypes []KeyType + NotKeytype KeyType + LoginSourceID int64 +} + +func (opts FindPublicKeyOptions) ToConds() builder.Cond { cond := builder.NewCond() - if uid != 0 { - cond = cond.And(builder.Eq{"owner_id": uid}) + if opts.OwnerID > 0 { + cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } - if fingerprint != "" { - cond = cond.And(builder.Eq{"fingerprint": fingerprint}) + if opts.Fingerprint != "" { + cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint}) } - return keys, db.GetEngine(ctx).Where(cond).Find(&keys) -} - -// ListPublicKeys returns a list of public keys belongs to given user. -func ListPublicKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*PublicKey, error) { - sess := db.GetEngine(ctx).Where("owner_id = ? AND type != ?", uid, KeyTypePrincipal) - if listOptions.Page != 0 { - sess = db.SetSessionPagination(sess, &listOptions) - - keys := make([]*PublicKey, 0, listOptions.PageSize) - return keys, sess.Find(&keys) + if len(opts.KeyTypes) > 0 { + cond = cond.And(builder.In("type", opts.KeyTypes)) } - - keys := make([]*PublicKey, 0, 5) - return keys, sess.Find(&keys) -} - -// CountPublicKeys count public keys a user has -func CountPublicKeys(ctx context.Context, userID int64) (int64, error) { - sess := db.GetEngine(ctx).Where("owner_id = ? AND type != ?", userID, KeyTypePrincipal) - return sess.Count(&PublicKey{}) -} - -// ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source. -func ListPublicKeysBySource(ctx context.Context, uid, authSourceID int64) ([]*PublicKey, error) { - keys := make([]*PublicKey, 0, 5) - return keys, db.GetEngine(ctx). - Where("owner_id = ? AND login_source_id = ?", uid, authSourceID). - Find(&keys) + if opts.NotKeytype > 0 { + cond = cond.And(builder.Neq{"type": opts.NotKeytype}) + } + if opts.LoginSourceID > 0 { + cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID}) + } + return cond } // UpdatePublicKeyUpdated updates public key use time. @@ -394,7 +382,10 @@ func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.So // Get Public Keys from DB with current LDAP source var giteaKeys []string - keys, err := ListPublicKeysBySource(ctx, usr.ID, s.ID) + keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{ + OwnerID: usr.ID, + LoginSourceID: s.ID, + }) if err != nil { log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err) } diff --git a/models/asymkey/ssh_key_commit_verification.go b/models/asymkey/ssh_key_commit_verification.go index a61f0663b16..27c6df35786 100644 --- a/models/asymkey/ssh_key_commit_verification.go +++ b/models/asymkey/ssh_key_commit_verification.go @@ -21,7 +21,10 @@ import ( func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification { // Now try to associate the signature with the committer, if present if committer.ID != 0 { - keys, err := ListPublicKeys(ctx, committer.ID, db.ListOptions{}) + keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{ + OwnerID: committer.ID, + NotKeytype: KeyTypePrincipal, + }) if err != nil { // Skipping failed to get ssh keys of user log.Error("ListPublicKeys: %v", err) return &CommitVerification{ diff --git a/models/asymkey/ssh_key_deploy.go b/models/asymkey/ssh_key_deploy.go index e347604bcda..8f9f454051b 100644 --- a/models/asymkey/ssh_key_deploy.go +++ b/models/asymkey/ssh_key_deploy.go @@ -210,7 +210,7 @@ type ListDeployKeysOptions struct { Fingerprint string } -func (opt ListDeployKeysOptions) toCond() builder.Cond { +func (opt ListDeployKeysOptions) ToConds() builder.Cond { cond := builder.NewCond() if opt.RepoID != 0 { cond = cond.And(builder.Eq{"repo_id": opt.RepoID}) @@ -223,23 +223,3 @@ func (opt ListDeployKeysOptions) toCond() builder.Cond { } return cond } - -// ListDeployKeys returns a list of deploy keys matching the provided arguments. -func ListDeployKeys(ctx context.Context, opts *ListDeployKeysOptions) ([]*DeployKey, error) { - sess := db.GetEngine(ctx).Where(opts.toCond()) - - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, opts) - - keys := make([]*DeployKey, 0, opts.PageSize) - return keys, sess.Find(&keys) - } - - keys := make([]*DeployKey, 0, 5) - return keys, sess.Find(&keys) -} - -// CountDeployKeys returns count deploy keys matching the provided arguments. -func CountDeployKeys(ctx context.Context, opts *ListDeployKeysOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toCond()).Count(&DeployKey{}) -} diff --git a/models/auth/access_token.go b/models/auth/access_token.go index 8abcc622bc8..63331b48412 100644 --- a/models/auth/access_token.go +++ b/models/auth/access_token.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/util" lru "github.com/hashicorp/golang-lru/v2" + "xorm.io/builder" ) // ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error. @@ -201,25 +202,18 @@ type ListAccessTokensOptions struct { UserID int64 } -// ListAccessTokens returns a list of access tokens belongs to given user. -func ListAccessTokens(ctx context.Context, opts ListAccessTokensOptions) ([]*AccessToken, error) { - sess := db.GetEngine(ctx).Where("uid=?", opts.UserID) - - if len(opts.Name) != 0 { - sess = sess.Where("name=?", opts.Name) +func (opts ListAccessTokensOptions) ToConds() builder.Cond { + cond := builder.NewCond() + // user id is required, otherwise it will return all result which maybe a possible bug + cond = cond.And(builder.Eq{"uid": opts.UserID}) + if len(opts.Name) > 0 { + cond = cond.And(builder.Eq{"name": opts.Name}) } + return cond +} - sess = sess.Desc("created_unix") - - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, &opts) - - tokens := make([]*AccessToken, 0, opts.PageSize) - return tokens, sess.Find(&tokens) - } - - tokens := make([]*AccessToken, 0, 5) - return tokens, sess.Find(&tokens) +func (opts ListAccessTokensOptions) ToOrders() string { + return "created_unix DESC" } // UpdateAccessToken updates information of access token. @@ -228,15 +222,6 @@ func UpdateAccessToken(ctx context.Context, t *AccessToken) error { return err } -// CountAccessTokens count access tokens belongs to given user by options -func CountAccessTokens(ctx context.Context, opts ListAccessTokensOptions) (int64, error) { - sess := db.GetEngine(ctx).Where("uid=?", opts.UserID) - if len(opts.Name) != 0 { - sess = sess.Where("name=?", opts.Name) - } - return sess.Count(&AccessToken{}) -} - // DeleteAccessTokenByID deletes access token by given ID. func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error { cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{ diff --git a/models/auth/access_token_test.go b/models/auth/access_token_test.go index 72c937ffd6a..4360f1a2143 100644 --- a/models/auth/access_token_test.go +++ b/models/auth/access_token_test.go @@ -85,7 +85,7 @@ func TestGetAccessTokenBySHA(t *testing.T) { func TestListAccessTokens(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - tokens, err := auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 1}) + tokens, err := db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 1}) assert.NoError(t, err) if assert.Len(t, tokens, 2) { assert.Equal(t, int64(1), tokens[0].UID) @@ -94,14 +94,14 @@ func TestListAccessTokens(t *testing.T) { assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B") } - tokens, err = auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 2}) + tokens, err = db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 2}) assert.NoError(t, err) if assert.Len(t, tokens, 1) { assert.Equal(t, int64(2), tokens[0].UID) assert.Equal(t, "Token A", tokens[0].Name) } - tokens, err = auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 100}) + tokens, err = db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 100}) assert.NoError(t, err) assert.Empty(t, tokens) } diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 76a4e9d835b..9d53fffc786 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -5,6 +5,7 @@ package auth import ( "context" + "crypto/sha256" "encoding/base32" "encoding/base64" "fmt" @@ -19,7 +20,6 @@ import ( "code.gitea.io/gitea/modules/util" uuid "github.com/google/uuid" - "github.com/minio/sha256-simd" "golang.org/x/crypto/bcrypt" "xorm.io/builder" "xorm.io/xorm" @@ -243,13 +243,6 @@ func GetOAuth2ApplicationByID(ctx context.Context, id int64) (app *OAuth2Applica return app, nil } -// GetOAuth2ApplicationsByUserID returns all oauth2 applications owned by the user -func GetOAuth2ApplicationsByUserID(ctx context.Context, userID int64) (apps []*OAuth2Application, err error) { - apps = make([]*OAuth2Application, 0) - err = db.GetEngine(ctx).Where("uid = ?", userID).Find(&apps) - return apps, err -} - // CreateOAuth2ApplicationOptions holds options to create an oauth2 application type CreateOAuth2ApplicationOptions struct { Name string @@ -372,25 +365,6 @@ func DeleteOAuth2Application(ctx context.Context, id, userid int64) error { return committer.Commit() } -// ListOAuth2Applications returns a list of oauth2 applications belongs to given user. -func ListOAuth2Applications(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*OAuth2Application, int64, error) { - sess := db.GetEngine(ctx). - Where("uid=?", uid). - Desc("id") - - if listOptions.Page != 0 { - sess = db.SetSessionPagination(sess, &listOptions) - - apps := make([]*OAuth2Application, 0, listOptions.PageSize) - total, err := sess.FindAndCount(&apps) - return apps, total, err - } - - apps := make([]*OAuth2Application, 0, 5) - total, err := sess.FindAndCount(&apps) - return apps, total, err -} - ////////////////////////////////////////////////////// // OAuth2AuthorizationCode is a code to obtain an access token in combination with the client secret once. It has a limited lifetime. diff --git a/models/auth/oauth2_list.go b/models/auth/oauth2_list.go new file mode 100644 index 00000000000..c55f10b3c85 --- /dev/null +++ b/models/auth/oauth2_list.go @@ -0,0 +1,32 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "code.gitea.io/gitea/models/db" + + "xorm.io/builder" +) + +type FindOAuth2ApplicationsOptions struct { + db.ListOptions + // OwnerID is the user id or org id of the owner of the application + OwnerID int64 + // find global applications, if true, then OwnerID will be igonred + IsGlobal bool +} + +func (opts FindOAuth2ApplicationsOptions) ToConds() builder.Cond { + conds := builder.NewCond() + if opts.IsGlobal { + conds = conds.And(builder.Eq{"uid": 0}) + } else if opts.OwnerID != 0 { + conds = conds.And(builder.Eq{"uid": opts.OwnerID}) + } + return conds +} + +func (opts FindOAuth2ApplicationsOptions) ToOrders() string { + return "id DESC" +} diff --git a/models/auth/source.go b/models/auth/source.go index b3f3262cc20..5e77afddc36 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -234,7 +234,7 @@ func CreateSource(ctx context.Context, source *Source) error { err = registerableSource.RegisterSource() if err != nil { // remove the AuthSource in case of errors while registering configuration - if _, err := db.GetEngine(ctx).Delete(source); err != nil { + if _, err := db.GetEngine(ctx).ID(source.ID).Delete(new(Source)); err != nil { log.Error("CreateSource: Error while wrapOpenIDConnectInitializeError: %v", err) } } @@ -242,6 +242,7 @@ func CreateSource(ctx context.Context, source *Source) error { } type FindSourcesOptions struct { + db.ListOptions IsActive util.OptionalBool LoginType Type } @@ -257,27 +258,22 @@ func (opts FindSourcesOptions) ToConds() builder.Cond { return conds } -// FindSources returns a slice of login sources found in DB according to given conditions. -func FindSources(ctx context.Context, opts FindSourcesOptions) ([]*Source, error) { - auths := make([]*Source, 0, 6) - return auths, db.GetEngine(ctx).Where(opts.ToConds()).Find(&auths) -} - // IsSSPIEnabled returns true if there is at least one activated login // source of type LoginSSPI func IsSSPIEnabled(ctx context.Context) bool { if !db.HasEngine { return false } - sources, err := FindSources(ctx, FindSourcesOptions{ + + exist, err := db.Exists[Source](ctx, FindSourcesOptions{ IsActive: util.OptionalBoolTrue, LoginType: SSPI, }) if err != nil { - log.Error("ActiveSources: %v", err) + log.Error("Active SSPI Sources: %v", err) return false } - return len(sources) > 0 + return exist } // GetSourceByID returns login source by given ID. @@ -346,12 +342,6 @@ func UpdateSource(ctx context.Context, source *Source) error { return err } -// CountSources returns number of login sources. -func CountSources(ctx context.Context, opts FindSourcesOptions) int64 { - count, _ := db.GetEngine(ctx).Where(opts.ToConds()).Count(new(Source)) - return count -} - // ErrSourceNotExist represents a "SourceNotExist" kind of error. type ErrSourceNotExist struct { ID int64 diff --git a/models/db/context.go b/models/db/context.go index 521857fae83..45765ef7d38 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -264,3 +264,8 @@ func inTransaction(ctx context.Context) (*xorm.Session, bool) { return nil, false } } + +func Exists[T any](ctx context.Context, opts FindOptions) (bool, error) { + var bean T + return GetEngine(ctx).Where(opts.ToConds()).Exist(&bean) +} diff --git a/models/db/list.go b/models/db/list.go index 9fb4d0741fd..b2f932e89bf 100644 --- a/models/db/list.go +++ b/models/db/list.go @@ -14,7 +14,8 @@ import ( const ( // DefaultMaxInSize represents default variables number on IN () in SQL - DefaultMaxInSize = 50 + DefaultMaxInSize = 50 + defaultFindSliceSize = 10 ) // Paginator is the base for different ListOptions types @@ -52,7 +53,12 @@ type ListOptions struct { ListAll bool // if true, then PageSize and Page will not be taken } -var _ Paginator = &ListOptions{} +var ListOptionsAll = ListOptions{ListAll: true} + +var ( + _ Paginator = &ListOptions{} + _ FindOptions = ListOptions{} +) // GetSkipTake returns the skip and take values func (opts *ListOptions) GetSkipTake() (skip, take int) { @@ -67,8 +73,16 @@ func (opts *ListOptions) GetStartEnd() (start, end int) { return start, end } +func (opts ListOptions) GetPage() int { + return opts.Page +} + +func (opts ListOptions) GetPageSize() int { + return opts.PageSize +} + // IsListAll indicates PageSize and Page will be ignored -func (opts *ListOptions) IsListAll() bool { +func (opts ListOptions) IsListAll() bool { return opts.ListAll } @@ -85,6 +99,10 @@ func (opts *ListOptions) SetDefaultValues() { } } +func (opts ListOptions) ToConds() builder.Cond { + return builder.NewCond() +} + // AbsoluteListOptions absolute options to paginate results type AbsoluteListOptions struct { skip int @@ -124,29 +142,63 @@ func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) { // FindOptions represents a find options type FindOptions interface { - Paginator + GetPage() int + GetPageSize() int + IsListAll() bool ToConds() builder.Cond } +type FindOptionsOrder interface { + ToOrders() string +} + // Find represents a common find function which accept an options interface -func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error { +func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) { sess := GetEngine(ctx).Where(opts.ToConds()) - if !opts.IsListAll() { - sess.Limit(opts.GetSkipTake()) + page, pageSize := opts.GetPage(), opts.GetPageSize() + if !opts.IsListAll() && pageSize > 0 && page >= 1 { + sess.Limit(pageSize, (page-1)*pageSize) } - return sess.Find(objects) + if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" { + sess.OrderBy(newOpt.ToOrders()) + } + + findPageSize := defaultFindSliceSize + if pageSize > 0 { + findPageSize = pageSize + } + objects := make([]*T, 0, findPageSize) + if err := sess.Find(&objects); err != nil { + return nil, err + } + return objects, nil } // Count represents a common count function which accept an options interface -func Count[T any](ctx context.Context, opts FindOptions, object T) (int64, error) { - return GetEngine(ctx).Where(opts.ToConds()).Count(object) +func Count[T any](ctx context.Context, opts FindOptions) (int64, error) { + var object T + return GetEngine(ctx).Where(opts.ToConds()).Count(&object) } // FindAndCount represents a common findandcount function which accept an options interface -func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (int64, error) { +func FindAndCount[T any](ctx context.Context, opts FindOptions) ([]*T, int64, error) { sess := GetEngine(ctx).Where(opts.ToConds()) - if !opts.IsListAll() { - sess.Limit(opts.GetSkipTake()) + page, pageSize := opts.GetPage(), opts.GetPageSize() + if !opts.IsListAll() && pageSize > 0 && page >= 1 { + sess.Limit(pageSize, (page-1)*pageSize) } - return sess.FindAndCount(objects) + if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" { + sess.OrderBy(newOpt.ToOrders()) + } + + findPageSize := defaultFindSliceSize + if pageSize > 0 { + findPageSize = pageSize + } + objects := make([]*T, 0, findPageSize) + cnt, err := sess.FindAndCount(&objects) + if err != nil { + return nil, 0, err + } + return objects, cnt, nil } diff --git a/models/db/list_test.go b/models/db/list_test.go index b923dc9c22d..45194611f8a 100644 --- a/models/db/list_test.go +++ b/models/db/list_test.go @@ -18,11 +18,11 @@ type mockListOptions struct { db.ListOptions } -func (opts *mockListOptions) IsListAll() bool { +func (opts mockListOptions) IsListAll() bool { return true } -func (opts *mockListOptions) ToConds() builder.Cond { +func (opts mockListOptions) ToConds() builder.Cond { return builder.NewCond() } @@ -37,17 +37,16 @@ func TestFind(t *testing.T) { assert.NotEmpty(t, repoUnitCount) opts := mockListOptions{} - var repoUnits []repo_model.RepoUnit - err = db.Find(db.DefaultContext, &opts, &repoUnits) + repoUnits, err := db.Find[repo_model.RepoUnit](db.DefaultContext, opts) assert.NoError(t, err) assert.Len(t, repoUnits, repoUnitCount) - cnt, err := db.Count(db.DefaultContext, &opts, new(repo_model.RepoUnit)) + cnt, err := db.Count[repo_model.RepoUnit](db.DefaultContext, opts) assert.NoError(t, err) assert.EqualValues(t, repoUnitCount, cnt) - repoUnits = make([]repo_model.RepoUnit, 0, 10) - newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits) + repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts) assert.NoError(t, err) assert.EqualValues(t, cnt, newCnt) + assert.Len(t, repoUnits, repoUnitCount) } diff --git a/models/fixtures/comment.yml b/models/fixtures/comment.yml index bd64680c8c7..17586caa219 100644 --- a/models/fixtures/comment.yml +++ b/models/fixtures/comment.yml @@ -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 diff --git a/models/fixtures/issue.yml b/models/fixtures/issue.yml index ccc1fe41fbd..0c9b6ff4060 100644 --- a/models/fixtures/issue.yml +++ b/models/fixtures/issue.yml @@ -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 diff --git a/models/issues/comment.go b/models/issues/comment.go index 8e06838f73a..ba5aed9c652 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -183,6 +183,14 @@ func (t CommentType) HasAttachmentSupport() bool { return false } +func (t CommentType) HasMailReplySupport() bool { + switch t { + case CommentTypeComment, CommentTypeCode, CommentTypeReview, CommentTypeDismissReview, CommentTypeReopen, CommentTypeClose, CommentTypeMergePull, CommentTypeAssignees: + return true + } + return false +} + // RoleInRepo presents the user's participation in the repo type RoleInRepo string @@ -342,7 +350,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,10 +1024,11 @@ type FindCommentsOptions struct { Type CommentType IssueIDs []int64 Invalidated util.OptionalBool + IsPull util.OptionalBool } // ToConds implements FindOptions interface -func (opts *FindCommentsOptions) ToConds() builder.Cond { +func (opts FindCommentsOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID}) @@ -1050,6 +1059,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 +1069,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") } diff --git a/models/issues/content_history.go b/models/issues/content_history.go index cc06b184d78..8c333bc6dd3 100644 --- a/models/issues/content_history.go +++ b/models/issues/content_history.go @@ -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 diff --git a/models/issues/content_history_test.go b/models/issues/content_history_test.go index 53638e967f2..0ea1d0f7b2e 100644 --- a/models/issues/content_history_test.go +++ b/models/issues/content_history_test.go @@ -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) diff --git a/models/organization/org.go b/models/organization/org.go index 07091194ebc..23a4e2f96a5 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -456,7 +456,7 @@ func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder { return builder.Select("org_id").From("org_user").Where(cond) } -func (opts FindOrgOptions) toConds() builder.Cond { +func (opts FindOrgOptions) ToConds() builder.Cond { var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization} if opts.UserID > 0 { cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate))) @@ -467,23 +467,8 @@ func (opts FindOrgOptions) toConds() builder.Cond { return cond } -// FindOrgs returns a list of organizations according given conditions -func FindOrgs(ctx context.Context, opts FindOrgOptions) ([]*Organization, error) { - orgs := make([]*Organization, 0, 10) - sess := db.GetEngine(ctx). - Where(opts.toConds()). - Asc("`user`.name") - if opts.Page > 0 && opts.PageSize > 0 { - sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1)) - } - return orgs, sess.Find(&orgs) -} - -// CountOrgs returns total count organizations according options -func CountOrgs(ctx context.Context, opts FindOrgOptions) (int64, error) { - return db.GetEngine(ctx). - Where(opts.toConds()). - Count(new(Organization)) +func (opts FindOrgOptions) ToOrders() string { + return "`user`.name ASC" } // HasOrgOrUserVisible tells if the given user can see the given org or user diff --git a/models/organization/org_test.go b/models/organization/org_test.go index aa72fc467ea..5e40dd41907 100644 --- a/models/organization/org_test.go +++ b/models/organization/org_test.go @@ -131,7 +131,7 @@ func TestCountOrganizations(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{}) assert.NoError(t, err) - cnt, err := organization.CountOrgs(db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true}) + cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true}) assert.NoError(t, err) assert.Equal(t, expected, cnt) } @@ -183,7 +183,7 @@ func TestIsPublicMembership(t *testing.T) { func TestFindOrgs(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - orgs, err := organization.FindOrgs(db.DefaultContext, organization.FindOrgOptions{ + orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: 4, IncludePrivate: true, }) @@ -192,14 +192,14 @@ func TestFindOrgs(t *testing.T) { assert.EqualValues(t, 3, orgs[0].ID) } - orgs, err = organization.FindOrgs(db.DefaultContext, organization.FindOrgOptions{ + orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: 4, IncludePrivate: false, }) assert.NoError(t, err) assert.Len(t, orgs, 0) - total, err := organization.CountOrgs(db.DefaultContext, organization.FindOrgOptions{ + total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: 4, IncludePrivate: true, }) diff --git a/models/project/project.go b/models/project/project.go index 3a1bfe1dbd3..d2fca6cdc8a 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -192,16 +192,16 @@ func IsTypeValid(p Type) bool { // SearchOptions are options for GetProjects type SearchOptions struct { + db.ListOptions OwnerID int64 RepoID int64 - Page int IsClosed util.OptionalBool OrderBy db.SearchOrderBy Type Type Title string } -func (opts *SearchOptions) toConds() builder.Cond { +func (opts SearchOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -226,9 +226,8 @@ func (opts *SearchOptions) toConds() builder.Cond { return cond } -// CountProjects counts projects -func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Project)) +func (opts SearchOptions) ToOrders() string { + return opts.OrderBy.String() } func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { @@ -244,22 +243,6 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { } } -// FindProjects returns a list of all projects that have been created in the repository -func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.OrderBy.String() != "" { - e = e.OrderBy(opts.OrderBy.String()) - } - projects := make([]*Project, 0, setting.UI.IssuePagingNum) - - if opts.Page > 0 { - e = e.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum) - } - - count, err := e.FindAndCount(&projects) - return projects, count, err -} - // NewProject creates a new Project func NewProject(ctx context.Context, p *Project) error { if !IsBoardTypeValid(p.BoardType) { @@ -311,6 +294,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) { diff --git a/models/project/project_test.go b/models/project/project_test.go index 6b5bd5b371d..7a37c1faf29 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -34,13 +34,13 @@ func TestIsProjectTypeValid(t *testing.T) { func TestGetProjects(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - projects, _, err := FindProjects(db.DefaultContext, SearchOptions{RepoID: 1}) + projects, err := db.Find[Project](db.DefaultContext, SearchOptions{RepoID: 1}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures assert.Len(t, projects, 1) - projects, _, err = FindProjects(db.DefaultContext, SearchOptions{RepoID: 3}) + projects, err = db.Find[Project](db.DefaultContext, SearchOptions{RepoID: 3}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures @@ -109,7 +109,7 @@ func TestProjectsSort(t *testing.T) { } for _, tt := range tests { - projects, count, err := FindProjects(db.DefaultContext, SearchOptions{ + projects, count, err := db.FindAndCount[Project](db.DefaultContext, SearchOptions{ OrderBy: GetSearchOrderByBySortType(tt.sortType), }) assert.NoError(t, err) diff --git a/models/repo/release.go b/models/repo/release.go index ff31ec45102..223d3f25019 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -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 diff --git a/models/repo/repo.go b/models/repo/repo.go index b66936d72cb..3b19825d7b8 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -607,9 +607,9 @@ func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML { }, repo.Description) if err != nil { log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err) - return template.HTML(markup.Sanitize(repo.Description)) + return template.HTML(markup.SanitizeDescription(repo.Description)) } - return template.HTML(markup.Sanitize(desc)) + return template.HTML(markup.SanitizeDescription(desc)) } // CloneLink represents different types of clone URLs of repository. diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 1668c23c774..533ca5251fe 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -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 diff --git a/models/secret/secret.go b/models/secret/secret.go index 8df46b6c38e..41e860d7f66 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -78,7 +78,7 @@ type FindSecretsOptions struct { Name string } -func (opts *FindSecretsOptions) toConds() builder.Cond { +func (opts FindSecretsOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) @@ -96,22 +96,6 @@ func (opts *FindSecretsOptions) toConds() builder.Cond { return cond } -func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error) { - var secrets []*Secret - sess := db.GetEngine(ctx) - if opts.PageSize != 0 { - sess = db.SetSessionPagination(sess, &opts.ListOptions) - } - return secrets, sess. - Where(opts.toConds()). - Find(&secrets) -} - -// CountSecrets counts the secrets -func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret)) -} - // UpdateSecret changes org or user reop secret. func UpdateSecret(ctx context.Context, secretID int64, data string) error { encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) diff --git a/models/system/setting.go b/models/system/setting.go index 1ae6f5c6528..cd1d373b98b 100644 --- a/models/system/setting.go +++ b/models/system/setting.go @@ -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 } diff --git a/models/system/setting_test.go b/models/system/setting_test.go index 4117420e5f2..0316ea144df 100644 --- a/models/system/setting_test.go +++ b/models/system/setting_test.go @@ -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) } diff --git a/models/user/external_login_user.go b/models/user/external_login_user.go index 4121c5d89fa..0db702f2252 100644 --- a/models/user/external_login_user.go +++ b/models/user/external_login_user.go @@ -96,19 +96,6 @@ func GetExternalLogin(ctx context.Context, externalLoginUser *ExternalLoginUser) return db.GetEngine(ctx).Get(externalLoginUser) } -// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource -func ListAccountLinks(ctx context.Context, user *User) ([]*ExternalLoginUser, error) { - externalAccounts := make([]*ExternalLoginUser, 0, 5) - err := db.GetEngine(ctx).Where("user_id=?", user.ID). - Desc("login_source_id"). - Find(&externalAccounts) - if err != nil { - return nil, err - } - - return externalAccounts, nil -} - // LinkExternalToUser link the external user to the user func LinkExternalToUser(ctx context.Context, user *User, externalLoginUser *ExternalLoginUser) error { has, err := db.GetEngine(ctx).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID). @@ -173,28 +160,23 @@ func UpdateExternalUserByExternalID(ctx context.Context, external *ExternalLogin // FindExternalUserOptions represents an options to find external users type FindExternalUserOptions struct { + db.ListOptions Provider string - Limit int - Start int + UserID int64 + OrderBy string } -func (opts FindExternalUserOptions) toConds() builder.Cond { +func (opts FindExternalUserOptions) ToConds() builder.Cond { cond := builder.NewCond() if len(opts.Provider) > 0 { cond = cond.And(builder.Eq{"provider": opts.Provider}) } + if opts.UserID > 0 { + cond = cond.And(builder.Eq{"user_id": opts.UserID}) + } return cond } -// FindExternalUsersByProvider represents external users via provider -func FindExternalUsersByProvider(ctx context.Context, opts FindExternalUserOptions) ([]ExternalLoginUser, error) { - var users []ExternalLoginUser - err := db.GetEngine(ctx).Where(opts.toConds()). - Limit(opts.Limit, opts.Start). - OrderBy("login_source_id ASC, external_id ASC"). - Find(&users) - if err != nil { - return nil, err - } - return users, nil +func (opts FindExternalUserOptions) ToOrders() string { + return opts.OrderBy } diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index b28cea6a9cc..a72bd938aac 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -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(ctx context.Context, bean *Webhook) (*Webhook, error) { - has, err := db.GetEngine(ctx).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(ctx context.Context, id int64) (*Webhook, error) { - return getWebhook(ctx, &Webhook{ - ID: id, - }) -} - // GetWebhookByRepoID returns webhook of repository by given ID. func GetWebhookByRepoID(ctx context.Context, repoID, id int64) (*Webhook, error) { - return getWebhook(ctx, &Webhook{ - ID: id, - RepoID: repoID, - }) + 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(ctx context.Context, ownerID, id int64) (*Webhook, error) { - return getWebhook(ctx, &Webhook{ - ID: id, - OwnerID: ownerID, - }) + 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 @@ -435,7 +436,7 @@ type ListWebhookOptions struct { IsActive util.OptionalBool } -func (opts *ListWebhookOptions) toCond() builder.Cond { +func (opts ListWebhookOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID != 0 { cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID}) @@ -449,27 +450,6 @@ func (opts *ListWebhookOptions) toCond() builder.Cond { return cond } -// ListWebhooksByOpts return webhooks based on options -func ListWebhooksByOpts(ctx context.Context, opts *ListWebhookOptions) ([]*Webhook, error) { - sess := db.GetEngine(ctx).Where(opts.toCond()) - - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, opts) - webhooks := make([]*Webhook, 0, opts.PageSize) - err := sess.Find(&webhooks) - return webhooks, err - } - - webhooks := make([]*Webhook, 0, 10) - err := sess.Find(&webhooks) - return webhooks, err -} - -// CountWebhooksByOpts count webhooks based on options and ignore pagination -func CountWebhooksByOpts(ctx context.Context, opts *ListWebhookOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toCond()).Count(&Webhook{}) -} - // UpdateWebhook updates information of webhook. func UpdateWebhook(ctx context.Context, w *Webhook) error { _, err := db.GetEngine(ctx).ID(w.ID).AllCols().Update(w) @@ -482,20 +462,20 @@ func UpdateWebhookLastStatus(ctx context.Context, 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(ctx context.Context, bean *Webhook) (err error) { +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 } @@ -504,16 +484,16 @@ func deleteWebhook(ctx context.Context, bean *Webhook) (err error) { // DeleteWebhookByRepoID deletes webhook of repository by given ID. func DeleteWebhookByRepoID(ctx context.Context, repoID, id int64) error { - return deleteWebhook(ctx, &Webhook{ - ID: id, - RepoID: repoID, - }) + 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(ctx context.Context, ownerID, id int64) error { - return deleteWebhook(ctx, &Webhook{ - ID: id, - OwnerID: ownerID, - }) + if _, err := GetWebhookByOwnerID(ctx, ownerID, id); err != nil { + return err + } + return DeleteWebhookByID(ctx, id) } diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index 6a01fdd75f5..694fd7a873f 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -123,7 +123,7 @@ func TestGetWebhookByOwnerID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(1), hooks[0].ID) @@ -133,7 +133,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{RepoID: 1}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1}) assert.NoError(t, err) if assert.Len(t, hooks, 2) { assert.Equal(t, int64(1), hooks[0].ID) @@ -143,7 +143,7 @@ func TestGetWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) @@ -153,7 +153,7 @@ func TestGetActiveWebhooksByOwnerID(t *testing.T) { func TestGetWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) diff --git a/modules/context/repo.go b/modules/context/repo.go index 9efa2ab3c08..f91dd6aa9a8 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -563,6 +563,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 { @@ -850,7 +851,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { return getRefNameFromPath(ctx, repo, path, func(s string) bool { b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s) if err != nil { - log.Error("FindRenamedBranch", err) + log.Error("FindRenamedBranch: %v", err) return false } diff --git a/modules/doctor/repository.go b/modules/doctor/repository.go index d69ba2048bb..b3a03aa422d 100644 --- a/modules/doctor/repository.go +++ b/modules/doctor/repository.go @@ -26,7 +26,7 @@ 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 @@ -43,7 +43,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) { default: 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 diff --git a/modules/git/blame.go b/modules/git/blame.go index 6728a6bed85..93c7f184fa2 100644 --- a/modules/git/blame.go +++ b/modules/git/blame.go @@ -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... diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go index 013350ac2f4..040f4e822da 100644 --- a/modules/git/blame_test.go +++ b/modules/git/blame_test.go @@ -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", ""}, }, }, }, diff --git a/modules/graceful/context.go b/modules/graceful/context.go index 4fcbcb04b6a..c9c4ca4e67a 100644 --- a/modules/graceful/context.go +++ b/modules/graceful/context.go @@ -7,6 +7,13 @@ import ( "context" ) +// Shutdown procedure: +// * cancel ShutdownContext: the registered context consumers have time to do their cleanup (they could use the hammer context) +// * cancel HammerContext: the all context consumers have limited time to do their cleanup (wait for a few seconds) +// * cancel TerminateContext: the registered context consumers have time to do their cleanup (but they shouldn't use shutdown/hammer context anymore) +// * cancel manager context +// If the shutdown is triggered again during the shutdown procedure, the hammer context will be canceled immediately to force to shut down. + // ShutdownContext returns a context.Context that is Done at shutdown // Callers using this context should ensure that they are registered as a running server // in order that they are waited for. diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go index 068de21076f..f3f412863ac 100644 --- a/modules/graceful/manager.go +++ b/modules/graceful/manager.go @@ -39,10 +39,10 @@ type RunCanceler interface { // and add a function to call manager.InformCleanup if it's not going to be used const numberOfServersToCreate = 4 -// Manager represents the graceful server manager interface -var manager *Manager - -var initOnce = sync.Once{} +var ( + manager *Manager + initOnce sync.Once +) // GetManager returns the Manager func GetManager() *Manager { @@ -147,12 +147,12 @@ func (g *Manager) doShutdown() { go g.doHammerTime(setting.GracefulHammerTime) } go func() { - g.WaitForServers() + g.runningServerWaitGroup.Wait() // Mop up any remaining unclosed events. g.doHammerTime(0) <-time.After(1 * time.Second) g.doTerminate() - g.WaitForTerminate() + g.terminateWaitGroup.Wait() g.lock.Lock() g.managerCtxCancel() g.lock.Unlock() @@ -199,26 +199,18 @@ func (g *Manager) IsChild() bool { } // IsShutdown returns a channel which will be closed at shutdown. -// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate +// The order of closure is shutdown, hammer (potentially), terminate func (g *Manager) IsShutdown() <-chan struct{} { return g.shutdownCtx.Done() } -// IsHammer returns a channel which will be closed at hammer -// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate +// IsHammer returns a channel which will be closed at hammer. // Servers running within the running server wait group should respond to IsHammer // if not shutdown already func (g *Manager) IsHammer() <-chan struct{} { return g.hammerCtx.Done() } -// IsTerminate returns a channel which will be closed at terminate -// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate -// IsTerminate will only close once all running servers have stopped -func (g *Manager) IsTerminate() <-chan struct{} { - return g.terminateCtx.Done() -} - // ServerDone declares a running server done and subtracts one from the // running server wait group. Users probably do not want to call this // and should use one of the RunWithShutdown* functions @@ -226,28 +218,7 @@ func (g *Manager) ServerDone() { g.runningServerWaitGroup.Done() } -// WaitForServers waits for all running servers to finish. Users should probably -// instead use AtTerminate or IsTerminate -func (g *Manager) WaitForServers() { - g.runningServerWaitGroup.Wait() -} - -// WaitForTerminate waits for all terminating actions to finish. -// Only the main go-routine should use this -func (g *Manager) WaitForTerminate() { - g.terminateWaitGroup.Wait() -} - -func (g *Manager) getState() state { - g.lock.RLock() - defer g.lock.RUnlock() - return g.state -} - func (g *Manager) setStateTransition(old, new state) bool { - if old != g.getState() { - return false - } g.lock.Lock() if g.state != old { g.lock.Unlock() @@ -258,13 +229,6 @@ func (g *Manager) setStateTransition(old, new state) bool { return true } -func (g *Manager) setState(st state) { - g.lock.Lock() - defer g.lock.Unlock() - - g.state = st -} - // InformCleanup tells the cleanup wait group that we have either taken a listener or will not be taking a listener. // At the moment the total number of servers (numberOfServersToCreate) are pre-defined as a const before global init, // so this function MUST be called if a server is not used. diff --git a/modules/graceful/manager_common.go b/modules/graceful/manager_common.go new file mode 100644 index 00000000000..aaf008670ce --- /dev/null +++ b/modules/graceful/manager_common.go @@ -0,0 +1,104 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package graceful + +import ( + "context" + "runtime/pprof" + "sync" + "time" +) + +type systemdNotifyMsg string + +const ( + readyMsg systemdNotifyMsg = "READY=1" + stoppingMsg systemdNotifyMsg = "STOPPING=1" + reloadingMsg systemdNotifyMsg = "RELOADING=1" + watchdogMsg systemdNotifyMsg = "WATCHDOG=1" +) + +func statusMsg(msg string) systemdNotifyMsg { + return systemdNotifyMsg("STATUS=" + msg) +} + +// Manager manages the graceful shutdown process +type Manager struct { + ctx context.Context + isChild bool + forked bool + lock sync.RWMutex + state state + shutdownCtx context.Context + hammerCtx context.Context + terminateCtx context.Context + managerCtx context.Context + shutdownCtxCancel context.CancelFunc + hammerCtxCancel context.CancelFunc + terminateCtxCancel context.CancelFunc + managerCtxCancel context.CancelFunc + runningServerWaitGroup sync.WaitGroup + createServerWaitGroup sync.WaitGroup + terminateWaitGroup sync.WaitGroup + shutdownRequested chan struct{} + + toRunAtShutdown []func() + toRunAtTerminate []func() +} + +func newGracefulManager(ctx context.Context) *Manager { + manager := &Manager{ctx: ctx, shutdownRequested: make(chan struct{})} + manager.createServerWaitGroup.Add(numberOfServersToCreate) + manager.prepare(ctx) + manager.start() + return manager +} + +func (g *Manager) prepare(ctx context.Context) { + g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx) + g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx) + g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx) + g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx) + + g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) + g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) + g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) + g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) + + if !g.setStateTransition(stateInit, stateRunning) { + panic("invalid graceful manager state: transition from init to running failed") + } +} + +// DoImmediateHammer causes an immediate hammer +func (g *Manager) DoImmediateHammer() { + g.notify(statusMsg("Sending immediate hammer")) + g.doHammerTime(0 * time.Second) +} + +// DoGracefulShutdown causes a graceful shutdown +func (g *Manager) DoGracefulShutdown() { + g.lock.Lock() + select { + case <-g.shutdownRequested: + default: + close(g.shutdownRequested) + } + forked := g.forked + g.lock.Unlock() + + if !forked { + g.notify(stoppingMsg) + } else { + g.notify(statusMsg("Shutting down after fork")) + } + g.doShutdown() +} + +// RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die. +// Any call to RegisterServer must be matched by a call to ServerDone +func (g *Manager) RegisterServer() { + KillParent() + g.runningServerWaitGroup.Add(1) +} diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go index b1fd6da76dd..f4af4993d97 100644 --- a/modules/graceful/manager_unix.go +++ b/modules/graceful/manager_unix.go @@ -12,7 +12,6 @@ import ( "os/signal" "runtime/pprof" "strconv" - "sync" "syscall" "time" @@ -22,51 +21,6 @@ import ( "code.gitea.io/gitea/modules/setting" ) -// Manager manages the graceful shutdown process -type Manager struct { - isChild bool - forked bool - lock *sync.RWMutex - state state - shutdownCtx context.Context - hammerCtx context.Context - terminateCtx context.Context - managerCtx context.Context - shutdownCtxCancel context.CancelFunc - hammerCtxCancel context.CancelFunc - terminateCtxCancel context.CancelFunc - managerCtxCancel context.CancelFunc - runningServerWaitGroup sync.WaitGroup - createServerWaitGroup sync.WaitGroup - terminateWaitGroup sync.WaitGroup - - toRunAtShutdown []func() - toRunAtTerminate []func() -} - -func newGracefulManager(ctx context.Context) *Manager { - manager := &Manager{ - isChild: len(os.Getenv(listenFDsEnv)) > 0 && os.Getppid() > 1, - lock: &sync.RWMutex{}, - } - manager.createServerWaitGroup.Add(numberOfServersToCreate) - manager.start(ctx) - return manager -} - -type systemdNotifyMsg string - -const ( - readyMsg systemdNotifyMsg = "READY=1" - stoppingMsg systemdNotifyMsg = "STOPPING=1" - reloadingMsg systemdNotifyMsg = "RELOADING=1" - watchdogMsg systemdNotifyMsg = "WATCHDOG=1" -) - -func statusMsg(msg string) systemdNotifyMsg { - return systemdNotifyMsg("STATUS=" + msg) -} - func pidMsg() systemdNotifyMsg { return systemdNotifyMsg("MAINPID=" + strconv.Itoa(os.Getpid())) } @@ -89,25 +43,13 @@ func (g *Manager) notify(msg systemdNotifyMsg) { } } -func (g *Manager) start(ctx context.Context) { - // Make contexts - g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx) - g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx) - g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx) - g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx) - - // Next add pprof labels to these contexts - g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) - g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) - g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) - g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) - +func (g *Manager) start() { // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager pprof.SetGoroutineLabels(g.managerCtx) - defer pprof.SetGoroutineLabels(ctx) + defer pprof.SetGoroutineLabels(g.ctx) + + g.isChild = len(os.Getenv(listenFDsEnv)) > 0 && os.Getppid() > 1 - // Set the running state & handle signals - g.setState(stateRunning) g.notify(statusMsg("Starting Gitea")) g.notify(pidMsg()) go g.handleSignals(g.managerCtx) @@ -116,11 +58,9 @@ func (g *Manager) start(ctx context.Context) { startupDone := make(chan struct{}) go func() { defer close(startupDone) - // Wait till we're done getting all of the listeners and then close - // the unused ones + // Wait till we're done getting all the listeners and then close the unused ones g.createServerWaitGroup.Wait() - // Ignore the error here there's not much we can do with it - // They're logged in the CloseProvidedListeners function + // Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function _ = CloseProvidedListeners() g.notify(readyMsg) }() @@ -131,7 +71,7 @@ func (g *Manager) start(ctx context.Context) { return case <-g.IsShutdown(): func() { - // When waitgroup counter goes negative it will panic - we don't care about this so we can just ignore it. + // When WaitGroup counter goes negative it will panic - we don't care about this so we can just ignore it. defer func() { _ = recover() }() @@ -253,29 +193,3 @@ func (g *Manager) DoGracefulRestart() { g.doShutdown() } } - -// DoImmediateHammer causes an immediate hammer -func (g *Manager) DoImmediateHammer() { - g.notify(statusMsg("Sending immediate hammer")) - g.doHammerTime(0 * time.Second) -} - -// DoGracefulShutdown causes a graceful shutdown -func (g *Manager) DoGracefulShutdown() { - g.lock.Lock() - if !g.forked { - g.lock.Unlock() - g.notify(stoppingMsg) - } else { - g.lock.Unlock() - g.notify(statusMsg("Shutting down after fork")) - } - g.doShutdown() -} - -// RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die. -// Any call to RegisterServer must be matched by a call to ServerDone -func (g *Manager) RegisterServer() { - KillParent() - g.runningServerWaitGroup.Add(1) -} diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go index f676f86d043..0248dcb24d2 100644 --- a/modules/graceful/manager_windows.go +++ b/modules/graceful/manager_windows.go @@ -7,11 +7,9 @@ package graceful import ( - "context" "os" "runtime/pprof" "strconv" - "sync" "time" "code.gitea.io/gitea/modules/log" @@ -30,62 +28,11 @@ const ( acceptHammerCode = svc.Accepted(hammerCode) ) -// Manager manages the graceful shutdown process -type Manager struct { - ctx context.Context - isChild bool - lock *sync.RWMutex - state state - shutdownCtx context.Context - hammerCtx context.Context - terminateCtx context.Context - managerCtx context.Context - shutdownCtxCancel context.CancelFunc - hammerCtxCancel context.CancelFunc - terminateCtxCancel context.CancelFunc - managerCtxCancel context.CancelFunc - runningServerWaitGroup sync.WaitGroup - createServerWaitGroup sync.WaitGroup - terminateWaitGroup sync.WaitGroup - shutdownRequested chan struct{} - - toRunAtShutdown []func() - toRunAtTerminate []func() -} - -func newGracefulManager(ctx context.Context) *Manager { - manager := &Manager{ - isChild: false, - lock: &sync.RWMutex{}, - ctx: ctx, - } - manager.createServerWaitGroup.Add(numberOfServersToCreate) - manager.start() - return manager -} - func (g *Manager) start() { - // Make contexts - g.terminateCtx, g.terminateCtxCancel = context.WithCancel(g.ctx) - g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(g.ctx) - g.hammerCtx, g.hammerCtxCancel = context.WithCancel(g.ctx) - g.managerCtx, g.managerCtxCancel = context.WithCancel(g.ctx) - - // Next add pprof labels to these contexts - g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) - g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) - g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) - g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) - // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager pprof.SetGoroutineLabels(g.managerCtx) defer pprof.SetGoroutineLabels(g.ctx) - // Make channels - g.shutdownRequested = make(chan struct{}) - - // Set the running state - g.setState(stateRunning) if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip { log.Trace("Skipping SVC check as SKIP_MINWINSVC is set") return @@ -199,30 +146,6 @@ hammerLoop: return false, 0 } -// DoImmediateHammer causes an immediate hammer -func (g *Manager) DoImmediateHammer() { - g.doHammerTime(0 * time.Second) -} - -// DoGracefulShutdown causes a graceful shutdown -func (g *Manager) DoGracefulShutdown() { - g.lock.Lock() - select { - case <-g.shutdownRequested: - g.lock.Unlock() - default: - close(g.shutdownRequested) - g.lock.Unlock() - g.doShutdown() - } -} - -// RegisterServer registers the running of a listening server. -// Any call to RegisterServer must be matched by a call to ServerDone -func (g *Manager) RegisterServer() { - g.runningServerWaitGroup.Add(1) -} - func (g *Manager) awaitServer(limit time.Duration) bool { c := make(chan struct{}) go func() { @@ -247,3 +170,11 @@ func (g *Manager) awaitServer(limit time.Duration) bool { } } } + +func (g *Manager) notify(msg systemdNotifyMsg) { + // Windows doesn't use systemd to notify +} + +func KillParent() { + // Windows doesn't need to "kill parent" because there is no graceful restart +} diff --git a/modules/graceful/net_unix.go b/modules/graceful/net_unix.go index f5af1e39378..4f8c036a696 100644 --- a/modules/graceful/net_unix.go +++ b/modules/graceful/net_unix.go @@ -150,12 +150,8 @@ func CloseProvidedListeners() error { return returnableError } -// DefaultGetListener obtains a listener for the local network address. The network must be -// a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It -// returns an provided net.Listener for the matching network and address, or -// creates a new one using net.Listen. This function can be replaced by changing the -// GetListener variable at the top of this file, for example to listen on an onion service using -// github.com/cretz/bine +// DefaultGetListener obtains a listener for the stream-oriented local network address: +// "tcp", "tcp4", "tcp6", "unix" or "unixpacket". func DefaultGetListener(network, address string) (net.Listener, error) { // Add a deferral to say that we've tried to grab a listener defer GetManager().InformCleanup() diff --git a/modules/graceful/net_windows.go b/modules/graceful/net_windows.go index 15d228d6b65..9667bd4d13e 100644 --- a/modules/graceful/net_windows.go +++ b/modules/graceful/net_windows.go @@ -10,9 +10,7 @@ package graceful import "net" // DefaultGetListener obtains a listener for the local network address. -// On windows this is basically just a shim around net.Listen. This function -// can be replaced by changing the GetListener variable at the top of this file, -// for example to listen on an onion service using github.com/cretz/bine +// On windows this is basically just a shim around net.Listen. func DefaultGetListener(network, address string) (net.Listener, error) { // Add a deferral to say that we've tried to grab a listener defer GetManager().InformCleanup() diff --git a/modules/graceful/server.go b/modules/graceful/server.go index bd917828bc8..2525a83e775 100644 --- a/modules/graceful/server.go +++ b/modules/graceful/server.go @@ -20,31 +20,11 @@ import ( "code.gitea.io/gitea/modules/setting" ) -var ( - // DefaultReadTimeOut default read timeout - DefaultReadTimeOut time.Duration - // DefaultWriteTimeOut default write timeout - DefaultWriteTimeOut time.Duration - // DefaultMaxHeaderBytes default max header bytes - DefaultMaxHeaderBytes int - // PerWriteWriteTimeout timeout for writes - PerWriteWriteTimeout = 30 * time.Second - // PerWriteWriteTimeoutKbTime is a timeout taking account of how much there is to be written - PerWriteWriteTimeoutKbTime = 10 * time.Second -) - -// GetListener returns a listener from a GetListener function, which must have the -// signature: `func FunctioName(network, address string) (net.Listener, error)`. -// This determines the implementation of net.Listener which the server will use.` -// It is implemented in this way so that downstreams may specify the type of listener -// they want to provide Gitea on by default, such as with a hidden service or a p2p network -// No need to worry about "breaking" if there would be a refactoring for the Listeners. No compatibility-guarantee for this mechanism +// GetListener returns a net listener +// This determines the implementation of net.Listener which the server will use, +// so that downstreams could provide their own Listener, such as with a hidden service or a p2p network var GetListener = DefaultGetListener -func init() { - DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB) -} - // ServeFunction represents a listen.Accept loop type ServeFunction = func(net.Listener) error diff --git a/modules/graceful/server_http.go b/modules/graceful/server_http.go index a1f8e0ff522..7c855ac64ec 100644 --- a/modules/graceful/server_http.go +++ b/modules/graceful/server_http.go @@ -13,11 +13,8 @@ import ( func newHTTPServer(network, address, name string, handler http.Handler) (*Server, ServeFunction) { server := NewServer(network, address, name) httpServer := http.Server{ - ReadTimeout: DefaultReadTimeOut, - WriteTimeout: DefaultWriteTimeOut, - MaxHeaderBytes: DefaultMaxHeaderBytes, - Handler: handler, - BaseContext: func(net.Listener) context.Context { return GetManager().HammerContext() }, + Handler: handler, + BaseContext: func(net.Listener) context.Context { return GetManager().HammerContext() }, } server.OnShutdown = func() { httpServer.SetKeepAlivesEnabled(false) diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 335395f2f67..ab8dcd0af45 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -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 diff --git a/modules/markup/html.go b/modules/markup/html.go index e53ccc6a794..774cbe1557e 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -66,7 +66,7 @@ var ( // well as the HTML5 spec: // http://spec.commonmark.org/0.28/#email-address // https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail) - emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|\\.(\\s|$))") + emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))") // blackfriday extensions create IDs like fn:user-content-footnote blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`) diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 21bfc8314a8..d1f4e9e8a35 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -264,6 +264,18 @@ func TestRender_email(t *testing.T) { "send email to info@gitea.co.uk.", `

send email to info@gitea.co.uk.

`) + test( + `j.doe@example.com, + j.doe@example.com. + j.doe@example.com; + j.doe@example.com? + j.doe@example.com!`, + `

j.doe@example.com,
+j.doe@example.com.
+j.doe@example.com;
+j.doe@example.com?
+j.doe@example.com!

`) + // Test that should *not* be turned into email links test( "\"info@gitea.com\"", diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index 9b6175649f1..c1e01441993 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -158,7 +158,7 @@ func (r *Writer) WriteRegularLink(l org.RegularLink) { case "image": if l.Description == nil { imageSrc := getMediaURL(link) - fmt.Fprintf(r, `%s`, imageSrc, link, link) + fmt.Fprintf(r, `%s`, imageSrc, link) } else { description := strings.TrimPrefix(org.String(l.Description...), "file:") imageSrc := getMediaURL([]byte(description)) @@ -167,18 +167,18 @@ func (r *Writer) WriteRegularLink(l org.RegularLink) { case "video": if l.Description == nil { imageSrc := getMediaURL(link) - fmt.Fprintf(r, ``, imageSrc, link, link) + fmt.Fprintf(r, ``, imageSrc, link) } else { description := strings.TrimPrefix(org.String(l.Description...), "file:") videoSrc := getMediaURL([]byte(description)) - fmt.Fprintf(r, ``, link, videoSrc, videoSrc) + fmt.Fprintf(r, ``, link, videoSrc, videoSrc) } default: description := string(link) if l.Description != nil { description = r.WriteNodesAsString(l.Description...) } - fmt.Fprintf(r, `%s`, link, description, description) + fmt.Fprintf(r, `%s`, link, description) } } diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index 8f454e99558..88ae14ebcf5 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -34,12 +34,12 @@ func TestRender_StandardLinks(t *testing.T) { assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } - googleRendered := "

https://google.com/

" - test("[[https://google.com/]]", googleRendered) + test("[[https://google.com/]]", + `

https://google.com/

`) lnk := util.URLJoin(AppSubURL, "WikiPage") test("[[WikiPage][WikiPage]]", - "

WikiPage

") + `

WikiPage

`) } func TestRender_Media(t *testing.T) { @@ -59,19 +59,23 @@ func TestRender_Media(t *testing.T) { result := util.URLJoin(AppSubURL, url) test("[[file:"+url+"]]", - "

\""+result+"\"

") + `

`+result+`

`) // With description. test("[[https://example.com][https://example.com/example.svg]]", `

https://example.com/example.svg

`) + test("[[https://example.com][pre https://example.com/example.svg post]]", + `

pre https://example.com/example.svg post

`) test("[[https://example.com][https://example.com/example.mp4]]", - `

`) + `

`) + test("[[https://example.com][pre https://example.com/example.mp4 post]]", + `

pre post

`) // Without description. test("[[https://example.com/example.svg]]", - `

https://example.com/example.svg

`) + `

https://example.com/example.svg

`) test("[[https://example.com/example.mp4]]", - `

`) + `

`) } func TestRender_Source(t *testing.T) { diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 48c08831f16..992e85b9898 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -18,9 +18,10 @@ import ( // Sanitizer is a protection wrapper of *bluemonday.Policy which does not allow // any modification to the underlying policies once it's been created. type Sanitizer struct { - defaultPolicy *bluemonday.Policy - rendererPolicies map[string]*bluemonday.Policy - init sync.Once + defaultPolicy *bluemonday.Policy + descriptionPolicy *bluemonday.Policy + rendererPolicies map[string]*bluemonday.Policy + init sync.Once } var ( @@ -41,6 +42,7 @@ func NewSanitizer() { func InitializeSanitizer() { sanitizer.rendererPolicies = map[string]*bluemonday.Policy{} sanitizer.defaultPolicy = createDefaultPolicy() + sanitizer.descriptionPolicy = createRepoDescriptionPolicy() for name, renderer := range renderers { sanitizerRules := renderer.SanitizerRules() @@ -161,6 +163,27 @@ func createDefaultPolicy() *bluemonday.Policy { return policy } +// createRepoDescriptionPolicy returns a minimal more strict policy that is used for +// repository descriptions. +func createRepoDescriptionPolicy() *bluemonday.Policy { + policy := bluemonday.NewPolicy() + + // Allow italics and bold. + policy.AllowElements("i", "b", "em", "strong") + + // Allow code. + policy.AllowElements("code") + + // Allow links + policy.AllowAttrs("href", "target", "rel").OnElements("a") + + // Allow classes for emojis + policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img", "span") + policy.AllowAttrs("aria-label").OnElements("span") + + return policy +} + func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) { for _, rule := range rules { if rule.AllowDataURIImages { @@ -176,6 +199,12 @@ func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitize } } +// SanitizeDescription sanitizes the HTML generated for a repository description. +func SanitizeDescription(s string) string { + NewSanitizer() + return sanitizer.descriptionPolicy.Sanitize(s) +} + // Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist. func Sanitize(s string) string { NewSanitizer() diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go index 0bc63ff0a7c..b7b8792bd73 100644 --- a/modules/markup/sanitizer_test.go +++ b/modules/markup/sanitizer_test.go @@ -73,6 +73,28 @@ func Test_Sanitizer(t *testing.T) { } } +func TestDescriptionSanitizer(t *testing.T) { + NewSanitizer() + + testCases := []string{ + `

Title

`, `Title`, + `image`, ``, + `THUMBS UP`, `THUMBS UP`, + `Hello World`, `Hello World`, + `
`, ``, + `https://example.com`, `https://example.com`, + `Important!`, `Important!`, + `
Click me! Nothing to see here.
`, `Click me! Nothing to see here.`, + ``, ``, + `I have a strong opinion about this.`, `I have a strong opinion about this.`, + `Provides alternative wg(8) tool`, `Provides alternative wg(8) tool`, + } + + for i := 0; i < len(testCases); i += 2 { + assert.Equal(t, testCases[i+1], SanitizeDescription(testCases[i])) + } +} + func TestSanitizeNonEscape(t *testing.T) { descStr := "<script>alert(document.domain)</script>" diff --git a/modules/repository/create.go b/modules/repository/create.go index 2dac35224e6..7c954a14121 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -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 } diff --git a/modules/setting/actions.go b/modules/setting/actions.go index 026bab4bfcf..9fd484c3b80 100644 --- a/modules/setting/actions.go +++ b/modules/setting/actions.go @@ -22,9 +22,11 @@ var ( ZombieTaskTimeout time.Duration `ini:"ZOMBIE_TASK_TIMEOUT"` EndlessTaskTimeout time.Duration `ini:"ENDLESS_TASK_TIMEOUT"` AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"` + SkipWorkflowStrings []string `ìni:"SKIP_WORKFLOW_STRINGS"` }{ - Enabled: true, - DefaultActionsURL: defaultActionsURLGitHub, + Enabled: true, + DefaultActionsURL: defaultActionsURLGitHub, + SkipWorkflowStrings: []string{"[skip ci]", "[ci skip]", "[no ci]", "[skip actions]", "[actions skip]"}, } ) diff --git a/modules/setting/attachment.go b/modules/setting/attachment.go index 491564c9dcc..007aae9d044 100644 --- a/modules/setting/attachment.go +++ b/modules/setting/attachment.go @@ -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, } diff --git a/modules/setting/database.go b/modules/setting/database.go index aa42f506bc5..761e767e8f5 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -109,7 +109,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) @@ -157,7 +157,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", diff --git a/modules/setting/database_test.go b/modules/setting/database_test.go index 85271c36cb3..1d5b416504b 100644 --- a/modules/setting/database_test.go +++ b/modules/setting/database_test.go @@ -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) } } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 8ed7df3f83f..b6483a2cdbe 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -186,7 +186,7 @@ var ( Enabled: true, TempPath: "data/tmp/uploads", AllowedTypes: "", - FileMaxSize: 3, + FileMaxSize: 50, MaxFiles: 5, }, diff --git a/modules/setting/ui.go b/modules/setting/ui.go index 231698bf605..31042d3ee0d 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -33,6 +33,7 @@ var UI = struct { CustomEmojisMap map[string]string `ini:"-"` SearchRepoDescription bool OnlyShowRelevantRepos bool + ExploreDefaultSort string `ini:"EXPLORE_PAGING_DEFAULT_SORT"` Notification struct { MinTimeout time.Duration diff --git a/modules/web/route.go b/modules/web/route.go index c24c8f4d67f..86b83dd7236 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -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...) diff --git a/options/license/AML-glslang b/options/license/AML-glslang new file mode 100644 index 00000000000..2a24aeac224 --- /dev/null +++ b/options/license/AML-glslang @@ -0,0 +1,41 @@ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/options/license/Adobe-Display-PostScript b/options/license/Adobe-Display-PostScript new file mode 100644 index 00000000000..6df57d3c80e --- /dev/null +++ b/options/license/Adobe-Display-PostScript @@ -0,0 +1,30 @@ +(c)Copyright 1988,1991 Adobe Systems Incorporated. +All rights reserved. + +Permission to use, copy, modify, distribute, and sublicense this software and its +documentation for any purpose and without fee is hereby granted, provided that +the above copyright notices appear in all copies and that both those copyright +notices and this permission notice appear in supporting documentation and that +the name of Adobe Systems Incorporated not be used in advertising or publicity +pertaining to distribution of the software without specific, written prior +permission. No trademark license to use the Adobe trademarks is hereby +granted. If the Adobe trademark "Display PostScript"(tm) is used to describe +this software, its functionality or for any other purpose, such use shall be +limited to a statement that this software works in conjunction with the Display +PostScript system. Proper trademark attribution to reflect Adobe's ownership +of the trademark shall be given whenever any such reference to the Display +PostScript system is made. + +ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY +PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. ADOBE +DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON- +INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE TO YOU +OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT +LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER +SUPPORT FOR THE SOFTWARE. + +Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems +Incorporated which may be registered in certain jurisdictions. diff --git a/options/license/DEC-3-Clause b/options/license/DEC-3-Clause new file mode 100644 index 00000000000..112edaa70d5 --- /dev/null +++ b/options/license/DEC-3-Clause @@ -0,0 +1,28 @@ +Copyright 1997 Digital Equipment Corporation. +All rights reserved. + +This software is furnished under license and may be used and copied only in +accordance with the following terms and conditions. Subject to these +conditions, you may download, copy, install, use, modify and distribute +this software in source and/or binary form. No title or ownership is +transferred hereby. + +1) Any source code used, modified or distributed must reproduce and retain + this copyright notice and list of conditions as they appear in the + source file. + +2) No right is granted to use any trade name, trademark, or logo of Digital + Equipment Corporation. Neither the "Digital Equipment Corporation" + name nor any trademark or logo of Digital Equipment Corporation may be + used to endorse or promote products derived from this software without + the prior written permission of Digital Equipment Corporation. + +3) This software is provided "AS-IS" and any express or implied warranties, + including but not limited to, any implied warranties of merchantability, + fitness for a particular purpose, or non-infringement are disclaimed. + In no event shall DIGITAL be liable for any damages whatsoever, and in + particular, DIGITAL shall not be liable for special, indirect, + consequential, or incidental damages or damages for lost profits, loss + of revenue or loss of use, whether such damages arise in contract, + negligence, tort, under statute, in equity, at law or otherwise, even + if advised of the possibility of such damage. diff --git a/options/license/DRL-1.1 b/options/license/DRL-1.1 new file mode 100644 index 00000000000..a6445601ff4 --- /dev/null +++ b/options/license/DRL-1.1 @@ -0,0 +1,17 @@ +Detection Rule License (DRL) 1.1 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this rule set and associated documentation files (the "Rules"), to deal in the Rules without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Rules, and to permit persons to whom the Rules are furnished to do so, subject to the following conditions: + +If you share the Rules (including in modified form), you must retain the following if it is supplied within the Rules: + +identification of the authors(s) ("author" field) of the Rule and any others designated to receive attribution, in any reasonable manner requested by the Rule author (including by pseudonym if designated). + +a URI or hyperlink to the Rule set or explicit Rule to the extent reasonably practicable + +indicate the Rules are licensed under this Detection Rule License, and include the text of, or the URI or hyperlink to, this Detection Rule License to the extent reasonably practicable + +If you use the Rules (including in modified form) on data, messages based on matches with the Rules must retain the following if it is supplied within the Rules: + +identification of the authors(s) ("author" field) of the Rule and any others designated to receive attribution, in any reasonable manner requested by the Rule author (including by pseudonym if designated). + +THE RULES ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE RULES OR THE USE OR OTHER DEALINGS IN THE RULES. diff --git a/options/license/GCR-docs b/options/license/GCR-docs new file mode 100644 index 00000000000..d5c1293c965 --- /dev/null +++ b/options/license/GCR-docs @@ -0,0 +1,30 @@ +This work may be reproduced and distributed in whole or in part, in +any medium, physical or electronic, so as long as this copyright +notice remains intact and unchanged on all copies. Commercial +redistribution is permitted and encouraged, but you may not +redistribute, in whole or in part, under terms more restrictive than +those under which you received it. If you redistribute a modified or +translated version of this work, you must also make the source code to +the modified or translated version available in electronic form +without charge. However, mere aggregation as part of a larger work +shall not count as a modification for this purpose. + +All code examples in this work are placed into the public domain, +and may be used, modified and redistributed without restriction. + +BECAUSE THIS WORK IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE WORK, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE WORK "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. SHOULD THE WORK PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY REPAIR OR CORRECTION. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE WORK AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +WORK, EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/options/license/HPND-sell-MIT-disclaimer-xserver b/options/license/HPND-sell-MIT-disclaimer-xserver new file mode 100644 index 00000000000..e7bea21d16e --- /dev/null +++ b/options/license/HPND-sell-MIT-disclaimer-xserver @@ -0,0 +1,12 @@ +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +this permission notice appear in supporting documentation. This permission +notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/options/license/Pixar b/options/license/Pixar new file mode 100644 index 00000000000..c7533090bbe --- /dev/null +++ b/options/license/Pixar @@ -0,0 +1,174 @@ + + Modified Apache 2.0 License + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor + and its affiliates, except as required to comply with Section 4(c) of + the License and to reproduce the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/options/license/hdparm b/options/license/hdparm new file mode 100644 index 00000000000..280a1c07974 --- /dev/null +++ b/options/license/hdparm @@ -0,0 +1,9 @@ +BSD-Style Open Source License: + +You may freely use, modify, and redistribute the hdparm program, +as either binary or source, or both. + +The only condition is that my name and copyright notice +remain in the source code as-is. + +Mark Lord (mlord@pobox.com) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c64c97859bc..626e6f0961a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2315,7 +2315,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 the documentation for pattern syntax. Examples: main, release/**" +settings.protect_branch_name_pattern_desc = "Protected branch name patterns. See the documentation 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 github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt." @@ -2886,6 +2886,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 @@ -3538,7 +3539,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 the quick start guide. +runs.no_workflows.documentation = For more information on the Gitea Action, see the documentation. 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. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index ebde7e5baeb..017444cbd09 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1330,9 +1330,9 @@ projects.column.new_title=Nom projects.column.new_submit=Créer une colonne projects.column.new=Nouvelle colonne projects.column.set_default=Définir par défaut -projects.column.set_default_desc=Missionne cette colonne d'accueillir les tickets et demande d'ajouts non catégorisés. +projects.column.set_default_desc=Les tickets et demandes d’ajout non-catégorisés seront placés dans cette colonne. projects.column.unset_default=Défaire par défaut -projects.column.unset_default_desc=Décharge cette colonne d'accueillir les tickets et demandes d'ajouts non catégorisées. Ceux-ci iront dans une colonne idoine. +projects.column.unset_default_desc=Les tickets et demandes d'ajouts non-catégorisés seront placés dans une colonne idoine. projects.column.delete=Supprimer la colonne projects.column.deletion_desc=La suppression d'une colonne de projet déplace tous les tickets liés à 'Non catégorisé'. Continuer ? projects.column.color=Couleur @@ -3214,22 +3214,22 @@ notices.delete_success=Les informations systèmes ont été supprimées. [action] create_repo=a créé le dépôt %s -rename_repo=a rebaptisé le dépôt de %[1]s vers %[3]s -commit_repo=a soumis sur %[3]s à %[4]s -create_issue=`ticket ouvert %[3]s#%[2]s` -close_issue=`ticket fermé %[3]s#%[2]s` +rename_repo=a rebaptisé le dépôt %[1]s en %[3]s +commit_repo=a soumis sur %[3]s dans %[4]s +create_issue=`a ouvert le ticket %[3]s#%[2]s` +close_issue=`a fermé le ticket %[3]s#%[2]s` reopen_issue=`a réouvert le ticket %[3]s#%[2]s` -create_pull_request=`a créé la demande d'ajout %[3]s#%[2]s` -close_pull_request=`a fermé la demande d'ajout %[3]s#%[2]s` -reopen_pull_request=`a réouvert la demande d'ajout %[3]s#%[2]s` +create_pull_request=`a créé la demande d’ajout %[3]s#%[2]s` +close_pull_request=`a fermé la demande d’ajout %[3]s#%[2]s` +reopen_pull_request=`a réouvert la demande d’ajout %[3]s#%[2]s` comment_issue=`a commenté le ticket %[3]s#%[2]s` -comment_pull=`a commenté la demande d'ajout %[3]s#%[2]s` -merge_pull_request=`a fusionné la demande d'ajout %[3]s#%[2]s` -auto_merge_pull_request=`a fusionné automatiquement la demande d’ajout %[3]s#%[2]s.` -transfer_repo=a transféré le dépôt %s à %s -push_tag=a poussé l'étiquette %[3]s vers %[4]s -delete_tag=étiquette supprimée %[2]s de %[3]s -delete_branch=branche %[2]s supprimée de %[3]s +comment_pull=`a commenté la demande d’ajout %[3]s#%[2]s` +merge_pull_request=`a fusionné la demande d’ajout %[3]s#%[2]s` +auto_merge_pull_request=`a fusionné automatiquement la demande d’ajout %[3]s#%[2]s` +transfer_repo=a transféré le dépôt %s vers %s +push_tag=a poussé l’étiquette %[3]s vers %[4]s +delete_tag=a supprimé l’étiquette %[2]s de %[3]s +delete_branch=a supprimée la branche %[2]s de %[3]s compare_branch=Comparer compare_commits=Comparer %d révisions compare_commits_general=Comparer les révisions @@ -3242,8 +3242,8 @@ publish_release=`a publié "%[4]s" à %[3] review_dismissed=`a révoqué l’évaluation de %[4]s sur %[3]s#%[2]s.` review_dismissed_reason=Raison : create_branch=a créé la branche %[3]s dans %[4]s. -starred_repo=est fan de %[2]s. -watched_repo=observe %[2]s. +starred_repo=aime %[2]s +watched_repo=observe %[2]s [tool] now=maintenant diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 3a560eba1d1..aad2d86b832 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -163,7 +163,7 @@ buttons.code.tooltip=Добавить код buttons.link.tooltip=Добавить ссылку buttons.list.unordered.tooltip=Добавить маркированный список buttons.list.ordered.tooltip=Добавить нумерованный список -buttons.list.task.tooltip=Добавить список задач +buttons.list.task.tooltip=Добавить список заданий buttons.mention.tooltip=Упомянуть пользователя или команду buttons.ref.tooltip=Сослаться на задачу или запрос на слияние buttons.switch_to_legacy.tooltip=Использовать старый редактор @@ -937,6 +937,7 @@ fork_from=Форк от already_forked=Вы уже форкнули %s fork_to_different_account=Ответвление для другой учётной записи fork_visibility_helper=Видимость форкнутого репозитория изменить нельзя. +fork_branch=Ветка для клонирования в форк all_branches=Все ветки use_template=Использовать этот шаблон clone_in_vsc=Клонировать в VS Code @@ -1316,7 +1317,7 @@ projects.card_type.desc=Предпросмотр карточек projects.card_type.images_and_text=Изображения и текст projects.card_type.text_only=Только текст -issues.desc=Организация отчетов об ошибках, задач и этапов. +issues.desc=Организация отчетов об ошибках, заданий и этапов. issues.filter_assignees=Фильтр назначений issues.filter_milestones=Фильтр этапов issues.filter_projects=Фильтровать проекты @@ -1801,7 +1802,13 @@ milestones.filter_sort.most_issues=Большинство задач milestones.filter_sort.least_issues=Меньшинство задач signing.will_sign=Этот коммит будет подписан ключом «%s». +signing.wont_sign.never=Коммиты никогда не подписываются. +signing.wont_sign.always=Коммиты всегда подписываются. +signing.wont_sign.pubkey=Этот коммит не будет подписан, поскольку к вашей учётной записи не привязано публичного ключа. +signing.wont_sign.twofa=Для подписания коммитов у вас должна быть включена двухфакторная аутентификация. signing.wont_sign.basesigned=Слияние не будет подписано, так как базовый коммит не подписан. +signing.wont_sign.commitssigned=Слияние не будет подписано, так как все связанные коммиты не подписаны. +signing.wont_sign.approved=Слияние не будет подписано, так как запрос на слияние не одобрен. signing.wont_sign.not_signed_in=Вы не вошли в систему. ext_wiki=Доступ к внешней вики @@ -1849,8 +1856,8 @@ activity.merged_prs_count_1=Принятый запрос на слияние activity.merged_prs_count_n=Принятых запросов на слияние activity.opened_prs_count_1=Новый запрос на слияние activity.opened_prs_count_n=Новых запросов на слияние -activity.title.user_1=%d пользователь -activity.title.user_n=%d пользователей +activity.title.user_1=%d пользователем +activity.title.user_n=%d пользователями activity.title.prs_1=%d запрос на слияние activity.title.prs_n=%d запросов на слияние activity.title.prs_merged_by=%s приняты %s @@ -1861,9 +1868,9 @@ activity.active_issues_count_1=%d Активная задача activity.active_issues_count_n=%d Активных задач activity.closed_issues_count_1=Закрытая задача activity.closed_issues_count_n=Закрытых задач -activity.title.issues_1=%d Задача -activity.title.issues_n=%d Задач -activity.title.issues_closed_from=%s закрыто %s +activity.title.issues_1=%d задача +activity.title.issues_n=%d задач +activity.title.issues_closed_from=%s закрыто от %s activity.title.issues_created_by=%s создано %s activity.closed_issue_label=Закрыто activity.new_issues_count_1=Новая задача @@ -1881,8 +1888,8 @@ activity.no_git_activity=В этот период не было новых ко activity.git_stats_exclude_merges=За исключением слияний, activity.git_stats_author_1=%d автор activity.git_stats_author_n=%d автора(ов) -activity.git_stats_pushed_1=отправлен -activity.git_stats_pushed_n=отправлено +activity.git_stats_pushed_1=отправил +activity.git_stats_pushed_n=отправили activity.git_stats_commit_1=%d коммит activity.git_stats_commit_n=%d коммитов activity.git_stats_push_to_branch=в %s и @@ -1890,8 +1897,8 @@ activity.git_stats_push_to_all_branches=во все ветки. activity.git_stats_on_default_branch=На %s, activity.git_stats_file_1=%d файл activity.git_stats_file_n=%d файлов -activity.git_stats_files_changed_1=изменилось -activity.git_stats_files_changed_n=изменено +activity.git_stats_files_changed_1=изменён, +activity.git_stats_files_changed_n=изменены, activity.git_stats_additions=и там было activity.git_stats_addition_1=%d добавление activity.git_stats_addition_n=%d добавлений @@ -1942,6 +1949,7 @@ settings.mirror_settings.last_update=Последнее обновление settings.mirror_settings.push_mirror.none=Push-зеркало не добавлено settings.mirror_settings.push_mirror.remote_url=URL удалённого хранилища settings.mirror_settings.push_mirror.add=Добавить Push-зеркало +settings.mirror_settings.push_mirror.edit_sync_time=Изменить интервал синхронизации зеркала settings.sync_mirror=Синхронизировать settings.site=Сайт @@ -2146,6 +2154,7 @@ settings.event_pull_request_sync_desc=Запрос на слияние синх settings.event_pull_request_review_request=Запрошена рецензия для запроса на слияние settings.event_pull_request_review_request_desc=Создан или удалён запрос на рецензию для запроса на слияние. settings.event_pull_request_approvals=Утверждения запросов на слияние +settings.event_pull_request_merge=Удовлетворение запросов на слияние settings.event_package=Пакеты settings.event_package_desc=Пакет создан или удален в репозитории. settings.branch_filter=Фильтр веток @@ -2208,6 +2217,7 @@ settings.protect_disable_push=Отключить отправку settings.protect_disable_push_desc=Отправка не будет разрешена в эту ветку. settings.protect_enable_push=Включить отправку settings.protect_enable_push_desc=Любому, у кого есть доступ на запись, будет разрешена отправка изменений в эту ветку (но не принудительная отправка). +settings.protect_enable_merge=Разрешить всем settings.protect_enable_merge_desc=Все, у кого есть доступ на запись, смогут удовлетворять запросы на слияние в эту ветку. settings.protect_whitelist_committers=Ограничение отправки по белому списку settings.protect_whitelist_committers_desc=Только пользователям или командам из белого списка будет разрешена отправка изменений в эту ветку (но не принудительная отправка). @@ -2432,6 +2442,7 @@ branch.already_exists=Ветка с именем «%s» уже существу branch.delete_head=Удалить branch.delete=Удалить ветку «%s» branch.delete_html=Удалить ветку +branch.delete_desc=Удаление ветки необратимо. Несмотря на то, что удаленная ветка может просуществовать некоторое время перед тем, как она будет окончательно удалена, это действие НЕВОЗМОЖНО отменить в большинстве случаев. Продолжить? branch.deletion_success=Ветка «%s» удалена. branch.deletion_failed=Не удалось удалить ветку «%s». branch.delete_branch_has_new_commits=Ветку «%s» нельзя удалить, поскольку после слияния были добавлены новые коммиты. @@ -2470,7 +2481,7 @@ tag.create_success=Тег «%s» создан. topic.manage_topics=Редактировать тематические метки topic.done=Сохранить -topic.count_prompt=Вы не можете выбрать более 25 тем +topic.count_prompt=Нельзя выбрать более 25 тем find_file.go_to_file=Перейти к файлу find_file.no_matching=Совпадающих файлов не найдено @@ -2509,6 +2520,7 @@ form.create_org_not_allowed=Этому пользователю не разре settings=Настройки settings.options=Организация settings.full_name=Полное имя +settings.email=Почта для связи settings.website=Сайт settings.location=Местоположение settings.permission=Разрешения @@ -2522,6 +2534,7 @@ settings.visibility.private_shortname=Приватный settings.update_settings=Обновить настройки settings.update_setting_success=Настройки организации обновлены. +settings.change_orgname_prompt=Обратите внимание: изменение названия организации также изменит URL вашей организации и освободит старое имя. settings.change_orgname_redirect_prompt=Старое имя будет перенаправлено до тех пор, пока оно не будет введено. settings.update_avatar_success=Аватар организации обновлён. settings.delete=Удалить организацию @@ -2625,17 +2638,18 @@ dashboard.operation_run=Запуск dashboard.clean_unbind_oauth=Очистить список незавершённых авторизаций OAuth dashboard.clean_unbind_oauth_success=Все незавершённые связи OAuth были удалены. dashboard.task.started=Началось задание: %[1]s -dashboard.task.process=Задача: %[1]s -dashboard.task.cancelled=Задача: %[1]s отменена: %[3]s -dashboard.task.error=Ошибка в Задаче: %[1]s: %[3]s -dashboard.task.finished=Задача: %[1]s, начатая %[2]s завершена -dashboard.task.unknown=Неизвестная задача: %[1]s -dashboard.cron.started=Стартовал Cron: %[1]s -dashboard.cron.process=Cron: %[1]s -dashboard.cron.error=Ошибка в задаче Cron: %s: %[3]s -dashboard.cron.finished=Крон: %[1]s завершено +dashboard.task.process=Задание: %[1]s +dashboard.task.cancelled=Задание %[1]s отменено: %[3]s +dashboard.task.error=Ошибка в задании: %[1]s: %[3]s +dashboard.task.finished=Задание %[1]s, начатое %[2]s, завершено +dashboard.task.unknown=Неизвестное задание: %[1]s +dashboard.cron.started=Планировщик запущен: %[1]s +dashboard.cron.process=Планировщик: %[1]s +dashboard.cron.cancelled=Планировщик: %[1]s отменено: %[3]s +dashboard.cron.error=Ошибка в запланированном задании: %s: %[3]s +dashboard.cron.finished=Планировщик: %[1]s завершено dashboard.delete_inactive_accounts=Удалить все неактивированные учётные записи -dashboard.delete_inactive_accounts.started=Удалить все запущенные задачи неактивированных аккаунтов. +dashboard.delete_inactive_accounts.started=Удаление всех неактивированных учётных записей началось. dashboard.delete_repo_archives=Удалить все архивы репозиториев (ZIP, TAR.GZ, и т.д..) dashboard.delete_repo_archives.started=Удаление всех архивов репозитория началось. dashboard.delete_missing_repos=Удалить все записи о репозиториях с отсутствующими файлами Git @@ -2689,8 +2703,8 @@ dashboard.delete_old_actions.started=Удалите все старые дейс dashboard.update_checker=Проверка обновлений dashboard.delete_old_system_notices=Удалить все старые системные уведомления из базы данных dashboard.gc_lfs=Выполнить сборку мусора метаобъектов LFS -dashboard.stop_zombie_tasks=Остановить задачи-зомби -dashboard.stop_endless_tasks=Остановить бесконечные задачи +dashboard.stop_zombie_tasks=Остановить задания-зомби +dashboard.stop_endless_tasks=Остановить бесконечные задания dashboard.cancel_abandoned_jobs=Отменить брошенные задания users.user_manage_panel=Панель управления пользователями @@ -2700,6 +2714,8 @@ users.full_name=Полное имя users.activated=Активирован users.admin=Администратор users.restricted=Ограничено +users.reserved=Зарезервировано +users.bot=Бот users.2fa=Двухфакторная авторизация users.repos=Репозитории users.created=Создано @@ -2746,6 +2762,7 @@ users.list_status_filter.is_prohibit_login=Запретить вход users.list_status_filter.not_prohibit_login=Разрешить вход users.list_status_filter.is_2fa_enabled=2FA включено users.list_status_filter.not_2fa_enabled=2FA отключено +users.details=О пользователе emails.email_manage_panel=Управление эл. почтой пользователя emails.primary=Первичный @@ -2782,6 +2799,7 @@ repos.lfs_size=Размер LFS packages.package_manage_panel=Управление пакетами packages.total_size=Общий размер: %s packages.unreferenced_size=Размер по ссылке: %s +packages.cleanup=Очистить устаревшие данные packages.owner=Владелец packages.creator=Автор packages.name=Наименование @@ -2928,6 +2946,7 @@ config.disable_router_log=Отключение журнала маршрутиз config.run_user=Запуск от имени пользователя config.run_mode=Режим выполнения config.git_version=Версия Git +config.app_data_path=Путь к данным приложения config.repo_root_path=Путь до папки репозиториев config.lfs_root_path=Корневой путь LFS config.log_file_root_path=Путь к журналу @@ -3053,7 +3072,7 @@ config.set_setting_failed=Задать параметр %s не удалось monitor.stats=Статистика -monitor.cron=Задачи cron +monitor.cron=Запланированные задания monitor.name=Название monitor.schedule=Расписание monitor.next=Следующий раз @@ -3100,7 +3119,7 @@ notices.delete_selected=Удалить выбранные notices.delete_all=Удалить все уведомления notices.type=Тип notices.type_1=Репозиторий -notices.type_2=Задача +notices.type_2=Задание notices.desc=Описание notices.op=Oп. notices.delete_success=Уведомления системы были удалены. @@ -3200,6 +3219,7 @@ desc=Управление пакетами репозитория. empty=Пока нет пакетов. empty.documentation=Дополнительную информацию о реестре пакетов можно найти в документации. empty.repo=Вы загрузили пакет, но он здесь не отображается? Перейдите в настройки пакета и свяжите его с этим репозиторием. +registry.documentation=Для получения дополнительной информации о реестре %s смотрите документацию. filter.type=Тип filter.type.all=Все filter.no_result=Фильтр не дал результатов. @@ -3289,6 +3309,8 @@ pub.install=Чтобы установить пакет с помощью Dart, pypi.requires=Требуется Python pypi.install=Чтобы установить пакет с помощью pip, выполните следующую команду: rpm.registry=Настроить реестр из командной строки: +rpm.distros.redhat=на дистрибутивах семейства RedHat +rpm.distros.suse=на дистрибутивах семейства SUSE rpm.install=Чтобы установить пакет, выполните следующую команду: rubygems.install=Чтобы установить пакет с помощью gem, выполните следующую команду: rubygems.install2=или добавьте его в Gemfile: @@ -3313,6 +3335,7 @@ settings.delete.success=Пакет удалён. settings.delete.error=Не удалось удалить пакет. owner.settings.cargo.title=Индекс реестра Cargo owner.settings.cargo.initialize=Инициализировать индекс +owner.settings.cargo.initialize.description=Для использования реестра Cargo необходим специальный Git-репозиторий с индексом. Эта опция (пере)создаст репозиторий и настроит его автоматически. owner.settings.cargo.initialize.error=Не удалось инициализировать индекс Cargo: %v owner.settings.cargo.initialize.success=Индекс Cargo успешно создан. owner.settings.cargo.rebuild=Перестроить индекс @@ -3381,7 +3404,8 @@ runners.description=Описание runners.labels=Метки runners.last_online=Был онлайн runners.runner_title=Раннер -runners.task_list=Недавние задачи на раннере +runners.task_list=Недавние задания на раннере +runners.task_list.no_tasks=Задания пока нет. runners.task_list.run=Запуск runners.task_list.status=Статус runners.task_list.repository=Репозиторий @@ -3395,7 +3419,7 @@ runners.delete_runner=Удалить этот раннер runners.delete_runner_success=Раннер успешно удалён runners.delete_runner_failed=Не удалось удалить раннер runners.delete_runner_header=Подтвердите удаление раннера -runners.delete_runner_notice=Если на этом раннере выполняется задача, она будет завершена и помечена как неудачная. Это может нарушить рабочий поток сборки. +runners.delete_runner_notice=Если на этом раннере выполняется задание, оно будет завершено и помечено как неудачное. Это может нарушить рабочий поток сборки. runners.none=Нет доступных раннеров runners.status.unspecified=Неизвестно runners.status.idle=Простаивает @@ -3406,10 +3430,19 @@ runners.reset_registration_token_success=Токен регистрации ра runs.all_workflows=Все рабочие потоки runs.commit=коммит +runs.pushed_by=отправлено runs.invalid_workflow_helper=Файл конфигурации рабочего потока некорректен. Пожалуйста, проверьте конфигурационный файл: %s runs.no_matching_runner_helper=Нет подходящего раннера: %s +runs.actor=Актор runs.status=Статус +runs.actors_no_select=Все акторы +runs.no_runs=Рабочий поток ещё не запускался. +workflow.disable=Выключить рабочий поток +workflow.disable_success=Рабочий поток «%s» успешно выключен. +workflow.enable=Включить рабочий поток +workflow.enable_success=Рабочий поток «%s» успешно включен. +workflow.disabled=Рабочий поток выключен. need_approval_desc=Требуется одобрение, чтобы запустить рабочие потоки для запроса на слияние. diff --git a/package-lock.json b/package-lock.json index fa9c8a73280..5a49efee23e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.1", "@github/relative-time-element": "4.3.0", - "@github/text-expander-element": "2.5.0", + "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.8.0", "@webcomponents/custom-elements": "1.6.0", @@ -25,12 +25,12 @@ "easymde": "2.18.0", "esbuild-loader": "4.0.2", "escape-goat": "4.0.0", - "fast-glob": "3.3.1", + "fast-glob": "3.3.2", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", "lightningcss-loader": "2.1.0", - "mermaid": "10.6.0", + "mermaid": "10.6.1", "mini-css-extract-plugin": "2.7.6", "minimatch": "9.0.3", "monaco-editor": "0.44.0", @@ -38,14 +38,14 @@ "pdfobject": "2.2.12", "pretty-ms": "8.0.0", "sortablejs": "1.15.0", - "swagger-ui-dist": "5.9.1", + "swagger-ui-dist": "5.10.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", - "vue": "3.3.7", + "vue": "3.3.8", "vue-bar-graph": "2.0.0", "vue-loader": "17.3.1", "vue3-calendar-heatmap": "2.0.5", @@ -55,11 +55,11 @@ }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.39.0", + "@playwright/test": "1.40.0", "@stoplight/spectral-cli": "6.11.0", - "@stylistic/eslint-plugin-js": "1.0.0", - "@vitejs/plugin-vue": "4.4.0", - "eslint": "8.53.0", + "@stylistic/eslint-plugin-js": "1.4.0", + "@vitejs/plugin-vue": "4.5.0", + "eslint": "8.54.0", "eslint-plugin-array-func": "4.0.0", "eslint-plugin-i": "2.29.0", "eslint-plugin-jquery": "1.5.1", @@ -80,7 +80,7 @@ "stylelint-declaration-block-no-ignored-properties": "2.7.0", "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.3", - "svgo": "3.0.2", + "svgo": "3.0.4", "updates": "15.0.4", "vite-string-plugin": "1.1.2", "vitest": "0.34.6" @@ -286,9 +286,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -987,9 +987,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", + "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1011,9 +1011,9 @@ "integrity": "sha512-+tFjX9//HRS1HnBa5cNgfEtE52arwiutYg1TOF+Trk40SPxst9Q8Rtc3BKD6aKsvfbtub68vfhipgchGjj9o7g==" }, "node_modules/@github/text-expander-element": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@github/text-expander-element/-/text-expander-element-2.5.0.tgz", - "integrity": "sha512-BjCxTshkUCgNXo/8iXUSK1sJ7kMJqhVsw6LAzIFtgaYrm4q2068WnPKjngfR+/QPhxN1nSvgd7CozblEIYjUZA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@github/text-expander-element/-/text-expander-element-2.6.1.tgz", + "integrity": "sha512-i6krPGXJRABfKXut0WArFd365Je4PT0MljtDoXUoCOEp+lGrmdosDMxmO0EfOYc97jBn+Hd2XO1mMsuI5+fwmQ==", "dependencies": { "@github/combobox-nav": "^2.0.2" } @@ -1349,12 +1349,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", - "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.0.tgz", + "integrity": "sha512-PdW+kn4eV99iP5gxWNSDQCbhMaDVej+RXL5xr6t04nbKLCBwYtA046t7ofoczHOm8u6c+45hpDKQVZqtqwkeQg==", "dev": true, "dependencies": { - "playwright": "1.39.0" + "playwright": "1.40.0" }, "bin": { "playwright": "cli.js" @@ -1882,17 +1882,15 @@ "dev": true }, "node_modules/@stylistic/eslint-plugin-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.0.0.tgz", - "integrity": "sha512-xxvjyYnUEgjBTnXKYMk6JbU0LHkf269d6y4IgW69bK/VwHrqLfdgE6mYvft42U7KVpp6Tbf6Z64tLRYD/rYd/A==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.4.0.tgz", + "integrity": "sha512-cANyn4ECWu8kxPmBM4K/Q4WocD3JbA0POmGbA2lJ4tynPE8jGyKpfP8SZj6BIidXV0pkyqvxEfaKppB4D16UsA==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "acorn": "^8.10.0", + "acorn": "^8.11.2", "escape-string-regexp": "^4.0.0", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", - "esutils": "^2.0.3", "graphemer": "^1.4.0" } }, @@ -2180,15 +2178,15 @@ "dev": true }, "node_modules/@vitejs/plugin-vue": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.4.0.tgz", - "integrity": "sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.5.0.tgz", + "integrity": "sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==", "dev": true, "engines": { "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.0.0", + "vite": "^4.0.0 || ^5.0.0", "vue": "^3.2.25" } }, @@ -2300,36 +2298,36 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.7.tgz", - "integrity": "sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.8.tgz", + "integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==", "dependencies": { "@babel/parser": "^7.23.0", - "@vue/shared": "3.3.7", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.7.tgz", - "integrity": "sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz", + "integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==", "dependencies": { - "@vue/compiler-core": "3.3.7", - "@vue/shared": "3.3.7" + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.7.tgz", - "integrity": "sha512-7pfldWy/J75U/ZyYIXRVqvLRw3vmfxDo2YLMwVtWVNew8Sm8d6wodM+OYFq4ll/UxfqVr0XKiVwti32PCrruAw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz", + "integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==", "dependencies": { "@babel/parser": "^7.23.0", - "@vue/compiler-core": "3.3.7", - "@vue/compiler-dom": "3.3.7", - "@vue/compiler-ssr": "3.3.7", - "@vue/reactivity-transform": "3.3.7", - "@vue/shared": "3.3.7", + "@vue/compiler-core": "3.3.8", + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-ssr": "3.3.8", + "@vue/reactivity-transform": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", "magic-string": "^0.30.5", "postcss": "^8.4.31", @@ -2348,30 +2346,30 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.7.tgz", - "integrity": "sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz", + "integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==", "dependencies": { - "@vue/compiler-dom": "3.3.7", - "@vue/shared": "3.3.7" + "@vue/compiler-dom": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/reactivity": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.7.tgz", - "integrity": "sha512-cZNVjWiw00708WqT0zRpyAgduG79dScKEPYJXq2xj/aMtk3SKvL3FBt2QKUlh6EHBJ1m8RhBY+ikBUzwc7/khg==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.8.tgz", + "integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==", "dependencies": { - "@vue/shared": "3.3.7" + "@vue/shared": "3.3.8" } }, "node_modules/@vue/reactivity-transform": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.7.tgz", - "integrity": "sha512-APhRmLVbgE1VPGtoLQoWBJEaQk4V8JUsqrQihImVqKT+8U6Qi3t5ATcg4Y9wGAPb3kIhetpufyZ1RhwbZCIdDA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz", + "integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==", "dependencies": { "@babel/parser": "^7.23.0", - "@vue/compiler-core": "3.3.7", - "@vue/shared": "3.3.7", + "@vue/compiler-core": "3.3.8", + "@vue/shared": "3.3.8", "estree-walker": "^2.0.2", "magic-string": "^0.30.5" } @@ -2388,40 +2386,40 @@ } }, "node_modules/@vue/runtime-core": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.7.tgz", - "integrity": "sha512-LHq9du3ubLZFdK/BP0Ysy3zhHqRfBn80Uc+T5Hz3maFJBGhci1MafccnL3rpd5/3wVfRHAe6c+PnlO2PAavPTQ==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.8.tgz", + "integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==", "dependencies": { - "@vue/reactivity": "3.3.7", - "@vue/shared": "3.3.7" + "@vue/reactivity": "3.3.8", + "@vue/shared": "3.3.8" } }, "node_modules/@vue/runtime-dom": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.7.tgz", - "integrity": "sha512-PFQU1oeJxikdDmrfoNQay5nD4tcPNYixUBruZzVX/l0eyZvFKElZUjW4KctCcs52nnpMGO6UDK+jF5oV4GT5Lw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz", + "integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==", "dependencies": { - "@vue/runtime-core": "3.3.7", - "@vue/shared": "3.3.7", + "@vue/runtime-core": "3.3.8", + "@vue/shared": "3.3.8", "csstype": "^3.1.2" } }, "node_modules/@vue/server-renderer": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.7.tgz", - "integrity": "sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.8.tgz", + "integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==", "dependencies": { - "@vue/compiler-ssr": "3.3.7", - "@vue/shared": "3.3.7" + "@vue/compiler-ssr": "3.3.8", + "@vue/shared": "3.3.8" }, "peerDependencies": { - "vue": "3.3.7" + "vue": "3.3.8" } }, "node_modules/@vue/shared": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.7.tgz", - "integrity": "sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg==" + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.8.tgz", + "integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==" }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", @@ -4741,15 +4739,15 @@ } }, "node_modules/eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", + "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", + "@eslint/js": "8.54.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -5298,9 +5296,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -7621,9 +7619,9 @@ } }, "node_modules/mermaid": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.6.0.tgz", - "integrity": "sha512-Hcti+Q2NiWnb2ZCijSX89Bn2i7TCUwosBdIn/d+u63Sz7y40XU6EKMctT4UX4qZuZGfKGZpfOeim2/KTrdR7aQ==", + "version": "10.6.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.6.1.tgz", + "integrity": "sha512-Hky0/RpOw/1il9X8AvzOEChfJtVvmXm+y7JML5C//ePYMy0/9jCEmW1E1g86x9oDfW9+iVEdTV/i+M6KWRNs4A==", "dependencies": { "@braintree/sanitize-url": "^6.0.1", "@types/d3-scale": "^4.0.3", @@ -8742,12 +8740,12 @@ "dev": true }, "node_modules/playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.0.tgz", + "integrity": "sha512-gyHAgQjiDf1m34Xpwzaqb76KgfzYrhK7iih+2IzcOCoZWr/8ZqmdBw+t0RU85ZmfJMgtgAiNtBQ/KS2325INXw==", "dev": true, "dependencies": { - "playwright-core": "1.39.0" + "playwright-core": "1.40.0" }, "bin": { "playwright": "cli.js" @@ -8760,9 +8758,9 @@ } }, "node_modules/playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz", + "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -10303,16 +10301,17 @@ "dev": true }, "node_modules/svgo": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.2.tgz", - "integrity": "sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.0.4.tgz", + "integrity": "sha512-T+Xul3JwuJ6VGXKo/p2ndqx1ibxNKnLTvRc1ZTWKCfyKS/GgNjRZcYsK84fxTsy/izr91g/Rwx6fGnVgaFSI5g==", "dev": true, "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.2.1", - "csso": "^5.0.5", + "css-what": "^6.1.0", + "csso": "5.0.5", "picocolors": "^1.0.0" }, "bin": { @@ -10336,9 +10335,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.9.1.tgz", - "integrity": "sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw==" + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.10.0.tgz", + "integrity": "sha512-PBTn5qDOQVtU29hrx74km86SnK3/mFtF3grI98y575y1aRpxiuStRTIvsfXFudPFkLofHU7H9a+fKrP+Oayc3g==" }, "node_modules/symbol-tree": { "version": "3.2.4", @@ -11512,15 +11511,15 @@ } }, "node_modules/vue": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.7.tgz", - "integrity": "sha512-YEMDia1ZTv1TeBbnu6VybatmSteGOS3A3YgfINOfraCbf85wdKHzscD6HSS/vB4GAtI7sa1XPX7HcQaJ1l24zA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.8.tgz", + "integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==", "dependencies": { - "@vue/compiler-dom": "3.3.7", - "@vue/compiler-sfc": "3.3.7", - "@vue/runtime-dom": "3.3.7", - "@vue/server-renderer": "3.3.7", - "@vue/shared": "3.3.7" + "@vue/compiler-dom": "3.3.8", + "@vue/compiler-sfc": "3.3.8", + "@vue/runtime-dom": "3.3.8", + "@vue/server-renderer": "3.3.8", + "@vue/shared": "3.3.8" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index f536894e26f..4aee1bb0494 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@claviska/jquery-minicolors": "2.3.6", "@github/markdown-toolbar-element": "2.2.1", "@github/relative-time-element": "4.3.0", - "@github/text-expander-element": "2.5.0", + "@github/text-expander-element": "2.6.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@primer/octicons": "19.8.0", "@webcomponents/custom-elements": "1.6.0", @@ -24,12 +24,12 @@ "easymde": "2.18.0", "esbuild-loader": "4.0.2", "escape-goat": "4.0.0", - "fast-glob": "3.3.1", + "fast-glob": "3.3.2", "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", "lightningcss-loader": "2.1.0", - "mermaid": "10.6.0", + "mermaid": "10.6.1", "mini-css-extract-plugin": "2.7.6", "minimatch": "9.0.3", "monaco-editor": "0.44.0", @@ -37,14 +37,14 @@ "pdfobject": "2.2.12", "pretty-ms": "8.0.0", "sortablejs": "1.15.0", - "swagger-ui-dist": "5.9.1", + "swagger-ui-dist": "5.10.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", - "vue": "3.3.7", + "vue": "3.3.8", "vue-bar-graph": "2.0.0", "vue-loader": "17.3.1", "vue3-calendar-heatmap": "2.0.5", @@ -54,11 +54,11 @@ }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.39.0", + "@playwright/test": "1.40.0", "@stoplight/spectral-cli": "6.11.0", - "@stylistic/eslint-plugin-js": "1.0.0", - "@vitejs/plugin-vue": "4.4.0", - "eslint": "8.53.0", + "@stylistic/eslint-plugin-js": "1.4.0", + "@vitejs/plugin-vue": "4.5.0", + "eslint": "8.54.0", "eslint-plugin-array-func": "4.0.0", "eslint-plugin-i": "2.29.0", "eslint-plugin-jquery": "1.5.1", @@ -79,7 +79,7 @@ "stylelint-declaration-block-no-ignored-properties": "2.7.0", "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.3", - "svgo": "3.0.2", + "svgo": "3.0.4", "updates": "15.0.4", "vite-string-plugin": "1.1.2", "vitest": "0.34.6" diff --git a/poetry.lock b/poetry.lock index 15745cf5407..822343d9e28 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "click" @@ -27,12 +27,12 @@ files = [ [[package]] name = "cssbeautifier" -version = "1.14.9" +version = "1.14.11" description = "CSS unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "cssbeautifier-1.14.9.tar.gz", hash = "sha256:2da432472f68170eb854aff97b16a24721f5090ee36af2e31199590a89e7f71f"}, + {file = "cssbeautifier-1.14.11.tar.gz", hash = "sha256:40544c2b62bbcb64caa5e7f37a02df95654e5ce1bcacadac4ca1f3dc89c31513"}, ] [package.dependencies] @@ -100,12 +100,12 @@ files = [ [[package]] name = "jsbeautifier" -version = "1.14.9" +version = "1.14.11" description = "JavaScript unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "jsbeautifier-1.14.9.tar.gz", hash = "sha256:c738ebc36b47bd94e4ca6dd17a9004c3cc74edad582ca1d60e0e5d5945a63cb9"}, + {file = "jsbeautifier-1.14.11.tar.gz", hash = "sha256:6b632581ea60dd1c133cd25a48ad187b4b91f526623c4b0fb5443ef805250505"}, ] [package.dependencies] @@ -149,6 +149,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -156,8 +157,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -174,6 +182,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -181,6 +190,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -327,13 +337,13 @@ telegram = ["requests"] [[package]] name = "yamllint" -version = "1.32.0" +version = "1.33.0" description = "A linter for YAML files." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "yamllint-1.32.0-py3-none-any.whl", hash = "sha256:d97a66e48da820829d96077d76b8dfbe6c6140f106e558dae87e81ac4e6b30b7"}, - {file = "yamllint-1.32.0.tar.gz", hash = "sha256:d01dde008c65de5b235188ab3110bebc59d18e5c65fc8a58267cd211cd9df34a"}, + {file = "yamllint-1.33.0-py3-none-any.whl", hash = "sha256:28a19f5d68d28d8fec538a1db21bb2d84c7dc2e2ea36266da8d4d1c5a683814d"}, + {file = "yamllint-1.33.0.tar.gz", hash = "sha256:2dceab9ef2d99518a2fcf4ffc964d44250ac4459be1ba3ca315118e4a1a81f7d"}, ] [package.dependencies] @@ -346,4 +356,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "a2e2a44801f0da2cc5cc1d1a3f707853f51a4346fef725f98c67a074dcb223d6" +content-hash = "5bf456fd73e69c13f16d080503f6dbaf60294f15d74343c16ef5a57b5b345db1" diff --git a/public/assets/img/svg/fontawesome-save.svg b/public/assets/img/svg/fontawesome-save.svg index ece457e5591..e7b3ea9d5b3 100644 --- a/public/assets/img/svg/fontawesome-save.svg +++ b/public/assets/img/svg/fontawesome-save.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/fontawesome-windows.svg b/public/assets/img/svg/fontawesome-windows.svg index ec8633761c0..b1c16040c97 100644 --- a/public/assets/img/svg/fontawesome-windows.svg +++ b/public/assets/img/svg/fontawesome-windows.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-alpine.svg b/public/assets/img/svg/gitea-alpine.svg index 1c878013ac1..79be41cb251 100644 --- a/public/assets/img/svg/gitea-alpine.svg +++ b/public/assets/img/svg/gitea-alpine.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-azuread.svg b/public/assets/img/svg/gitea-azuread.svg index 36845b7ee36..1aee2aa40d7 100644 --- a/public/assets/img/svg/gitea-azuread.svg +++ b/public/assets/img/svg/gitea-azuread.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-azureadv2.svg b/public/assets/img/svg/gitea-azureadv2.svg index a5fa73a76cf..3d4af13e51a 100644 --- a/public/assets/img/svg/gitea-azureadv2.svg +++ b/public/assets/img/svg/gitea-azureadv2.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-bitbucket.svg b/public/assets/img/svg/gitea-bitbucket.svg index 7794df3d29b..1443f378d57 100644 --- a/public/assets/img/svg/gitea-bitbucket.svg +++ b/public/assets/img/svg/gitea-bitbucket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-cargo.svg b/public/assets/img/svg/gitea-cargo.svg index 0d82ee3c4a2..1b720b1dacb 100644 --- a/public/assets/img/svg/gitea-cargo.svg +++ b/public/assets/img/svg/gitea-cargo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-codebase.svg b/public/assets/img/svg/gitea-codebase.svg index 7871527b2c2..5f3085f58c7 100644 --- a/public/assets/img/svg/gitea-codebase.svg +++ b/public/assets/img/svg/gitea-codebase.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-composer.svg b/public/assets/img/svg/gitea-composer.svg index 2ffe9e2af02..aa311993c3a 100644 --- a/public/assets/img/svg/gitea-composer.svg +++ b/public/assets/img/svg/gitea-composer.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-conda.svg b/public/assets/img/svg/gitea-conda.svg index 90080440cf0..15b6458ca42 100644 --- a/public/assets/img/svg/gitea-conda.svg +++ b/public/assets/img/svg/gitea-conda.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-cran.svg b/public/assets/img/svg/gitea-cran.svg index de85ccab529..b3b3e312bcb 100644 --- a/public/assets/img/svg/gitea-cran.svg +++ b/public/assets/img/svg/gitea-cran.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-debian.svg b/public/assets/img/svg/gitea-debian.svg index 96f8f468e50..2471f35776c 100644 --- a/public/assets/img/svg/gitea-debian.svg +++ b/public/assets/img/svg/gitea-debian.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-discord.svg b/public/assets/img/svg/gitea-discord.svg index 5397e30ca5f..1bb3a647c8d 100644 --- a/public/assets/img/svg/gitea-discord.svg +++ b/public/assets/img/svg/gitea-discord.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-empty-checkbox.svg b/public/assets/img/svg/gitea-empty-checkbox.svg index dc4083e255d..40d75366f47 100644 --- a/public/assets/img/svg/gitea-empty-checkbox.svg +++ b/public/assets/img/svg/gitea-empty-checkbox.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-exclamation.svg b/public/assets/img/svg/gitea-exclamation.svg index cf5a7b76f52..b899f8d7f6a 100644 --- a/public/assets/img/svg/gitea-exclamation.svg +++ b/public/assets/img/svg/gitea-exclamation.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-facebook.svg b/public/assets/img/svg/gitea-facebook.svg index 497c5652995..4154e1fc46b 100644 --- a/public/assets/img/svg/gitea-facebook.svg +++ b/public/assets/img/svg/gitea-facebook.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-git.svg b/public/assets/img/svg/gitea-git.svg index ba4318c8b1b..33a98deb164 100644 --- a/public/assets/img/svg/gitea-git.svg +++ b/public/assets/img/svg/gitea-git.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-gitbucket.svg b/public/assets/img/svg/gitea-gitbucket.svg index edea1e5fe69..4c0ef46d92e 100644 --- a/public/assets/img/svg/gitea-gitbucket.svg +++ b/public/assets/img/svg/gitea-gitbucket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-gitea.svg b/public/assets/img/svg/gitea-gitea.svg index f9015a31bdd..6574fbce5ec 100644 --- a/public/assets/img/svg/gitea-gitea.svg +++ b/public/assets/img/svg/gitea-gitea.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-go.svg b/public/assets/img/svg/gitea-go.svg index a432bdbf21b..bc7969d47d9 100644 --- a/public/assets/img/svg/gitea-go.svg +++ b/public/assets/img/svg/gitea-go.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-google.svg b/public/assets/img/svg/gitea-google.svg index 95fa7a37611..9629ca03b6a 100644 --- a/public/assets/img/svg/gitea-google.svg +++ b/public/assets/img/svg/gitea-google.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-join.svg b/public/assets/img/svg/gitea-join.svg index 574c8b272f4..c1d2f275e8d 100644 --- a/public/assets/img/svg/gitea-join.svg +++ b/public/assets/img/svg/gitea-join.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-lock-cog.svg b/public/assets/img/svg/gitea-lock-cog.svg index c7be1f09c0d..7775b35f6d3 100644 --- a/public/assets/img/svg/gitea-lock-cog.svg +++ b/public/assets/img/svg/gitea-lock-cog.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-lock.svg b/public/assets/img/svg/gitea-lock.svg index 3f1df87b790..87069eae4ba 100644 --- a/public/assets/img/svg/gitea-lock.svg +++ b/public/assets/img/svg/gitea-lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-matrix.svg b/public/assets/img/svg/gitea-matrix.svg index 33e2708d647..16023b0caee 100644 --- a/public/assets/img/svg/gitea-matrix.svg +++ b/public/assets/img/svg/gitea-matrix.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-maven.svg b/public/assets/img/svg/gitea-maven.svg index b739ae486bf..8f889918812 100644 --- a/public/assets/img/svg/gitea-maven.svg +++ b/public/assets/img/svg/gitea-maven.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-microsoftonline.svg b/public/assets/img/svg/gitea-microsoftonline.svg index 84bea0305da..ce4f1a5c8f2 100644 --- a/public/assets/img/svg/gitea-microsoftonline.svg +++ b/public/assets/img/svg/gitea-microsoftonline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-nextcloud.svg b/public/assets/img/svg/gitea-nextcloud.svg index c28b620ccd7..42c53017ac1 100644 --- a/public/assets/img/svg/gitea-nextcloud.svg +++ b/public/assets/img/svg/gitea-nextcloud.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-npm.svg b/public/assets/img/svg/gitea-npm.svg index 68f3f6c9455..7ef74e72bd1 100644 --- a/public/assets/img/svg/gitea-npm.svg +++ b/public/assets/img/svg/gitea-npm.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-onedev.svg b/public/assets/img/svg/gitea-onedev.svg index 75a79ab3bae..956aa2c9a95 100644 --- a/public/assets/img/svg/gitea-onedev.svg +++ b/public/assets/img/svg/gitea-onedev.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-openid.svg b/public/assets/img/svg/gitea-openid.svg index f8e313f5159..a7d22376057 100644 --- a/public/assets/img/svg/gitea-openid.svg +++ b/public/assets/img/svg/gitea-openid.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-pub.svg b/public/assets/img/svg/gitea-pub.svg index fd6bfe90b9d..bfb32125068 100644 --- a/public/assets/img/svg/gitea-pub.svg +++ b/public/assets/img/svg/gitea-pub.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-python.svg b/public/assets/img/svg/gitea-python.svg index 87585b2b694..a10bad9e073 100644 --- a/public/assets/img/svg/gitea-python.svg +++ b/public/assets/img/svg/gitea-python.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-rubygems.svg b/public/assets/img/svg/gitea-rubygems.svg index db89cf9e05b..4741b37a7d9 100644 --- a/public/assets/img/svg/gitea-rubygems.svg +++ b/public/assets/img/svg/gitea-rubygems.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-split.svg b/public/assets/img/svg/gitea-split.svg index e2c6f7db724..56b2ff193da 100644 --- a/public/assets/img/svg/gitea-split.svg +++ b/public/assets/img/svg/gitea-split.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-swift.svg b/public/assets/img/svg/gitea-swift.svg index 0e26bcd4529..6e93083b536 100644 --- a/public/assets/img/svg/gitea-swift.svg +++ b/public/assets/img/svg/gitea-swift.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-unlock.svg b/public/assets/img/svg/gitea-unlock.svg index b6339348507..595dec0e68e 100644 --- a/public/assets/img/svg/gitea-unlock.svg +++ b/public/assets/img/svg/gitea-unlock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-whitespace.svg b/public/assets/img/svg/gitea-whitespace.svg index 35e19439f04..d046d16e0b1 100644 --- a/public/assets/img/svg/gitea-whitespace.svg +++ b/public/assets/img/svg/gitea-whitespace.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-yandex.svg b/public/assets/img/svg/gitea-yandex.svg index 54ded7eb872..58f26e12694 100644 --- a/public/assets/img/svg/gitea-yandex.svg +++ b/public/assets/img/svg/gitea-yandex.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/material-invert-colors.svg b/public/assets/img/svg/material-invert-colors.svg index 576ec72a785..feddf73ca4d 100644 --- a/public/assets/img/svg/material-invert-colors.svg +++ b/public/assets/img/svg/material-invert-colors.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/material-palette.svg b/public/assets/img/svg/material-palette.svg index f719dadc5d0..f98cef7bdcf 100644 --- a/public/assets/img/svg/material-palette.svg +++ b/public/assets/img/svg/material-palette.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-accessibility-inset.svg b/public/assets/img/svg/octicon-accessibility-inset.svg index 2ab660c5004..6d5fcd20bd4 100644 --- a/public/assets/img/svg/octicon-accessibility-inset.svg +++ b/public/assets/img/svg/octicon-accessibility-inset.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-accessibility.svg b/public/assets/img/svg/octicon-accessibility.svg index 6e26a53f3b8..e12784f3407 100644 --- a/public/assets/img/svg/octicon-accessibility.svg +++ b/public/assets/img/svg/octicon-accessibility.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-alert-fill.svg b/public/assets/img/svg/octicon-alert-fill.svg index 6173f0648c0..a2135affc17 100644 --- a/public/assets/img/svg/octicon-alert-fill.svg +++ b/public/assets/img/svg/octicon-alert-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-alert.svg b/public/assets/img/svg/octicon-alert.svg index 0d81ca2f4b7..1d97fbeb54f 100644 --- a/public/assets/img/svg/octicon-alert.svg +++ b/public/assets/img/svg/octicon-alert.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-archive.svg b/public/assets/img/svg/octicon-archive.svg index 6816984e69c..48ad67ec630 100644 --- a/public/assets/img/svg/octicon-archive.svg +++ b/public/assets/img/svg/octicon-archive.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-arrow-right.svg b/public/assets/img/svg/octicon-arrow-right.svg index 993df7ecf28..b421c393237 100644 --- a/public/assets/img/svg/octicon-arrow-right.svg +++ b/public/assets/img/svg/octicon-arrow-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-arrow-up-right.svg b/public/assets/img/svg/octicon-arrow-up-right.svg index 4d760cec658..6ba2bf63236 100644 --- a/public/assets/img/svg/octicon-arrow-up-right.svg +++ b/public/assets/img/svg/octicon-arrow-up-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-arrow-up.svg b/public/assets/img/svg/octicon-arrow-up.svg index fdd8fa6a2f3..7b6647f1af2 100644 --- a/public/assets/img/svg/octicon-arrow-up.svg +++ b/public/assets/img/svg/octicon-arrow-up.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-bell-fill.svg b/public/assets/img/svg/octicon-bell-fill.svg index 96cdfb2842f..a385b9e5fda 100644 --- a/public/assets/img/svg/octicon-bell-fill.svg +++ b/public/assets/img/svg/octicon-bell-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-bell-slash.svg b/public/assets/img/svg/octicon-bell-slash.svg index e1989c6b39f..07668ca654c 100644 --- a/public/assets/img/svg/octicon-bell-slash.svg +++ b/public/assets/img/svg/octicon-bell-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-bell.svg b/public/assets/img/svg/octicon-bell.svg index c2f18ab371c..aed30a47652 100644 --- a/public/assets/img/svg/octicon-bell.svg +++ b/public/assets/img/svg/octicon-bell.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-blocked.svg b/public/assets/img/svg/octicon-blocked.svg index be9ec7e6e9b..fb38943a639 100644 --- a/public/assets/img/svg/octicon-blocked.svg +++ b/public/assets/img/svg/octicon-blocked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-bold.svg b/public/assets/img/svg/octicon-bold.svg index 396bf74ccfa..ea2545975e6 100644 --- a/public/assets/img/svg/octicon-bold.svg +++ b/public/assets/img/svg/octicon-bold.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-bookmark-slash.svg b/public/assets/img/svg/octicon-bookmark-slash.svg index c3ebabe79d7..79c07ef2a21 100644 --- a/public/assets/img/svg/octicon-bookmark-slash.svg +++ b/public/assets/img/svg/octicon-bookmark-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-briefcase.svg b/public/assets/img/svg/octicon-briefcase.svg index 7d3559638c4..c4a89cb3ac6 100644 --- a/public/assets/img/svg/octicon-briefcase.svg +++ b/public/assets/img/svg/octicon-briefcase.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-broadcast.svg b/public/assets/img/svg/octicon-broadcast.svg index a89f1250b7b..6d999c43e86 100644 --- a/public/assets/img/svg/octicon-broadcast.svg +++ b/public/assets/img/svg/octicon-broadcast.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-bug.svg b/public/assets/img/svg/octicon-bug.svg index f398ef82b83..8245e6cd536 100644 --- a/public/assets/img/svg/octicon-bug.svg +++ b/public/assets/img/svg/octicon-bug.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-cache.svg b/public/assets/img/svg/octicon-cache.svg index 1630aa2224b..c7bb5cfe3ff 100644 --- a/public/assets/img/svg/octicon-cache.svg +++ b/public/assets/img/svg/octicon-cache.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-calendar.svg b/public/assets/img/svg/octicon-calendar.svg index 3d43f26bc93..55fd2f49da1 100644 --- a/public/assets/img/svg/octicon-calendar.svg +++ b/public/assets/img/svg/octicon-calendar.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-check-circle-fill.svg b/public/assets/img/svg/octicon-check-circle-fill.svg index f3a9f6a15da..689d38023cc 100644 --- a/public/assets/img/svg/octicon-check-circle-fill.svg +++ b/public/assets/img/svg/octicon-check-circle-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-check-circle.svg b/public/assets/img/svg/octicon-check-circle.svg index 89ce3a750cb..28d99f19b11 100644 --- a/public/assets/img/svg/octicon-check-circle.svg +++ b/public/assets/img/svg/octicon-check-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-check.svg b/public/assets/img/svg/octicon-check.svg index e38a8f4103c..10dffe55de0 100644 --- a/public/assets/img/svg/octicon-check.svg +++ b/public/assets/img/svg/octicon-check.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-checkbox.svg b/public/assets/img/svg/octicon-checkbox.svg index 88ff9cf487f..eebffde2e08 100644 --- a/public/assets/img/svg/octicon-checkbox.svg +++ b/public/assets/img/svg/octicon-checkbox.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-checklist.svg b/public/assets/img/svg/octicon-checklist.svg index 7d4cd8566d4..33ab2c32298 100644 --- a/public/assets/img/svg/octicon-checklist.svg +++ b/public/assets/img/svg/octicon-checklist.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-chevron-right.svg b/public/assets/img/svg/octicon-chevron-right.svg index e9d04c151a4..d5a610bd354 100644 --- a/public/assets/img/svg/octicon-chevron-right.svg +++ b/public/assets/img/svg/octicon-chevron-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-chevron-up.svg b/public/assets/img/svg/octicon-chevron-up.svg index 958bd3ab980..952dd484afa 100644 --- a/public/assets/img/svg/octicon-chevron-up.svg +++ b/public/assets/img/svg/octicon-chevron-up.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-circle-slash.svg b/public/assets/img/svg/octicon-circle-slash.svg index 817158aa077..1c0a2e03587 100644 --- a/public/assets/img/svg/octicon-circle-slash.svg +++ b/public/assets/img/svg/octicon-circle-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-circle.svg b/public/assets/img/svg/octicon-circle.svg index 6dc288a16ea..c2fa88b929e 100644 --- a/public/assets/img/svg/octicon-circle.svg +++ b/public/assets/img/svg/octicon-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-clock-fill.svg b/public/assets/img/svg/octicon-clock-fill.svg index 43e87de195f..423e5fd29d7 100644 --- a/public/assets/img/svg/octicon-clock-fill.svg +++ b/public/assets/img/svg/octicon-clock-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-clock.svg b/public/assets/img/svg/octicon-clock.svg index f1140b8efdc..ae277ee82f0 100644 --- a/public/assets/img/svg/octicon-clock.svg +++ b/public/assets/img/svg/octicon-clock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-cloud-offline.svg b/public/assets/img/svg/octicon-cloud-offline.svg index f0963b818d2..5193a54d281 100644 --- a/public/assets/img/svg/octicon-cloud-offline.svg +++ b/public/assets/img/svg/octicon-cloud-offline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-code-of-conduct.svg b/public/assets/img/svg/octicon-code-of-conduct.svg index 2477aa7279a..2bf627843ca 100644 --- a/public/assets/img/svg/octicon-code-of-conduct.svg +++ b/public/assets/img/svg/octicon-code-of-conduct.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-code-review.svg b/public/assets/img/svg/octicon-code-review.svg index ce9e16cb421..595e48eb49a 100644 --- a/public/assets/img/svg/octicon-code-review.svg +++ b/public/assets/img/svg/octicon-code-review.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-codescan-checkmark.svg b/public/assets/img/svg/octicon-codescan-checkmark.svg index 9e150c49c32..773ad56dbea 100644 --- a/public/assets/img/svg/octicon-codescan-checkmark.svg +++ b/public/assets/img/svg/octicon-codescan-checkmark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-codescan.svg b/public/assets/img/svg/octicon-codescan.svg index 85cbc27392c..ac4b6b4a186 100644 --- a/public/assets/img/svg/octicon-codescan.svg +++ b/public/assets/img/svg/octicon-codescan.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-codespaces.svg b/public/assets/img/svg/octicon-codespaces.svg index 701ceefd5fb..30a3890b568 100644 --- a/public/assets/img/svg/octicon-codespaces.svg +++ b/public/assets/img/svg/octicon-codespaces.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-columns.svg b/public/assets/img/svg/octicon-columns.svg index a6344001a16..a88b8071c2b 100644 --- a/public/assets/img/svg/octicon-columns.svg +++ b/public/assets/img/svg/octicon-columns.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-command-palette.svg b/public/assets/img/svg/octicon-command-palette.svg index e41255b40be..6c8528180ec 100644 --- a/public/assets/img/svg/octicon-command-palette.svg +++ b/public/assets/img/svg/octicon-command-palette.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-comment-discussion.svg b/public/assets/img/svg/octicon-comment-discussion.svg index 6be15c7bcfb..7abc3f135f7 100644 --- a/public/assets/img/svg/octicon-comment-discussion.svg +++ b/public/assets/img/svg/octicon-comment-discussion.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-copilot-warning.svg b/public/assets/img/svg/octicon-copilot-warning.svg index ce956452046..e690dfb507d 100644 --- a/public/assets/img/svg/octicon-copilot-warning.svg +++ b/public/assets/img/svg/octicon-copilot-warning.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-copilot.svg b/public/assets/img/svg/octicon-copilot.svg index b2a143c5c63..83872243cf6 100644 --- a/public/assets/img/svg/octicon-copilot.svg +++ b/public/assets/img/svg/octicon-copilot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-cpu.svg b/public/assets/img/svg/octicon-cpu.svg index 41b6e7cfaec..753b9b5799e 100644 --- a/public/assets/img/svg/octicon-cpu.svg +++ b/public/assets/img/svg/octicon-cpu.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-credit-card.svg b/public/assets/img/svg/octicon-credit-card.svg index 1ba32dee728..94c8f1581e7 100644 --- a/public/assets/img/svg/octicon-credit-card.svg +++ b/public/assets/img/svg/octicon-credit-card.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-cross-reference.svg b/public/assets/img/svg/octicon-cross-reference.svg index cca24cd13f6..edd42fa473c 100644 --- a/public/assets/img/svg/octicon-cross-reference.svg +++ b/public/assets/img/svg/octicon-cross-reference.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-dash.svg b/public/assets/img/svg/octicon-dash.svg index f3665638a67..39ebd0d264e 100644 --- a/public/assets/img/svg/octicon-dash.svg +++ b/public/assets/img/svg/octicon-dash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-dependabot.svg b/public/assets/img/svg/octicon-dependabot.svg index cfda70fee28..250d10cc1b8 100644 --- a/public/assets/img/svg/octicon-dependabot.svg +++ b/public/assets/img/svg/octicon-dependabot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-desktop-download.svg b/public/assets/img/svg/octicon-desktop-download.svg index 643829c9e94..4b1446a18ec 100644 --- a/public/assets/img/svg/octicon-desktop-download.svg +++ b/public/assets/img/svg/octicon-desktop-download.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-device-camera-video.svg b/public/assets/img/svg/octicon-device-camera-video.svg index ebbed57707f..4e7e1e7c4d6 100644 --- a/public/assets/img/svg/octicon-device-camera-video.svg +++ b/public/assets/img/svg/octicon-device-camera-video.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-device-camera.svg b/public/assets/img/svg/octicon-device-camera.svg index 7ad8d402ac8..bf4de4a59cb 100644 --- a/public/assets/img/svg/octicon-device-camera.svg +++ b/public/assets/img/svg/octicon-device-camera.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-device-desktop.svg b/public/assets/img/svg/octicon-device-desktop.svg index 2bb49e47784..0911947ee13 100644 --- a/public/assets/img/svg/octicon-device-desktop.svg +++ b/public/assets/img/svg/octicon-device-desktop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-device-mobile.svg b/public/assets/img/svg/octicon-device-mobile.svg index 2f0ca597529..cf247e215c1 100644 --- a/public/assets/img/svg/octicon-device-mobile.svg +++ b/public/assets/img/svg/octicon-device-mobile.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-devices.svg b/public/assets/img/svg/octicon-devices.svg index c351640dea7..178ea74a6f8 100644 --- a/public/assets/img/svg/octicon-devices.svg +++ b/public/assets/img/svg/octicon-devices.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-diff-added.svg b/public/assets/img/svg/octicon-diff-added.svg index 9ac76132be8..276d162129d 100644 --- a/public/assets/img/svg/octicon-diff-added.svg +++ b/public/assets/img/svg/octicon-diff-added.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-diff-modified.svg b/public/assets/img/svg/octicon-diff-modified.svg index 68969b68654..1c0d7296aa1 100644 --- a/public/assets/img/svg/octicon-diff-modified.svg +++ b/public/assets/img/svg/octicon-diff-modified.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-diff-removed.svg b/public/assets/img/svg/octicon-diff-removed.svg index 86bcb54838b..d366a107559 100644 --- a/public/assets/img/svg/octicon-diff-removed.svg +++ b/public/assets/img/svg/octicon-diff-removed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-diff-renamed.svg b/public/assets/img/svg/octicon-diff-renamed.svg index 96bec22a26b..f7b9eb2bdb1 100644 --- a/public/assets/img/svg/octicon-diff-renamed.svg +++ b/public/assets/img/svg/octicon-diff-renamed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-diff.svg b/public/assets/img/svg/octicon-diff.svg index 30bc1e95df7..4714b0fdd43 100644 --- a/public/assets/img/svg/octicon-diff.svg +++ b/public/assets/img/svg/octicon-diff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-discussion-duplicate.svg b/public/assets/img/svg/octicon-discussion-duplicate.svg index 8c705dbd982..ed93bf3b0fa 100644 --- a/public/assets/img/svg/octicon-discussion-duplicate.svg +++ b/public/assets/img/svg/octicon-discussion-duplicate.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-discussion-outdated.svg b/public/assets/img/svg/octicon-discussion-outdated.svg index 960920d6965..388dac3de15 100644 --- a/public/assets/img/svg/octicon-discussion-outdated.svg +++ b/public/assets/img/svg/octicon-discussion-outdated.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-dot-fill.svg b/public/assets/img/svg/octicon-dot-fill.svg index d9c61d9db55..17db30b0e00 100644 --- a/public/assets/img/svg/octicon-dot-fill.svg +++ b/public/assets/img/svg/octicon-dot-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-dot.svg b/public/assets/img/svg/octicon-dot.svg index 62dcc189fe9..fe03e3ded73 100644 --- a/public/assets/img/svg/octicon-dot.svg +++ b/public/assets/img/svg/octicon-dot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-duplicate.svg b/public/assets/img/svg/octicon-duplicate.svg index 323c8f3df62..289ac5905f1 100644 --- a/public/assets/img/svg/octicon-duplicate.svg +++ b/public/assets/img/svg/octicon-duplicate.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-ellipsis.svg b/public/assets/img/svg/octicon-ellipsis.svg index 8f4da5d8054..152e6eb3249 100644 --- a/public/assets/img/svg/octicon-ellipsis.svg +++ b/public/assets/img/svg/octicon-ellipsis.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-eye-closed.svg b/public/assets/img/svg/octicon-eye-closed.svg index 2cfdf664065..364777d1688 100644 --- a/public/assets/img/svg/octicon-eye-closed.svg +++ b/public/assets/img/svg/octicon-eye-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-eye.svg b/public/assets/img/svg/octicon-eye.svg index 5f63e08dea5..101194041ea 100644 --- a/public/assets/img/svg/octicon-eye.svg +++ b/public/assets/img/svg/octicon-eye.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-discussion.svg b/public/assets/img/svg/octicon-feed-discussion.svg index 7ffa53cdca7..e8ccfff386a 100644 --- a/public/assets/img/svg/octicon-feed-discussion.svg +++ b/public/assets/img/svg/octicon-feed-discussion.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-forked.svg b/public/assets/img/svg/octicon-feed-forked.svg index c64a5a1a22a..65b0eb1b133 100644 --- a/public/assets/img/svg/octicon-feed-forked.svg +++ b/public/assets/img/svg/octicon-feed-forked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-heart.svg b/public/assets/img/svg/octicon-feed-heart.svg index 3179473eeb1..f2d620dd47f 100644 --- a/public/assets/img/svg/octicon-feed-heart.svg +++ b/public/assets/img/svg/octicon-feed-heart.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-issue-closed.svg b/public/assets/img/svg/octicon-feed-issue-closed.svg index f31776e661f..ee1d4ff8a7b 100644 --- a/public/assets/img/svg/octicon-feed-issue-closed.svg +++ b/public/assets/img/svg/octicon-feed-issue-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-issue-draft.svg b/public/assets/img/svg/octicon-feed-issue-draft.svg index b50504935eb..b1f8aca8718 100644 --- a/public/assets/img/svg/octicon-feed-issue-draft.svg +++ b/public/assets/img/svg/octicon-feed-issue-draft.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-issue-open.svg b/public/assets/img/svg/octicon-feed-issue-open.svg index 6a6697a31ab..6d8989895b0 100644 --- a/public/assets/img/svg/octicon-feed-issue-open.svg +++ b/public/assets/img/svg/octicon-feed-issue-open.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-issue-reopen.svg b/public/assets/img/svg/octicon-feed-issue-reopen.svg index 6cac76da581..1ee31ec368f 100644 --- a/public/assets/img/svg/octicon-feed-issue-reopen.svg +++ b/public/assets/img/svg/octicon-feed-issue-reopen.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-merged.svg b/public/assets/img/svg/octicon-feed-merged.svg index 4679ea202b7..47f487167da 100644 --- a/public/assets/img/svg/octicon-feed-merged.svg +++ b/public/assets/img/svg/octicon-feed-merged.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-person.svg b/public/assets/img/svg/octicon-feed-person.svg index 7c9bbf4c37b..0854866216d 100644 --- a/public/assets/img/svg/octicon-feed-person.svg +++ b/public/assets/img/svg/octicon-feed-person.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-plus.svg b/public/assets/img/svg/octicon-feed-plus.svg index 8606bdef203..6d0286e22ea 100644 --- a/public/assets/img/svg/octicon-feed-plus.svg +++ b/public/assets/img/svg/octicon-feed-plus.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-public.svg b/public/assets/img/svg/octicon-feed-public.svg index 7c6c5b2529a..926f7fe26a6 100644 --- a/public/assets/img/svg/octicon-feed-public.svg +++ b/public/assets/img/svg/octicon-feed-public.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-pull-request-closed.svg b/public/assets/img/svg/octicon-feed-pull-request-closed.svg index 10f89d48552..b594acd9cc2 100644 --- a/public/assets/img/svg/octicon-feed-pull-request-closed.svg +++ b/public/assets/img/svg/octicon-feed-pull-request-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-pull-request-draft.svg b/public/assets/img/svg/octicon-feed-pull-request-draft.svg index 78596ff7888..1ae02e68efc 100644 --- a/public/assets/img/svg/octicon-feed-pull-request-draft.svg +++ b/public/assets/img/svg/octicon-feed-pull-request-draft.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-pull-request-open.svg b/public/assets/img/svg/octicon-feed-pull-request-open.svg index 82dd8e0aea5..d1349c2d6c6 100644 --- a/public/assets/img/svg/octicon-feed-pull-request-open.svg +++ b/public/assets/img/svg/octicon-feed-pull-request-open.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-repo.svg b/public/assets/img/svg/octicon-feed-repo.svg index 69a1989476c..fe099c52d21 100644 --- a/public/assets/img/svg/octicon-feed-repo.svg +++ b/public/assets/img/svg/octicon-feed-repo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-rocket.svg b/public/assets/img/svg/octicon-feed-rocket.svg index 843560a9de6..e74dfdddcce 100644 --- a/public/assets/img/svg/octicon-feed-rocket.svg +++ b/public/assets/img/svg/octicon-feed-rocket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-star.svg b/public/assets/img/svg/octicon-feed-star.svg index 1688aab55db..70f6859fbad 100644 --- a/public/assets/img/svg/octicon-feed-star.svg +++ b/public/assets/img/svg/octicon-feed-star.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-tag.svg b/public/assets/img/svg/octicon-feed-tag.svg index c598c4d4c4b..90a796f38b5 100644 --- a/public/assets/img/svg/octicon-feed-tag.svg +++ b/public/assets/img/svg/octicon-feed-tag.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-feed-trophy.svg b/public/assets/img/svg/octicon-feed-trophy.svg index 6dc8d5a56ba..ed39c769c03 100644 --- a/public/assets/img/svg/octicon-feed-trophy.svg +++ b/public/assets/img/svg/octicon-feed-trophy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-badge.svg b/public/assets/img/svg/octicon-file-badge.svg index 944b5227575..2f1ad34fd08 100644 --- a/public/assets/img/svg/octicon-file-badge.svg +++ b/public/assets/img/svg/octicon-file-badge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-binary.svg b/public/assets/img/svg/octicon-file-binary.svg index 45f4e86fb6e..f6dd459eed5 100644 --- a/public/assets/img/svg/octicon-file-binary.svg +++ b/public/assets/img/svg/octicon-file-binary.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-diff.svg b/public/assets/img/svg/octicon-file-diff.svg index 6fdf5c5735e..a58df3e53db 100644 --- a/public/assets/img/svg/octicon-file-diff.svg +++ b/public/assets/img/svg/octicon-file-diff.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-directory-fill.svg b/public/assets/img/svg/octicon-file-directory-fill.svg index f16ba39e0e8..800e6ba9528 100644 --- a/public/assets/img/svg/octicon-file-directory-fill.svg +++ b/public/assets/img/svg/octicon-file-directory-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-directory-open-fill.svg b/public/assets/img/svg/octicon-file-directory-open-fill.svg index ca7a4adf6b4..0d1bac328a7 100644 --- a/public/assets/img/svg/octicon-file-directory-open-fill.svg +++ b/public/assets/img/svg/octicon-file-directory-open-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-directory-symlink.svg b/public/assets/img/svg/octicon-file-directory-symlink.svg index ddc2e3fd670..8a6142b2299 100644 --- a/public/assets/img/svg/octicon-file-directory-symlink.svg +++ b/public/assets/img/svg/octicon-file-directory-symlink.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-moved.svg b/public/assets/img/svg/octicon-file-moved.svg index 86670ef6ce9..03735b0e987 100644 --- a/public/assets/img/svg/octicon-file-moved.svg +++ b/public/assets/img/svg/octicon-file-moved.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-submodule.svg b/public/assets/img/svg/octicon-file-submodule.svg index ba947cc988d..8eab90f8c6e 100644 --- a/public/assets/img/svg/octicon-file-submodule.svg +++ b/public/assets/img/svg/octicon-file-submodule.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-symlink-file.svg b/public/assets/img/svg/octicon-file-symlink-file.svg index bc712b5ba1a..5b1d9aad235 100644 --- a/public/assets/img/svg/octicon-file-symlink-file.svg +++ b/public/assets/img/svg/octicon-file-symlink-file.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-file-zip.svg b/public/assets/img/svg/octicon-file-zip.svg index 2f025030544..69e05f00b8f 100644 --- a/public/assets/img/svg/octicon-file-zip.svg +++ b/public/assets/img/svg/octicon-file-zip.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-filter-remove.svg b/public/assets/img/svg/octicon-filter-remove.svg index e4394f4453e..dd968226b17 100644 --- a/public/assets/img/svg/octicon-filter-remove.svg +++ b/public/assets/img/svg/octicon-filter-remove.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-filter.svg b/public/assets/img/svg/octicon-filter.svg index f93cc0159b4..63cd16e647c 100644 --- a/public/assets/img/svg/octicon-filter.svg +++ b/public/assets/img/svg/octicon-filter.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-fiscal-host.svg b/public/assets/img/svg/octicon-fiscal-host.svg index 67f683604bf..877850ad243 100644 --- a/public/assets/img/svg/octicon-fiscal-host.svg +++ b/public/assets/img/svg/octicon-fiscal-host.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-flame.svg b/public/assets/img/svg/octicon-flame.svg index 6fa5050d6b8..022f7edd237 100644 --- a/public/assets/img/svg/octicon-flame.svg +++ b/public/assets/img/svg/octicon-flame.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-fold-down.svg b/public/assets/img/svg/octicon-fold-down.svg index 22e6ad1d442..957a95fceac 100644 --- a/public/assets/img/svg/octicon-fold-down.svg +++ b/public/assets/img/svg/octicon-fold-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-fold-up.svg b/public/assets/img/svg/octicon-fold-up.svg index 2b7d4bdf8dc..6429cf4573b 100644 --- a/public/assets/img/svg/octicon-fold-up.svg +++ b/public/assets/img/svg/octicon-fold-up.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-fold.svg b/public/assets/img/svg/octicon-fold.svg index 7fd4e9d56de..5657e930c1b 100644 --- a/public/assets/img/svg/octicon-fold.svg +++ b/public/assets/img/svg/octicon-fold.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-gear.svg b/public/assets/img/svg/octicon-gear.svg index 6f5e36af10e..2da0cff438b 100644 --- a/public/assets/img/svg/octicon-gear.svg +++ b/public/assets/img/svg/octicon-gear.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-gift.svg b/public/assets/img/svg/octicon-gift.svg index 866461c4532..dd2b6dda98c 100644 --- a/public/assets/img/svg/octicon-gift.svg +++ b/public/assets/img/svg/octicon-gift.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-branch.svg b/public/assets/img/svg/octicon-git-branch.svg index fdd05567b4b..f97a3c96901 100644 --- a/public/assets/img/svg/octicon-git-branch.svg +++ b/public/assets/img/svg/octicon-git-branch.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-commit.svg b/public/assets/img/svg/octicon-git-commit.svg index 6e5b5fc07f4..6c2ac50ac3d 100644 --- a/public/assets/img/svg/octicon-git-commit.svg +++ b/public/assets/img/svg/octicon-git-commit.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-compare.svg b/public/assets/img/svg/octicon-git-compare.svg index 3c61c102570..6bf455942d1 100644 --- a/public/assets/img/svg/octicon-git-compare.svg +++ b/public/assets/img/svg/octicon-git-compare.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-merge-queue.svg b/public/assets/img/svg/octicon-git-merge-queue.svg index 4890a68d8cf..bfe39b34e6b 100644 --- a/public/assets/img/svg/octicon-git-merge-queue.svg +++ b/public/assets/img/svg/octicon-git-merge-queue.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-merge.svg b/public/assets/img/svg/octicon-git-merge.svg index 6aa3d4ce4e8..f6081f984ba 100644 --- a/public/assets/img/svg/octicon-git-merge.svg +++ b/public/assets/img/svg/octicon-git-merge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-pull-request-closed.svg b/public/assets/img/svg/octicon-git-pull-request-closed.svg index 47b18f87e92..61a903c9180 100644 --- a/public/assets/img/svg/octicon-git-pull-request-closed.svg +++ b/public/assets/img/svg/octicon-git-pull-request-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-pull-request-draft.svg b/public/assets/img/svg/octicon-git-pull-request-draft.svg index 43ebe4569c8..74d9af987a4 100644 --- a/public/assets/img/svg/octicon-git-pull-request-draft.svg +++ b/public/assets/img/svg/octicon-git-pull-request-draft.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-git-pull-request.svg b/public/assets/img/svg/octicon-git-pull-request.svg index e9965677d26..2277666fe04 100644 --- a/public/assets/img/svg/octicon-git-pull-request.svg +++ b/public/assets/img/svg/octicon-git-pull-request.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-globe.svg b/public/assets/img/svg/octicon-globe.svg index a90f1d16ad3..b4f21b5b035 100644 --- a/public/assets/img/svg/octicon-globe.svg +++ b/public/assets/img/svg/octicon-globe.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-goal.svg b/public/assets/img/svg/octicon-goal.svg index 46f224661f8..012a4f0c308 100644 --- a/public/assets/img/svg/octicon-goal.svg +++ b/public/assets/img/svg/octicon-goal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-grabber.svg b/public/assets/img/svg/octicon-grabber.svg index 33cc2478197..9239188d424 100644 --- a/public/assets/img/svg/octicon-grabber.svg +++ b/public/assets/img/svg/octicon-grabber.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-graph.svg b/public/assets/img/svg/octicon-graph.svg index fa108db0a0a..075c6afa7b1 100644 --- a/public/assets/img/svg/octicon-graph.svg +++ b/public/assets/img/svg/octicon-graph.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-heading.svg b/public/assets/img/svg/octicon-heading.svg index 9caceb6157a..597e7949a50 100644 --- a/public/assets/img/svg/octicon-heading.svg +++ b/public/assets/img/svg/octicon-heading.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-heart.svg b/public/assets/img/svg/octicon-heart.svg index 9f178cfdedb..effbee4f768 100644 --- a/public/assets/img/svg/octicon-heart.svg +++ b/public/assets/img/svg/octicon-heart.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-history.svg b/public/assets/img/svg/octicon-history.svg index 72526f113ce..006d908d113 100644 --- a/public/assets/img/svg/octicon-history.svg +++ b/public/assets/img/svg/octicon-history.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-horizontal-rule.svg b/public/assets/img/svg/octicon-horizontal-rule.svg index 1cdc4a8ccf1..978874be5a4 100644 --- a/public/assets/img/svg/octicon-horizontal-rule.svg +++ b/public/assets/img/svg/octicon-horizontal-rule.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-hubot.svg b/public/assets/img/svg/octicon-hubot.svg index 6ba0c672e69..46418adc08c 100644 --- a/public/assets/img/svg/octicon-hubot.svg +++ b/public/assets/img/svg/octicon-hubot.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-id-badge.svg b/public/assets/img/svg/octicon-id-badge.svg index 927d780883b..ed3acea8bf6 100644 --- a/public/assets/img/svg/octicon-id-badge.svg +++ b/public/assets/img/svg/octicon-id-badge.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-image.svg b/public/assets/img/svg/octicon-image.svg index 47b70c1663a..a3ce77a876a 100644 --- a/public/assets/img/svg/octicon-image.svg +++ b/public/assets/img/svg/octicon-image.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-infinity.svg b/public/assets/img/svg/octicon-infinity.svg index 7e52e4ad6e7..aa94289fed7 100644 --- a/public/assets/img/svg/octicon-infinity.svg +++ b/public/assets/img/svg/octicon-infinity.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-info.svg b/public/assets/img/svg/octicon-info.svg index ab4b4c59708..de6616b0b7b 100644 --- a/public/assets/img/svg/octicon-info.svg +++ b/public/assets/img/svg/octicon-info.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-issue-closed.svg b/public/assets/img/svg/octicon-issue-closed.svg index 51be962aac9..af0422c4463 100644 --- a/public/assets/img/svg/octicon-issue-closed.svg +++ b/public/assets/img/svg/octicon-issue-closed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-issue-draft.svg b/public/assets/img/svg/octicon-issue-draft.svg index 8ec85824796..5b3f598a957 100644 --- a/public/assets/img/svg/octicon-issue-draft.svg +++ b/public/assets/img/svg/octicon-issue-draft.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-issue-opened.svg b/public/assets/img/svg/octicon-issue-opened.svg index 9f60583d99e..fb0752dcf3c 100644 --- a/public/assets/img/svg/octicon-issue-opened.svg +++ b/public/assets/img/svg/octicon-issue-opened.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-issue-reopened.svg b/public/assets/img/svg/octicon-issue-reopened.svg index 48eebc2e0e2..faac17d1199 100644 --- a/public/assets/img/svg/octicon-issue-reopened.svg +++ b/public/assets/img/svg/octicon-issue-reopened.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-issue-tracked-by.svg b/public/assets/img/svg/octicon-issue-tracked-by.svg index 2fd4c95f3ed..3cabd7851d2 100644 --- a/public/assets/img/svg/octicon-issue-tracked-by.svg +++ b/public/assets/img/svg/octicon-issue-tracked-by.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-issue-tracks.svg b/public/assets/img/svg/octicon-issue-tracks.svg index 503ed5cdafc..7eb86e51519 100644 --- a/public/assets/img/svg/octicon-issue-tracks.svg +++ b/public/assets/img/svg/octicon-issue-tracks.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-italic.svg b/public/assets/img/svg/octicon-italic.svg index 11237486474..2f71fcc9331 100644 --- a/public/assets/img/svg/octicon-italic.svg +++ b/public/assets/img/svg/octicon-italic.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-iterations.svg b/public/assets/img/svg/octicon-iterations.svg index a8a6a2555ee..33e98c1de36 100644 --- a/public/assets/img/svg/octicon-iterations.svg +++ b/public/assets/img/svg/octicon-iterations.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-kebab-horizontal.svg b/public/assets/img/svg/octicon-kebab-horizontal.svg index faa581329fa..d744abf8d81 100644 --- a/public/assets/img/svg/octicon-kebab-horizontal.svg +++ b/public/assets/img/svg/octicon-kebab-horizontal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-key-asterisk.svg b/public/assets/img/svg/octicon-key-asterisk.svg index f04c044590e..8b57e47885e 100644 --- a/public/assets/img/svg/octicon-key-asterisk.svg +++ b/public/assets/img/svg/octicon-key-asterisk.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-key.svg b/public/assets/img/svg/octicon-key.svg index 10c0b1aa8ac..4b9b2990890 100644 --- a/public/assets/img/svg/octicon-key.svg +++ b/public/assets/img/svg/octicon-key.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-law.svg b/public/assets/img/svg/octicon-law.svg index 8df6eec3bbd..9d6cfb15af1 100644 --- a/public/assets/img/svg/octicon-law.svg +++ b/public/assets/img/svg/octicon-law.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-light-bulb.svg b/public/assets/img/svg/octicon-light-bulb.svg index f3c58d47a5a..1833a06a889 100644 --- a/public/assets/img/svg/octicon-light-bulb.svg +++ b/public/assets/img/svg/octicon-light-bulb.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-link-external.svg b/public/assets/img/svg/octicon-link-external.svg index 4479d3aac0a..2adda55d2c4 100644 --- a/public/assets/img/svg/octicon-link-external.svg +++ b/public/assets/img/svg/octicon-link-external.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-list-ordered.svg b/public/assets/img/svg/octicon-list-ordered.svg index dabed4edce4..93c3f4f453f 100644 --- a/public/assets/img/svg/octicon-list-ordered.svg +++ b/public/assets/img/svg/octicon-list-ordered.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-list-unordered.svg b/public/assets/img/svg/octicon-list-unordered.svg index 32640eca971..1976bd89db9 100644 --- a/public/assets/img/svg/octicon-list-unordered.svg +++ b/public/assets/img/svg/octicon-list-unordered.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-location.svg b/public/assets/img/svg/octicon-location.svg index 81c3ed60d4b..d4fc960d2ca 100644 --- a/public/assets/img/svg/octicon-location.svg +++ b/public/assets/img/svg/octicon-location.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-lock.svg b/public/assets/img/svg/octicon-lock.svg index 3e3dae06caf..b737b56f771 100644 --- a/public/assets/img/svg/octicon-lock.svg +++ b/public/assets/img/svg/octicon-lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-log.svg b/public/assets/img/svg/octicon-log.svg index 21c263e7926..3bc4b5e799f 100644 --- a/public/assets/img/svg/octicon-log.svg +++ b/public/assets/img/svg/octicon-log.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-logo-gist.svg b/public/assets/img/svg/octicon-logo-gist.svg index 861764a6634..14092875268 100644 --- a/public/assets/img/svg/octicon-logo-gist.svg +++ b/public/assets/img/svg/octicon-logo-gist.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-logo-github.svg b/public/assets/img/svg/octicon-logo-github.svg index 546a7cd2529..e17a7ef3fc1 100644 --- a/public/assets/img/svg/octicon-logo-github.svg +++ b/public/assets/img/svg/octicon-logo-github.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-mail.svg b/public/assets/img/svg/octicon-mail.svg index 6a6a036410d..03bb8e5b406 100644 --- a/public/assets/img/svg/octicon-mail.svg +++ b/public/assets/img/svg/octicon-mail.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-mark-github.svg b/public/assets/img/svg/octicon-mark-github.svg index 0e5bf3b4d67..3b4322658aa 100644 --- a/public/assets/img/svg/octicon-mark-github.svg +++ b/public/assets/img/svg/octicon-mark-github.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-megaphone.svg b/public/assets/img/svg/octicon-megaphone.svg index f2a69adb7d7..14c8c74a60d 100644 --- a/public/assets/img/svg/octicon-megaphone.svg +++ b/public/assets/img/svg/octicon-megaphone.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-mention.svg b/public/assets/img/svg/octicon-mention.svg index 60667575583..75b414e123d 100644 --- a/public/assets/img/svg/octicon-mention.svg +++ b/public/assets/img/svg/octicon-mention.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-meter.svg b/public/assets/img/svg/octicon-meter.svg index d60c0689877..e192d5e8e0e 100644 --- a/public/assets/img/svg/octicon-meter.svg +++ b/public/assets/img/svg/octicon-meter.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-milestone.svg b/public/assets/img/svg/octicon-milestone.svg index 69da41891d6..19667b613cf 100644 --- a/public/assets/img/svg/octicon-milestone.svg +++ b/public/assets/img/svg/octicon-milestone.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-mirror.svg b/public/assets/img/svg/octicon-mirror.svg index 0acd01b01b9..05efbf2fc74 100644 --- a/public/assets/img/svg/octicon-mirror.svg +++ b/public/assets/img/svg/octicon-mirror.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-mortar-board.svg b/public/assets/img/svg/octicon-mortar-board.svg index 871d1ae7025..6a8d634f0b8 100644 --- a/public/assets/img/svg/octicon-mortar-board.svg +++ b/public/assets/img/svg/octicon-mortar-board.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-move-to-bottom.svg b/public/assets/img/svg/octicon-move-to-bottom.svg index 3fb8f973915..10595b106c1 100644 --- a/public/assets/img/svg/octicon-move-to-bottom.svg +++ b/public/assets/img/svg/octicon-move-to-bottom.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-move-to-end.svg b/public/assets/img/svg/octicon-move-to-end.svg index d4b5cb62bf9..cd6e3e6ec9a 100644 --- a/public/assets/img/svg/octicon-move-to-end.svg +++ b/public/assets/img/svg/octicon-move-to-end.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-move-to-start.svg b/public/assets/img/svg/octicon-move-to-start.svg index 00a24a4c9f6..14ffc798381 100644 --- a/public/assets/img/svg/octicon-move-to-start.svg +++ b/public/assets/img/svg/octicon-move-to-start.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-move-to-top.svg b/public/assets/img/svg/octicon-move-to-top.svg index f0726dd9888..05316ded4ac 100644 --- a/public/assets/img/svg/octicon-move-to-top.svg +++ b/public/assets/img/svg/octicon-move-to-top.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-multi-select.svg b/public/assets/img/svg/octicon-multi-select.svg index cebcebce6c3..e079b24a5ed 100644 --- a/public/assets/img/svg/octicon-multi-select.svg +++ b/public/assets/img/svg/octicon-multi-select.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-mute.svg b/public/assets/img/svg/octicon-mute.svg index db465e41d51..a683c111523 100644 --- a/public/assets/img/svg/octicon-mute.svg +++ b/public/assets/img/svg/octicon-mute.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-no-entry.svg b/public/assets/img/svg/octicon-no-entry.svg index ac897b84b48..e7117cd1ca6 100644 --- a/public/assets/img/svg/octicon-no-entry.svg +++ b/public/assets/img/svg/octicon-no-entry.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-north-star.svg b/public/assets/img/svg/octicon-north-star.svg index 69b64feebaf..2fef71871aa 100644 --- a/public/assets/img/svg/octicon-north-star.svg +++ b/public/assets/img/svg/octicon-north-star.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-note.svg b/public/assets/img/svg/octicon-note.svg index d3eb92b3b54..39e7e4e7e55 100644 --- a/public/assets/img/svg/octicon-note.svg +++ b/public/assets/img/svg/octicon-note.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-number.svg b/public/assets/img/svg/octicon-number.svg index 22cf4684684..53c4d4e8f75 100644 --- a/public/assets/img/svg/octicon-number.svg +++ b/public/assets/img/svg/octicon-number.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-organization.svg b/public/assets/img/svg/octicon-organization.svg index beef876a970..d2c02261057 100644 --- a/public/assets/img/svg/octicon-organization.svg +++ b/public/assets/img/svg/octicon-organization.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-package.svg b/public/assets/img/svg/octicon-package.svg index aab1e40c4d4..ce79726ae4b 100644 --- a/public/assets/img/svg/octicon-package.svg +++ b/public/assets/img/svg/octicon-package.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-passkey-fill.svg b/public/assets/img/svg/octicon-passkey-fill.svg index bca3a247572..2394d3eaaf2 100644 --- a/public/assets/img/svg/octicon-passkey-fill.svg +++ b/public/assets/img/svg/octicon-passkey-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-paste.svg b/public/assets/img/svg/octicon-paste.svg index 1b3ee09ce1b..13b4d7430dc 100644 --- a/public/assets/img/svg/octicon-paste.svg +++ b/public/assets/img/svg/octicon-paste.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-people.svg b/public/assets/img/svg/octicon-people.svg index 14e9d75d4dd..9bf6400b649 100644 --- a/public/assets/img/svg/octicon-people.svg +++ b/public/assets/img/svg/octicon-people.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-person-add.svg b/public/assets/img/svg/octicon-person-add.svg index a04727ce0bf..6dcdd20d830 100644 --- a/public/assets/img/svg/octicon-person-add.svg +++ b/public/assets/img/svg/octicon-person-add.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-person-fill.svg b/public/assets/img/svg/octicon-person-fill.svg index aef50598071..e3cd5eedf05 100644 --- a/public/assets/img/svg/octicon-person-fill.svg +++ b/public/assets/img/svg/octicon-person-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-person.svg b/public/assets/img/svg/octicon-person.svg index 0c18220b418..92aa6987642 100644 --- a/public/assets/img/svg/octicon-person.svg +++ b/public/assets/img/svg/octicon-person.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-play.svg b/public/assets/img/svg/octicon-play.svg index 39a3650dfdc..dca65727818 100644 --- a/public/assets/img/svg/octicon-play.svg +++ b/public/assets/img/svg/octicon-play.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-plug.svg b/public/assets/img/svg/octicon-plug.svg index e496b584d36..aac41be9449 100644 --- a/public/assets/img/svg/octicon-plug.svg +++ b/public/assets/img/svg/octicon-plug.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-plus-circle.svg b/public/assets/img/svg/octicon-plus-circle.svg index d8f4cee27d6..71c55630a82 100644 --- a/public/assets/img/svg/octicon-plus-circle.svg +++ b/public/assets/img/svg/octicon-plus-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-plus.svg b/public/assets/img/svg/octicon-plus.svg index 227b946e351..1fd3743532f 100644 --- a/public/assets/img/svg/octicon-plus.svg +++ b/public/assets/img/svg/octicon-plus.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-project-roadmap.svg b/public/assets/img/svg/octicon-project-roadmap.svg index a61cd569b63..a6b15c1ff2c 100644 --- a/public/assets/img/svg/octicon-project-roadmap.svg +++ b/public/assets/img/svg/octicon-project-roadmap.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-project-symlink.svg b/public/assets/img/svg/octicon-project-symlink.svg index 0ca269236b0..bc9104a8715 100644 --- a/public/assets/img/svg/octicon-project-symlink.svg +++ b/public/assets/img/svg/octicon-project-symlink.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-project-template.svg b/public/assets/img/svg/octicon-project-template.svg index 8a7013a7a02..559465e7dcd 100644 --- a/public/assets/img/svg/octicon-project-template.svg +++ b/public/assets/img/svg/octicon-project-template.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-project.svg b/public/assets/img/svg/octicon-project.svg index 52d86d4b77d..9fb23c758da 100644 --- a/public/assets/img/svg/octicon-project.svg +++ b/public/assets/img/svg/octicon-project.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-pulse.svg b/public/assets/img/svg/octicon-pulse.svg index b164f2ea762..5b393ebaa86 100644 --- a/public/assets/img/svg/octicon-pulse.svg +++ b/public/assets/img/svg/octicon-pulse.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-question.svg b/public/assets/img/svg/octicon-question.svg index b21e8675257..fbc075a2db8 100644 --- a/public/assets/img/svg/octicon-question.svg +++ b/public/assets/img/svg/octicon-question.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-quote.svg b/public/assets/img/svg/octicon-quote.svg index 2647b0bf47a..c3b02f48314 100644 --- a/public/assets/img/svg/octicon-quote.svg +++ b/public/assets/img/svg/octicon-quote.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-rel-file-path.svg b/public/assets/img/svg/octicon-rel-file-path.svg index 4f235bae2e6..d7617487e12 100644 --- a/public/assets/img/svg/octicon-rel-file-path.svg +++ b/public/assets/img/svg/octicon-rel-file-path.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-repo-clone.svg b/public/assets/img/svg/octicon-repo-clone.svg index 72e245fc342..a34eb677c07 100644 --- a/public/assets/img/svg/octicon-repo-clone.svg +++ b/public/assets/img/svg/octicon-repo-clone.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-repo-deleted.svg b/public/assets/img/svg/octicon-repo-deleted.svg index 59fbeaff625..c2f0fc8cf89 100644 --- a/public/assets/img/svg/octicon-repo-deleted.svg +++ b/public/assets/img/svg/octicon-repo-deleted.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-repo-forked.svg b/public/assets/img/svg/octicon-repo-forked.svg index 08bd8e5a70f..a45bee68ca0 100644 --- a/public/assets/img/svg/octicon-repo-forked.svg +++ b/public/assets/img/svg/octicon-repo-forked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-repo-locked.svg b/public/assets/img/svg/octicon-repo-locked.svg index 382a1f0a786..34aa27d5cba 100644 --- a/public/assets/img/svg/octicon-repo-locked.svg +++ b/public/assets/img/svg/octicon-repo-locked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-repo-pull.svg b/public/assets/img/svg/octicon-repo-pull.svg index 743aeec31d2..8cee37524f6 100644 --- a/public/assets/img/svg/octicon-repo-pull.svg +++ b/public/assets/img/svg/octicon-repo-pull.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-repo-template.svg b/public/assets/img/svg/octicon-repo-template.svg index 12da1960e08..05b951e6413 100644 --- a/public/assets/img/svg/octicon-repo-template.svg +++ b/public/assets/img/svg/octicon-repo-template.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-report.svg b/public/assets/img/svg/octicon-report.svg index 615c52b20d2..fc678f7c1f9 100644 --- a/public/assets/img/svg/octicon-report.svg +++ b/public/assets/img/svg/octicon-report.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-rocket.svg b/public/assets/img/svg/octicon-rocket.svg index e3a5f805d1b..967b2d63f41 100644 --- a/public/assets/img/svg/octicon-rocket.svg +++ b/public/assets/img/svg/octicon-rocket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-rows.svg b/public/assets/img/svg/octicon-rows.svg index 9cd752698da..4596215238a 100644 --- a/public/assets/img/svg/octicon-rows.svg +++ b/public/assets/img/svg/octicon-rows.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-rss.svg b/public/assets/img/svg/octicon-rss.svg index d9aa600d333..757ec69e13f 100644 --- a/public/assets/img/svg/octicon-rss.svg +++ b/public/assets/img/svg/octicon-rss.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-screen-full.svg b/public/assets/img/svg/octicon-screen-full.svg index 040ddf9ed99..8074a22cf2d 100644 --- a/public/assets/img/svg/octicon-screen-full.svg +++ b/public/assets/img/svg/octicon-screen-full.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-screen-normal.svg b/public/assets/img/svg/octicon-screen-normal.svg index 1cf1e086891..98fe6a87219 100644 --- a/public/assets/img/svg/octicon-screen-normal.svg +++ b/public/assets/img/svg/octicon-screen-normal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-search.svg b/public/assets/img/svg/octicon-search.svg index 5c3a88bca6a..2623d5d5946 100644 --- a/public/assets/img/svg/octicon-search.svg +++ b/public/assets/img/svg/octicon-search.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-server.svg b/public/assets/img/svg/octicon-server.svg index 3d80f856bad..7943abf27c2 100644 --- a/public/assets/img/svg/octicon-server.svg +++ b/public/assets/img/svg/octicon-server.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-share-android.svg b/public/assets/img/svg/octicon-share-android.svg index 81d0df456e7..78b3464c8da 100644 --- a/public/assets/img/svg/octicon-share-android.svg +++ b/public/assets/img/svg/octicon-share-android.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-shield-check.svg b/public/assets/img/svg/octicon-shield-check.svg index 13df0052fb3..68c653adaeb 100644 --- a/public/assets/img/svg/octicon-shield-check.svg +++ b/public/assets/img/svg/octicon-shield-check.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-shield-lock.svg b/public/assets/img/svg/octicon-shield-lock.svg index cc85d282110..ef96e9a1e8e 100644 --- a/public/assets/img/svg/octicon-shield-lock.svg +++ b/public/assets/img/svg/octicon-shield-lock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-shield-slash.svg b/public/assets/img/svg/octicon-shield-slash.svg index b4c2fe18df4..eb9360fbdf0 100644 --- a/public/assets/img/svg/octicon-shield-slash.svg +++ b/public/assets/img/svg/octicon-shield-slash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-shield.svg b/public/assets/img/svg/octicon-shield.svg index be209575db5..668979d7e5e 100644 --- a/public/assets/img/svg/octicon-shield.svg +++ b/public/assets/img/svg/octicon-shield.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-sidebar-collapse.svg b/public/assets/img/svg/octicon-sidebar-collapse.svg index 7b307bdda27..6885cd277f0 100644 --- a/public/assets/img/svg/octicon-sidebar-collapse.svg +++ b/public/assets/img/svg/octicon-sidebar-collapse.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-skip-fill.svg b/public/assets/img/svg/octicon-skip-fill.svg index d3e854314e1..01a5cc8d3f0 100644 --- a/public/assets/img/svg/octicon-skip-fill.svg +++ b/public/assets/img/svg/octicon-skip-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-skip.svg b/public/assets/img/svg/octicon-skip.svg index 97905054381..778827da664 100644 --- a/public/assets/img/svg/octicon-skip.svg +++ b/public/assets/img/svg/octicon-skip.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-sliders.svg b/public/assets/img/svg/octicon-sliders.svg index a76bf5180f7..940a3abde17 100644 --- a/public/assets/img/svg/octicon-sliders.svg +++ b/public/assets/img/svg/octicon-sliders.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-smiley.svg b/public/assets/img/svg/octicon-smiley.svg index 619b0960f76..0b3d8c9769c 100644 --- a/public/assets/img/svg/octicon-smiley.svg +++ b/public/assets/img/svg/octicon-smiley.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-sort-asc.svg b/public/assets/img/svg/octicon-sort-asc.svg index fe05e58ed87..45d95c49e23 100644 --- a/public/assets/img/svg/octicon-sort-asc.svg +++ b/public/assets/img/svg/octicon-sort-asc.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-sort-desc.svg b/public/assets/img/svg/octicon-sort-desc.svg index b35567d75ff..1ae84a7fe7d 100644 --- a/public/assets/img/svg/octicon-sort-desc.svg +++ b/public/assets/img/svg/octicon-sort-desc.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-sponsor-tiers.svg b/public/assets/img/svg/octicon-sponsor-tiers.svg index 08e0ae6e601..a5afb85e23c 100644 --- a/public/assets/img/svg/octicon-sponsor-tiers.svg +++ b/public/assets/img/svg/octicon-sponsor-tiers.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-square-fill.svg b/public/assets/img/svg/octicon-square-fill.svg index 24deb106db8..06d32b3c78d 100644 --- a/public/assets/img/svg/octicon-square-fill.svg +++ b/public/assets/img/svg/octicon-square-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-star-fill.svg b/public/assets/img/svg/octicon-star-fill.svg index 3d5c976fef1..174ae0c2c07 100644 --- a/public/assets/img/svg/octicon-star-fill.svg +++ b/public/assets/img/svg/octicon-star-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-star.svg b/public/assets/img/svg/octicon-star.svg index 42e42ab5e6f..31e008b2811 100644 --- a/public/assets/img/svg/octicon-star.svg +++ b/public/assets/img/svg/octicon-star.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-stop.svg b/public/assets/img/svg/octicon-stop.svg index 03cdceb1f21..6838a7cead1 100644 --- a/public/assets/img/svg/octicon-stop.svg +++ b/public/assets/img/svg/octicon-stop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-stopwatch.svg b/public/assets/img/svg/octicon-stopwatch.svg index b63aba3421d..6e1588ac2e0 100644 --- a/public/assets/img/svg/octicon-stopwatch.svg +++ b/public/assets/img/svg/octicon-stopwatch.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-strikethrough.svg b/public/assets/img/svg/octicon-strikethrough.svg index d75258995bf..4a45dfc41d4 100644 --- a/public/assets/img/svg/octicon-strikethrough.svg +++ b/public/assets/img/svg/octicon-strikethrough.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-sun.svg b/public/assets/img/svg/octicon-sun.svg index 1abeab2d1c6..f54a4163270 100644 --- a/public/assets/img/svg/octicon-sun.svg +++ b/public/assets/img/svg/octicon-sun.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-sync.svg b/public/assets/img/svg/octicon-sync.svg index 146e48f379a..8b4d0c8c1e5 100644 --- a/public/assets/img/svg/octicon-sync.svg +++ b/public/assets/img/svg/octicon-sync.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-tab-external.svg b/public/assets/img/svg/octicon-tab-external.svg index b86888e6873..e1a979df1cc 100644 --- a/public/assets/img/svg/octicon-tab-external.svg +++ b/public/assets/img/svg/octicon-tab-external.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-tag.svg b/public/assets/img/svg/octicon-tag.svg index 35c4f2e8174..9aaad7f5fea 100644 --- a/public/assets/img/svg/octicon-tag.svg +++ b/public/assets/img/svg/octicon-tag.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-tasklist.svg b/public/assets/img/svg/octicon-tasklist.svg index a568cc8c04a..227cd8a61bc 100644 --- a/public/assets/img/svg/octicon-tasklist.svg +++ b/public/assets/img/svg/octicon-tasklist.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-telescope.svg b/public/assets/img/svg/octicon-telescope.svg index ccac95cd594..9ca9a6b372b 100644 --- a/public/assets/img/svg/octicon-telescope.svg +++ b/public/assets/img/svg/octicon-telescope.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-terminal.svg b/public/assets/img/svg/octicon-terminal.svg index 4744caec0af..065ef103930 100644 --- a/public/assets/img/svg/octicon-terminal.svg +++ b/public/assets/img/svg/octicon-terminal.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-three-bars.svg b/public/assets/img/svg/octicon-three-bars.svg index 2b6fc0abedb..cf97b03044c 100644 --- a/public/assets/img/svg/octicon-three-bars.svg +++ b/public/assets/img/svg/octicon-three-bars.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-thumbsdown.svg b/public/assets/img/svg/octicon-thumbsdown.svg index 1a22693ea0d..2d9f4d8afc1 100644 --- a/public/assets/img/svg/octicon-thumbsdown.svg +++ b/public/assets/img/svg/octicon-thumbsdown.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-thumbsup.svg b/public/assets/img/svg/octicon-thumbsup.svg index ed38245f05a..7871ab71dd3 100644 --- a/public/assets/img/svg/octicon-thumbsup.svg +++ b/public/assets/img/svg/octicon-thumbsup.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-tracked-by-closed-completed.svg b/public/assets/img/svg/octicon-tracked-by-closed-completed.svg index 7a3af6dee2b..b12638d87d0 100644 --- a/public/assets/img/svg/octicon-tracked-by-closed-completed.svg +++ b/public/assets/img/svg/octicon-tracked-by-closed-completed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-tracked-by-closed-not-planned.svg b/public/assets/img/svg/octicon-tracked-by-closed-not-planned.svg index bbeb817b822..e96e93a40b9 100644 --- a/public/assets/img/svg/octicon-tracked-by-closed-not-planned.svg +++ b/public/assets/img/svg/octicon-tracked-by-closed-not-planned.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-trash.svg b/public/assets/img/svg/octicon-trash.svg index d0c0a5f712b..e0a1e569090 100644 --- a/public/assets/img/svg/octicon-trash.svg +++ b/public/assets/img/svg/octicon-trash.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-trophy.svg b/public/assets/img/svg/octicon-trophy.svg index 0f1328e2e0e..ae33cff336b 100644 --- a/public/assets/img/svg/octicon-trophy.svg +++ b/public/assets/img/svg/octicon-trophy.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-unfold.svg b/public/assets/img/svg/octicon-unfold.svg index ff4a0dd56a8..e383765b0c1 100644 --- a/public/assets/img/svg/octicon-unfold.svg +++ b/public/assets/img/svg/octicon-unfold.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-unlink.svg b/public/assets/img/svg/octicon-unlink.svg index 0f77b14734c..778d5af83cf 100644 --- a/public/assets/img/svg/octicon-unlink.svg +++ b/public/assets/img/svg/octicon-unlink.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-unlock.svg b/public/assets/img/svg/octicon-unlock.svg index b0739c67035..efeb0ef3838 100644 --- a/public/assets/img/svg/octicon-unlock.svg +++ b/public/assets/img/svg/octicon-unlock.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-unmute.svg b/public/assets/img/svg/octicon-unmute.svg index 79234174e97..6ebdf38d148 100644 --- a/public/assets/img/svg/octicon-unmute.svg +++ b/public/assets/img/svg/octicon-unmute.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-unread.svg b/public/assets/img/svg/octicon-unread.svg index 9652a18ff01..82982ef5e3c 100644 --- a/public/assets/img/svg/octicon-unread.svg +++ b/public/assets/img/svg/octicon-unread.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-unverified.svg b/public/assets/img/svg/octicon-unverified.svg index c4bbddfab0b..f91e66c8aff 100644 --- a/public/assets/img/svg/octicon-unverified.svg +++ b/public/assets/img/svg/octicon-unverified.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-video.svg b/public/assets/img/svg/octicon-video.svg index 95d92d87a63..600a42bbea1 100644 --- a/public/assets/img/svg/octicon-video.svg +++ b/public/assets/img/svg/octicon-video.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-webhook.svg b/public/assets/img/svg/octicon-webhook.svg index afb2e08209a..cce7537fb8b 100644 --- a/public/assets/img/svg/octicon-webhook.svg +++ b/public/assets/img/svg/octicon-webhook.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-x-circle-fill.svg b/public/assets/img/svg/octicon-x-circle-fill.svg index eafea6df78a..3688189a458 100644 --- a/public/assets/img/svg/octicon-x-circle-fill.svg +++ b/public/assets/img/svg/octicon-x-circle-fill.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-x-circle.svg b/public/assets/img/svg/octicon-x-circle.svg index 26cce460d5c..e4beb382fae 100644 --- a/public/assets/img/svg/octicon-x-circle.svg +++ b/public/assets/img/svg/octicon-x-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-x.svg b/public/assets/img/svg/octicon-x.svg index de54c0928e7..a39b8e57b6a 100644 --- a/public/assets/img/svg/octicon-x.svg +++ b/public/assets/img/svg/octicon-x.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-zoom-in.svg b/public/assets/img/svg/octicon-zoom-in.svg index 88540b57668..1713ae7df11 100644 --- a/public/assets/img/svg/octicon-zoom-in.svg +++ b/public/assets/img/svg/octicon-zoom-in.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/octicon-zoom-out.svg b/public/assets/img/svg/octicon-zoom-out.svg index d03925e0df8..9d682db55f3 100644 --- a/public/assets/img/svg/octicon-zoom-out.svg +++ b/public/assets/img/svg/octicon-zoom-out.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index b4f1a06a345..4b89d809996 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ python = "^3.8" [tool.poetry.group.dev.dependencies] djlint = "1.34.0" -yamllint = "1.32.0" +yamllint = "1.33.0" [tool.djlint] profile="golang" diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go index c45dc667afc..5411237103a 100644 --- a/routers/api/actions/artifacts.go +++ b/routers/api/actions/artifacts.go @@ -70,6 +70,7 @@ import ( "strings" "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -314,7 +315,7 @@ func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) { return } - artifacts, err := actions.ListArtifactsByRunID(ctx, runID) + artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{RunID: runID}) if err != nil { log.Error("Error getting artifacts: %v", err) ctx.Error(http.StatusInternalServerError, err.Error()) @@ -376,7 +377,10 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) { return } - artifacts, err := actions.ListArtifactsByRunIDAndArtifactName(ctx, runID, itemPath) + artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{ + RunID: runID, + ArtifactName: itemPath, + }) if err != nil { log.Error("Error getting artifacts: %v", err) ctx.Error(http.StatusInternalServerError, err.Error()) diff --git a/routers/api/actions/artifacts_chunks.go b/routers/api/actions/artifacts_chunks.go index 458d671cff6..c7ab70afa95 100644 --- a/routers/api/actions/artifacts_chunks.go +++ b/routers/api/actions/artifacts_chunks.go @@ -13,6 +13,7 @@ import ( "time" "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" ) @@ -86,7 +87,10 @@ func listChunksByRunID(st storage.ObjectStorage, runID int64) (map[int64][]*chun func mergeChunksForRun(ctx *ArtifactContext, st storage.ObjectStorage, runID int64, artifactName string) error { // read all db artifacts by name - artifacts, err := actions.ListArtifactsByRunIDAndName(ctx, runID, artifactName) + artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{ + RunID: runID, + ArtifactName: artifactName, + }) if err != nil { return err } diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index 24432ab6b20..bf913f2c057 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -8,6 +8,7 @@ import ( "fmt" actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" actions_module "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/container" @@ -67,12 +68,12 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s return secrets } - ownerSecrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) + ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) if err != nil { log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) // go on } - repoSecrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID}) + repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID}) if err != nil { log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) // go on @@ -94,13 +95,13 @@ func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map variables := map[string]string{} // Org / User level - ownerVariables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID}) + ownerVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID}) if err != nil { log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err) } // Repo level - repoVariables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID}) + repoVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID}) if err != nil { log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err) } @@ -200,7 +201,7 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str } needs := container.SetOf(task.Job.Needs...) - jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID}) + jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID}) if err != nil { return nil, fmt.Errorf("FindRunJobs: %w", err) } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index cadddb44c39..623c798feea 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -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. // @@ -1259,8 +1259,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() { diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go index b22ea8a7715..c87da9399f0 100644 --- a/routers/api/v1/notify/notifications.go +++ b/routers/api/v1/notify/notifications.go @@ -8,6 +8,7 @@ import ( "strings" activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" @@ -21,7 +22,17 @@ func NewAvailable(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/NotificationCount" - ctx.JSON(http.StatusOK, api.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)}) + + total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, + }) + if err != nil { + ctx.Error(http.StatusUnprocessableEntity, "db.Count[activities_model.Notification]", err) + return + } + + ctx.JSON(http.StatusOK, api.NotificationCount{New: total}) } func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions { diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 0e4efcc640f..55ca6ad1fd5 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -9,6 +9,7 @@ import ( "time" activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" @@ -108,18 +109,18 @@ func ListRepoNotifications(ctx *context.APIContext) { } opts.RepoID = ctx.Repo.Repository.ID - totalCount, err := activities_model.CountNotifications(ctx, opts) + totalCount, err := db.Count[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - err = nl.LoadAttributes(ctx) + err = activities_model.NotificationList(nl).LoadAttributes(ctx) if err != nil { ctx.InternalServerError(err) return @@ -202,7 +203,7 @@ func ReadRepoNotifications(ctx *context.APIContext) { opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) log.Error("%v", opts.Status) } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index 267b7d8ea8c..4abdfb2e925 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -8,6 +8,7 @@ import ( "time" activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/convert" @@ -68,18 +69,18 @@ func ListNotifications(ctx *context.APIContext) { return } - totalCount, err := activities_model.CountNotifications(ctx, opts) + totalCount, err := db.Count[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - err = nl.LoadAttributes(ctx) + err = activities_model.NotificationList(nl).LoadAttributes(ctx) if err != nil { ctx.InternalServerError(err) return @@ -147,7 +148,7 @@ func ReadNotifications(ctx *context.APIContext) { statuses := ctx.FormStrings("status-types") opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 5af61257735..ddc74d865b1 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -7,6 +7,7 @@ import ( "errors" "net/http" + "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" @@ -48,13 +49,7 @@ func ListActionsSecrets(ctx *context.APIContext) { ListOptions: utils.GetListOptions(ctx), } - count, err := secret_model.CountSecrets(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - secrets, err := secret_model.FindSecrets(ctx, *opts) + secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 6fb8ecd403a..d5fac1e5b8c 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -30,14 +30,9 @@ func listUserOrgs(ctx *context.APIContext, u *user_model.User) { UserID: u.ID, IncludePrivate: showPrivate, } - orgs, err := organization.FindOrgs(ctx, opts) + orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "FindOrgs", err) - return - } - maxResults, err := organization.CountOrgs(ctx, opts) - if err != nil { - ctx.Error(http.StatusInternalServerError, "CountOrgs", err) + ctx.Error(http.StatusInternalServerError, "db.FindAndCount[organization.Organization]", err) return } diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index 7d0142748ac..8859e3ae236 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -7,6 +7,7 @@ package repo import ( "net/http" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/models/webhook" @@ -58,13 +59,7 @@ func ListHooks(ctx *context.APIContext) { RepoID: ctx.Repo.Repository.ID, } - count, err := webhook.CountWebhooksByOpts(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - hooks, err := webhook.ListWebhooksByOpts(ctx, opts) + hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 74e6361f6c5..0f76a4b4ff2 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -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)) } diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index c718424f7eb..4db2c68a798 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -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) @@ -367,6 +389,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 @@ -436,6 +463,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 @@ -555,7 +587,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 } @@ -658,7 +700,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 { diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go index c327c54d101..21e2f4dabd6 100644 --- a/routers/api/v1/repo/issue_comment_attachment.go +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -329,6 +329,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 diff --git a/routers/api/v1/repo/issue_reaction.go b/routers/api/v1/repo/issue_reaction.go index 29c99184e76..c886bd71b76 100644 --- a/routers/api/v1/repo/issue_reaction.go +++ b/routers/api/v1/repo/issue_reaction.go @@ -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) { diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 47a4d14842d..af48c40885d 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -83,20 +83,14 @@ func ListDeployKeys(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - opts := &asymkey_model.ListDeployKeysOptions{ + opts := asymkey_model.ListDeployKeysOptions{ ListOptions: utils.GetListOptions(ctx), RepoID: ctx.Repo.Repository.ID, KeyID: ctx.FormInt64("key_id"), Fingerprint: ctx.FormString("fingerprint"), } - keys, err := asymkey_model.ListDeployKeys(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - count, err := asymkey_model.CountDeployKeys(ctx, opts) + keys, count, err := db.FindAndCount[asymkey_model.DeployKey](ctx, opts) if err != nil { ctx.InternalServerError(err) return @@ -159,6 +153,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(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "GetContent", err) return diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 61e5bdd6794..6c70bffca33 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -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 diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 168ef550c5e..c36bf12e6d3 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -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(ctx, 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 { diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go index 926a713c947..9f2098df066 100644 --- a/routers/api/v1/repo/release_tags.go +++ b/routers/api/v1/repo/release_tags.go @@ -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 diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index dbc8df0ef81..2f19f95e66d 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -272,7 +272,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 diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index fef0240b0ff..f045fb4d5d8 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -12,6 +12,7 @@ import ( "strings" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" @@ -48,12 +49,7 @@ func ListAccessTokens(ctx *context.APIContext) { opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)} - count, err := auth_model.CountAccessTokens(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - tokens, err := auth_model.ListAccessTokens(ctx, opts) + tokens, count, err := db.FindAndCount[auth_model.AccessToken](ctx, opts) if err != nil { ctx.InternalServerError(err) return @@ -168,7 +164,7 @@ func DeleteAccessToken(ctx *context.APIContext) { tokenID, _ := strconv.ParseInt(token, 0, 64) if tokenID == 0 { - tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{ + tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{ Name: token, UserID: ctx.ContextUser.ID, }) @@ -266,7 +262,10 @@ func ListOauth2Applications(ctx *context.APIContext) { // "200": // "$ref": "#/responses/OAuth2ApplicationList" - apps, total, err := auth_model.ListOAuth2Applications(ctx, ctx.Doer.ID, utils.GetListOptions(ctx)) + apps, total, err := db.FindAndCount[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{ + ListOptions: utils.GetListOptions(ctx), + OwnerID: ctx.Doer.ID, + }) if err != nil { ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err) return @@ -343,6 +342,10 @@ func GetOauth2Application(ctx *context.APIContext) { } return } + if app.UID != ctx.Doer.ID { + ctx.NotFound() + return + } app.ClientSecret = "" diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 404b1d221e1..4f8bcaca3e6 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -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() diff --git a/routers/api/v1/user/hook.go b/routers/api/v1/user/hook.go index 50be519c815..e87385e4a26 100644 --- a/routers/api/v1/user/hook.go +++ b/routers/api/v1/user/hook.go @@ -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) diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index fd891699ce2..dd185aa7d62 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -8,6 +8,7 @@ import ( "net/http" asymkey_model "code.gitea.io/gitea/models/asymkey" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" @@ -56,25 +57,26 @@ func listPublicKeys(ctx *context.APIContext, user *user_model.User) { username := ctx.Params("username") if fingerprint != "" { + var userID int64 // Unrestricted // Querying not just listing if username != "" { // Restrict to provided uid - keys, err = asymkey_model.SearchPublicKey(ctx, user.ID, fingerprint) - } else { - // Unrestricted - keys, err = asymkey_model.SearchPublicKey(ctx, 0, fingerprint) + userID = user.ID } + keys, err = db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + OwnerID: userID, + Fingerprint: fingerprint, + }) count = len(keys) } else { - total, err2 := asymkey_model.CountPublicKeys(ctx, user.ID) - if err2 != nil { - ctx.InternalServerError(err) - return - } - count = int(total) - + var total int64 // Use ListPublicKeys - keys, err = asymkey_model.ListPublicKeys(ctx, user.ID, utils.GetListOptions(ctx)) + keys, total, err = db.FindAndCount[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + ListOptions: utils.GetListOptions(ctx), + OwnerID: user.ID, + NotKeytype: asymkey_model.KeyTypePrincipal, + }) + count = int(total) } if err != nil { diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 63591383695..fb8f67d0722 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -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) diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index 362f4bfc4d3..1207be25acc 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/context" @@ -26,13 +27,7 @@ func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) { OwnerID: owner.ID, } - count, err := webhook.CountWebhooksByOpts(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - hooks, err := webhook.ListWebhooksByOpts(ctx, opts) + hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/web/admin/applications.go b/routers/web/admin/applications.go index b26912db482..b6f7bcd2a5b 100644 --- a/routers/web/admin/applications.go +++ b/routers/web/admin/applications.go @@ -8,6 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" @@ -33,7 +34,9 @@ func Applications(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings.applications") ctx.Data["PageIsAdminApplications"] = true - apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, 0) + apps, err := db.Find[auth.OAuth2Application](ctx, auth.FindOAuth2ApplicationsOptions{ + IsGlobal: true, + }) if err != nil { ctx.ServerError("GetOAuth2ApplicationsByUserID", err) return diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 23946d64afa..2cf63c646d3 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -13,6 +13,7 @@ import ( "strings" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/auth/pam" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -48,13 +49,12 @@ func Authentications(ctx *context.Context) { ctx.Data["PageIsAdminAuthentications"] = true var err error - ctx.Data["Sources"], err = auth.FindSources(ctx, auth.FindSourcesOptions{}) + ctx.Data["Sources"], ctx.Data["Total"], err = db.FindAndCount[auth.Source](ctx, auth.FindSourcesOptions{}) if err != nil { ctx.ServerError("auth.Sources", err) return } - ctx.Data["Total"] = auth.CountSources(ctx, auth.FindSourcesOptions{}) ctx.HTML(http.StatusOK, tplAuths) } @@ -284,7 +284,7 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.RenderWithErr(err.Error(), tplAuthNew, form) return } - existing, err := auth.FindSources(ctx, auth.FindSourcesOptions{LoginType: auth.SSPI}) + existing, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{LoginType: auth.SSPI}) if err != nil || len(existing) > 0 { ctx.Data["Err_Type"] = true ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) diff --git a/routers/web/admin/orgs.go b/routers/web/admin/orgs.go index ab44f8048bf..00131c9e2f7 100644 --- a/routers/web/admin/orgs.go +++ b/routers/web/admin/orgs.go @@ -24,7 +24,7 @@ func Organizations(ctx *context.Context) { ctx.Data["PageIsAdminOrganizations"] = true if ctx.FormString("sort") == "" { - ctx.SetFormString("sort", explore.UserSearchDefaultAdminSort) + ctx.SetFormString("sort", UserSearchDefaultAdminSort) } explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{ diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 630d739836b..44c4fa75125 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -37,6 +37,9 @@ const ( tplUserEdit base.TplName = "admin/user/edit" ) +// UserSearchDefaultAdminSort is the default sort type for admin view +const UserSearchDefaultAdminSort = "alphabetically" + // Users show all the users func Users(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.users") @@ -56,7 +59,7 @@ func Users(ctx *context.Context) { sortType := ctx.FormString("sort") if sortType == "" { - sortType = explore.UserSearchDefaultAdminSort + sortType = UserSearchDefaultAdminSort ctx.SetFormString("sort", sortType) } ctx.PageData["adminUserListSearchForm"] = map[string]any{ @@ -90,7 +93,7 @@ func NewUser(ctx *context.Context) { ctx.Data["login_type"] = "0-0" - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, }) if err != nil { @@ -111,7 +114,7 @@ func NewUserPost(ctx *context.Context) { ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, }) if err != nil { @@ -234,7 +237,7 @@ func prepareUserInfo(ctx *context.Context) *user_model.User { ctx.Data["LoginSource"] = &auth.Source{} } - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{}) + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{}) if err != nil { ctx.ServerError("auth.Sources", err) return nil @@ -293,7 +296,7 @@ func ViewUser(ctx *context.Context) { ctx.Data["Emails"] = emails ctx.Data["EmailsTotal"] = len(emails) - orgs, err := org_model.FindOrgs(ctx, org_model.FindOrgOptions{ + orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{ ListOptions: db.ListOptions{ ListAll: true, }, diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go index e37bce6b40e..dc1318beefe 100644 --- a/routers/web/explore/org.go +++ b/routers/web/explore/org.go @@ -25,7 +25,7 @@ func Organizations(ctx *context.Context) { } if ctx.FormString("sort") == "" { - ctx.SetFormString("sort", UserSearchDefaultSortType) + ctx.SetFormString("sort", setting.UI.ExploreDefaultSort) } RenderUserSearch(ctx, &user_model.SearchUserOptions{ diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go index 0a217767f32..737e34f0b33 100644 --- a/routers/web/explore/repo.go +++ b/routers/web/explore/repo.go @@ -57,8 +57,13 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { orderBy db.SearchOrderBy ) - ctx.Data["SortType"] = ctx.FormString("sort") - switch ctx.FormString("sort") { + sortOrder := ctx.FormString("sort") + if sortOrder == "" { + sortOrder = setting.UI.ExploreDefaultSort + } + ctx.Data["SortType"] = sortOrder + + switch sortOrder { case "newest": orderBy = db.SearchOrderByNewest case "oldest": diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index c760004088e..09d31f95ef4 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -23,12 +23,6 @@ const ( tplExploreUsers base.TplName = "explore/users" ) -// UserSearchDefaultSortType is the default sort type for user search -const ( - UserSearchDefaultSortType = "recentupdate" - UserSearchDefaultAdminSort = "alphabetically" -) - var nullByte = []byte{0x00} func isKeywordValid(keyword string) bool { @@ -60,8 +54,13 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, // we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns - ctx.Data["SortType"] = ctx.FormString("sort") - switch ctx.FormString("sort") { + sortOrder := ctx.FormString("sort") + if sortOrder == "" { + sortOrder = setting.UI.ExploreDefaultSort + } + ctx.Data["SortType"] = sortOrder + + switch sortOrder { case "newest": orderBy = "`user`.id DESC" case "oldest": @@ -134,7 +133,7 @@ func Users(ctx *context.Context) { ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled if ctx.FormString("sort") == "" { - ctx.SetFormString("sort", UserSearchDefaultSortType) + ctx.SetFormString("sort", setting.UI.ExploreDefaultSort) } RenderUserSearch(ctx, &user_model.SearchUserOptions{ diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go index 54c93763f6a..e3519940107 100644 --- a/routers/web/misc/misc.go +++ b/routers/web/misc/misc.go @@ -33,6 +33,10 @@ func DummyOK(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) } +func DummyBadRequest(w http.ResponseWriter, req *http.Request) { + w.WriteHeader(http.StatusBadRequest) +} + func RobotsTxt(w http.ResponseWriter, req *http.Request) { robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt") if ok, _ := util.IsExist(robotsTxt); !ok { diff --git a/routers/web/org/home.go b/routers/web/org/home.go index ec866eb6b3b..a9adfdc03cd 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -13,6 +13,8 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" @@ -155,5 +157,29 @@ func Home(ctx *context.Context) { ctx.Data["ShowMemberAndTeamTab"] = ctx.Org.IsMember || len(members) > 0 + profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer) + defer profileClose() + prepareOrgProfileReadme(ctx, profileGitRepo, profileReadmeBlob) + ctx.HTML(http.StatusOK, tplOrgHome) } + +func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repository, profileReadme *git.Blob) { + if profileGitRepo == nil || profileReadme == nil { + return + } + + if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { + log.Error("failed to GetBlobContent: %v", err) + } else { + if profileContent, err := markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + GitRepo: profileGitRepo, + Metas: map[string]string{"mode": "document"}, + }, bytes); err != nil { + log.Error("failed to RenderString: %v", err) + } else { + ctx.Data["ProfileReadme"] = profileContent + } + } +} diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 439fdf644bb..03798a712c2 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" attachment_model "code.gitea.io/gitea/models/repo" @@ -59,9 +60,12 @@ func Projects(ctx *context.Context) { } else { projectType = project_model.TypeIndividual } - projects, total, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + projects, total, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: setting.UI.IssuePagingNum, + }, OwnerID: ctx.ContextUser.ID, - Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: projectType, @@ -72,7 +76,7 @@ func Projects(ctx *context.Context) { return } - opTotal, err := project_model.CountProjects(ctx, project_model.SearchOptions{ + opTotal, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, IsClosed: util.OptionalBoolOf(!isShowClosed), Type: projectType, diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index fac83b36128..f0d9259d3fe 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -215,7 +215,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks" ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc") - ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID}) + ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID}) if err != nil { ctx.ServerError("ListWebhooksByOpts", err) return diff --git a/routers/web/org/setting_oauth2.go b/routers/web/org/setting_oauth2.go index 0045bce4c93..ca4fe09f380 100644 --- a/routers/web/org/setting_oauth2.go +++ b/routers/web/org/setting_oauth2.go @@ -8,6 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" @@ -35,7 +36,9 @@ func Applications(ctx *context.Context) { ctx.Data["PageIsOrgSettings"] = true ctx.Data["PageIsSettingsApplications"] = true - apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, ctx.Org.Organization.ID) + apps, err := db.Find[auth.OAuth2Application](ctx, auth.FindOAuth2ApplicationsOptions{ + OwnerID: ctx.Org.Organization.ID, + }) if err != nil { ctx.ServerError("GetOAuth2ApplicationsByUserID", err) return diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 6284d21463f..3b10f0b9571 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -75,11 +75,10 @@ func List(ctx *context.Context) { } // Get all runner labels - opts := actions_model.FindRunnerOptions{ + runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{ RepoID: ctx.Repo.Repository.ID, WithAvailable: true, - } - runners, err := actions_model.FindRunners(ctx, opts) + }) if err != nil { ctx.ServerError("FindRunners", err) return @@ -169,7 +168,7 @@ func List(ctx *context.Context) { opts.Status = []actions_model.Status{actions_model.Status(status)} } - runs, total, err := actions_model.FindRuns(ctx, opts) + runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return @@ -179,7 +178,7 @@ func List(ctx *context.Context) { run.Repo = ctx.Repo.Repository } - if err := runs.LoadTriggerUser(ctx); err != nil { + if err := actions_model.RunList(runs).LoadTriggerUser(ctx); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 2c69b136161..1cdae32a32f 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -512,7 +512,7 @@ func ArtifactsView(ctx *context_module.Context) { } for _, art := range artifacts { status := "completed" - if art.Status == int64(actions_model.ArtifactStatusExpired) { + if art.Status == actions_model.ArtifactStatusExpired { status = "expired" } artifactsResponse.Artifacts = append(artifactsResponse.Artifacts, &ArtifactsViewItem{ @@ -538,7 +538,10 @@ func ArtifactsDownloadView(ctx *context_module.Context) { return } - artifacts, err := actions_model.ListArtifactsByRunIDAndName(ctx, run.ID, artifactName) + artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{ + RunID: run.ID, + ArtifactName: artifactName, + }) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 1f1cca897ef..52d350ff665 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -114,12 +114,12 @@ func RefBlame(ctx *context.Context) { return } - commitNames, previousCommits := processBlameParts(ctx, result.Parts) + commitNames := processBlameParts(ctx, result.Parts) if ctx.Written() { return } - renderBlame(ctx, result.Parts, commitNames, previousCommits) + renderBlame(ctx, result.Parts, commitNames) ctx.HTML(http.StatusOK, tplRepoHome) } @@ -185,12 +185,9 @@ func fillBlameResult(br *git.BlameReader, r *blameResult) error { return nil } -func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[string]*user_model.UserCommit, map[string]string) { +func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) map[string]*user_model.UserCommit { // store commit data by SHA to look up avatar info etc commitNames := make(map[string]*user_model.UserCommit) - // previousCommits contains links from SHA to parent SHA, - // if parent also contains the current TreePath. - previousCommits := make(map[string]string) // and as blameParts can reference the same commits multiple // times, we cache the lookup work locally commits := make([]*git.Commit, 0, len(blameParts)) @@ -214,29 +211,11 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st } else { ctx.ServerError("Repo.GitRepo.GetCommit", err) } - return nil, nil + return nil } commitCache[sha] = commit } - // find parent commit - if commit.ParentCount() > 0 { - psha := commit.Parents[0] - previousCommit, ok := commitCache[psha.String()] - if !ok { - previousCommit, _ = commit.Parent(0) - if previousCommit != nil { - commitCache[psha.String()] = previousCommit - } - } - // only store parent commit ONCE, if it has the file - if previousCommit != nil { - if haz1, _ := previousCommit.HasFile(ctx.Repo.TreePath); haz1 { - previousCommits[commit.ID.String()] = previousCommit.ID.String() - } - } - } - commits = append(commits, commit) } @@ -245,10 +224,10 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st commitNames[c.ID.String()] = c } - return commitNames, previousCommits + return commitNames } -func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit, previousCommits map[string]string) { +func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit) { repoLink := ctx.Repo.RepoLink language := "" @@ -295,7 +274,6 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m } commit := commitNames[part.Sha] - previousSha := previousCommits[part.Sha] if index == 0 { // Count commit number commitCnt++ @@ -313,8 +291,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m br.Avatar = gotemplate.HTML(avatar) br.RepoLink = repoLink br.PartSha = part.Sha - br.PreviousSha = previousSha - br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath)) + br.PreviousSha = part.PreviousSha + br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(part.PreviousSha), util.PathEscapeSegments(part.PreviousPath)) br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha)) br.CommitMessage = commit.CommitMessage br.CommitSince = commitSince diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 1ad091b70fd..5e7cd1caa39 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -287,7 +287,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b Operation: operation, FromTreePath: ctx.Repo.TreePath, TreePath: form.TreePath, - ContentReader: strings.NewReader(form.Content), + ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")), }, }, Signoff: form.Signoff, diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 94300da8683..ba8b49774b9 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -569,21 +569,21 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { repoOwnerType = project_model.TypeOrganization } var err error - projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeRepository, + projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + RepoID: repo.ID, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeRepository, }) if err != nil { ctx.ServerError("GetProjects", err) return } - projects2, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: repoOwnerType, + projects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + OwnerID: repo.OwnerID, + IsClosed: util.OptionalBoolFalse, + Type: repoOwnerType, }) if err != nil { ctx.ServerError("GetProjects", err) @@ -592,21 +592,21 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.Data["OpenProjects"] = append(projects, projects2...) - projects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeRepository, + projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + RepoID: repo.ID, + IsClosed: util.OptionalBoolTrue, + Type: project_model.TypeRepository, }) if err != nil { ctx.ServerError("GetProjects", err) return } - projects2, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: repoOwnerType, + projects2, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + OwnerID: repo.OwnerID, + IsClosed: util.OptionalBoolTrue, + Type: repoOwnerType, }) if err != nil { ctx.ServerError("GetProjects", err) @@ -1319,7 +1319,7 @@ func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *use return roleDescriptor, err } else if hasMergedPR { roleDescriptor.RoleInRepo = issues_model.RoleRepoContributor - } else { + } else if issue.IsPull { // only display first time contributor in the first opening pull request roleDescriptor.RoleInRepo = issues_model.RoleRepoFirstTimeContributor } @@ -3106,6 +3106,11 @@ func UpdateCommentContent(ctx *context.Context) { return } + if comment.Issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{}) + return + } + if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { ctx.Error(http.StatusForbidden) return @@ -3172,6 +3177,11 @@ func DeleteComment(ctx *context.Context) { return } + if comment.Issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{}) + return + } + if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { ctx.Error(http.StatusForbidden) return @@ -3298,6 +3308,11 @@ func ChangeCommentReaction(ctx *context.Context) { return } + if comment.Issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{}) + return + } + if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull)) { if log.IsTrace() { if ctx.IsSigned { @@ -3441,6 +3456,21 @@ func GetCommentAttachments(ctx *context.Context) { return } + if err := comment.LoadIssue(ctx); err != nil { + ctx.NotFoundOrServerError("LoadIssue", issues_model.IsErrIssueNotExist, err) + return + } + + if comment.Issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{}) + return + } + + if !ctx.Repo.Permission.CanReadIssuesOrPulls(comment.Issue.IsPull) { + ctx.NotFound("CanReadIssuesOrPulls", issues_model.ErrCommentNotExist{}) + return + } + if !comment.Type.HasAttachmentSupport() { ctx.ServerError("GetCommentAttachments", fmt.Errorf("comment type %v does not support attachments", comment.Type)) return diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go index 5c378fe9d79..473ab260f3e 100644 --- a/routers/web/repo/issue_content_history.go +++ b/routers/web/repo/issue_content_history.go @@ -122,7 +122,7 @@ func GetContentHistoryDetail(ctx *context.Context) { } historyID := ctx.FormInt64("history_id") - history, prevHistory, err := issues_model.GetIssueContentHistoryAndPrev(ctx, historyID) + history, prevHistory, err := issues_model.GetIssueContentHistoryAndPrev(ctx, issue.ID, historyID) if err != nil { ctx.JSON(http.StatusNotFound, map[string]any{ "message": "Can not find the content history", diff --git a/routers/web/repo/middlewares.go b/routers/web/repo/middlewares.go index b50e96be3c4..5f4a219aa33 100644 --- a/routers/web/repo/middlewares.go +++ b/routers/web/repo/middlewares.go @@ -16,7 +16,6 @@ import ( // SetEditorconfigIfExists set editor config as render variable func SetEditorconfigIfExists(ctx *context.Context) { if ctx.Repo.Repository.IsEmpty { - ctx.Data["Editorconfig"] = nil return } diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 6417024f8ba..5694575b468 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -10,6 +10,7 @@ import ( "net/url" "strings" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/perm" project_model "code.gitea.io/gitea/models/project" @@ -71,9 +72,12 @@ func Projects(ctx *context.Context) { total = repo.NumClosedProjects } - projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptions{ + PageSize: setting.UI.IssuePagingNum, + Page: page, + }, RepoID: repo.ID, - Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: project_model.TypeRepository, @@ -464,7 +468,7 @@ func AddBoardToProjectPost(ctx *context.Context) { return } - project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + project, err := project_model.GetProjectForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) if err != nil { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", nil) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 5cbd6b3d51c..595d599fe13 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -174,6 +174,7 @@ func TagsList(ctx *context.Context) { // Disable the showCreateNewBranch form in the dropdown on this page. ctx.Data["CanCreateBranch"] = false ctx.Data["HideBranchesInDropdown"] = true + ctx.Data["CanCreateRelease"] = ctx.Repo.CanWrite(unit.TypeReleases) && !ctx.Repo.Repository.IsArchived listOptions := db.ListOptions{ Page: ctx.FormInt("page"), @@ -616,7 +617,27 @@ func DeleteTag(ctx *context.Context) { } func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) { - if err := releaseservice.DeleteReleaseByID(ctx, ctx.FormInt64("id"), ctx.Doer, isDelTag); err != nil { + redirect := func() { + if isDelTag { + ctx.JSONRedirect(ctx.Repo.RepoLink + "/tags") + return + } + + ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases") + } + + rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id")) + if err != nil { + if repo_model.IsErrReleaseNotExist(err) { + ctx.NotFound("GetReleaseForRepoByID", err) + } else { + ctx.Flash.Error("DeleteReleaseByID: " + err.Error()) + redirect() + } + return + } + + if err := releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, isDelTag); err != nil { if models.IsErrProtectedTagName(err) { ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected")) } else { @@ -630,10 +651,5 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) { } } - if isDelTag { - ctx.JSONRedirect(ctx.Repo.RepoLink + "/tags") - return - } - - ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases") + redirect() } diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go index 579743ef3ca..3d4420006c4 100644 --- a/routers/web/repo/setting/deploy_key.go +++ b/routers/web/repo/setting/deploy_key.go @@ -22,7 +22,7 @@ func DeployKeys(ctx *context.Context) { ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled - keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) + keys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("ListDeployKeys", err) return @@ -39,7 +39,7 @@ func DeployKeysPost(ctx *context.Context) { ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled - keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) + keys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("ListDeployKeys", err) return diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index ea5abb05792..5100bf782f0 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -12,6 +12,7 @@ import ( "path" "strings" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" user_model "code.gitea.io/gitea/models/user" @@ -46,7 +47,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks" ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.com/usage/webhooks") - ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID}) + ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("GetWebhooksByRepoID", err) return diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 89bb1839e17..ba2ac3af115 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -331,6 +331,10 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr ctx.Data["FileContent"] = buf.String() } + + if !fInfo.isLFSFile && ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { + ctx.Data["CanEditReadmeFile"] = true + } } func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) { diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go index 1da4ddf14fc..ae9a3767241 100644 --- a/routers/web/shared/actions/runners.go +++ b/routers/web/shared/actions/runners.go @@ -17,18 +17,13 @@ import ( // RunnersList prepares data for runners list func RunnersList(ctx *context.Context, opts actions_model.FindRunnerOptions) { - count, err := actions_model.CountRunners(ctx, opts) + runners, count, err := db.FindAndCount[actions_model.ActionRunner](ctx, opts) if err != nil { ctx.ServerError("CountRunners", err) return } - runners, err := actions_model.FindRunners(ctx, opts) - if err != nil { - ctx.ServerError("FindRunners", err) - return - } - if err := runners.LoadAttributes(ctx); err != nil { + if err := actions_model.RunnerList(runners).LoadAttributes(ctx); err != nil { ctx.ServerError("LoadAttributes", err) return } @@ -89,18 +84,13 @@ func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int RunnerID: runner.ID, } - count, err := actions_model.CountTasks(ctx, opts) + tasks, count, err := db.FindAndCount[actions_model.ActionTask](ctx, opts) if err != nil { ctx.ServerError("CountTasks", err) return } - tasks, err := actions_model.FindTasks(ctx, opts) - if err != nil { - ctx.ServerError("FindTasks", err) - return - } - if err = tasks.LoadAttributes(ctx); err != nil { + if err = actions_model.TaskList(tasks).LoadAttributes(ctx); err != nil { ctx.ServerError("TasksLoadAttributes", err) return } diff --git a/routers/web/shared/actions/variables.go b/routers/web/shared/actions/variables.go index 341c18f589c..07a05752078 100644 --- a/routers/web/shared/actions/variables.go +++ b/routers/web/shared/actions/variables.go @@ -18,7 +18,7 @@ import ( ) func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) { - variables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{ + variables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{ OwnerID: ownerID, RepoID: repoID, }) diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go index 875cb0cfec7..c805da734ac 100644 --- a/routers/web/shared/secrets/secrets.go +++ b/routers/web/shared/secrets/secrets.go @@ -4,6 +4,7 @@ package secrets import ( + "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" @@ -14,7 +15,7 @@ import ( ) func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { - secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID}) + secrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID}) if err != nil { ctx.ServerError("FindSecrets", err) return diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 6d1901dd2bb..411d499eb4f 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -6,7 +6,10 @@ package user import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" + access_model "code.gitea.io/gitea/models/perm/access" + project_model "code.gitea.io/gitea/models/project" 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" "code.gitea.io/gitea/modules/git" @@ -57,7 +60,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { } showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) - orgs, err := organization.FindOrgs(ctx, organization.FindOrgOptions{ + orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{ UserID: ctx.ContextUser.ID, IncludePrivate: showPrivate, }) @@ -84,18 +87,23 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { } } -func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) { +func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) { profileDbRepo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ".profile") - if err == nil && !profileDbRepo.IsEmpty && !profileDbRepo.IsPrivate { - if profileGitRepo, err = git.OpenRepository(ctx, profileDbRepo.RepoPath()); err != nil { - log.Error("FindUserProfileReadme failed to OpenRepository: %v", err) - } else { - if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil { - log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err) + if err == nil { + perm, err := access_model.GetUserRepoPermission(ctx, profileDbRepo, doer) + if err == nil && !profileDbRepo.IsEmpty && perm.CanRead(unit.TypeCode) { + if profileGitRepo, err = git.OpenRepository(ctx, profileDbRepo.RepoPath()); err != nil { + log.Error("FindUserProfileReadme failed to OpenRepository: %v", err) } else { - profileReadmeBlob, _ = commit.GetBlobByPath("README.md") + if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil { + log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err) + } else { + profileReadmeBlob, _ = commit.GetBlobByPath("README.md") + } } } + } else if !repo_model.IsErrRepoNotExist(err) { + log.Error("FindUserProfileReadme failed to GetRepositoryByName: %v", err) } return profileGitRepo, profileReadmeBlob, func() { if profileGitRepo != nil { @@ -107,7 +115,7 @@ func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository func RenderUserHeader(ctx *context.Context) { prepareContextForCommonProfile(ctx) - _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx) + _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx, ctx.Doer) defer profileClose() ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil } @@ -127,5 +135,21 @@ func LoadHeaderCount(ctx *context.Context) error { } ctx.Data["RepoCount"] = repoCount + var projectType project_model.Type + if ctx.ContextUser.IsOrganization() { + projectType = project_model.TypeOrganization + } else { + projectType = project_model.TypeIndividual + } + projectCount, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{ + OwnerID: ctx.ContextUser.ID, + IsClosed: util.OptionalBoolOf(false), + Type: projectType, + }) + if err != nil { + return err + } + ctx.Data["ProjectCount"] = projectCount + return nil } diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 8b9a4cd2241..b4fb25dfe0e 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -759,7 +759,9 @@ func loadRepoByIDs(ctx *context.Context, ctxUser *user_model.User, issueCountByR // ShowSSHKeys output all the ssh keys of user by uid func ShowSSHKeys(ctx *context.Context) { - keys, err := asymkey_model.ListPublicKeys(ctx, ctx.ContextUser.ID, db.ListOptions{}) + keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + OwnerID: ctx.ContextUser.ID, + }) if err != nil { ctx.ServerError("ListPublicKeys", err) return @@ -822,6 +824,11 @@ func UsernameSubRoute(ctx *context.Context) { reloadParam := func(suffix string) (success bool) { ctx.SetParams("username", strings.TrimSuffix(username, suffix)) context_service.UserAssignmentWeb()(ctx) + // check view permissions + if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) { + ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name)) + return false + } return !ctx.Written() } switch { diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 579287ffac6..26f77cfc3a3 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -42,7 +42,10 @@ func GetNotificationCount(ctx *context.Context) { } ctx.Data["NotificationUnreadCount"] = func() int64 { - count, err := activities_model.GetNotificationCount(ctx, ctx.Doer, activities_model.NotificationStatusUnread) + count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, + }) if err != nil { if err != goctx.Canceled { log.Error("Unable to GetNotificationCount for user:%-v: %v", ctx.Doer, err) @@ -89,7 +92,10 @@ func getNotifications(ctx *context.Context) { status = activities_model.NotificationStatusUnread } - total, err := activities_model.GetNotificationCount(ctx, ctx.Doer, status) + total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{status}, + }) if err != nil { ctx.ServerError("ErrGetNotificationCount", err) return @@ -103,12 +109,21 @@ func getNotifications(ctx *context.Context) { } statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned} - notifications, err := activities_model.NotificationsForUser(ctx, ctx.Doer, statuses, page, perPage) + nls, err := db.Find[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + ListOptions: db.ListOptions{ + PageSize: perPage, + Page: page, + }, + UserID: ctx.Doer.ID, + Status: statuses, + }) if err != nil { - ctx.ServerError("ErrNotificationsForUser", err) + ctx.ServerError("db.Find[activities_model.Notification]", err) return } + notifications := activities_model.NotificationList(nls) + failCount := 0 repos, failures, err := notifications.LoadRepos(ctx) @@ -409,5 +424,15 @@ func NotificationWatching(ctx *context.Context) { // NewAvailable returns the notification counts func NewAvailable(ctx *context.Context) { - ctx.JSON(http.StatusOK, structs.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)}) + total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, + }) + if err != nil { + log.Error("db.Count[activities_model.Notification]", err) + ctx.JSON(http.StatusOK, structs.NotificationCount{New: 0}) + return + } + + ctx.JSON(http.StatusOK, structs.NotificationCount{New: total}) } diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 48a4b94c190..ac278e300d7 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -64,7 +64,7 @@ func userProfile(ctx *context.Context) { ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data) } - profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx) + profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer) defer profileClose() showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index ee44d48dce5..69a93dbf03a 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -8,6 +8,7 @@ import ( "net/http" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" @@ -88,7 +89,7 @@ func DeleteApplication(ctx *context.Context) { func loadApplicationsData(ctx *context.Context) { ctx.Data["AccessTokenScopePublicOnly"] = auth_model.AccessTokenScopePublicOnly - tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) + tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) if err != nil { ctx.ServerError("ListAccessTokens", err) return @@ -97,7 +98,9 @@ func loadApplicationsData(ctx *context.Context) { ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin if setting.OAuth2.Enable { - ctx.Data["Applications"], err = auth_model.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID) + ctx.Data["Applications"], err = db.Find[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{ + OwnerID: ctx.Doer.ID, + }) if err != nil { ctx.ServerError("GetOAuth2ApplicationsByUserID", err) return diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 440885bb076..0dfb506fa96 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -260,7 +260,10 @@ func DeleteKey(ctx *context.Context) { } func loadKeysData(ctx *context.Context) { - keys, err := asymkey_model.ListPublicKeys(ctx, ctx.Doer.ID, db.ListOptions{}) + keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + OwnerID: ctx.Doer.ID, + NotKeytype: asymkey_model.KeyTypePrincipal, + }) if err != nil { ctx.ServerError("ListPublicKeys", err) return diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 2390b0746c1..d8331fef43f 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -214,16 +214,12 @@ func Organization(ctx *context.Context) { opts.Page = 1 } - orgs, err := organization.FindOrgs(ctx, opts) + orgs, total, err := db.FindAndCount[organization.Organization](ctx, opts) if err != nil { ctx.ServerError("FindOrgs", err) return } - total, err := organization.CountOrgs(ctx, opts) - if err != nil { - ctx.ServerError("CountOrgs", err) - return - } + ctx.Data["Orgs"] = orgs pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) pager.SetDefaultParams(ctx) diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index ec269776e2b..3647d606eef 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -9,6 +9,7 @@ import ( "sort" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -68,14 +69,17 @@ func loadSecurityData(ctx *context.Context) { } ctx.Data["WebAuthnCredentials"] = credentials - tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) + tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) if err != nil { ctx.ServerError("ListAccessTokens", err) return } ctx.Data["Tokens"] = tokens - accountLinks, err := user_model.ListAccountLinks(ctx, ctx.Doer) + accountLinks, err := db.Find[user_model.ExternalLoginUser](ctx, user_model.FindExternalUserOptions{ + UserID: ctx.Doer.ID, + OrderBy: "login_source_id DESC", + }) if err != nil { ctx.ServerError("ListAccountLinks", err) return @@ -107,7 +111,7 @@ func loadSecurityData(ctx *context.Context) { } ctx.Data["AccountLinks"] = sources - authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{ + authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{ IsActive: util.OptionalBoolNone, LoginType: auth_model.OAuth2, }) diff --git a/routers/web/user/setting/webhooks.go b/routers/web/user/setting/webhooks.go index 50cebc2a3de..679b72e5011 100644 --- a/routers/web/user/setting/webhooks.go +++ b/routers/web/user/setting/webhooks.go @@ -6,6 +6,7 @@ package setting import ( "net/http" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -24,7 +25,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = setting.AppSubURL + "/user/settings/hooks" ctx.Data["Description"] = ctx.Tr("settings.hooks.desc") - ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Doer.ID}) + ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{OwnerID: ctx.Doer.ID}) if err != nil { ctx.ServerError("ListWebhooksByOpts", err) return diff --git a/routers/web/web.go b/routers/web/web.go index a485436da5b..3a0471411f6 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -533,8 +533,10 @@ func registerRoutes(m *web.Route) { m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) }, ignSignInAndCsrf, reqSignIn) m.Get("/login/oauth/userinfo", ignSignInAndCsrf, auth.InfoOAuth) + m.Options("/login/oauth/access_token", CorsHandler(), misc.DummyBadRequest) m.Post("/login/oauth/access_token", CorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) m.Get("/login/oauth/keys", ignSignInAndCsrf, auth.OIDCKeys) + m.Options("/login/oauth/introspect", CorsHandler(), misc.DummyBadRequest) m.Post("/login/oauth/introspect", CorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) m.Group("/user/settings", func() { diff --git a/services/actions/clear_tasks.go b/services/actions/clear_tasks.go index 7c7043c42f4..67373782d5c 100644 --- a/services/actions/clear_tasks.go +++ b/services/actions/clear_tasks.go @@ -33,7 +33,7 @@ func StopEndlessTasks(ctx context.Context) error { } func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error { - tasks, err := actions_model.FindTasks(ctx, opts) + tasks, err := db.Find[actions_model.ActionTask](ctx, opts) if err != nil { return fmt.Errorf("find tasks: %w", err) } @@ -74,7 +74,7 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error { // CancelAbandonedJobs cancels the jobs which have waiting status, but haven't been picked by a runner for a long time func CancelAbandonedJobs(ctx context.Context) error { - jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{ + jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{ Statuses: []actions_model.Status{actions_model.StatusWaiting, actions_model.StatusBlocked}, UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.AbandonedJobTimeout).Unix()), }) diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go index f7ec615364a..fe39312386d 100644 --- a/services/actions/job_emitter.go +++ b/services/actions/job_emitter.go @@ -44,7 +44,7 @@ func jobEmitterQueueHandler(items ...*jobUpdate) []*jobUpdate { } func checkJobsOfRun(ctx context.Context, runID int64) error { - jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: runID}) + jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: runID}) if err != nil { return err } diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 7d5f6c6c0a3..175b8a41187 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -7,9 +7,11 @@ import ( "bytes" "context" "fmt" + "slices" "strings" actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" packages_model "code.gitea.io/gitea/models/packages" access_model "code.gitea.io/gitea/models/perm/access" @@ -20,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" "code.gitea.io/gitea/services/convert" @@ -144,6 +147,10 @@ func notify(ctx context.Context, input *notifyInput) error { return fmt.Errorf("gitRepo.GetCommit: %w", err) } + if skipWorkflowsForCommit(input, commit) { + return nil + } + var detectedWorkflows []*actions_module.DetectedWorkflow actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig() workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload) @@ -195,6 +202,25 @@ func notify(ctx context.Context, input *notifyInput) error { return handleWorkflows(ctx, detectedWorkflows, commit, input, ref) } +func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool { + // skip workflow runs with a configured skip-ci string in commit message if the event is push or pull_request(_sync) + // https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs + skipWorkflowEvents := []webhook_module.HookEventType{ + webhook_module.HookEventPush, + webhook_module.HookEventPullRequest, + webhook_module.HookEventPullRequestSync, + } + if slices.Contains(skipWorkflowEvents, input.Event) { + for _, s := range setting.Actions.SkipWorkflowStrings { + if strings.Contains(commit.CommitMessage, s) { + log.Debug("repo %s with commit %s: skipped run because of %s string", input.Repo.RepoPath(), commit.ID, s) + return true + } + } + } + return false +} + func handleWorkflows( ctx context.Context, detectedWorkflows []*actions_module.DetectedWorkflow, @@ -273,7 +299,7 @@ func handleWorkflows( continue } - alljobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: run.ID}) + alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) if err != nil { log.Error("FindRunJobs: %v", err) continue @@ -352,7 +378,7 @@ func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun, repo *rep } // don't need approval if the user has been approved before - if count, err := actions_model.CountRuns(ctx, actions_model.FindRunOptions{ + if count, err := db.Count[actions_model.ActionRun](ctx, actions_model.FindRunOptions{ RepoID: repo.ID, TriggerUserID: user.ID, Approved: true, @@ -383,7 +409,7 @@ func handleSchedules( return nil } - if count, err := actions_model.CountSchedules(ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}); err != nil { + if count, err := db.Count[actions_model.ActionSchedule](ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}); err != nil { log.Error("CountSchedules: %v", err) return err } else if count > 0 { diff --git a/services/asymkey/ssh_key_test.go b/services/asymkey/ssh_key_test.go index 3a39a9a1db4..fbd5d13ab2d 100644 --- a/services/asymkey/ssh_key_test.go +++ b/services/asymkey/ssh_key_test.go @@ -67,7 +67,10 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib for i, kase := range testCases { s.ID = int64(i) + 20 asymkey_model.AddPublicKeysBySource(db.DefaultContext, user, s, []string{kase.keyString}) - keys, err := asymkey_model.ListPublicKeysBySource(db.DefaultContext, user.ID, s.ID) + keys, err := db.Find[asymkey_model.PublicKey](db.DefaultContext, asymkey_model.FindPublicKeyOptions{ + OwnerID: user.ID, + LoginSourceID: s.ID, + }) assert.NoError(t, err) if err != nil { continue diff --git a/services/auth/httpsign.go b/services/auth/httpsign.go index d5c8ea33aa3..b604349f80d 100644 --- a/services/auth/httpsign.go +++ b/services/auth/httpsign.go @@ -12,6 +12,7 @@ import ( "strings" asymkey_model "code.gitea.io/gitea/models/asymkey" + "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/setting" @@ -92,7 +93,9 @@ func VerifyPubKey(r *http.Request) (*asymkey_model.PublicKey, error) { keyID := verifier.KeyId() - publicKeys, err := asymkey_model.SearchPublicKey(r.Context(), 0, keyID) + publicKeys, err := db.Find[asymkey_model.PublicKey](r.Context(), asymkey_model.FindPublicKeyOptions{ + Fingerprint: keyID, + }) if err != nil { return nil, err } diff --git a/services/auth/signin.go b/services/auth/signin.go index 2e534536817..fafe3ef3c67 100644 --- a/services/auth/signin.go +++ b/services/auth/signin.go @@ -86,7 +86,7 @@ func UserSignIn(ctx context.Context, username, password string) (*user_model.Use } } - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, }) if err != nil { diff --git a/services/auth/source/oauth2/init.go b/services/auth/source/oauth2/init.go index 0ebbdaebd41..3ad6e307f10 100644 --- a/services/auth/source/oauth2/init.go +++ b/services/auth/source/oauth2/init.go @@ -10,6 +10,7 @@ import ( "sync" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -64,7 +65,7 @@ func ResetOAuth2(ctx context.Context) error { // initOAuth2Sources is used to load and register all active OAuth2 providers func initOAuth2Sources(ctx context.Context) error { - authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, LoginType: auth.OAuth2, }) diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index 3b45b252f70..f4edb507f27 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -13,6 +13,7 @@ import ( "sort" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -107,7 +108,7 @@ func CreateProviderFromSource(source *auth.Source) (Provider, error) { // GetOAuth2Providers returns the list of configured OAuth2 providers func GetOAuth2Providers(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) { - authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: isActive, LoginType: auth.OAuth2, }) diff --git a/services/auth/sspi.go b/services/auth/sspi.go index bc8ec948f29..57ba0462c57 100644 --- a/services/auth/sspi.go +++ b/services/auth/sspi.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/avatars" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" gitea_context "code.gitea.io/gitea/modules/context" @@ -130,7 +131,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, // getConfig retrieves the SSPI configuration from login sources func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) { - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, LoginType: auth.SSPI, }) diff --git a/services/auth/sync.go b/services/auth/sync.go index 11a59d41ae1..7562ac812bd 100644 --- a/services/auth/sync.go +++ b/services/auth/sync.go @@ -15,7 +15,7 @@ import ( func SyncExternalUsers(ctx context.Context, updateExisting bool) error { log.Trace("Doing: SyncExternalUsers") - ls, err := auth.FindSources(ctx, auth.FindSourcesOptions{}) + ls, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{}) if err != nil { log.Error("SyncExternalUsers: %v", err) return err diff --git a/services/forms/user_form.go b/services/forms/user_form.go index c0eb03f5547..cbab2742388 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -365,7 +365,7 @@ func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) bind // NewAccessTokenForm form for creating access token type NewAccessTokenForm struct { - Name string `binding:"Required;MaxSize(255)"` + Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"` Scope []string } diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 1f139d233ff..b597dd0487c 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -290,8 +290,10 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient reference := createReference(ctx.Issue, nil, activities_model.ActionType(0)) var replyPayload []byte - if ctx.Comment != nil && ctx.Comment.Type == issues_model.CommentTypeCode { - replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Comment) + if ctx.Comment != nil { + if ctx.Comment.Type.HasMailReplySupport() { + replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Comment) + } } else { replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Issue) } @@ -316,7 +318,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient listUnsubscribe := []string{"<" + ctx.Issue.HTMLURL() + ">"} if setting.IncomingEmail.Enabled { - if ctx.Comment != nil { + if replyPayload != nil { token, err := token.CreateToken(token.ReplyHandlerType, recipient, replyPayload) if err != nil { log.Error("CreateToken failed: %v", err) diff --git a/services/migrations/update.go b/services/migrations/update.go index d466832363b..4a49206f82e 100644 --- a/services/migrations/update.go +++ b/services/migrations/update.go @@ -36,8 +36,7 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ } const batchSize = 100 - var start int - for { + for page := 0; ; page++ { select { case <-ctx.Done(): log.Warn("UpdateMigrationPosterIDByGitService(%s) cancelled", tp.Name()) @@ -45,10 +44,13 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ default: } - users, err := user_model.FindExternalUsersByProvider(ctx, user_model.FindExternalUserOptions{ + users, err := db.Find[user_model.ExternalLoginUser](ctx, user_model.FindExternalUserOptions{ + ListOptions: db.ListOptions{ + PageSize: batchSize, + Page: page, + }, Provider: provider, - Start: start, - Limit: batchSize, + OrderBy: "login_source_id ASC, external_id ASC", }) if err != nil { return err @@ -70,7 +72,6 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ if len(users) < batchSize { break } - start += len(users) } return nil } diff --git a/services/pull/review.go b/services/pull/review.go index 3f5644b0cd4..e48f3801547 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -49,16 +49,16 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis return nil } issueIDs := prs.GetIssueIDs() - var codeComments []*issues_model.Comment - if err := db.Find(ctx, &issues_model.FindCommentsOptions{ + codeComments, err := db.Find[issues_model.Comment](ctx, issues_model.FindCommentsOptions{ ListOptions: db.ListOptions{ ListAll: true, }, Type: issues_model.CommentTypeCode, Invalidated: util.OptionalBoolFalse, IssueIDs: issueIDs, - }, &codeComments); err != nil { + }) + if err != nil { return fmt.Errorf("find code comments: %v", err) } for _, comment := range codeComments { diff --git a/services/release/release.go b/services/release/release.go index e0035d42fc2..3ba2a3f6117 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -301,17 +301,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo } // DeleteReleaseByID deletes a release and corresponding Git tag by given ID. -func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, delTag bool) error { - rel, err := repo_model.GetReleaseByID(ctx, id) - if err != nil { - return fmt.Errorf("GetReleaseByID: %w", err) - } - - repo, err := repo_model.GetRepositoryByID(ctx, rel.RepoID) - if err != nil { - return fmt.Errorf("GetRepositoryByID: %w", err) - } - +func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *repo_model.Release, doer *user_model.User, delTag bool) error { if delTag { protectedTags, err := git_model.GetProtectedTags(ctx, rel.RepoID) if err != nil { @@ -344,19 +334,19 @@ func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, del }, repository.NewPushCommits()) notify_service.DeleteRef(ctx, doer, repo, refName) - if err := repo_model.DeleteReleaseByID(ctx, id); err != nil { + if err := repo_model.DeleteReleaseByID(ctx, rel.ID); err != nil { return fmt.Errorf("DeleteReleaseByID: %w", err) } } else { rel.IsTag = true - if err = repo_model.UpdateRelease(ctx, rel); err != nil { + if err := repo_model.UpdateRelease(ctx, rel); err != nil { return fmt.Errorf("Update: %w", err) } } rel.Repo = repo - if err = rel.LoadAttributes(ctx); err != nil { + if err := rel.LoadAttributes(ctx); err != nil { return fmt.Errorf("LoadAttributes: %w", err) } diff --git a/services/repository/delete.go b/services/repository/delete.go index 861dfa2dcd0..08d6800ee76 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -54,13 +54,13 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } // Query the action tasks of this repo, they will be needed after they have been deleted to remove the logs - tasks, err := actions_model.FindTasks(ctx, actions_model.FindTaskOptions{RepoID: repoID}) + tasks, err := db.Find[actions_model.ActionTask](ctx, actions_model.FindTaskOptions{RepoID: repoID}) if err != nil { return fmt.Errorf("find actions tasks of repo %v: %w", repoID, err) } // Query the artifacts of this repo, they will be needed after they have been deleted to remove artifacts files in ObjectStorage - artifacts, err := actions_model.ListArtifactsByRepoID(ctx, repoID) + artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{RepoID: repoID}) if err != nil { return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err) } @@ -75,7 +75,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } // Delete Deploy Keys - deployKeys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: repoID}) + deployKeys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: repoID}) if err != nil { return fmt.Errorf("listDeployKeys: %w", err) } diff --git a/services/repository/hooks.go b/services/repository/hooks.go index 8506fa34136..7b82f36b43f 100644 --- a/services/repository/hooks.go +++ b/services/repository/hooks.go @@ -85,7 +85,7 @@ func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_mode // GenerateWebhooks generates webhooks from a template repository func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { - templateWebhooks, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: templateRepo.ID}) + templateWebhooks, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{RepoID: templateRepo.ID}) if err != nil { return err } diff --git a/services/repository/push.go b/services/repository/push.go index 97da45f52b4..391c8ad4ca7 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -35,7 +35,9 @@ var pushQueue *queue.WorkerPoolQueue[[]*repo_module.PushUpdateOptions] func handler(items ...[]*repo_module.PushUpdateOptions) [][]*repo_module.PushUpdateOptions { for _, opts := range items { if err := pushUpdates(opts); err != nil { - log.Error("pushUpdate failed: %v", err) + // Username and repository stays the same between items in opts. + pushUpdate := opts[0] + log.Error("pushUpdate[%s/%s] failed: %v", pushUpdate.RepoUserName, pushUpdate.RepoName, err) } } return nil diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 1c4772d6bf9..97e15ba6c7f 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -15,7 +15,7 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data return nil, false, err } - s, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{ + s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, Name: name, @@ -40,7 +40,7 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data } func DeleteSecretByID(ctx context.Context, ownerID, repoID, secretID int64) error { - s, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{ + s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, SecretID: secretID, @@ -60,7 +60,7 @@ func DeleteSecretByName(ctx context.Context, ownerID, repoID int64, name string) return err } - s, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{ + s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, Name: name, diff --git a/services/user/user.go b/services/user/user.go index 4a4908fe8e5..932e359c9f9 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -172,7 +172,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { // An alternative option here would be write a function which would delete all organizations but it seems // but such a function would likely get out of date for { - orgs, err := organization.FindOrgs(ctx, organization.FindOrgOptions{ + orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{ ListOptions: db.ListOptions{ PageSize: repo_model.RepositoryListDefaultPageSize, Page: 1, diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go index 7289c751f38..a03fa46f145 100644 --- a/services/webhook/dingtalk_test.go +++ b/services/webhook/dingtalk_test.go @@ -188,6 +188,21 @@ func TestDingTalkPayload(t *testing.T) { assert.Equal(t, "http://localhost:3000/test/repo", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(DingtalkPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DingtalkPayload{}, pl) + + assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*DingtalkPayload).ActionCard.Text) + assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*DingtalkPayload).ActionCard.Title) + assert.Equal(t, "view package", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go index 6a276e9e874..b567cbc395d 100644 --- a/services/webhook/discord_test.go +++ b/services/webhook/discord_test.go @@ -211,6 +211,24 @@ func TestDiscordPayload(t *testing.T) { assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(DiscordPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &DiscordPayload{}, pl) + + assert.Len(t, pl.(*DiscordPayload).Embeds, 1) + assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*DiscordPayload).Embeds[0].Title) + assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description) + assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.(*DiscordPayload).Embeds[0].URL) + assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name) + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL) + assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go index a3182e82b09..98bc50dedec 100644 --- a/services/webhook/feishu_test.go +++ b/services/webhook/feishu_test.go @@ -144,6 +144,18 @@ func TestFeishuPayload(t *testing.T) { assert.Equal(t, "[test/repo] Repository created", pl.(*FeishuPayload).Content.Text) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(FeishuPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &FeishuPayload{}, pl) + + assert.Equal(t, "Package created: GiteaContainer:latest by user1", pl.(*FeishuPayload).Content.Text) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go index a9a8c6b5212..41bac3fd04d 100644 --- a/services/webhook/general_test.go +++ b/services/webhook/general_test.go @@ -303,6 +303,36 @@ func repositoryTestPayload() *api.RepositoryPayload { } } +func packageTestPayload() *api.PackagePayload { + return &api.PackagePayload{ + Action: api.HookPackageCreated, + Sender: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + Repository: nil, + Organization: &api.User{ + UserName: "org1", + AvatarURL: "http://localhost:3000/org1/avatar", + }, + Package: &api.Package{ + Owner: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + Repository: nil, + Creator: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + Type: "container", + Name: "GiteaContainer", + Version: "latest", + HTMLURL: "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", + }, + } +} + func TestGetIssuesPayloadInfo(t *testing.T) { p := issueTestPayload() diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index ab7e6b72c25..602d16ef39b 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -212,14 +212,14 @@ func (m *MatrixPayload) Repository(p *api.RepositoryPayload) (api.Payloader, err func (m *MatrixPayload) Package(p *api.PackagePayload) (api.Payloader, error) { senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) - repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) + packageLink := MatrixLinkFormatter(p.Package.HTMLURL, p.Package.Name) var text string switch p.Action { case api.HookPackageCreated: - text = fmt.Sprintf("[%s] Package published by %s", repoLink, senderLink) + text = fmt.Sprintf("[%s] Package published by %s", packageLink, senderLink) case api.HookPackageDeleted: - text = fmt.Sprintf("[%s] Package deleted by %s", repoLink, senderLink) + text = fmt.Sprintf("[%s] Package deleted by %s", packageLink, senderLink) } return getMatrixPayload(text, nil, m.MsgType), nil diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 8c710942280..99a22fbd7eb 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -155,6 +155,19 @@ func TestMatrixPayload(t *testing.T) { assert.Equal(t, `[test/repo] Repository created by user1`, pl.(*MatrixPayload).FormattedBody) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(MatrixPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MatrixPayload{}, pl) + + assert.Equal(t, `[[GiteaContainer](http://localhost:3000/user1/-/packages/container/GiteaContainer/latest)] Package published by [user1](https://try.gitea.io/user1)`, pl.(*MatrixPayload).Body) + assert.Equal(t, `[GiteaContainer] Package published by user1`, pl.(*MatrixPayload).FormattedBody) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go index f58da3fe1cd..37810b4cd39 100644 --- a/services/webhook/msteams.go +++ b/services/webhook/msteams.go @@ -316,11 +316,12 @@ func GetMSTeamsPayload(p api.Payloader, event webhook_module.HookEventType, _ st } func createMSTeamsPayload(r *api.Repository, s *api.User, title, text, actionTarget string, color int, fact *MSTeamsFact) *MSTeamsPayload { - facts := []MSTeamsFact{ - { + facts := make([]MSTeamsFact, 0, 2) + if r != nil { + facts = append(facts, MSTeamsFact{ Name: "Repository:", Value: r.FullName, - }, + }) } if fact != nil { facts = append(facts, *fact) diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go index 990a535df5c..8d1aed60408 100644 --- a/services/webhook/msteams_test.go +++ b/services/webhook/msteams_test.go @@ -329,6 +329,33 @@ func TestMSTeamsPayload(t *testing.T) { assert.Equal(t, "http://localhost:3000/test/repo", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(MSTeamsPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &MSTeamsPayload{}, pl) + + assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*MSTeamsPayload).Title) + assert.Equal(t, "Package created: GiteaContainer:latest", pl.(*MSTeamsPayload).Summary) + assert.Len(t, pl.(*MSTeamsPayload).Sections, 1) + assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle) + assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text) + assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 1) + for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts { + if fact.Name == "Package:" { + assert.Equal(t, p.Package.Name, fact.Value) + } else { + t.Fail() + } + } + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1) + assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1) + assert.Equal(t, "http://localhost:3000/user1/-/packages/container/GiteaContainer/latest", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go index 932b56fe9b9..26d01b0555d 100644 --- a/services/webhook/packagist_test.go +++ b/services/webhook/packagist_test.go @@ -115,6 +115,15 @@ func TestPackagistPayload(t *testing.T) { require.Nil(t, pl) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(PackagistPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go index d9828f374f0..b1340963e23 100644 --- a/services/webhook/slack_test.go +++ b/services/webhook/slack_test.go @@ -144,6 +144,18 @@ func TestSlackPayload(t *testing.T) { assert.Equal(t, "[] Repository created by ", pl.(*SlackPayload).Text) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(SlackPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &SlackPayload{}, pl) + + assert.Equal(t, "Package created: by ", pl.(*SlackPayload).Text) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go index b42b0ccda88..5b9927d0571 100644 --- a/services/webhook/telegram_test.go +++ b/services/webhook/telegram_test.go @@ -144,6 +144,18 @@ func TestTelegramPayload(t *testing.T) { assert.Equal(t, `[test/repo] Repository created`, pl.(*TelegramPayload).Message) }) + t.Run("Package", func(t *testing.T) { + p := packageTestPayload() + + d := new(TelegramPayload) + pl, err := d.Package(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &TelegramPayload{}, pl) + + assert.Equal(t, `Package created: GiteaContainer:latest by user1`, pl.(*TelegramPayload).Message) + }) + t.Run("Wiki", func(t *testing.T) { p := wikiTestPayload() diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index 9d5dab85f79..ac18da35252 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" webhook_model "code.gitea.io/gitea/models/webhook" @@ -222,7 +223,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu var ws []*webhook_model.Webhook if source.Repository != nil { - repoHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{ + repoHooks, err := db.Find[webhook_model.Webhook](ctx, webhook_model.ListWebhookOptions{ RepoID: source.Repository.ID, IsActive: util.OptionalBoolTrue, }) @@ -236,7 +237,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu // append additional webhooks of a user or organization if owner != nil { - ownerHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{ + ownerHooks, err := db.Find[webhook_model.Webhook](ctx, webhook_model.ListWebhookOptions{ OwnerID: owner.ID, IsActive: util.OptionalBoolTrue, }) diff --git a/templates/admin/base/search.tmpl b/templates/admin/base/search.tmpl index d4bba8c0427..0fecb61d9e3 100644 --- a/templates/admin/base/search.tmpl +++ b/templates/admin/base/search.tmpl @@ -1,7 +1,7 @@