Compare commits

...

55 Commits

Author SHA1 Message Date
Michael Shaw
8c5c2a2ce9 backport of knacker fix attached to gitea pull 21862 2022-11-22 12:11:18 -05:00
zeripath
e77b76425e
Prepend refs/heads/ to issue template refs (#20461)
Fix #20456

At some point during the 1.17 cycle abbreviated refishs to issue
branches started breaking. This is likely due serious inconsistencies in
our management of refs throughout Gitea - which is a bug needing to be
addressed in a different PR. (Likely more than one)

We should try to use non-abbreviated `fullref`s as much as possible.
That is where a user has inputted a abbreviated `refish` we should add
`refs/heads/` if it is `branch` etc. I know people keep writing and
merging PRs that remove prefixes from stored content but it is just
wrong and it keeps causing problems like this. We should only remove the
prefix at the time of
presentation as the prefix is the only way of knowing umambiguously and
permanently if the `ref` is referring to a `branch`, `tag` or `commit` /
`SHA`. We need to make it so that every ref has the appropriate prefix,
and probably also need to come up with some definitely unambiguous way
of storing `SHA`s if they're used in a `ref` or `refish` field. We must
not store a potentially
ambiguous `refish` as a `ref`. (Especially when referring a `tag` -
there is no reason why users cannot create a `branch` with the same
short name as a `tag` and vice versa and any attempt to prevent this
will fail. You can even create a `branch` and a
`tag` that matches the `SHA` pattern.)

To that end in order to fix this bug, when parsing issue templates check
the provided `Ref` (here a `refish` because almost all users do not know
or understand the subtly), if it does not start with `refs/` add the
`BranchPrefix` to it. This allows people to make their templates refer
to a `tag` but not to a `SHA` directly. (I don't think that is
particularly unreasonable but if people disagree I can make the `refish`
be checked to see if it matches the `SHA` pattern.)

Next we need to handle the issue links that are already written. The
links here are created with `git.RefURL`

Here we see there is a bug introduced in #17551 whereby the provided
`ref` argument can be double-escaped so we remove the incorrect external
escape. (The escape added in #17551 is in the right place -
unfortunately I missed that the calling function was doing the wrong
thing.)

Then within `RefURL()` we check if an unprefixed `ref` (therefore
potentially a `refish`) matches the `SHA` pattern before assuming that
is actually a `commit` - otherwise is assumed to be a `branch`. This
will handle most of the problem cases excepting the very unusual cases
where someone has deliberately written a `branch` to look like a `SHA1`.

But please if something is called a `ref` or interpreted as a `ref` make
it a full-ref before storing or using it. By all means if something is a
`branch` assume the prefix is removed but always add it back in if you
are using it as a `ref`. Stop storing abbreviated `branch` names and
`tag` names - which are `refish` as a `ref`. It will keep on causing
problems like this.

Fix #20456

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-22 20:58:49 +08:00
Felipe Leopoldo Sologuren Gutiérrez
1d52228ee7
Fixes #21895: standardize UTC tz for util tests (#21897)
Standardize UTC timezone for `translateMonth` and `translateDay` tests.
2022-11-22 16:46:29 +08:00
mpeter50
371dd96e3e
Clarify logging documentation (#21665)
My pull request changes the logging documentation that is visible here:
https://docs.gitea.io/en-us/logging-configuration/
The reason behind the changes is that for some time I've found the
logging documentation confusing, and wanted to give a try at making it
more clear.

---

If you find the existing changes to be ok, please don't merge yet, as I
have further ideas which I want to discuss with you before making the
changes.

### Swap the "Log Groups" and "Log outputs" sections.
I want to move the "Log outputs" section before the "Log Groups"
section. The reason is that the "Log Groups" section refers to ini
sections that are only later explained, and to concepts that are general
and should be documented in "Log outputs" or a different section.

This change is essentially a swap of the "Log Groups" and "Log outputs"
sections. That way the doumentation would follow the structure in which
the ini file is built: first explaining the outer sections, and then the
inner ones ([log], [log.name], [log.name.default], ...)

### Explain the workings of ambigous settings below the settings listing
Right now the basics of a setting is shown later than the explanation of
its special workings, for example with `FILE_NAME` at [the file output
mode](https://docs.gitea.io/en-us/logging-configuration/#file-mode)
(well, if the first changes are taken into account).

Currently I have `TODO` witten at 2 settings, which I have to figure out
how do they exactly work before I can document them.

### New section about [log]
New section after "Collecting Logs for Help" about how the top level
[log] itself works and what can go there.
Currently, variables that directly go into [log] are noted throughout
the whole document.

---

Please let me know what you think about the changes.

A counterargument that I myself see is that some of this is already
present in the cheatsheet, but I think it would be better to have [this
document](https://docs.gitea.io/en-us/logging-configuration/) as a
throrough explanation of how logging is configured, and the cheatsheet
would only have a short outline of the possible sections and variables.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-22 09:04:29 +08:00
silverwind
c8b217110b
Update JS dependencies (#21881)
- Update all JS deps
- Regenerate SVGs
- Add new eslint rules, fix issues
- Tested Mermaid, Swagger, Vue, Webpack, Citation

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-22 08:58:55 +08:00
silverwind
1c7496c7a7
Webhook list enhancements (#21893) 2022-11-22 07:10:42 +08:00
silverwind
2e5ac53bdc
Embed Matrix icon as SVG (#21890)
Embed the SVG icon directly, making further invertion unnecessary
because the icon color can now follow text color.

<img width="240" alt="Screenshot 2022-11-21 at 20 16 32"
src="https://user-images.githubusercontent.com/115237/203142189-89f20de9-c0bd-4d05-92c0-44dadf20d78f.png">
<img width="245" alt="Screenshot 2022-11-21 at 20 16 46"
src="https://user-images.githubusercontent.com/115237/203142191-658239ba-1859-49c6-91ad-10ddf14780d0.png">
2022-11-21 20:25:26 +00:00
Percy Ma
2836382f34
fix(web): add alt for logo in home page (#21887)
add `alt` for logo in home page
2022-11-21 15:09:55 -05:00
silverwind
c2fb27beb4
Improvements for Content Copy (#21842)
It now supports copying Markdown, SVG and Images (not in Firefox
currently because of lacking
[`ClipboardItem`](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem)
support, but can be enabled in `about:config` and works). It will fetch
the data if in a rendered view or when it's an image.

Followup to https://github.com/go-gitea/gitea/pull/21629.
2022-11-21 17:59:42 +08:00
Jason Song
e4eaa68a2b
Replace yaml.v2 with yaml.v3 (#21832)
I don't see why we have to use two versions of yaml. The difference
between the two versions has nothing to do with our usage.
2022-11-21 16:36:59 +08:00
Xinyu Zhou
b4802b9b2e
Allow disable RSS/Atom feed (#21622)
This patch provide a mechanism to disable RSS/Atom feed.

Signed-off-by: Xinyu Zhou <i@sourcehut.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: 6543 <6543@obermui.de>
2022-11-21 13:14:58 +08:00
silverwind
9380bb6d0c
Consolidate security-check into checks-backend (#21882)
Also, run it via exact version instead of relying on global binary.
2022-11-21 10:39:00 +08:00
Andrew Buettner
43aafc5ba1
Improve documentation for PAM and static deployment (#21866)
## Changes proposed in [referenced issue 21845][1]

- Expand PAM configuration description with working examples.
- Clarify `STATIC_URL_PREFIX` use (include "assets" and only works after
database has been initialized)
- Add note for HTTPS proxy support VIA Apache.

[1]: https://github.com/go-gitea/gitea/issues/21845
2022-11-20 20:14:03 +00:00
KN4CK3R
32db62515f
Add package registry cleanup rules (#21658)
Fixes #20514
Fixes #20766
Fixes #20631

This PR adds Cleanup Rules for the package registry. This allows to
delete unneeded packages automatically. Cleanup rules can be set up from
the user or org settings.
Please have a look at the documentation because I'm not a native english
speaker.

Rule Form

![grafik](https://user-images.githubusercontent.com/1666336/199330792-c13918a6-e196-4e71-9f53-18554515edca.png)

Rule List

![grafik](https://user-images.githubusercontent.com/1666336/199331261-5f6878e8-a80c-4985-800d-ebb3524b1a8d.png)

Rule Preview

![grafik](https://user-images.githubusercontent.com/1666336/199330917-c95e4017-cf64-4142-a3e4-af18c4f127c3.png)

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-20 16:08:38 +02:00
Jason Song
d3f850cc0e
Support comma-delimited string as labels in issue template (#21831)
The [labels in issue YAML
templates](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms#top-level-syntax)
can be a string array or a comma-delimited string, so a single string
should be valid labels.

The old codes committed in #20987 ignore this, that's why the warning is
displayed:

<img width="618" alt="image"
src="https://user-images.githubusercontent.com/9418365/202112642-93dc72d0-71c3-40a2-9720-30fc2d48c97c.png">

Fixes #17877.
2022-11-19 15:22:15 +00:00
Jim Kirisame
c8f3eb6acb
Fix wechatwork webhook sends empty content in PR review (#21762)
Wechatwork webhook is sending the following string for pull request reviews:

``` markdown
# 
>
```

This commit fixes this problem.
2022-11-19 15:19:14 +00:00
silverwind
eec1c71880
Show syntax lexer name in file view/blame (#21814)
Show which Chroma Lexer is used to highlight the file in the file
header. It's useful for development to see what was detected, and I
think it's not bad info to have for the user:

<img width="233" alt="Screenshot 2022-11-14 at 22 31 16"
src="https://user-images.githubusercontent.com/115237/201770854-44933dfc-70a4-487c-8457-1bb3cc43ea62.png">
<img width="226" alt="Screenshot 2022-11-14 at 22 36 06"
src="https://user-images.githubusercontent.com/115237/201770856-9260ce6f-6c0f-442c-92b5-201e5b113188.png">
<img width="194" alt="Screenshot 2022-11-14 at 22 36 26"
src="https://user-images.githubusercontent.com/115237/201770857-6f56591b-80ea-42cc-8ea5-21b9156c018b.png">

Also, I improved the way this header overflows on small screens:

<img width="354" alt="Screenshot 2022-11-14 at 22 44 36"
src="https://user-images.githubusercontent.com/115237/201774828-2ddbcde1-da15-403f-bf7a-6248449fa2c5.png">

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2022-11-19 13:08:06 +02:00
KN4CK3R
044c754ea5
Add context.Context to more methods (#21546)
This PR adds a context parameter to a bunch of methods. Some helper
`xxxCtx()` methods got replaced with the normal name now.

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-19 16:12:33 +08:00
silverwind
fefdb7ffd1
Timeline and color tweaks (#21799)
Followup to https://github.com/go-gitea/gitea/pull/21784.

- Restore muted effect on timeline author and issuelist comment icon
- Remove whitespace inside shared user templates, fixing link hover
underline
- Use shared author link template more
- Use `bold` class instead of CSS
- Fix grey-light color being too dark on arc-green
- Add missing black-light color
- Fix issuelist progress bar color
- Fix various other cases of missing `.muted`

<img width="416" alt="Screenshot 2022-11-13 at 12 15 22"
src="https://user-images.githubusercontent.com/115237/201519497-1d4725c6-bc8b-47b5-9f68-1278ac9a8c92.png">
<img width="324" alt="Screenshot 2022-11-13 at 12 16 52"
src="https://user-images.githubusercontent.com/115237/201519501-c0d03700-f9af-4316-ab46-482f2c7c738b.png">
<img width="79" alt="Screenshot 2022-11-13 at 12 30 55"
src="https://user-images.githubusercontent.com/115237/201519502-46dc2d73-bbdf-4a2e-84d3-d2976f793163.png">
<img width="440" alt="Screenshot 2022-11-13 at 12 41 03"
src="https://user-images.githubusercontent.com/115237/201519876-ada33948-f84a-4aeb-a40d-5c873f9a49e9.png">
<img width="213" alt="Screenshot 2022-11-13 at 12 52 54"
src="https://user-images.githubusercontent.com/115237/201520291-a4d7238e-aeca-46c7-9008-8b644b1b676e.png">
<img width="208" alt="Screenshot 2022-11-13 at 12 56 16"
src="https://user-images.githubusercontent.com/115237/201520436-aa8ba109-b959-42fb-831a-021e806c7082.png">

Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-11-19 12:02:30 +08:00
silverwind
595d940daa
Fix webpack license warning (#21815)
#19999 introduced a indirect dependency with a license that was not on
our allowlist yet which produced this warning during webpack:

````
WARNING in License: citeproc@2.4.62 has disallowed license CPAL-1.0 OR AGPL-1.0
````

I've added both licenses to the allowed list and made it so webpack will
now abort on such license errors so that we don't miss those next time.

Co-authored-by: John Olheiser <john.olheiser@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-18 11:54:32 -06:00
Percy Ma
6da8bc6be9
chore: add webpack export type check (#21857)
add webpack export type check

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-11-18 11:01:06 -06:00
Gusted
20385b52a3
Prevent dangling user redirects (#21856)
- It's possible that the `user_redirect` table contains a user id that
no longer exists.
- Delete a user redirect upon deleting the user.
- Add a check for these dangling user redirects to check-db-consistency.
2022-11-18 22:23:34 +08:00
wxiaoguang
0b993a0d04
Fix "build from source" document to clarify the bindata tag is required. (#21853) 2022-11-18 14:34:39 +08:00
dependabot[bot]
6dbcf724ac
Bump loader-utils from 2.0.3 to 2.0.4 (#21852)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.3
to 2.0.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/webpack/loader-utils/releases">loader-utils's
releases</a>.</em></p>
<blockquote>
<h2>v2.0.4</h2>
<h3><a
href="https://github.com/webpack/loader-utils/compare/v2.0.3...v2.0.4">2.0.4</a>
(2022-11-11)</h3>
<h3>Bug Fixes</h3>
<ul>
<li>ReDoS problem (<a
href="https://github-redirect.dependabot.com/webpack/loader-utils/issues/225">#225</a>)
(<a
href="ac09944dfa">ac09944</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md">loader-utils's
changelog</a>.</em></p>
<blockquote>
<h3><a
href="https://github.com/webpack/loader-utils/compare/v2.0.3...v2.0.4">2.0.4</a>
(2022-11-11)</h3>
<h3>Bug Fixes</h3>
<ul>
<li>ReDoS problem (<a
href="https://github-redirect.dependabot.com/webpack/loader-utils/issues/225">#225</a>)
(<a
href="ac09944dfa">ac09944</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6688b50281"><code>6688b50</code></a>
chore(release): 2.0.4</li>
<li><a
href="ac09944dfa"><code>ac09944</code></a>
fix: ReDoS problem (<a
href="https://github-redirect.dependabot.com/webpack/loader-utils/issues/225">#225</a>)</li>
<li>See full diff in <a
href="https://github.com/webpack/loader-utils/compare/v2.0.3...v2.0.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=loader-utils&package-manager=npm_and_yarn&previous-version=2.0.3&new-version=2.0.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the
default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as
the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as
the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the
default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/go-gitea/gitea/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-17 21:32:59 -05:00
KN4CK3R
88d5275614
Do not allow Ghost access to limited visible user/org (#21849)
The Ghost user should not be allowed to have access to a limited visible
user/org.

Co-authored-by: Lauris BH <lauris@nix.lv>
2022-11-17 14:29:33 -05:00
KN4CK3R
43ab9324c5
Fix setting HTTP headers after write (#21833)
The headers can't be modified after it was send to the client.
2022-11-18 01:55:15 +08:00
silverwind
c144942b23
Tweak katex options (#21828)
- Render directly into DOM, skipping string conversion
- Add limiting options to prevent excessive size/macros
- Remove invalid `display` option previously passed

Ref: https://katex.org/docs/options.html

Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2022-11-17 09:04:09 +08:00
Jason Song
92dd24716d
Ignore issue template with a special name (#21830)
A file in `ISSUE_TEMPLATE` with the name `config.yml` shouldn't be
treated as a YAML template, it's for [configuring the template
chooser](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser).

The old code tried to ignore the file, but it didn't work, caused by
#20987. That's why the warning is displayed:

<img width="415" alt="image"
src="https://user-images.githubusercontent.com/9418365/202094067-804c42fe-0e9e-4fc5-bf01-d95fa336f54f.png">

Note that this PR is not an implementation of `config.yml`, there will
be another one to do it.
2022-11-16 19:14:58 +08:00
May
f311d15a0b
Added space between avatar and username (#21825)
Added space between avatar and username which is missing on verified
commit message and avatar is too close to username which is don't look
nice.

Current state

![image](https://user-images.githubusercontent.com/3164256/202007728-d7d6ecac-f754-454c-a67f-e422f4aac5a5.png)


This is how it looks after change

![image](https://user-images.githubusercontent.com/3164256/202007984-d4a38a1c-7c24-4278-aa0f-9aa51c10f772.png)

Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-11-15 16:36:53 -05:00
Gary Moon
40229a7dd8
Skip GitHub migration tests if the API token is undefined (#21824)
GitHub migration tests will be skipped if the secret for the GitHub API
token hasn't been set.

This change should make all tests pass (or skip in the case of this one)
for anyone running the pipeline on their own infrastructure without
further action on their part.

Resolves https://github.com/go-gitea/gitea/issues/21739

Signed-off-by: Gary Moon <gary@garymoon.net>
2022-11-15 15:22:16 -05:00
Yarden Shoham
6c8ff32511
Add updated_at field to PullReview API object (#21812)
* Closes #19997

Adds an `updated_at` time field to the `PullReview` API object to
specify when the pull request review's state changed.

Signed-off-by: Yarden Shoham <hrsi88@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-15 11:33:52 +02:00
zeripath
c772934ff6
Adjust gitea doctor --run storages to check all storage types (#21785)
The doctor check `storages` currently only checks the attachment
storage. This PR adds some basic garbage collection functionality for
the other types of storage.

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-15 16:08:59 +08:00
Yarden Shoham
de6dfb7141
Remove href="javascript:;" in "save topics (Done)" button (#21813)
To use an anchor tag as a button and have it be accessible I added
`role="button" tabindex="0"`,
[reference](https://stackoverflow.com/a/10510353/7414734).

* Closes #19912
2022-11-15 10:10:50 +08:00
delvh
55115dbb73
Improve pull/ push mirror documentation (especially for GitHub) (#21801) 2022-11-14 21:17:39 +00:00
zeripath
d9ba7f7442
Prevent panic in doctor command when running default checks (#21791)
There was a bug introduced in #21352 due to a change of behaviour caused
by #19280. This causes a panic on running the default doctor checks
because the panic introduced by #19280 assumes that the only way
opts.StdOut and opts.Stderr can be set in RunOpts is deliberately.
Unfortunately, when running a git.Command the provided RunOpts can be
set, therefore if you share a common set of RunOpts these two values can
be set by the previous commands.

This PR stops using common RunOpts for the commands in that doctor check
but secondly stops RunCommand variants from changing the provided
RunOpts.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-11-13 20:45:20 +00:00
silverwind
3e3975e0fa
fix webpackChunkName for citation-js-csl (#21806) 2022-11-13 15:10:58 -05:00
delvh
8ce2dd588a
Add plural definitions for German translations (#21802)
Previously, there were discussions on how to write certain plurals.
So, we explicitly document the special plurals to end the discussion.
2022-11-13 11:58:21 -05:00
Jim Kirisame
0ace4cee33
Fix webhook attachment text is not set in review comment (#21763)
The `getPullRequestPayloadInfo` function is widely used in many webhook,
it works well when PR is open or edit. But when we comment in PR review
panel (not PR panel), the comment content is not set as
`attachmentText`.

This commit set comment content as `attachmentText` when PR review, so
webhook could obtain this information via this function.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-11-13 11:57:52 -05:00
Yarden Shoham
6f3efdfe11
Render number of commits in repo page in a user friendly way (#21786)
Use `JsPrettyNumber` to render the number of commits

* Closes #12637

### Before

![1094](https://user-images.githubusercontent.com/20454870/201484428-eaa80d27-eeed-444e-9dc5-ae046424de2f.png)

### After

![1,094](https://user-images.githubusercontent.com/20454870/201484385-b5bdc290-86fd-493b-a87c-c987012b18ad.png)

Signed-off-by: Yarden Shoham <hrsi88@gmail.com>
Co-authored-by: 6543 <6543@obermui.de>
2022-11-13 00:55:19 -05:00
Gusted
bea25d77ce
Upgrade golang.org/x/crypto (#21792)
- Update the crypto dependency to include
6fad3dfc18
- Resolves #17798

Executed: `go get
golang.org/x/crypto@6fad3dfc18918c2ac9c112e46b32473bd2e5e2f9 && rm
go.sum && go mod tidy`
2022-11-12 22:14:35 -06:00
Lunny Xiao
34283a74e8
Allow detect whether it's in a database transaction for a context.Context (#21756)
Fix #19513

This PR introduce a new db method `InTransaction(context.Context)`,
and also builtin check on `db.TxContext` and `db.WithTx`.
There is also a new method `db.AutoTx` has been introduced but could be used by other PRs.

`WithTx` will always open a new transaction, if a transaction exist in context, return an error.
`AutoTx` will try to open a new transaction if no transaction exist in context.
That means it will always enter a transaction if there is no error.

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: 6543 <6543@obermui.de>
2022-11-12 21:18:50 +01:00
zeripath
a0a425a13b
Add some documentation to packages (#21648)
In #21637 it was mentioned that the purpose of the API routes for the
packages is unclear. This PR adds some documentation.

Fix #21637

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2022-11-12 18:59:15 +00:00
zeripath
158b088ec3
Adjust clone timeout error to suggest increasing timeout (#21769)
There are far too many error reports regarding timeouts from migrations.
We should adjust error report to suggest increasing this timeout.

Ref #20680

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-11-12 18:58:26 +00:00
silverwind
aed1622766
Simplify text color selectors and tweak arc-green colors (#21784)
Move the text color rules out of the unneeded `.ui` block, add missing
colors, tweak colors on arc-green to be more readable (red was
particulary bad to read).

Also, this removes the previous inheritance of link colors. I think
links should always be in primary color and if they are to be
discolored, the color should be set on them explicitely.

<img width="165" alt="Screenshot 2022-11-12 at 13 28 30"
src="https://user-images.githubusercontent.com/115237/201474098-700d9fed-3133-43c7-b57e-d4cc5c2795cb.png">

<img width="152" alt="Screenshot 2022-11-12 at 13 18 48"
src="https://user-images.githubusercontent.com/115237/201474156-b6de4cb5-bce8-4553-b3d4-8365aff9a3a7.png">

HTML to test with:

```html
<div class="text red">some text with <a href="#foo">a link</a>.</div>
<div class="text orange">some text with <a href="#foo">a link</a>.</div>
<div class="text yellow">some text with <a href="#foo">a link</a>.</div>
<div class="text olive">some text with <a href="#foo">a link</a>.</div>
<div class="text green">some text with <a href="#foo">a link</a>.</div>
<div class="text teal">some text with <a href="#foo">a link</a>.</div>
<div class="text blue">some text with <a href="#foo">a link</a>.</div>
<div class="text violet">some text with <a href="#foo">a link</a>.</div>
<div class="text purple">some text with <a href="#foo">a link</a>.</div>
<div class="text pink">some text with <a href="#foo">a link</a>.</div>
<div class="text brown">some text with <a href="#foo">a link</a>.</div>
<div class="text grey">some text with <a href="#foo">a link</a>.</div>
2022-11-12 16:30:52 +02:00
Jason Song
bf2078640f
Load GitRepo in API before deleting issue (#21720)
Fix #20921.

The `ctx.Repo.GitRepo` has been used in deleting issues when the issue
is a PR.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Lauris BH <lauris@nix.lv>
2022-11-12 08:32:04 +08:00
Nolann
9f8e778918
Copy citation file content, in APA and BibTex format, on repo home page (#19999)
Add feature to easily copy CITATION.cff content in APA and BibTex format.
2022-11-11 18:02:50 +01:00
silverwind
9db221780f
Ignore line anchor links with leading zeroes (#21728)
Fixes: https://github.com/go-gitea/gitea/issues/21722

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-11 18:22:36 +08:00
Jason Song
50dd32ede4
Update message of reach_limit_of_creation (#21757)
When I'm trying to use a user which has no repo numbers limit to create
a repo for a org which has reached the limit, it says "You have already
reached your limit of 5 repositories."

That's confusing. "I" haven't reached the limit, the owner has.

<img width="828" alt="xnip_2022-11-10_11-57-45"
src="https://user-images.githubusercontent.com/9418365/200997290-d0819e40-fb10-4c37-917c-167e2070b4f9.png">

Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-11-11 17:19:12 +08:00
Drew Noel
2cbea23d70
Add configuration for CORS allowed headers (#21747)
This PR enhances the CORS middleware usage by allowing for the headers
to be configured in `app.ini`.

Fixes #21746

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-11-11 14:39:27 +08:00
wxiaoguang
fb704f6c72
Revert unrelated changes for SMTP auth (#21767)
The purpose of #18982 is to improve the SMTP mailer, but there were some
unrelated changes made to the SMTP auth in
d60c438694

This PR reverts these unrelated changes, fix #21744
2022-11-10 16:12:23 -05:00
wxiaoguang
92525ddffd
Init git module before database migration (#21764)
Close #21761

Some database migrations depend on the git module.
2022-11-10 14:22:39 +00:00
Jason Song
1d22911cfe
Extract updateSession function to reduce repetition (#21735)
A simple refactor to reduce duplicate codes.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: delvh <dev.lh@web.de>
2022-11-10 19:43:06 +08:00
Lunny Xiao
385462d36c
Fix dashboard ignored system setting cache (#21621)
This is a performance regression from #18058

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Andrew Thornton <art27@cantab.net>
2022-11-10 14:43:53 +08:00
Jason Song
ce5aafbc69
Add .dockerignore (#21753)
There's a lot of work that has been done on `.dockerignore`:

- #329
- #2927
- #8338

And finally, it has been deleted by #2927.

This is a copy of the `.gitignore`.
Creating a soft link is more elegant, but it may cause trouble to the Windows users.
2022-11-10 04:04:09 +01:00
zeripath
99688ef994
Attempt clarify AppWorkPath etc. (#21656)
Attempt clarify the AppWorkPath in the documentation by using different
notation and adding a section to the start of the cheat sheet.

Fix #21523

Signed-off-by: Andrew Thornton <art27@cantab.net>

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-11-09 20:22:31 -05:00
381 changed files with 6635 additions and 4184 deletions

114
.dockerignore Normal file
View File

@ -0,0 +1,114 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# IntelliJ
.idea
# Goland's output filename can not be set manually
/go_build_*
# MS VSCode
.vscode
__debug_bin
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
*coverage.out
coverage.all
cpu.out
/modules/migration/bindata.go
/modules/migration/bindata.go.hash
/modules/options/bindata.go
/modules/options/bindata.go.hash
/modules/public/bindata.go
/modules/public/bindata.go.hash
/modules/templates/bindata.go
/modules/templates/bindata.go.hash
*.db
*.log
/gitea
/gitea-vet
/debug
/integrations.test
/bin
/dist
/custom/*
!/custom/conf
/custom/conf/*
!/custom/conf/app.example.ini
/data
/indexers
/log
/public/img/avatar
/tests/integration/gitea-integration-*
/tests/integration/indexers-*
/tests/e2e/gitea-e2e-*
/tests/e2e/indexers-*
/tests/e2e/reports
/tests/e2e/test-artifacts
/tests/e2e/test-snapshots
/tests/*.ini
/node_modules
/yarn.lock
/yarn-error.log
/npm-debug.log*
/public/js
/public/serviceworker.js
/public/css
/public/fonts
/public/img/webpack
/vendor
/web_src/fomantic/node_modules
/web_src/fomantic/build/*
!/web_src/fomantic/build/semantic.js
!/web_src/fomantic/build/semantic.css
!/web_src/fomantic/build/themes
/web_src/fomantic/build/themes/*
!/web_src/fomantic/build/themes/default
/web_src/fomantic/build/themes/default/assets/*
!/web_src/fomantic/build/themes/default/assets/fonts
/web_src/fomantic/build/themes/default/assets/fonts/*
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
/VERSION
/.air
/.go-licenses
# Snapcraft
snap/.snapcraft/
parts/
stage/
prime/
*.snap
*.snap-build
*_source.tar.bz2
.DS_Store
# Make evidence files
/.make_evidence
# Manpage
/man

View File

@ -39,16 +39,6 @@ steps:
- make lint-frontend - make lint-frontend
depends_on: [deps-frontend] depends_on: [deps-frontend]
- name: security-check
image: golang:1.19
pull: always
commands:
- make security-check
depends_on: [deps-backend]
volumes:
- name: deps
path: /go
- name: lint-backend - name: lint-backend
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
pull: always pull: always
@ -561,7 +551,7 @@ steps:
# TODO: We should probably build all dependencies into a test image # TODO: We should probably build all dependencies into a test image
- name: test-e2e - name: test-e2e
image: mcr.microsoft.com/playwright:v1.27.1-focal image: mcr.microsoft.com/playwright:v1.28.0-focal
commands: commands:
- curl -sLO https://go.dev/dl/go1.19.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz - curl -sLO https://go.dev/dl/go1.19.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz
- groupadd --gid 1001 gitea && useradd -m --gid 1001 --uid 1001 gitea - groupadd --gid 1001 gitea && useradd -m --gid 1001 --uid 1001 gitea

View File

@ -199,7 +199,7 @@ rules:
newline-per-chained-call: [0] newline-per-chained-call: [0]
no-alert: [0] no-alert: [0]
no-array-constructor: [2] no-array-constructor: [2]
no-async-promise-executor: [2] no-async-promise-executor: [0]
no-await-in-loop: [0] no-await-in-loop: [0]
no-bitwise: [0] no-bitwise: [0]
no-buffer-constructor: [0] no-buffer-constructor: [0]
@ -229,6 +229,7 @@ rules:
no-empty-character-class: [2] no-empty-character-class: [2]
no-empty-function: [0] no-empty-function: [0]
no-empty-pattern: [2] no-empty-pattern: [2]
no-empty-static-block: [2]
no-empty: [2, {allowEmptyCatch: true}] no-empty: [2, {allowEmptyCatch: true}]
no-eq-null: [2] no-eq-null: [2]
no-eval: [2] no-eval: [2]
@ -269,6 +270,7 @@ rules:
no-negated-condition: [0] no-negated-condition: [0]
no-nested-ternary: [0] no-nested-ternary: [0]
no-new-func: [2] no-new-func: [2]
no-new-native-nonconstructor: [2]
no-new-object: [2] no-new-object: [2]
no-new-symbol: [2] no-new-symbol: [2]
no-new-wrappers: [2] no-new-wrappers: [2]
@ -443,6 +445,7 @@ rules:
unicorn/no-invalid-remove-event-listener: [2] unicorn/no-invalid-remove-event-listener: [2]
unicorn/no-keyword-prefix: [0] unicorn/no-keyword-prefix: [0]
unicorn/no-lonely-if: [2] unicorn/no-lonely-if: [2]
unicorn/no-negated-condition: [0]
unicorn/no-nested-ternary: [0] unicorn/no-nested-ternary: [0]
unicorn/no-new-array: [0] unicorn/no-new-array: [0]
unicorn/no-new-buffer: [0] unicorn/no-new-buffer: [0]
@ -453,6 +456,7 @@ rules:
unicorn/no-static-only-class: [2] unicorn/no-static-only-class: [2]
unicorn/no-thenable: [2] unicorn/no-thenable: [2]
unicorn/no-this-assignment: [2] unicorn/no-this-assignment: [2]
unicorn/no-typeof-undefined: [2]
unicorn/no-unnecessary-await: [2] unicorn/no-unnecessary-await: [2]
unicorn/no-unreadable-array-destructuring: [0] unicorn/no-unreadable-array-destructuring: [0]
unicorn/no-unreadable-iife: [2] unicorn/no-unreadable-iife: [2]
@ -503,6 +507,7 @@ rules:
unicorn/prefer-regexp-test: [2] unicorn/prefer-regexp-test: [2]
unicorn/prefer-replace-all: [0] unicorn/prefer-replace-all: [0]
unicorn/prefer-set-has: [0] unicorn/prefer-set-has: [0]
unicorn/prefer-set-size: [2]
unicorn/prefer-spread: [0] unicorn/prefer-spread: [0]
unicorn/prefer-starts-ends-with: [2] unicorn/prefer-starts-ends-with: [2]
unicorn/prefer-string-slice: [0] unicorn/prefer-string-slice: [0]

View File

@ -333,7 +333,7 @@ checks: checks-frontend checks-backend
checks-frontend: lockfile-check svg-check checks-frontend: lockfile-check svg-check
.PHONY: checks-backend .PHONY: checks-backend
checks-backend: tidy-check swagger-check fmt-check misspell-check swagger-validate checks-backend: tidy-check swagger-check fmt-check misspell-check swagger-validate security-check
.PHONY: lint .PHONY: lint
lint: lint-frontend lint-backend lint: lint-frontend lint-backend
@ -745,7 +745,7 @@ generate-go: $(TAGS_PREREQ)
.PHONY: security-check .PHONY: security-check
security-check: security-check:
govulncheck -v ./... go run $(GOVULNCHECK_PACKAGE) -v ./...
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ) $(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@

View File

@ -413,9 +413,9 @@ var (
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN", Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "addr", Name: "host",
Value: "", Value: "",
Usage: "SMTP Addr", Usage: "SMTP Host",
}, },
cli.IntFlag{ cli.IntFlag{
Name: "port", Name: "port",
@ -727,7 +727,7 @@ func runRepoSyncReleases(_ *cli.Context) error {
log.Trace("Synchronizing repository releases (this may take a while)") log.Trace("Synchronizing repository releases (this may take a while)")
for page := 1; ; page++ { for page := 1; ; page++ {
repos, count, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{ repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
PageSize: repo_model.RepositoryListDefaultPageSize, PageSize: repo_model.RepositoryListDefaultPageSize,
Page: page, Page: page,
@ -955,8 +955,8 @@ func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
} }
conf.Auth = c.String("auth-type") conf.Auth = c.String("auth-type")
} }
if c.IsSet("addr") { if c.IsSet("host") {
conf.Addr = c.String("addr") conf.Host = c.String("host")
} }
if c.IsSet("port") { if c.IsSet("port") {
conf.Port = c.Int("port") conf.Port = c.Int("port")

View File

@ -7,6 +7,38 @@
;; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation. ;; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Default Configuration (non-`app.ini` configuration)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; These values are environment-dependent but form the basis of a lot of values. They will be
;; reported as part of the default configuration when running `gitea --help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up.
;;
;; - _`AppPath`_: This is the absolute path of the running gitea binary.
;; - _`AppWorkPath`_: This refers to "working path" of the `gitea` binary. It is determined by using the first set thing in the following hierarchy:
;; - The `--work-path` flag passed to the binary
;; - The environment variable `$GITEA_WORK_DIR`
;; - A built-in value set at build time (see building from source)
;; - Otherwise it defaults to the directory of the _`AppPath`_
;; - If any of the above are relative paths then they are made absolute against the
;; the directory of the _`AppPath`_
;; - _`CustomPath`_: This is the base directory for custom templates and other options.
;; It is determined by using the first set thing in the following hierarchy:
;; - The `--custom-path` flag passed to the binary
;; - The environment variable `$GITEA_CUSTOM`
;; - A built-in value set at build time (see building from source)
;; - Otherwise it defaults to _`AppWorkPath`_`/custom`
;; - If any of the above are relative paths then they are made absolute against the
;; the directory of the _`AppWorkPath`_
;; - _`CustomConf`_: This is the path to the `app.ini` file.
;; - The `--config` flag passed to the binary
;; - A built-in value set at build time (see building from source)
;; - Otherwise it defaults to _`CustomPath`_`/conf/app.ini`
;; - If any of the above are relative paths then they are made absolute against the
;; the directory of the _`CustomPath`_
;;
;; In addition there is _`StaticRootPath`_ which can be set as a built-in at build time, but will otherwise default to _`AppWorkPath`_
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; General Settings ;; General Settings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -26,7 +58,7 @@ RUN_MODE = ; prod
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'. Defaults to 'http' ;; The protocol the server listens on. One of 'http', 'https', 'http+unix', 'fcgi' or 'fcgi+unix'. Defaults to 'http'
;PROTOCOL = http ;PROTOCOL = http
;; ;;
;; Expect PROXY protocol headers on connections ;; Expect PROXY protocol headers on connections
@ -51,6 +83,8 @@ RUN_MODE = ; prod
;STATIC_URL_PREFIX = ;STATIC_URL_PREFIX =
;; ;;
;; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket. ;; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
;; If PROTOCOL is set to `http+unix` or `fcgi+unix`, this should be the name of the Unix socket file to use.
;; Relative paths will be made absolute against the _`AppWorkPath`_.
;HTTP_ADDR = 0.0.0.0 ;HTTP_ADDR = 0.0.0.0
;; ;;
;; The port to listen on. Leave empty when using a unix socket. ;; The port to listen on. Leave empty when using a unix socket.
@ -64,7 +98,7 @@ RUN_MODE = ; prod
;PORT_TO_REDIRECT = 80 ;PORT_TO_REDIRECT = 80
;; ;;
;; expect PROXY protocol header on connections to https redirector. ;; expect PROXY protocol header on connections to https redirector.
;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL) ;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
;; Minimum and maximum supported TLS versions ;; Minimum and maximum supported TLS versions
;SSL_MIN_VERSION=TLSv1.2 ;SSL_MIN_VERSION=TLSv1.2
;SSL_MAX_VERSION= ;SSL_MAX_VERSION=
@ -91,7 +125,7 @@ RUN_MODE = ; prod
;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/ ;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
;; ;;
;; When making local connections pass the PROXY protocol header. ;; When making local connections pass the PROXY protocol header.
;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL) ;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
;; ;;
;; Disable SSH feature when not available ;; Disable SSH feature when not available
;DISABLE_SSH = false ;DISABLE_SSH = false
@ -145,7 +179,7 @@ RUN_MODE = ; prod
;; ;;
;; For the built-in SSH server, choose the keypair to offer as the host key ;; For the built-in SSH server, choose the keypair to offer as the host key
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub ;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
;; relative paths are made absolute relative to the APP_DATA_PATH ;; relative paths are made absolute relative to the %(APP_DATA_PATH)s
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa ;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
;; ;;
;; Directory to create temporary files in when testing public keys using ssh-keygen, ;; Directory to create temporary files in when testing public keys using ssh-keygen,
@ -241,10 +275,10 @@ RUN_MODE = ; prod
;; ;;
;; Root directory containing templates and static files. ;; Root directory containing templates and static files.
;; default is the path where Gitea is executed ;; default is the path where Gitea is executed
;STATIC_ROOT_PATH = ;STATIC_ROOT_PATH = ; Will default to the built-in value _`StaticRootPath`_
;; ;;
;; Default path for App data ;; Default path for App data
;APP_DATA_PATH = data ;APP_DATA_PATH = data ; relative paths will be made absolute with _`AppWorkPath`_
;; ;;
;; Enable gzip compression for runtime-generated content, static resources excluded ;; Enable gzip compression for runtime-generated content, static resources excluded
;ENABLE_GZIP = false ;ENABLE_GZIP = false
@ -255,7 +289,7 @@ RUN_MODE = ; prod
;ENABLE_PPROF = false ;ENABLE_PPROF = false
;; ;;
;; PPROF_DATA_PATH, use an absolute path when you start gitea as service ;; PPROF_DATA_PATH, use an absolute path when you start gitea as service
;PPROF_DATA_PATH = data/tmp/pprof ;PPROF_DATA_PATH = data/tmp/pprof ; Path is relative to _`AppWorkPath`_
;; ;;
;; Landing page, can be "home", "explore", "organizations", "login", or any URL such as "/org/repo" or even "https://anotherwebsite.com" ;; Landing page, can be "home", "explore", "organizations", "login", or any URL such as "/org/repo" or even "https://anotherwebsite.com"
;; The "login" choice is not a security measure but just a UI flow change, use REQUIRE_SIGNIN_VIEW to force users to log in. ;; The "login" choice is not a security measure but just a UI flow change, use REQUIRE_SIGNIN_VIEW to force users to log in.
@ -633,7 +667,7 @@ ROUTER = console
;PATH = ;PATH =
;; ;;
;; The HOME directory for Git ;; The HOME directory for Git
;HOME_PATH = %(APP_DATA_PATH)/home ;HOME_PATH = %(APP_DATA_PATH)s/home
;; ;;
;; Disables highlight of added and removed changes ;; Disables highlight of added and removed changes
;DISABLE_DIFF_HIGHLIGHT = false ;DISABLE_DIFF_HIGHLIGHT = false
@ -838,8 +872,8 @@ ROUTER = console
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository] ;[repository]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Root path for storing all repository data. By default, it is set to %(APP_DATA_PATH)/gitea-repositories. ;; Root path for storing all repository data. By default, it is set to %(APP_DATA_PATH)s/gitea-repositories.
;; A relative path is interpreted as %(GITEA_WORK_DIR)/%(ROOT) ;; A relative path is interpreted as _`AppWorkPath`_/%(ROOT)s
;ROOT = ;ROOT =
;; ;;
;; The script type this server supports. Usually this is `bash`, but some users report that only `sh` is available. ;; The script type this server supports. Usually this is `bash`, but some users report that only `sh` is available.
@ -1104,6 +1138,9 @@ ROUTER = console
;; allow request with credentials ;; allow request with credentials
;ALLOW_CREDENTIALS = false ;ALLOW_CREDENTIALS = false
;; ;;
;; headers to permit
;HEADERS = Content-Type,User-Agent
;;
;; set X-FRAME-OPTIONS header ;; set X-FRAME-OPTIONS header
;X_FRAME_OPTIONS = SAMEORIGIN ;X_FRAME_OPTIONS = SAMEORIGIN
@ -1296,7 +1333,7 @@ ROUTER = console
;ISSUE_INDEXER_TYPE = bleve ;ISSUE_INDEXER_TYPE = bleve
;; ;;
;; Issue indexer storage path, available when ISSUE_INDEXER_TYPE is bleve ;; Issue indexer storage path, available when ISSUE_INDEXER_TYPE is bleve
;ISSUE_INDEXER_PATH = indexers/issues.bleve ;ISSUE_INDEXER_PATH = indexers/issues.bleve ; Relative paths will be made absolute against _`AppWorkPath`_.
;; ;;
;; Issue indexer connection string, available when ISSUE_INDEXER_TYPE is elasticsearch ;; Issue indexer connection string, available when ISSUE_INDEXER_TYPE is elasticsearch
;ISSUE_INDEXER_CONN_STR = http://elastic:changeme@localhost:9200 ;ISSUE_INDEXER_CONN_STR = http://elastic:changeme@localhost:9200
@ -1314,7 +1351,7 @@ ROUTER = console
;; When ISSUE_INDEXER_QUEUE_TYPE is levelqueue, this will be the path where the queue will be saved. ;; When ISSUE_INDEXER_QUEUE_TYPE is levelqueue, this will be the path where the queue will be saved.
;; This can be overridden by `ISSUE_INDEXER_QUEUE_CONN_STR`. ;; This can be overridden by `ISSUE_INDEXER_QUEUE_CONN_STR`.
;; default is queues/common ;; default is queues/common
;ISSUE_INDEXER_QUEUE_DIR = queues/common; **DEPRECATED** use settings in `[queue.issue_indexer]`. ;ISSUE_INDEXER_QUEUE_DIR = queues/common; **DEPRECATED** use settings in `[queue.issue_indexer]`. Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
;; ;;
;; When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string. ;; When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string.
;; When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of ;; When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of
@ -1370,7 +1407,7 @@ ROUTER = console
;TYPE = persistable-channel ;TYPE = persistable-channel
;; ;;
;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared. ;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared.
;DATADIR = queues/ ;DATADIR = queues/ ; Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
;; ;;
;; Default queue length before a channel queue will block ;; Default queue length before a channel queue will block
;LENGTH = 20 ;LENGTH = 20
@ -1672,7 +1709,7 @@ ROUTER = console
;; file: session file path, e.g. `data/sessions` ;; file: session file path, e.g. `data/sessions`
;; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180 ;; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table` ;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
;PROVIDER_CONFIG = data/sessions ;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_.
;; ;;
;; Session cookie name ;; Session cookie name
;COOKIE_NAME = i_like_gitea ;COOKIE_NAME = i_like_gitea
@ -2197,7 +2234,9 @@ ROUTER = console
;; Show template execution time in the footer ;; Show template execution time in the footer
;SHOW_FOOTER_TEMPLATE_LOAD_TIME = true ;SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
;; Generate sitemap. Defaults to `true`. ;; Generate sitemap. Defaults to `true`.
; ENABLE_SITEMAP = true ;ENABLE_SITEMAP = true
;; Enable/Disable RSS/Atom feed
;ENABLE_FEED = true
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -27,23 +27,56 @@ accurately recorded in [app.example.ini](https://github.com/go-gitea/gitea/blob/
(s/main/\<tag|release\>). Any string in the format `%(X)s` is a feature powered (s/main/\<tag|release\>). Any string in the format `%(X)s` is a feature powered
by [ini](https://github.com/go-ini/ini/#recursive-values), for reading values recursively. by [ini](https://github.com/go-ini/ini/#recursive-values), for reading values recursively.
In the default values below, a value in the form `$XYZ` refers to an environment variable. (However, see `environment-to-ini`.) Values in the form _`XxYyZz`_ refer to values listed as part of the default configuration. These notation forms will not work in your own `app.ini` file and are only listed here as documentation.
Values containing `#` or `;` must be quoted using `` ` `` or `"""`. Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
**Note:** A full restart is required for Gitea configuration changes to take effect. **Note:** A full restart is required for Gitea configuration changes to take effect.
{{< toc >}} {{< toc >}}
## Default Configuration (non-`app.ini` configuration)
These values are environment-dependent but form the basis of a lot of values. They will be
reported as part of the default configuration when running `gitea --help` or on start-up. The order they are emitted there is slightly different but we will list them here in the order they are set-up.
- _`AppPath`_: This is the absolute path of the running gitea binary.
- _`AppWorkPath`_: This refers to "working path" of the `gitea` binary. It is determined by using the first set thing in the following hierarchy:
- The `--work-path` flag passed to the binary
- The environment variable `$GITEA_WORK_DIR`
- A built-in value set at build time (see building from source)
- Otherwise it defaults to the directory of the _`AppPath`_
- If any of the above are relative paths then they are made absolute against the
the directory of the _`AppPath`_
- _`CustomPath`_: This is the base directory for custom templates and other options.
It is determined by using the first set thing in the following hierarchy:
- The `--custom-path` flag passed to the binary
- The environment variable `$GITEA_CUSTOM`
- A built-in value set at build time (see building from source)
- Otherwise it defaults to _`AppWorkPath`_`/custom`
- If any of the above are relative paths then they are made absolute against the
the directory of the _`AppWorkPath`_
- _`CustomConf`_: This is the path to the `app.ini` file.
- The `--config` flag passed to the binary
- A built-in value set at build time (see building from source)
- Otherwise it defaults to _`CustomPath`_`/conf/app.ini`
- If any of the above are relative paths then they are made absolute against the
the directory of the _`CustomPath`_
In addition there is _`StaticRootPath`_ which can be set as a built-in at build time, but will otherwise default to _`AppWorkPath`_
## Overall (`DEFAULT`) ## Overall (`DEFAULT`)
- `APP_NAME`: **Gitea: Git with a cup of tea**: Application name, used in the page title. - `APP_NAME`: **Gitea: Git with a cup of tea**: Application name, used in the page title.
- `RUN_USER`: **git**: The user Gitea will run as. This should be a dedicated system - `RUN_USER`: **_current OS username_/`$USER`/`$USERNAME` e.g. git**: The user Gitea will run as.
(non-user) account. Setting this incorrectly will cause Gitea to not start. This should be a dedicated system (non-user) account. Setting this incorrectly will cause Gitea
to not start.
- `RUN_MODE`: **prod**: Application run mode, affects performance and debugging. Either "dev", "prod" or "test". - `RUN_MODE`: **prod**: Application run mode, affects performance and debugging. Either "dev", "prod" or "test".
## Repository (`repository`) ## Repository (`repository`)
- `ROOT`: **%(APP_DATA_PATH)/gitea-repositories**: Root path for storing all repository data. - `ROOT`: **%(APP_DATA_PATH)s/gitea-repositories**: Root path for storing all repository data.
A relative path is interpreted as **%(GITEA_WORK_DIR)/%(ROOT)**. A relative path is interpreted as **_`AppWorkPath`_/%(ROOT)s**.
- `SCRIPT_TYPE`: **bash**: The script type this server supports. Usually this is `bash`, - `SCRIPT_TYPE`: **bash**: The script type this server supports. Usually this is `bash`,
but some users report that only `sh` is available. but some users report that only `sh` is available.
- `DETECTED_CHARSETS_ORDER`: **UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF-32LE, ISO-8859, windows-1252, ISO-8859, windows-1250, ISO-8859, ISO-8859, ISO-8859, windows-1253, ISO-8859, windows-1255, ISO-8859, windows-1251, windows-1256, KOI8-R, ISO-8859, windows-1254, Shift_JIS, GB18030, EUC-JP, EUC-KR, Big5, ISO-2022, ISO-2022, ISO-2022, IBM424_rtl, IBM424_ltr, IBM420_rtl, IBM420_ltr**: Tie-break order of detected charsets - if the detected charsets have equal confidence, charsets earlier in the list will be chosen in preference to those later. Adding `defaults` will place the unnamed charsets at that point. - `DETECTED_CHARSETS_ORDER`: **UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF-32LE, ISO-8859, windows-1252, ISO-8859, windows-1250, ISO-8859, ISO-8859, ISO-8859, windows-1253, ISO-8859, windows-1255, ISO-8859, windows-1251, windows-1256, KOI8-R, ISO-8859, windows-1254, Shift_JIS, GB18030, EUC-JP, EUC-KR, Big5, ISO-2022, ISO-2022, ISO-2022, IBM424_rtl, IBM424_ltr, IBM420_rtl, IBM420_ltr**: Tie-break order of detected charsets - if the detected charsets have equal confidence, charsets earlier in the list will be chosen in preference to those later. Adding `defaults` will place the unnamed charsets at that point.
@ -167,6 +200,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: list of methods allowed to request - `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: list of methods allowed to request
- `MAX_AGE`: **10m**: max time to cache response - `MAX_AGE`: **10m**: max time to cache response
- `ALLOW_CREDENTIALS`: **false**: allow request with credentials - `ALLOW_CREDENTIALS`: **false**: allow request with credentials
- `HEADERS`: **Content-Type,User-Agent**: additional headers that are permitted in requests
- `X_FRAME_OPTIONS`: **SAMEORIGIN**: Set the `X-Frame-Options` header value. - `X_FRAME_OPTIONS`: **SAMEORIGIN**: Set the `X-Frame-Options` header value.
## UI (`ui`) ## UI (`ui`)
@ -240,6 +274,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
## Server (`server`) ## Server (`server`)
- `APP_DATA_PATH`: **_`AppWorkPath`_/data**: This is the default root path for storing data.
- `PROTOCOL`: **http**: \[http, https, fcgi, http+unix, fcgi+unix\] - `PROTOCOL`: **http**: \[http, https, fcgi, http+unix, fcgi+unix\]
- `USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol headers on connections - `USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol headers on connections
- `PROXY_PROTOCOL_TLS_BRIDGING`: **false**: When protocol is https, expect PROXY protocol headers after TLS negotiation. - `PROXY_PROTOCOL_TLS_BRIDGING`: **false**: When protocol is https, expect PROXY protocol headers after TLS negotiation.
@ -254,12 +289,17 @@ The following configuration set `Content-Type: application/vnd.android.package-a
This includes CSS files, images, JS files and web fonts. This includes CSS files, images, JS files and web fonts.
Avatar images are dynamic resources and still served by Gitea. Avatar images are dynamic resources and still served by Gitea.
The option can be just a different path, as in `/static`, or another domain, as in `https://cdn.example.com`. The option can be just a different path, as in `/static`, or another domain, as in `https://cdn.example.com`.
Requests are then made as `%(ROOT_URL)s/static/css/index.css` and `https://cdn.example.com/css/index.css` respective. Requests are then made as `%(ROOT_URL)s/static/assets/css/index.css` or `https://cdn.example.com/assets/css/index.css` respectively.
The static files are located in the `public/` directory of the Gitea source repository. The static files are located in the `public/` directory of the Gitea source repository.
You can proxy the STATIC_URL_PREFIX requests to Gitea server to serve the static
assets, or copy the manually built Gitea assets from `$GITEA_BUILD/public` to
the assets location, eg: `/var/www/assets`, make sure `$STATIC_URL_PREFIX/assets/css/index.css`
points to `/var/www/assets/css/index.css`.
- `HTTP_ADDR`: **0.0.0.0**: HTTP listen address. - `HTTP_ADDR`: **0.0.0.0**: HTTP listen address.
- If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket - If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket
defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings. defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
- If `PROTOCOL` is set to `http+unix` or `fcgi+unix`, this should be the name of the Unix socket file to use. Relative paths will be made absolute against the AppWorkPath. - If `PROTOCOL` is set to `http+unix` or `fcgi+unix`, this should be the name of the Unix socket file to use. Relative paths will be made absolute against the _`AppWorkPath`_.
- `HTTP_PORT`: **3000**: HTTP listen port. - `HTTP_PORT`: **3000**: HTTP listen port.
- If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket - If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket
defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings. defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
@ -269,7 +309,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
most cases you do not need to change the default value. Alter it only if most cases you do not need to change the default value. Alter it only if
your SSH server node is not the same as HTTP node. Do not set this variable your SSH server node is not the same as HTTP node. Do not set this variable
if `PROTOCOL` is set to `http+unix`. if `PROTOCOL` is set to `http+unix`.
- `LOCAL_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: When making local connections pass the PROXY protocol header. - `LOCAL_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)s**: When making local connections pass the PROXY protocol header.
This should be set to false if the local connection will go through the proxy. This should be set to false if the local connection will go through the proxy.
- `PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the connection. (Set to -1 to - `PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the connection. (Set to -1 to
disable all timeouts.) disable all timeouts.)
@ -279,7 +319,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `START_SSH_SERVER`: **false**: When enabled, use the built-in SSH server. - `START_SSH_SERVER`: **false**: When enabled, use the built-in SSH server.
- `SSH_SERVER_USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol header on connections to the built-in SSH Server. - `SSH_SERVER_USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol header on connections to the built-in SSH Server.
- `BUILTIN_SSH_SERVER_USER`: **%(RUN_USER)s**: Username to use for the built-in SSH Server. - `BUILTIN_SSH_SERVER_USER`: **%(RUN_USER)s**: Username to use for the built-in SSH Server.
- `SSH_USER`: **%(BUILTIN_SSH_SERVER_USER)**: SSH username displayed in clone URLs. This is only for people who configure the SSH server themselves; in most cases, you want to leave this blank and modify the `BUILTIN_SSH_SERVER_USER`. - `SSH_USER`: **%(BUILTIN_SSH_SERVER_USER)s**: SSH username displayed in clone URLs. This is only for people who configure the SSH server themselves; in most cases, you want to leave this blank and modify the `BUILTIN_SSH_SERVER_USER`.
- `SSH_DOMAIN`: **%(DOMAIN)s**: Domain name of this server, used for displayed clone URL. - `SSH_DOMAIN`: **%(DOMAIN)s**: Domain name of this server, used for displayed clone URL.
- `SSH_PORT`: **22**: SSH port displayed in clone URL. - `SSH_PORT`: **22**: SSH port displayed in clone URL.
- `SSH_LISTEN_HOST`: **0.0.0.0**: Listen address for the built-in SSH server. - `SSH_LISTEN_HOST`: **0.0.0.0**: Listen address for the built-in SSH server.
@ -308,22 +348,22 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures. - `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures.
- `CERT_FILE`: **https/cert.pem**: Cert file path used for HTTPS. When chaining, the server certificate must come first, then intermediate CA certificates (if any). This is ignored if `ENABLE_ACME=true`. Paths are relative to `CUSTOM_PATH`. - `CERT_FILE`: **https/cert.pem**: Cert file path used for HTTPS. When chaining, the server certificate must come first, then intermediate CA certificates (if any). This is ignored if `ENABLE_ACME=true`. Paths are relative to `CUSTOM_PATH`.
- `KEY_FILE`: **https/key.pem**: Key file path used for HTTPS. This is ignored if `ENABLE_ACME=true`. Paths are relative to `CUSTOM_PATH`. - `KEY_FILE`: **https/key.pem**: Key file path used for HTTPS. This is ignored if `ENABLE_ACME=true`. Paths are relative to `CUSTOM_PATH`.
- `STATIC_ROOT_PATH`: **./**: Upper level of template and static files path. - `STATIC_ROOT_PATH`: **_`StaticRootPath`_**: Upper level of template and static files path.
- `APP_DATA_PATH`: **data** (**/data/gitea** on docker): Default path for application data. - `APP_DATA_PATH`: **data** (**/data/gitea** on docker): Default path for application data. Relative paths will be made absolute against _`AppWorkPath`_.
- `STATIC_CACHE_TIME`: **6h**: Web browser cache time for static resources on `custom/`, `public/` and all uploaded avatars. Note that this cache is disabled when `RUN_MODE` is "dev". - `STATIC_CACHE_TIME`: **6h**: Web browser cache time for static resources on `custom/`, `public/` and all uploaded avatars. Note that this cache is disabled when `RUN_MODE` is "dev".
- `ENABLE_GZIP`: **false**: Enable gzip compression for runtime-generated content, static resources excluded. - `ENABLE_GZIP`: **false**: Enable gzip compression for runtime-generated content, static resources excluded.
- `ENABLE_PPROF`: **false**: Application profiling (memory and cpu). For "web" command it listens on `localhost:6060`. For "serv" command it dumps to disk at `PPROF_DATA_PATH` as `(cpuprofile|memprofile)_<username>_<temporary id>` - `ENABLE_PPROF`: **false**: Application profiling (memory and cpu). For "web" command it listens on `localhost:6060`. For "serv" command it dumps to disk at `PPROF_DATA_PATH` as `(cpuprofile|memprofile)_<username>_<temporary id>`
- `PPROF_DATA_PATH`: **data/tmp/pprof**: `PPROF_DATA_PATH`, use an absolute path when you start Gitea as service - `PPROF_DATA_PATH`: **_`AppWorkPath`_/data/tmp/pprof**: `PPROF_DATA_PATH`, use an absolute path when you start Gitea as service
- `LANDING_PAGE`: **home**: Landing page for unauthenticated users \[home, explore, organizations, login, **custom**\]. Where custom would instead be any URL such as "/org/repo" or even `https://anotherwebsite.com` - `LANDING_PAGE`: **home**: Landing page for unauthenticated users \[home, explore, organizations, login, **custom**\]. Where custom would instead be any URL such as "/org/repo" or even `https://anotherwebsite.com`
- `LFS_START_SERVER`: **false**: Enables Git LFS support. - `LFS_START_SERVER`: **false**: Enables Git LFS support.
- `LFS_CONTENT_PATH`: **%(APP_DATA_PATH)/lfs**: Default LFS content path. (if it is on local storage.) **DEPRECATED** use settings in `[lfs]`. - `LFS_CONTENT_PATH`: **%(APP_DATA_PATH)s/lfs**: Default LFS content path. (if it is on local storage.) **DEPRECATED** use settings in `[lfs]`.
- `LFS_JWT_SECRET`: **\<empty\>**: LFS authentication secret, change this a unique string. - `LFS_JWT_SECRET`: **\<empty\>**: LFS authentication secret, change this a unique string.
- `LFS_HTTP_AUTH_EXPIRY`: **20m**: LFS authentication validity period in time.Duration, pushes taking longer than this may fail. - `LFS_HTTP_AUTH_EXPIRY`: **20m**: LFS authentication validity period in time.Duration, pushes taking longer than this may fail.
- `LFS_MAX_FILE_SIZE`: **0**: Maximum allowed LFS file size in bytes (Set to 0 for no limit). - `LFS_MAX_FILE_SIZE`: **0**: Maximum allowed LFS file size in bytes (Set to 0 for no limit).
- `LFS_LOCKS_PAGING_NUM`: **50**: Maximum number of LFS Locks returned per page. - `LFS_LOCKS_PAGING_NUM`: **50**: Maximum number of LFS Locks returned per page.
- `REDIRECT_OTHER_PORT`: **false**: If true and `PROTOCOL` is https, allows redirecting http requests on `PORT_TO_REDIRECT` to the https port Gitea listens on. - `REDIRECT_OTHER_PORT`: **false**: If true and `PROTOCOL` is https, allows redirecting http requests on `PORT_TO_REDIRECT` to the https port Gitea listens on.
- `REDIRECTOR_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: expect PROXY protocol header on connections to https redirector. - `REDIRECTOR_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)s**: expect PROXY protocol header on connections to https redirector.
- `PORT_TO_REDIRECT`: **80**: Port for the http redirection service to listen on. Used when `REDIRECT_OTHER_PORT` is true. - `PORT_TO_REDIRECT`: **80**: Port for the http redirection service to listen on. Used when `REDIRECT_OTHER_PORT` is true.
- `SSL_MIN_VERSION`: **TLSv1.2**: Set the minimum version of ssl support. - `SSL_MIN_VERSION`: **TLSv1.2**: Set the minimum version of ssl support.
- `SSL_MAX_VERSION`: **\<empty\>**: Set the maximum version of ssl support. - `SSL_MAX_VERSION`: **\<empty\>**: Set the maximum version of ssl support.
@ -413,10 +453,10 @@ relation to port exhaustion.
- `ISSUE_INDEXER_TYPE`: **bleve**: Issue indexer type, currently supported: `bleve`, `db` or `elasticsearch`. - `ISSUE_INDEXER_TYPE`: **bleve**: Issue indexer type, currently supported: `bleve`, `db` or `elasticsearch`.
- `ISSUE_INDEXER_CONN_STR`: ****: Issue indexer connection string, available when ISSUE_INDEXER_TYPE is elasticsearch. i.e. http://elastic:changeme@localhost:9200 - `ISSUE_INDEXER_CONN_STR`: ****: Issue indexer connection string, available when ISSUE_INDEXER_TYPE is elasticsearch. i.e. http://elastic:changeme@localhost:9200
- `ISSUE_INDEXER_NAME`: **gitea_issues**: Issue indexer name, available when ISSUE_INDEXER_TYPE is elasticsearch - `ISSUE_INDEXER_NAME`: **gitea_issues**: Issue indexer name, available when ISSUE_INDEXER_TYPE is elasticsearch
- `ISSUE_INDEXER_PATH`: **indexers/issues.bleve**: Index file used for issue search; available when ISSUE_INDEXER_TYPE is bleve and elasticsearch. - `ISSUE_INDEXER_PATH`: **indexers/issues.bleve**: Index file used for issue search; available when ISSUE_INDEXER_TYPE is bleve and elasticsearch. Relative paths will be made absolute against _`AppWorkPath`_.
- The next 4 configuration values are deprecated and should be set in `queue.issue_indexer` however are kept for backwards compatibility: - The next 4 configuration values are deprecated and should be set in `queue.issue_indexer` however are kept for backwards compatibility:
- `ISSUE_INDEXER_QUEUE_TYPE`: **levelqueue**: Issue indexer queue, currently supports:`channel`, `levelqueue`, `redis`. **DEPRECATED** use settings in `[queue.issue_indexer]`. - `ISSUE_INDEXER_QUEUE_TYPE`: **levelqueue**: Issue indexer queue, currently supports:`channel`, `levelqueue`, `redis`. **DEPRECATED** use settings in `[queue.issue_indexer]`.
- `ISSUE_INDEXER_QUEUE_DIR`: **queues/common**: When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this will be the path where the queue will be saved. **DEPRECATED** use settings in `[queue.issue_indexer]`. - `ISSUE_INDEXER_QUEUE_DIR`: **queues/common**: When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this will be the path where the queue will be saved. **DEPRECATED** use settings in `[queue.issue_indexer]`. Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
- `ISSUE_INDEXER_QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string. When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of the form `leveldb://path/to/db?option=value&....`, and overrides `ISSUE_INDEXER_QUEUE_DIR`. **DEPRECATED** use settings in `[queue.issue_indexer]`. - `ISSUE_INDEXER_QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string. When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of the form `leveldb://path/to/db?option=value&....`, and overrides `ISSUE_INDEXER_QUEUE_DIR`. **DEPRECATED** use settings in `[queue.issue_indexer]`.
- `ISSUE_INDEXER_QUEUE_BATCH_NUMBER`: **20**: Batch queue number. **DEPRECATED** use settings in `[queue.issue_indexer]`. - `ISSUE_INDEXER_QUEUE_BATCH_NUMBER`: **20**: Batch queue number. **DEPRECATED** use settings in `[queue.issue_indexer]`.
@ -438,7 +478,7 @@ relation to port exhaustion.
Configuration at `[queue]` will set defaults for queues with overrides for individual queues at `[queue.*]`. (However see below.) Configuration at `[queue]` will set defaults for queues with overrides for individual queues at `[queue.*]`. (However see below.)
- `TYPE`: **persistable-channel**: General queue type, currently support: `persistable-channel` (uses a LevelDB internally), `channel`, `level`, `redis`, `dummy` - `TYPE`: **persistable-channel**: General queue type, currently support: `persistable-channel` (uses a LevelDB internally), `channel`, `level`, `redis`, `dummy`
- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for individual queues can be set in `queue.name` sections but will default to `DATADIR/`**`common`**. (Previously each queue would default to `DATADIR/`**`name`**.) - `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for individual queues can be set in `queue.name` sections but will default to `DATADIR/`**`common`**. (Previously each queue would default to `DATADIR/`**`name`**.) Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
- `LENGTH`: **20**: Maximal queue size before channel queues block - `LENGTH`: **20**: Maximal queue size before channel queues block
- `BATCH_LENGTH`: **20**: Batch data before passing to the handler - `BATCH_LENGTH`: **20**: Batch data before passing to the handler
- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. Options can be set using query params. Similarly LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR` - `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. Options can be set using query params. Similarly LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
@ -722,7 +762,7 @@ and
## Session (`session`) ## Session (`session`)
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]` - `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, db, mysql, couchbase, memcache, postgres\]. Setting `db` will reuse the configuration in `[database]`
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. - `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for db, empty (database config will be used); for others, the connection string. Relative paths will be made absolute against _`AppWorkPath`_.
- `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access. - `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access.
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID. - `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
- `GC_INTERVAL_TIME`: **86400**: GC interval in seconds. - `GC_INTERVAL_TIME`: **86400**: GC interval in seconds.
@ -980,7 +1020,7 @@ Default templates for project boards:
## Git (`git`) ## Git (`git`)
- `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment. - `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment.
- `HOME_PATH`: **%(APP_DATA_PATH)/home**: The HOME directory for Git. - `HOME_PATH`: **%(APP_DATA_PATH)s/home**: The HOME directory for Git.
This directory will be used to contain the `.gitconfig` and possible `.gnupg` directories that Gitea's git calls will use. If you can confirm Gitea is the only application running in this environment, you can set it to the normal home directory for Gitea user. This directory will be used to contain the `.gitconfig` and possible `.gnupg` directories that Gitea's git calls will use. If you can confirm Gitea is the only application running in this environment, you can set it to the normal home directory for Gitea user.
- `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes. - `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes.
- `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view. - `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view.
@ -1248,3 +1288,4 @@ PROXY_HOSTS = *.github.com
- `SHOW_FOOTER_VERSION`: **true**: Show Gitea and Go version information in the footer. - `SHOW_FOOTER_VERSION`: **true**: Show Gitea and Go version information in the footer.
- `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer. - `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer.
- `ENABLE_SITEMAP`: **true**: Generate sitemap. - `ENABLE_SITEMAP`: **true**: Generate sitemap.
- `ENABLE_FEED`: **true**: Enable/Disable RSS/Atom feed.

View File

@ -15,6 +15,14 @@ menu:
# Logging Configuration # Logging Configuration
The logging configuration of Gitea mainly consists of 3 types of components:
- The `[log]` section for general configuration
- `[log.<sublogger>]` sections for the configuration of different log outputs
- `[log.<sublogger>.<group>]` sections for output specific configuration of a log group
As mentioned below, there is a fully functional log output by default, so it is not necessary to define one.
**Table of Contents** **Table of Contents**
{{< toc >}} {{< toc >}}
@ -23,6 +31,166 @@ menu:
To collect logs for help and issue report, see [Support Options]({{< relref "doc/help/seek-help.en-us.md" >}}). To collect logs for help and issue report, see [Support Options]({{< relref "doc/help/seek-help.en-us.md" >}}).
## The `[log]` section
Configuration of logging facilities in Gitea happen in the `[log]` section and it's subsections.
In the top level `[log]` section the following configurations can be placed:
- `ROOT_PATH`: (Default: **%(GITEA_WORK_DIR)/log**): Base path for log files
- `MODE`: (Default: **console**) List of log outputs to use for the Default logger.
- `ROUTER`: (Default: **console**): List of log outputs to use for the Router logger.
- `ACCESS`: List of log outputs to use for the Access logger.
- `XORM`: (Default: **,**) List of log outputs to use for the XORM logger.
- `ENABLE_ACCESS_LOG`: (Default: **false**): whether the Access logger is allowed to emit logs
- `ENABLE_XORM_LOG`: (Default: **true**): whether the XORM logger is allowed to emit logs
For details on the loggers check the "Log Groups" section.
Important: log outputs won't be used if you don't enable them for the desired loggers in the corresponding list value.
Lists are specified as comma separated values. This format also works in subsection.
This section may be used for defining default values for subsections.
Examples:
- `LEVEL`: (Default: **Info**) Least severe log events to persist. Case insensitive. The full list of levels as of v1.17.3 can be read [here](https://github.com/go-gitea/gitea/blob/v1.17.3/custom/conf/app.example.ini#L507).
- `STACKTRACE_LEVEL`: (Default: **None**) For this and more severe events the stacktrace will be printed upon getting logged.
Some values are not inherited by subsections. For details see the "Non-inherited default values" section.
## Log outputs
Log outputs are the targets to which log messages will be sent.
The content and the format of the log messages to be saved can be configured in these.
Log outputs are also called subloggers.
Gitea provides 4 possible log outputs:
- `console` - Log to `os.Stdout` or `os.Stderr`
- `file` - Log to a file
- `conn` - Log to a socket (network or unix)
- `smtp` - Log via email
By default, Gitea has a `console` output configured, which is used by the loggers as seen in the section "The log section" above.
### Common configuration
Certain configuration is common to all modes of log output:
- `MODE` is the mode of the log output. It will default to the sublogger
name, thus `[log.console.router]` will default to `MODE = console`.
For mode specific confgurations read further.
- `LEVEL` is the lowest level that this output will log. This value
is inherited from `[log]` and in the case of the non-default loggers
from `[log.sublogger]`.
- `STACKTRACE_LEVEL` is the lowest level that this output will print
a stacktrace. This value is inherited.
- `COLORIZE` will default to `true` for `console` as
described, otherwise it will default to `false`.
### Non-inherited default values
There are several values which are not inherited as described above but
rather default to those specific to type of logger, these are:
`EXPRESSION`, `FLAGS`, `PREFIX` and `FILE_NAME`.
#### `EXPRESSION`
`EXPRESSION` represents a regular expression that log events must match to be logged by the sublogger. Either the log message, (with colors removed), must match or the `longfilename:linenumber:functionname` must match. NB: the whole message or string doesn't need to completely match.
Please note this expression will be run in the sublogger's goroutine
not the logging event subroutine. Therefore it can be complicated.
#### `FLAGS`
`FLAGS` represents the preceding logging context information that is
printed before each message. It is a comma-separated string set. The order of values does not matter.
Possible values are:
- `none` or `,` - No flags.
- `date` - the date in the local time zone: `2009/01/23`.
- `time` - the time in the local time zone: `01:23:23`.
- `microseconds` - microsecond resolution: `01:23:23.123123`. Assumes
time.
- `longfile` - full file name and line number: `/a/b/c/d.go:23`.
- `shortfile` - final file name element and line number: `d.go:23`.
- `funcname` - function name of the caller: `runtime.Caller()`.
- `shortfuncname` - last part of the function name. Overrides
`funcname`.
- `utc` - if date or time is set, use UTC rather than the local time
zone.
- `levelinitial` - Initial character of the provided level in brackets eg. `[I]` for info.
- `level` - Provided level in brackets `[INFO]`
- `medfile` - Last 20 characters of the filename - equivalent to
`shortfile,longfile`.
- `stdflags` - Equivalent to `date,time,medfile,shortfuncname,levelinitial`
### Console mode
In this mode the logger will forward log messages to the stdout and
stderr streams attached to the Gitea process.
For loggers in console mode, `COLORIZE` will default to `true` if not
on windows, or the windows terminal can be set into ANSI mode or is a
cygwin or Msys pipe.
Settings:
- `STDERR`: **false**: Whether the logger should print to `stderr` instead of `stdout`.
### File mode
In this mode the logger will save log messages to a file.
Settings:
- `FILE_NAME`: The file to write the log events to. For details see below.
- `MAX_SIZE_SHIFT`: **28**: Maximum size shift of a single file. 28 represents 256Mb. For details see below.
- `LOG_ROTATE` **true**: Whether to rotate the log files. TODO: if false, will it delete instead on daily rotate, or do nothing?.
- `DAILY_ROTATE`: **true**: Whether to rotate logs daily.
- `MAX_DAYS`: **7**: Delete rotated log files after this number of days.
- `COMPRESS`: **true**: Whether to compress old log files by default with gzip.
- `COMPRESSION_LEVEL`: **-1**: Compression level. For details see below.
The default value of `FILE_NAME` depends on the respective logger facility.
If unset, their own default will be used.
If set it will be relative to the provided `ROOT_PATH` in the master `[log]` section.
`MAX_SIZE_SHIFT` defines the maximum size of a file by left shifting 1 the given number of times (`1 << x`).
The exact behavior at the time of v1.17.3 can be seen [here](https://github.com/go-gitea/gitea/blob/v1.17.3/modules/setting/log.go#L185).
The useful values of `COMPRESSION_LEVEL` are from 1 to (and including) 9, where higher numbers mean better compression.
Beware that better compression might come with higher resource usage.
Must be preceded with a `-` sign.
### Conn mode
In this mode the logger will send log messages over a network socket.
Settings:
- `ADDR`: **:7020**: Sets the address to connect to.
- `PROTOCOL`: **tcp**: Set the protocol, either "tcp", "unix" or "udp".
- `RECONNECT`: **false**: Try to reconnect when connection is lost.
- `RECONNECT_ON_MSG`: **false**: Reconnect host for every single message.
### SMTP mode
In this mode the logger will send log messages in email.
It is not recommended to use this logger to send general logging
messages. However, you could perhaps set this logger to work on `FATAL` messages only.
Settings:
- `HOST`: **127.0.0.1:25**: The SMTP host to connect to.
- `USER`: User email address to send from.
- `PASSWD`: Password for the smtp server.
- `RECEIVERS`: Email addresses to send to.
- `SUBJECT`: **Diagnostic message from Gitea**. The content of the email's subject field.
## Log Groups ## Log Groups
The fundamental thing to be aware of in Gitea is that there are several The fundamental thing to be aware of in Gitea is that there are several
@ -172,106 +340,6 @@ which will not be inherited from the `[log]` or relevant
- `EXPRESSION` will default to `""` - `EXPRESSION` will default to `""`
- `PREFIX` will default to `""` - `PREFIX` will default to `""`
## Log outputs
Gitea provides 4 possible log outputs:
- `console` - Log to `os.Stdout` or `os.Stderr`
- `file` - Log to a file
- `conn` - Log to a keep-alive TCP connection
- `smtp` - Log via email
Certain configuration is common to all modes of log output:
- `LEVEL` is the lowest level that this output will log. This value
is inherited from `[log]` and in the case of the non-default loggers
from `[log.sublogger]`.
- `STACKTRACE_LEVEL` is the lowest level that this output will print
a stacktrace. This value is inherited.
- `MODE` is the mode of the log output. It will default to the sublogger
name. Thus `[log.console.router]` will default to `MODE = console`.
- `COLORIZE` will default to `true` for `console` as
described, otherwise it will default to `false`.
### Non-inherited default values
There are several values which are not inherited as described above but
rather default to those specific to type of logger, these are:
`EXPRESSION`, `FLAGS`, `PREFIX` and `FILE_NAME`.
#### `EXPRESSION`
`EXPRESSION` represents a regular expression that log events must match to be logged by the sublogger. Either the log message, (with colors removed), must match or the `longfilename:linenumber:functionname` must match. NB: the whole message or string doesn't need to completely match.
Please note this expression will be run in the sublogger's goroutine
not the logging event subroutine. Therefore it can be complicated.
#### `FLAGS`
`FLAGS` represents the preceding logging context information that is
printed before each message. It is a comma-separated string set. The order of values does not matter.
Possible values are:
- `none` or `,` - No flags.
- `date` - the date in the local time zone: `2009/01/23`.
- `time` - the time in the local time zone: `01:23:23`.
- `microseconds` - microsecond resolution: `01:23:23.123123`. Assumes
time.
- `longfile` - full file name and line number: `/a/b/c/d.go:23`.
- `shortfile` - final file name element and line number: `d.go:23`.
- `funcname` - function name of the caller: `runtime.Caller()`.
- `shortfuncname` - last part of the function name. Overrides
`funcname`.
- `utc` - if date or time is set, use UTC rather than the local time
zone.
- `levelinitial` - Initial character of the provided level in brackets eg. `[I]` for info.
- `level` - Provided level in brackets `[INFO]`
- `medfile` - Last 20 characters of the filename - equivalent to
`shortfile,longfile`.
- `stdflags` - Equivalent to `date,time,medfile,shortfuncname,levelinitial`
### Console mode
For loggers in console mode, `COLORIZE` will default to `true` if not
on windows, or the windows terminal can be set into ANSI mode or is a
cygwin or Msys pipe.
If `STDERR` is set to `true` the logger will use `os.Stderr` instead of
`os.Stdout`.
### File mode
The `FILE_NAME` defaults as described above. If set it will be relative
to the provided `ROOT_PATH` in the master `[log]` section.
Other values:
- `LOG_ROTATE`: **true**: Rotate the log files.
- `MAX_SIZE_SHIFT`: **28**: Maximum size shift of a single file, 28 represents 256Mb.
- `DAILY_ROTATE`: **true**: Rotate logs daily.
- `MAX_DAYS`: **7**: Delete the log file after n days
- `COMPRESS`: **true**: Compress old log files by default with gzip
- `COMPRESSION_LEVEL`: **-1**: Compression level
### Conn mode
- `RECONNECT_ON_MSG`: **false**: Reconnect host for every single message.
- `RECONNECT`: **false**: Try to reconnect when connection is lost.
- `PROTOCOL`: **tcp**: Set the protocol, either "tcp", "unix" or "udp".
- `ADDR`: **:7020**: Sets the address to connect to.
### SMTP mode
It is not recommended to use this logger to send general logging
messages. However, you could perhaps set this logger to work on `FATAL`.
- `USER`: User email address to send from.
- `PASSWD`: Password for the smtp server.
- `HOST`: **127.0.0.1:25**: The SMTP host to connect to.
- `RECEIVERS`: Email addresses to send to.
- `SUBJECT`: **Diagnostic message from Gitea**
## Debugging problems ## Debugging problems
When submitting logs in Gitea issues it is often helpful to submit When submitting logs in Gitea issues it is often helpful to submit

View File

@ -41,13 +41,15 @@ For an existing remote repository, you can set up pull mirroring as follows:
The repository now gets mirrored periodically from the remote repository. You can force a sync by selecting **Synchronize Now** in the repository settings. The repository now gets mirrored periodically from the remote repository. You can force a sync by selecting **Synchronize Now** in the repository settings.
:exclamation::exclamation: **NOTE:** You can only set up pull mirroring for repos that don't exist yet on your instance. Once the repo is created, you can't convert it into a pull mirror anymore. :exclamation::exclamation:
## Pushing to a remote repository ## Pushing to a remote repository
For an existing repository, you can set up push mirroring as follows: For an existing repository, you can set up push mirroring as follows:
1. In your repository, go to **Settings** > **Repository**, and then the **Mirror Settings** section. 1. In your repository, go to **Settings** > **Repository**, and then the **Mirror Settings** section.
2. Enter a repository URL. 2. Enter a repository URL.
3. If the repository needs authentication expand the **Authorization** section and fill in your authentication information. 3. If the repository needs authentication expand the **Authorization** section and fill in your authentication information. Note that the requested **password** can also be your access token.
4. Select **Add Push Mirror** to save the configuration. 4. Select **Add Push Mirror** to save the configuration.
The repository now gets mirrored periodically to the remote repository. You can force a sync by selecting **Synchronize Now**. In case of an error a message displayed to help you resolve it. The repository now gets mirrored periodically to the remote repository. You can force a sync by selecting **Synchronize Now**. In case of an error a message displayed to help you resolve it.
@ -59,9 +61,11 @@ The repository now gets mirrored periodically to the remote repository. You can
To set up a mirror from Gitea to GitHub, you need to follow these steps: To set up a mirror from Gitea to GitHub, you need to follow these steps:
1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the *public_repo* box checked. 1. Create a [GitHub personal access token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) with the *public_repo* box checked.
2. Fill in the **Git Remote Repository URL**: `https://github.com/<your_github_group>/<your_github_project>.git`. 2. Create a repository with that name on GitHub. Unlike Gitea, GitHub does not support creating repositories by pushing to the remote. You can also use an existing remote repo if it has the same commit history as your Gitea repo.
3. Fill in the **Authorization** fields with your GitHub username and the personal access token. 3. In the settings of your Gitea repo, fill in the **Git Remote Repository URL**: `https://github.com/<your_github_group>/<your_github_project>.git`.
4. Select **Add Push Mirror** to save the configuration. 4. Fill in the **Authorization** fields with your GitHub username and the personal access token as **Password**.
5. (Optional, available on Gitea 1.18+) Select `Sync when new commits are pushed` so that the mirror will be updated as well as soon as there are changes. You can also disable the periodic sync if you like.
6. Select **Add Push Mirror** to save the configuration.
The repository pushes shortly thereafter. To force a push, select the **Synchronize Now** button. The repository pushes shortly thereafter. To force a push, select the **Synchronize Now** button.

View File

@ -166,11 +166,47 @@ Uses the following fields:
## PAM (Pluggable Authentication Module) ## PAM (Pluggable Authentication Module)
To configure PAM, set the 'PAM Service Name' to a filename in `/etc/pam.d/`. To This procedure enables PAM authentication. Users may still be added to the
work with normal Linux passwords, the user running Gitea must have read access system manually using the user administration. PAM provides a mechanism to
to `/etc/shadow`. automatically add users to the current database by testing them against PAM
authentication. To work with normal Linux passwords, the user running Gitea
must also have read access to `/etc/shadow` in order to check the validity of
the account when logging in using a public key.
**Note**: PAM support is added via [build-time flags](https://docs.gitea.io/en-us/install-from-source/#build), and the official binaries provided do not have this enabled. **Note**: If a user has added SSH public keys into Gitea, the use of these
keys _may_ bypass the login check system. Therefore, if you wish to disable a user who
authenticates with PAM, you _should_ also manually disable the account in Gitea using the
built-in user manager.
1. Configure and prepare the installation.
- It is recommended that you create an administrative user.
- Deselecting automatic sign-up may also be desired.
1. Once the database has been initialized, log in as the newly created
administrative user.
1. Navigate to the user setting (icon in top-right corner), and select
`Site Administration` -> `Authentication Sources`, and select
`Add Authentication Source`.
1. Fill out the field as follows:
- `Authentication Type` : `PAM`
- `Name` : Any value should be valid here, use "System Authentication" if
you'd like.
- `PAM Service Name` : Select the appropriate file listed under `/etc/pam.d/`
that performs the authentication desired.[^1]
- `PAM Email Domain` : The e-mail suffix to append to user authentication.
For example, if the login system expects a user called `gituser`, and this
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](https://docs.gitea.io/en-us/install-from-source/#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.
[^1]: For example, using standard Linux log-in on Debian "Bullseye" use
`common-session-noninteractive` - this value may be valid for other flavors of
Debian including Ubuntu and Mint, consult your distribution's documentation.
[^2]: **This is a required field for PAM**. Be aware: In the above example, the
user will log into the Gitea web interface as `gituser` and not `gituser@mail.com`
## SMTP (Simple Mail Transfer Protocol) ## SMTP (Simple Mail Transfer Protocol)

View File

@ -58,29 +58,33 @@ https://github.com/loganinak/MigrateGitlabToGogs
## Where does Gitea store what file ## Where does Gitea store what file
- WorkPath - _`AppWorkPath`_
- Environment variable `GITEA_WORK_DIR` - The `--work-path` flag
- Else `--work-path` flag - Else Environment variable `GITEA_WORK_DIR`
- Else a built-in value set at build time
- Else the directory that contains the Gitea binary - Else the directory that contains the Gitea binary
- AppDataPath (default for database, indexers, etc.) - `%(APP_DATA_PATH)` (default for database, indexers, etc.)
- `APP_DATA_PATH` from `app.ini` - `APP_DATA_PATH` from `app.ini`
- Else `%(WorkPath)/data` - Else _`AppWorkPath`_`/data`
- CustomPath (custom templates) - _`CustomPath`_ (custom templates)
- Environment variable `GITEA_CUSTOM` - The `--custom-path` flag
- Else `%(WorkPath)/custom` - Else Environment variable `GITEA_CUSTOM`
- Else a built-in value set at build time
- Else _`AppWorkPath`_`/custom`
- HomeDir - HomeDir
- Unix: Environment variable `HOME` - Unix: Environment variable `HOME`
- Windows: Environment variable `USERPROFILE`, else environment variables `HOMEDRIVE`+`HOMEPATH` - Windows: Environment variable `USERPROFILE`, else environment variables `HOMEDRIVE`+`HOMEPATH`
- RepoRootPath - RepoRootPath
- `ROOT` in the \[repository] section of `app.ini` if absolute - `ROOT` in the \[repository] section of `app.ini` if absolute
- Else `%(AppWorkPath)/ROOT` if `ROOT` in the \[repository] section of `app.ini` if relative - Else _`AppWorkPath`_`/ROOT` if `ROOT` in the \[repository] section of `app.ini` if relative
- Default `%(AppDataPath)/gitea-repositories` - Default `%(APP_DATA_PATH)/gitea-repositories`
- INI (config file) - INI (config file)
- `-c` flag - `--config` flag
- Else `%(CustomPath)/conf/app.ini` - A possible built-in value set a build time
- Else _`CustomPath`_`/conf/app.ini`
- SQLite Database - SQLite Database
- `PATH` in `database` section of `app.ini` - `PATH` in `database` section of `app.ini`
- Else `%(AppDataPath)/gitea.db` - Else `%(APP_DATA_PATH)/gitea.db`
## Not seeing a clone URL or the clone URL being incorrect ## Not seeing a clone URL or the clone URL being incorrect

View File

@ -94,7 +94,7 @@ are provided to keep the build process as simple as possible.
Depending on requirements, the following build tags can be included. Depending on requirements, the following build tags can be included.
- `bindata`: Build a single monolithic binary, with all assets included. - `bindata`: Build a single monolithic binary, with all assets included. Required for production build.
- `sqlite sqlite_unlock_notify`: Enable support for a - `sqlite sqlite_unlock_notify`: Enable support for a
[SQLite3](https://sqlite.org/) database. Suggested only for tiny [SQLite3](https://sqlite.org/) database. Suggested only for tiny
installations. installations.
@ -103,11 +103,10 @@ Depending on requirements, the following build tags can be included.
available to PAM. available to PAM.
- `gogit`: (EXPERIMENTAL) Use go-git variants of Git commands. - `gogit`: (EXPERIMENTAL) Use go-git variants of Git commands.
Bundling assets into the binary using the `bindata` build tag is recommended for Bundling all assets (JS/CSS/templates, etc) into the binary. Using the `bindata` build tag is required for
production deployments. It is possible to serve the static assets directly via a reverse proxy, production deployments. You could exclude `bindata` when you are developing/testing Gitea or able to separate the assets correctly.
but in most cases it is not necessary, and assets should still be bundled in the binary.
You may want to exclude bindata while developing/testing Gitea. To include all assets, use the `bindata` tag:
To include assets, add the `bindata` tag:
```bash ```bash
TAGS="bindata" make build TAGS="bindata" make build
@ -144,11 +143,11 @@ launched manually from command line, it can be killed by pressing `Ctrl + C`.
## Changing default paths ## Changing default paths
Gitea will search for a number of things from the `CustomPath`. By default this is Gitea will search for a number of things from the _`CustomPath`_. By default this is
the `custom/` directory in the current working directory when running Gitea. It will also the `custom/` directory in the current working directory when running Gitea. It will also
look for its configuration file `CustomConf` in `$CustomPath/conf/app.ini`, and will use the look for its configuration file _`CustomConf`_ in _`CustomPath`_/conf/app.ini`, and will use the
current working directory as the relative base path `AppWorkPath` for a number configurable current working directory as the relative base path _`AppWorkPath`_ for a number configurable
values. Finally the static files will be served from `StaticRootPath` which defaults to the `AppWorkPath`. values. Finally the static files will be served from _`StaticRootPath`_ which defaults to the _`AppWorkPath`_.
These values, although useful when developing, may conflict with downstream users preferences. These values, although useful when developing, may conflict with downstream users preferences.
@ -156,10 +155,10 @@ One option is to use a script file to shadow the `gitea` binary and create an ap
environment before running Gitea. However, when building you can change these defaults environment before running Gitea. However, when building you can change these defaults
using the `LDFLAGS` environment variable for `make`. The appropriate settings are as follows using the `LDFLAGS` environment variable for `make`. The appropriate settings are as follows
- To set the `CustomPath` use `LDFLAGS="-X \"code.gitea.io/gitea/modules/setting.CustomPath=custom-path\""` - To set the _`CustomPath`_ use `LDFLAGS="-X \"code.gitea.io/gitea/modules/setting.CustomPath=custom-path\""`
- For `CustomConf` you should use `-X \"code.gitea.io/gitea/modules/setting.CustomConf=conf.ini\"` - For _`CustomConf`_ you should use `-X \"code.gitea.io/gitea/modules/setting.CustomConf=conf.ini\"`
- For `AppWorkPath` you should use `-X \"code.gitea.io/gitea/modules/setting.AppWorkPath=working-path\"` - For _`AppWorkPath`_ you should use `-X \"code.gitea.io/gitea/modules/setting.AppWorkPath=working-path\"`
- For `StaticRootPath` you should use `-X \"code.gitea.io/gitea/modules/setting.StaticRootPath=static-root-path\"` - For _`StaticRootPath`_ you should use `-X \"code.gitea.io/gitea/modules/setting.StaticRootPath=static-root-path\"`
- To change the default PID file location use `-X \"code.gitea.io/gitea/modules/setting.PIDFile=/run/gitea.pid\"` - To change the default PID file location use `-X \"code.gitea.io/gitea/modules/setting.PIDFile=/run/gitea.pid\"`
Add as many of the strings with their preceding `-X` to the `LDFLAGS` variable and run `make build` Add as many of the strings with their preceding `-X` to the `LDFLAGS` variable and run `make build`

View File

@ -0,0 +1,84 @@
---
date: "2022-11-01T00:00:00+00:00"
title: "Storage"
slug: "packages/storage"
draft: false
toc: false
menu:
sidebar:
parent: "packages"
name: "storage"
weight: 5
identifier: "storage"
---
# Storage
This document describes the storage of the package registry and how it can be managed.
**Table of Contents**
{{< toc >}}
## Deduplication
The package registry has a build-in deduplication of uploaded blobs.
If two identical files are uploaded only one blob is saved on the filesystem.
This ensures no space is wasted for duplicated files.
If two packages are uploaded with identical files, both packages will display the same size but on the filesystem they require only half of the size.
Whenever a package gets deleted only the references to the underlaying blobs are removed.
The blobs get not removed at this moment, so they still require space on the filesystem.
When a new package gets uploaded the existing blobs may get referenced again.
These unreferenced blobs get deleted by a [clean up job]({{< relref "doc/advanced/config-cheat-sheet.en-us.md#cron---cleanup-expired-packages-croncleanup_packages" >}}).
The config setting `OLDER_THAN` configures how long unreferenced blobs are kept before they get deleted.
## Cleanup Rules
Package registries can become large over time without cleanup.
It's recommended to delete unnecessary packages and set up cleanup rules to automatically manage the package registry usage.
Every package owner (user or organization) manages the cleanup rules which are applied to their packages.
|Setting|Description|
|-|-|
|Enabled|Turn the cleanup rule on or off.|
|Type|Every rule manages a specific package type.|
|Apply pattern to full package name|If enabled, the patterns below are applied to the full package name (`package/version`). Otherwise only the version (`version`) is used.|
|Keep the most recent|How many versions to *always* keep for each package.|
|Keep versions matching|The regex pattern that determines which versions to keep. An empty pattern keeps no version while `.+` keeps all versions. The container registry will always keep the `latest` version even if not configured.|
|Remove versions older than|Remove only versions older than the selected days.|
|Remove versions matching|The regex pattern that determines which versions to remove. An empty pattern or `.+` leads to the removal of every package if no other setting tells otherwise.|
Every cleanup rule can show a preview of the affected packages.
This can be used to check if the cleanup rules is proper configured.
### Regex examples
Regex patterns are automatically surrounded with `\A` and `\z` anchors.
Do not include any `\A`, `\z`, `^` or `$` token in the regex patterns as they are not necessary.
The patterns are case-insensitive which matches the behaviour of the package registry in Gitea.
|Pattern|Description|
|-|-|
|`.*`|Match every possible version.|
|`v.+`|Match versions that start with `v`.|
|`release`|Match only the version `release`.|
|`release.*`|Match versions that are either named or start with `release`.|
|`.+-temp-.+`|Match versions that contain `-temp-`.|
|`v.+\|release`|Match versions that either start with `v` or are named `release`.|
|`package/v.+\|other/release`|Match versions of the package `package` that start with `v` or the version `release` of the package `other`. This needs the setting *Apply pattern to full package name* enabled.|
### How the cleanup rules work
The cleanup rules are part of the [clean up job]({{< relref "doc/advanced/config-cheat-sheet.en-us.md#cron---cleanup-expired-packages-croncleanup_packages" >}}) and run periodicly.
The cleanup rule:
1. Collects all packages of the package type for the owners registry.
1. For every package it collects all versions.
1. Excludes from the list the # versions based on the *Keep the most recent* value.
1. Excludes from the list any versions matching the *Keep versions matching* value.
1. Excludes from the list the versions more recent than the *Remove versions older than* value.
1. Excludes from the list any versions not matching the *Remove versions matching* value.
1. Deletes the remaining versions.

View File

@ -45,15 +45,15 @@ Beispiel: Wenn der Button mit `löschen` beschriftet ist, sollte im Modal die Fr
## Artikeldefinitionen für Anglizismen ## Artikeldefinitionen für Anglizismen
* _Der_ Commit (m.) * _Der_ Commit (m.)
* _Der_ Branch (m.) * _Der_ Branch (m.), plural: die Branches
* _Das_ Issue (n.) * _Das_ Issue (n.)
* _Der_ Fork (m.) * _Der_ Fork (m.)
* _Das_ Repository (n.) * _Das_ Repository (n.), plural: die Repositories
* _Der_ Pull-Request (m.) * _Der_ Pull-Request (m.)
* _Der_ Token (m.) * _Der_ Token (m.), plural: die Token
* _Das_ Review (n.) * _Das_ Review (n.)
* _Der_ Key (m.) * _Der_ Key (m.)
* _Der_ Committer (m.) * _Der_ Committer (m.), plural: die Committer
## Weiterführende Links ## Weiterführende Links

10
go.mod
View File

@ -94,15 +94,14 @@ require (
github.com/yuin/goldmark-meta v1.1.0 github.com/yuin/goldmark-meta v1.1.0
go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/hcaptcha v0.0.4
go.jolheiser.com/pwn v0.0.3 go.jolheiser.com/pwn v0.0.3
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891
golang.org/x/net v0.0.0-20220927171203-f486391704dc golang.org/x/net v0.2.0
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec golang.org/x/sys v0.2.0
golang.org/x/text v0.3.8 golang.org/x/text v0.4.0
golang.org/x/tools v0.1.12 golang.org/x/tools v0.1.12
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.4.0 mvdan.cc/xurls/v2 v2.4.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
@ -293,6 +292,7 @@ require (
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect sigs.k8s.io/yaml v1.2.0 // indirect
) )

18
go.sum
View File

@ -1608,8 +1608,8 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891 h1:WhEPFM1Ck5gaKybeSWvzI7Y/cd8K9K5tJGRxXMACOBA=
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1721,8 +1721,8 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1876,13 +1876,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1892,8 +1892,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -461,7 +461,8 @@ func DeleteOldActions(olderThan time.Duration) (err error) {
return err return err
} }
func notifyWatchers(ctx context.Context, actions ...*Action) error { // NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers(ctx context.Context, actions ...*Action) error {
var watchers []*repo_model.Watch var watchers []*repo_model.Watch
var repo *repo_model.Repository var repo *repo_model.Repository
var err error var err error
@ -565,20 +566,15 @@ func notifyWatchers(ctx context.Context, actions ...*Action) error {
return nil return nil
} }
// NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers(actions ...*Action) error {
return notifyWatchers(db.DefaultContext, actions...)
}
// NotifyWatchersActions creates batch of actions for every watcher. // NotifyWatchersActions creates batch of actions for every watcher.
func NotifyWatchersActions(acts []*Action) error { func NotifyWatchersActions(acts []*Action) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
defer committer.Close() defer committer.Close()
for _, act := range acts { for _, act := range acts {
if err := notifyWatchers(ctx, act); err != nil { if err := NotifyWatchers(ctx, act); err != nil {
return err return err
} }
} }
@ -603,17 +599,17 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error {
} }
// CountActionCreatedUnixString count actions where created_unix is an empty string // CountActionCreatedUnixString count actions where created_unix is an empty string
func CountActionCreatedUnixString() (int64, error) { func CountActionCreatedUnixString(ctx context.Context) (int64, error) {
if setting.Database.UseSQLite3 { if setting.Database.UseSQLite3 {
return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action)) return db.GetEngine(ctx).Where(`created_unix = ""`).Count(new(Action))
} }
return 0, nil return 0, nil
} }
// FixActionCreatedUnixString set created_unix to zero if it is an empty string // FixActionCreatedUnixString set created_unix to zero if it is an empty string
func FixActionCreatedUnixString() (int64, error) { func FixActionCreatedUnixString(ctx context.Context) (int64, error) {
if setting.Database.UseSQLite3 { if setting.Database.UseSQLite3 {
res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`) res, err := db.GetEngine(ctx).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
if err != nil { if err != nil {
return 0, err return 0, err
} }

View File

@ -188,7 +188,7 @@ func TestNotifyWatchers(t *testing.T) {
RepoID: 1, RepoID: 1,
OpType: activities_model.ActionStarRepo, OpType: activities_model.ActionStarRepo,
} }
assert.NoError(t, activities_model.NotifyWatchers(action)) assert.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action))
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11 // One watchers are inactive, thus action is only created for user 8, 1, 4, 11
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
@ -256,17 +256,17 @@ func TestConsistencyUpdateAction(t *testing.T) {
// //
// Get rid of incorrectly set created_unix // Get rid of incorrectly set created_unix
// //
count, err := activities_model.CountActionCreatedUnixString() count, err := activities_model.CountActionCreatedUnixString(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, count) assert.EqualValues(t, 1, count)
count, err = activities_model.FixActionCreatedUnixString() count, err = activities_model.FixActionCreatedUnixString(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, count) assert.EqualValues(t, 1, count)
count, err = activities_model.CountActionCreatedUnixString() count, err = activities_model.CountActionCreatedUnixString(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 0, count) assert.EqualValues(t, 0, count)
count, err = activities_model.FixActionCreatedUnixString() count, err = activities_model.FixActionCreatedUnixString(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 0, count) assert.EqualValues(t, 0, count)

View File

@ -136,56 +136,48 @@ func GetNotifications(ctx context.Context, options *FindNotificationOptions) (nl
} }
// CountNotifications count all notifications that fit to the given options and ignore pagination. // CountNotifications count all notifications that fit to the given options and ignore pagination.
func CountNotifications(opts *FindNotificationOptions) (int64, error) { func CountNotifications(ctx context.Context, opts *FindNotificationOptions) (int64, error) {
return db.GetEngine(db.DefaultContext).Where(opts.ToCond()).Count(&Notification{}) return db.GetEngine(ctx).Where(opts.ToCond()).Count(&Notification{})
} }
// CreateRepoTransferNotification creates notification for the user a repository was transferred to // CreateRepoTransferNotification creates notification for the user a repository was transferred to
func CreateRepoTransferNotification(doer, newOwner *user_model.User, repo *repo_model.Repository) error { func CreateRepoTransferNotification(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) error {
ctx, committer, err := db.TxContext() return db.AutoTx(ctx, func(ctx context.Context) error {
if err != nil { var notify []*Notification
return err
}
defer committer.Close()
var notify []*Notification if newOwner.IsOrganization() {
users, err := organization.GetUsersWhoCanCreateOrgRepo(ctx, newOwner.ID)
if newOwner.IsOrganization() { if err != nil || len(users) == 0 {
users, err := organization.GetUsersWhoCanCreateOrgRepo(ctx, newOwner.ID) return err
if err != nil || len(users) == 0 { }
return err for i := range users {
} notify = append(notify, &Notification{
for i := range users { UserID: users[i].ID,
notify = append(notify, &Notification{ RepoID: repo.ID,
UserID: users[i].ID, Status: NotificationStatusUnread,
UpdatedBy: doer.ID,
Source: NotificationSourceRepository,
})
}
} else {
notify = []*Notification{{
UserID: newOwner.ID,
RepoID: repo.ID, RepoID: repo.ID,
Status: NotificationStatusUnread, Status: NotificationStatusUnread,
UpdatedBy: doer.ID, UpdatedBy: doer.ID,
Source: NotificationSourceRepository, Source: NotificationSourceRepository,
}) }}
} }
} else {
notify = []*Notification{{
UserID: newOwner.ID,
RepoID: repo.ID,
Status: NotificationStatusUnread,
UpdatedBy: doer.ID,
Source: NotificationSourceRepository,
}}
}
if err := db.Insert(ctx, notify); err != nil { return db.Insert(ctx, notify)
return err })
}
return committer.Commit()
} }
// CreateOrUpdateIssueNotifications creates an issue notification // CreateOrUpdateIssueNotifications creates an issue notification
// for each watcher, or updates it if already exists // for each watcher, or updates it if already exists
// receiverID > 0 just send to receiver, else send to all watcher // receiverID > 0 just send to receiver, else send to all watcher
func CreateOrUpdateIssueNotifications(issueID, commentID, notificationAuthorID, receiverID int64) error { func CreateOrUpdateIssueNotifications(issueID, commentID, notificationAuthorID, receiverID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -379,11 +371,7 @@ func CountUnread(ctx context.Context, userID int64) int64 {
} }
// LoadAttributes load Repo Issue User and Comment if not loaded // LoadAttributes load Repo Issue User and Comment if not loaded
func (n *Notification) LoadAttributes() (err error) { func (n *Notification) LoadAttributes(ctx context.Context) (err error) {
return n.loadAttributes(db.DefaultContext)
}
func (n *Notification) loadAttributes(ctx context.Context) (err error) {
if err = n.loadRepo(ctx); err != nil { if err = n.loadRepo(ctx); err != nil {
return return
} }
@ -481,10 +469,10 @@ func (n *Notification) APIURL() string {
type NotificationList []*Notification type NotificationList []*Notification
// LoadAttributes load Repo Issue User and Comment if not loaded // LoadAttributes load Repo Issue User and Comment if not loaded
func (nl NotificationList) LoadAttributes() error { func (nl NotificationList) LoadAttributes(ctx context.Context) error {
var err error var err error
for i := 0; i < len(nl); i++ { for i := 0; i < len(nl); i++ {
err = nl[i].LoadAttributes() err = nl[i].LoadAttributes(ctx)
if err != nil && !issues_model.IsErrCommentNotExist(err) { if err != nil && !issues_model.IsErrCommentNotExist(err) {
return err return err
} }
@ -504,7 +492,7 @@ func (nl NotificationList) getPendingRepoIDs() []int64 {
} }
// LoadRepos loads repositories from database // LoadRepos loads repositories from database
func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error) { func (nl NotificationList) LoadRepos(ctx context.Context) (repo_model.RepositoryList, []int, error) {
if len(nl) == 0 { if len(nl) == 0 {
return repo_model.RepositoryList{}, []int{}, nil return repo_model.RepositoryList{}, []int{}, nil
} }
@ -517,7 +505,7 @@ func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error)
if left < limit { if left < limit {
limit = left limit = left
} }
rows, err := db.GetEngine(db.DefaultContext). rows, err := db.GetEngine(ctx).
In("id", repoIDs[:limit]). In("id", repoIDs[:limit]).
Rows(new(repo_model.Repository)) Rows(new(repo_model.Repository))
if err != nil { if err != nil {
@ -578,7 +566,7 @@ func (nl NotificationList) getPendingIssueIDs() []int64 {
} }
// LoadIssues loads issues from database // LoadIssues loads issues from database
func (nl NotificationList) LoadIssues() ([]int, error) { func (nl NotificationList) LoadIssues(ctx context.Context) ([]int, error) {
if len(nl) == 0 { if len(nl) == 0 {
return []int{}, nil return []int{}, nil
} }
@ -591,7 +579,7 @@ func (nl NotificationList) LoadIssues() ([]int, error) {
if left < limit { if left < limit {
limit = left limit = left
} }
rows, err := db.GetEngine(db.DefaultContext). rows, err := db.GetEngine(ctx).
In("id", issueIDs[:limit]). In("id", issueIDs[:limit]).
Rows(new(issues_model.Issue)) Rows(new(issues_model.Issue))
if err != nil { if err != nil {
@ -662,7 +650,7 @@ func (nl NotificationList) getPendingCommentIDs() []int64 {
} }
// LoadComments loads comments from database // LoadComments loads comments from database
func (nl NotificationList) LoadComments() ([]int, error) { func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
if len(nl) == 0 { if len(nl) == 0 {
return []int{}, nil return []int{}, nil
} }
@ -675,7 +663,7 @@ func (nl NotificationList) LoadComments() ([]int, error) {
if left < limit { if left < limit {
limit = left limit = left
} }
rows, err := db.GetEngine(db.DefaultContext). rows, err := db.GetEngine(ctx).
In("id", commentIDs[:limit]). In("id", commentIDs[:limit]).
Rows(new(issues_model.Comment)) Rows(new(issues_model.Comment))
if err != nil { if err != nil {
@ -775,8 +763,8 @@ func SetRepoReadBy(ctx context.Context, userID, repoID int64) error {
} }
// SetNotificationStatus change the notification status // SetNotificationStatus change the notification status
func SetNotificationStatus(notificationID int64, user *user_model.User, status NotificationStatus) (*Notification, error) { func SetNotificationStatus(ctx context.Context, notificationID int64, user *user_model.User, status NotificationStatus) (*Notification, error) {
notification, err := getNotificationByID(db.DefaultContext, notificationID) notification, err := GetNotificationByID(ctx, notificationID)
if err != nil { if err != nil {
return notification, err return notification, err
} }
@ -787,16 +775,12 @@ func SetNotificationStatus(notificationID int64, user *user_model.User, status N
notification.Status = status notification.Status = status
_, err = db.GetEngine(db.DefaultContext).ID(notificationID).Update(notification) _, err = db.GetEngine(ctx).ID(notificationID).Update(notification)
return notification, err return notification, err
} }
// GetNotificationByID return notification by ID // GetNotificationByID return notification by ID
func GetNotificationByID(notificationID int64) (*Notification, error) { func GetNotificationByID(ctx context.Context, notificationID int64) (*Notification, error) {
return getNotificationByID(db.DefaultContext, notificationID)
}
func getNotificationByID(ctx context.Context, notificationID int64) (*Notification, error) {
notification := new(Notification) notification := new(Notification)
ok, err := db.GetEngine(ctx). ok, err := db.GetEngine(ctx).
Where("id = ?", notificationID). Where("id = ?", notificationID).
@ -813,9 +797,9 @@ func getNotificationByID(ctx context.Context, notificationID int64) (*Notificati
} }
// UpdateNotificationStatuses updates the statuses of all of a user's notifications that are of the currentStatus type to the desiredStatus // UpdateNotificationStatuses updates the statuses of all of a user's notifications that are of the currentStatus type to the desiredStatus
func UpdateNotificationStatuses(user *user_model.User, currentStatus, desiredStatus NotificationStatus) error { func UpdateNotificationStatuses(ctx context.Context, user *user_model.User, currentStatus, desiredStatus NotificationStatus) error {
n := &Notification{Status: desiredStatus, UpdatedBy: user.ID} n := &Notification{Status: desiredStatus, UpdatedBy: user.ID}
_, err := db.GetEngine(db.DefaultContext). _, err := db.GetEngine(ctx).
Where("user_id = ? AND status = ?", user.ID, currentStatus). Where("user_id = ? AND status = ?", user.ID, currentStatus).
Cols("status", "updated_by", "updated_unix"). Cols("status", "updated_by", "updated_unix").
Update(n) Update(n)

View File

@ -82,14 +82,14 @@ func TestSetNotificationStatus(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
notf := unittest.AssertExistsAndLoadBean(t, notf := unittest.AssertExistsAndLoadBean(t,
&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead}) &activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
_, err := activities_model.SetNotificationStatus(notf.ID, user, activities_model.NotificationStatusPinned) _, err := activities_model.SetNotificationStatus(db.DefaultContext, notf.ID, user, activities_model.NotificationStatusPinned)
assert.NoError(t, err) assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, unittest.AssertExistsAndLoadBean(t,
&activities_model.Notification{ID: notf.ID, Status: activities_model.NotificationStatusPinned}) &activities_model.Notification{ID: notf.ID, Status: activities_model.NotificationStatusPinned})
_, err = activities_model.SetNotificationStatus(1, user, activities_model.NotificationStatusRead) _, err = activities_model.SetNotificationStatus(db.DefaultContext, 1, user, activities_model.NotificationStatusRead)
assert.Error(t, err) assert.Error(t, err)
_, err = activities_model.SetNotificationStatus(unittest.NonexistentID, user, activities_model.NotificationStatusRead) _, err = activities_model.SetNotificationStatus(db.DefaultContext, unittest.NonexistentID, user, activities_model.NotificationStatusRead)
assert.Error(t, err) assert.Error(t, err)
} }
@ -102,7 +102,7 @@ func TestUpdateNotificationStatuses(t *testing.T) {
&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead}) &activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
notfPinned := unittest.AssertExistsAndLoadBean(t, notfPinned := unittest.AssertExistsAndLoadBean(t,
&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusPinned}) &activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusPinned})
assert.NoError(t, activities_model.UpdateNotificationStatuses(user, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead)) assert.NoError(t, activities_model.UpdateNotificationStatuses(db.DefaultContext, user, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead))
unittest.AssertExistsAndLoadBean(t, unittest.AssertExistsAndLoadBean(t,
&activities_model.Notification{ID: notfUnread.ID, Status: activities_model.NotificationStatusRead}) &activities_model.Notification{ID: notfUnread.ID, Status: activities_model.NotificationStatusRead})
unittest.AssertExistsAndLoadBean(t, unittest.AssertExistsAndLoadBean(t,

View File

@ -234,7 +234,7 @@ func DeleteGPGKey(doer *user_model.User, id int64) (err error) {
return ErrGPGKeyAccessDenied{doer.ID, key.ID} return ErrGPGKeyAccessDenied{doer.ID, key.ID}
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -73,7 +73,7 @@ func AddGPGKey(ownerID int64, content, token, signature string) ([]*GPGKey, erro
return nil, err return nil, err
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -31,7 +31,7 @@ import (
// VerifyGPGKey marks a GPG key as verified // VerifyGPGKey marks a GPG key as verified
func VerifyGPGKey(ownerID int64, keyID, token, signature string) (string, error) { func VerifyGPGKey(ownerID int64, keyID, token, signature string) (string, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -100,7 +100,7 @@ func AddPublicKey(ownerID int64, name, content string, authSourceID int64) (*Pub
return nil, err return nil, err
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -321,7 +321,7 @@ func PublicKeyIsExternallyManaged(id int64) (bool, error) {
// deleteKeysMarkedForDeletion returns true if ssh keys needs update // deleteKeysMarkedForDeletion returns true if ssh keys needs update
func deleteKeysMarkedForDeletion(keys []string) (bool, error) { func deleteKeysMarkedForDeletion(keys []string) (bool, error) {
// Start session // Start session
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -126,7 +126,7 @@ func AddDeployKey(repoID int64, name, content string, readOnly bool) (*DeployKey
accessMode = perm.AccessModeWrite accessMode = perm.AccessModeWrite
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -26,7 +26,7 @@ import (
// AddPrincipalKey adds new principal to database and authorized_principals file. // AddPrincipalKey adds new principal to database and authorized_principals file.
func AddPrincipalKey(ownerID int64, content string, authSourceID int64) (*PublicKey, error) { func AddPrincipalKey(ownerID int64, content string, authSourceID int64) (*PublicKey, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -15,7 +15,7 @@ import (
// VerifySSHKey marks a SSH key as verified // VerifySSHKey marks a SSH key as verified
func VerifySSHKey(ownerID int64, fingerprint, token, signature string) (string, error) { func VerifySSHKey(ownerID int64, fingerprint, token, signature string) (string, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -201,7 +201,7 @@ type UpdateOAuth2ApplicationOptions struct {
// UpdateOAuth2Application updates an oauth2 application // UpdateOAuth2Application updates an oauth2 application
func UpdateOAuth2Application(opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) { func UpdateOAuth2Application(opts UpdateOAuth2ApplicationOptions) (*OAuth2Application, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -265,7 +265,7 @@ func deleteOAuth2Application(ctx context.Context, id, userid int64) error {
// DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app. // DeleteOAuth2Application deletes the application with the given id and the grants and auth codes related to it. It checks if the userid was the creator of the app.
func DeleteOAuth2Application(id, userid int64) error { func DeleteOAuth2Application(id, userid int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -37,7 +37,7 @@ func ReadSession(key string) (*Session, error) {
Key: key, Key: key,
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -73,7 +73,7 @@ func DestroySession(key string) error {
// RegenerateSession regenerates a session from the old id // RegenerateSession regenerates a session from the old id
func RegenerateSession(oldKey, newKey string) (*Session, error) { func RegenerateSession(oldKey, newKey string) (*Session, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -97,7 +97,7 @@ func saveEmailHash(email string) string {
Hash: emailHash, Hash: emailHash,
} }
// OK we're going to open a session just because I think that that might hide away any problems with postgres reporting errors // OK we're going to open a session just because I think that that might hide away any problems with postgres reporting errors
if err := db.WithTx(func(ctx context.Context) error { if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error {
has, err := db.GetEngine(ctx).Where("email = ? AND hash = ?", emailHash.Email, emailHash.Hash).Get(new(EmailHash)) has, err := db.GetEngine(ctx).Where("email = ? AND hash = ?", emailHash.Email, emailHash.Hash).Get(new(EmailHash))
if has || err != nil { if has || err != nil {
// Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time // Seriously we don't care about any DB problems just return the lowerEmail - we expect the transaction to fail most of the time
@ -150,10 +150,11 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
return DefaultAvatarLink() return DefaultAvatarLink()
} }
enableFederatedAvatar, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar) enableFederatedAvatarSetting, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar)
enableFederatedAvatar := enableFederatedAvatarSetting.GetValueBool()
var err error var err error
if enableFederatedAvatar != nil && enableFederatedAvatar.GetValueBool() && system_model.LibravatarService != nil { if enableFederatedAvatar && system_model.LibravatarService != nil {
emailHash := saveEmailHash(email) emailHash := saveEmailHash(email)
if final { if final {
// for final link, we can spend more time on slow external query // for final link, we can spend more time on slow external query
@ -171,8 +172,10 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
return urlStr return urlStr
} }
disableGravatar, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar) disableGravatarSetting, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar)
if disableGravatar != nil && !disableGravatar.GetValueBool() {
disableGravatar := disableGravatarSetting.GetValueBool()
if !disableGravatar {
// copy GravatarSourceURL, because we will modify its Path. // copy GravatarSourceURL, because we will modify its Path.
avatarURLCopy := *system_model.GravatarSourceURL avatarURLCopy := *system_model.GravatarSourceURL
avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email)) avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email))

View File

@ -4,11 +4,16 @@
package db package db
import "xorm.io/builder" import (
"context"
"xorm.io/builder"
)
// CountOrphanedObjects count subjects with have no existing refobject anymore // CountOrphanedObjects count subjects with have no existing refobject anymore
func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) { func CountOrphanedObjects(ctx context.Context, subject, refobject, joinCond string) (int64, error) {
return GetEngine(DefaultContext).Table("`"+subject+"`"). return GetEngine(ctx).
Table("`"+subject+"`").
Join("LEFT", "`"+refobject+"`", joinCond). Join("LEFT", "`"+refobject+"`", joinCond).
Where(builder.IsNull{"`" + refobject + "`.id"}). Where(builder.IsNull{"`" + refobject + "`.id"}).
Select("COUNT(`" + subject + "`.`id`)"). Select("COUNT(`" + subject + "`.`id`)").
@ -16,12 +21,12 @@ func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) {
} }
// DeleteOrphanedObjects delete subjects with have no existing refobject anymore // DeleteOrphanedObjects delete subjects with have no existing refobject anymore
func DeleteOrphanedObjects(subject, refobject, joinCond string) error { func DeleteOrphanedObjects(ctx context.Context, subject, refobject, joinCond string) error {
subQuery := builder.Select("`"+subject+"`.id"). subQuery := builder.Select("`"+subject+"`.id").
From("`"+subject+"`"). From("`"+subject+"`").
Join("LEFT", "`"+refobject+"`", joinCond). Join("LEFT", "`"+refobject+"`", joinCond).
Where(builder.IsNull{"`" + refobject + "`.id"}) Where(builder.IsNull{"`" + refobject + "`.id"})
b := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`") b := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`")
_, err := GetEngine(DefaultContext).Exec(b) _, err := GetEngine(ctx).Exec(b)
return err return err
} }

View File

@ -8,6 +8,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"xorm.io/xorm"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
) )
@ -86,7 +87,11 @@ type Committer interface {
} }
// TxContext represents a transaction Context // TxContext represents a transaction Context
func TxContext() (*Context, Committer, error) { func TxContext(parentCtx context.Context) (*Context, Committer, error) {
if InTransaction(parentCtx) {
return nil, nil, ErrAlreadyInTransaction
}
sess := x.NewSession() sess := x.NewSession()
if err := sess.Begin(); err != nil { if err := sess.Begin(); err != nil {
sess.Close() sess.Close()
@ -97,14 +102,24 @@ func TxContext() (*Context, Committer, error) {
} }
// WithTx represents executing database operations on a transaction // WithTx represents executing database operations on a transaction
// you can optionally change the context to a parent one // This function will always open a new transaction, if a transaction exist in parentCtx return an error.
func WithTx(f func(ctx context.Context) error, stdCtx ...context.Context) error { func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error {
parentCtx := DefaultContext if InTransaction(parentCtx) {
if len(stdCtx) != 0 && stdCtx[0] != nil { return ErrAlreadyInTransaction
// TODO: make sure parent context has no open session
parentCtx = stdCtx[0]
} }
return txWithNoCheck(parentCtx, f)
}
// AutoTx represents executing database operations on a transaction, if the transaction exist,
// this function will reuse it otherwise will create a new one and close it when finished.
func AutoTx(parentCtx context.Context, f func(ctx context.Context) error) error {
if InTransaction(parentCtx) {
return f(newContext(parentCtx, GetEngine(parentCtx), true))
}
return txWithNoCheck(parentCtx, f)
}
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
sess := x.NewSession() sess := x.NewSession()
defer sess.Close() defer sess.Close()
if err := sess.Begin(); err != nil { if err := sess.Begin(); err != nil {
@ -180,3 +195,28 @@ func EstimateCount(ctx context.Context, bean interface{}) (int64, error) {
} }
return rows, err return rows, err
} }
// InTransaction returns true if the engine is in a transaction otherwise return false
func InTransaction(ctx context.Context) bool {
var e Engine
if engined, ok := ctx.(Engined); ok {
e = engined.Engine()
} else {
enginedInterface := ctx.Value(enginedContextKey)
if enginedInterface != nil {
e = enginedInterface.(Engined).Engine()
}
}
if e == nil {
return false
}
switch t := e.(type) {
case *xorm.Engine:
return false
case *xorm.Session:
return t.IsInTx()
default:
return false
}
}

33
models/db/context_test.go Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package db_test
import (
"context"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func TestInTransaction(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.False(t, db.InTransaction(db.DefaultContext))
assert.NoError(t, db.WithTx(db.DefaultContext, func(ctx context.Context) error {
assert.True(t, db.InTransaction(ctx))
return nil
}))
ctx, committer, err := db.TxContext(db.DefaultContext)
assert.NoError(t, err)
defer committer.Close()
assert.True(t, db.InTransaction(ctx))
assert.Error(t, db.WithTx(ctx, func(ctx context.Context) error {
assert.True(t, db.InTransaction(ctx))
return nil
}))
}

View File

@ -41,11 +41,11 @@ func TestDeleteOrphanedObjects(t *testing.T) {
_, err = db.GetEngine(db.DefaultContext).Insert(&issues_model.PullRequest{IssueID: 1000}, &issues_model.PullRequest{IssueID: 1001}, &issues_model.PullRequest{IssueID: 1003}) _, err = db.GetEngine(db.DefaultContext).Insert(&issues_model.PullRequest{IssueID: 1000}, &issues_model.PullRequest{IssueID: 1001}, &issues_model.PullRequest{IssueID: 1003})
assert.NoError(t, err) assert.NoError(t, err)
orphaned, err := db.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") orphaned, err := db.CountOrphanedObjects(db.DefaultContext, "pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 3, orphaned) assert.EqualValues(t, 3, orphaned)
err = db.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") err = db.DeleteOrphanedObjects(db.DefaultContext, "pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err) assert.NoError(t, err)
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{}) countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})

View File

@ -5,11 +5,14 @@
package db package db
import ( import (
"errors"
"fmt" "fmt"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
var ErrAlreadyInTransaction = errors.New("database connection has already been in a transaction")
// ErrCancelled represents an error due to context cancellation // ErrCancelled represents an error due to context cancellation
type ErrCancelled struct { type ErrCancelled struct {
Message string Message string

View File

@ -59,7 +59,7 @@ func TestSyncMaxResourceIndex(t *testing.T) {
assert.EqualValues(t, 62, maxIndex) assert.EqualValues(t, 62, maxIndex)
// commit transaction // commit transaction
err = db.WithTx(func(ctx context.Context) error { err = db.WithTx(db.DefaultContext, func(ctx context.Context) error {
err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 73) err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 73)
assert.NoError(t, err) assert.NoError(t, err)
maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10) maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10)
@ -73,7 +73,7 @@ func TestSyncMaxResourceIndex(t *testing.T) {
assert.EqualValues(t, 73, maxIndex) assert.EqualValues(t, 73, maxIndex)
// rollback transaction // rollback transaction
err = db.WithTx(func(ctx context.Context) error { err = db.WithTx(db.DefaultContext, func(ctx context.Context) error {
err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 84) err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 84)
maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10) maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10)
assert.NoError(t, err) assert.NoError(t, err)
@ -102,7 +102,7 @@ func TestGetNextResourceIndex(t *testing.T) {
assert.EqualValues(t, 2, maxIndex) assert.EqualValues(t, 2, maxIndex)
// commit transaction // commit transaction
err = db.WithTx(func(ctx context.Context) error { err = db.WithTx(db.DefaultContext, func(ctx context.Context) error {
maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20) maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 3, maxIndex) assert.EqualValues(t, 3, maxIndex)
@ -114,7 +114,7 @@ func TestGetNextResourceIndex(t *testing.T) {
assert.EqualValues(t, 3, maxIndex) assert.EqualValues(t, 3, maxIndex)
// rollback transaction // rollback transaction
err = db.WithTx(func(ctx context.Context) error { err = db.WithTx(db.DefaultContext, func(ctx context.Context) error {
maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20) maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 4, maxIndex) assert.EqualValues(t, 4, maxIndex)

View File

@ -5,6 +5,7 @@
package db package db
import ( import (
"context"
"fmt" "fmt"
"regexp" "regexp"
@ -12,7 +13,7 @@ import (
) )
// CountBadSequences looks for broken sequences from recreate-table mistakes // CountBadSequences looks for broken sequences from recreate-table mistakes
func CountBadSequences() (int64, error) { func CountBadSequences(_ context.Context) (int64, error) {
if !setting.Database.UsePostgreSQL { if !setting.Database.UsePostgreSQL {
return 0, nil return 0, nil
} }
@ -33,7 +34,7 @@ func CountBadSequences() (int64, error) {
} }
// FixBadSequences fixes for broken sequences from recreate-table mistakes // FixBadSequences fixes for broken sequences from recreate-table mistakes
func FixBadSequences() error { func FixBadSequences(_ context.Context) error {
if !setting.Database.UsePostgreSQL { if !setting.Database.UsePostgreSQL {
return nil return nil
} }

View File

@ -22,7 +22,7 @@ func GetYamlFixturesAccess() (string, error) {
} }
for _, repo := range repos { for _, repo := range repos {
repo.MustOwner() repo.MustOwner(db.DefaultContext)
if err := access_model.RecalculateAccesses(db.DefaultContext, repo); err != nil { if err := access_model.RecalculateAccesses(db.DefaultContext, repo); err != nil {
return "", err return "", err
} }

View File

@ -544,7 +544,7 @@ func FindRenamedBranch(repoID int64, from string) (branch *RenamedBranch, exist
// RenameBranch rename a branch // RenameBranch rename a branch
func RenameBranch(repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) { func RenameBranch(repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -102,7 +102,7 @@ func TestRenameBranch(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
_isDefault := false _isDefault := false
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
defer committer.Close() defer committer.Close()
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, git_model.UpdateProtectBranch(ctx, repo1, &git_model.ProtectedBranch{ assert.NoError(t, git_model.UpdateProtectBranch(ctx, repo1, &git_model.ProtectedBranch{

View File

@ -94,7 +94,7 @@ func GetNextCommitStatusIndex(repoID int64, sha string) (int64, error) {
// getNextCommitStatusIndex return the next index // getNextCommitStatusIndex return the next index
func getNextCommitStatusIndex(repoID int64, sha string) (int64, error) { func getNextCommitStatusIndex(repoID int64, sha string) (int64, error) {
ctx, commiter, err := db.TxContext() ctx, commiter, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -297,7 +297,7 @@ func NewCommitStatus(opts NewCommitStatusOptions) error {
return fmt.Errorf("generate commit status index failed: %w", err) return fmt.Errorf("generate commit status index failed: %w", err)
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", opts.Repo.ID, opts.Creator.ID, opts.SHA, err) return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", opts.Repo.ID, opts.Creator.ID, opts.SHA, err)
} }

View File

@ -137,7 +137,7 @@ var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
func NewLFSMetaObject(m *LFSMetaObject) (*LFSMetaObject, error) { func NewLFSMetaObject(m *LFSMetaObject) (*LFSMetaObject, error) {
var err error var err error
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -185,7 +185,7 @@ func RemoveLFSMetaObjectByOid(repoID int64, oid string) (int64, error) {
return 0, ErrLFSObjectNotExist return 0, ErrLFSObjectNotExist
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -235,14 +235,14 @@ func LFSObjectAccessible(user *user_model.User, oid string) (bool, error) {
return count > 0, err return count > 0, err
} }
// LFSObjectIsAssociated checks if a provided Oid is associated // ExistsLFSObject checks if a provided Oid exists within the DB
func LFSObjectIsAssociated(oid string) (bool, error) { func ExistsLFSObject(ctx context.Context, oid string) (bool, error) {
return db.GetEngine(db.DefaultContext).Exist(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}}) return db.GetEngine(ctx).Exist(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}})
} }
// LFSAutoAssociate auto associates accessible LFSMetaObjects // LFSAutoAssociate auto associates accessible LFSMetaObjects
func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int64) error { func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -44,7 +44,7 @@ func cleanPath(p string) string {
// CreateLFSLock creates a new lock. // CreateLFSLock creates a new lock.
func CreateLFSLock(repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) { func CreateLFSLock(repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
dbCtx, committer, err := db.TxContext() dbCtx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -137,7 +137,7 @@ func CountLFSLockByRepoID(repoID int64) (int64, error) {
// DeleteLFSLockByID deletes a lock by given ID. // DeleteLFSLockByID deletes a lock by given ID.
func DeleteLFSLockByID(id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) { func DeleteLFSLockByID(id int64, repo *repo_model.Repository, u *user_model.User, force bool) (*LFSLock, error) {
dbCtx, committer, err := db.TxContext() dbCtx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -5,6 +5,7 @@
package git package git
import ( import (
"context"
"regexp" "regexp"
"strings" "strings"
@ -69,13 +70,13 @@ func UpdateProtectedTag(pt *ProtectedTag) error {
} }
// DeleteProtectedTag deletes a protected tag by ID // DeleteProtectedTag deletes a protected tag by ID
func DeleteProtectedTag(pt *ProtectedTag) error { func DeleteProtectedTag(ctx context.Context, pt *ProtectedTag) error {
_, err := db.GetEngine(db.DefaultContext).ID(pt.ID).Delete(&ProtectedTag{}) _, err := db.GetEngine(ctx).ID(pt.ID).Delete(&ProtectedTag{})
return err return err
} }
// IsUserAllowedModifyTag returns true if the user is allowed to modify the tag // IsUserAllowedModifyTag returns true if the user is allowed to modify the tag
func IsUserAllowedModifyTag(pt *ProtectedTag, userID int64) (bool, error) { func IsUserAllowedModifyTag(ctx context.Context, pt *ProtectedTag, userID int64) (bool, error) {
if base.Int64sContains(pt.AllowlistUserIDs, userID) { if base.Int64sContains(pt.AllowlistUserIDs, userID) {
return true, nil return true, nil
} }
@ -84,7 +85,7 @@ func IsUserAllowedModifyTag(pt *ProtectedTag, userID int64) (bool, error) {
return false, nil return false, nil
} }
in, err := organization.IsUserInTeams(db.DefaultContext, userID, pt.AllowlistTeamIDs) in, err := organization.IsUserInTeams(ctx, userID, pt.AllowlistTeamIDs)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -92,9 +93,9 @@ func IsUserAllowedModifyTag(pt *ProtectedTag, userID int64) (bool, error) {
} }
// GetProtectedTags gets all protected tags of the repository // GetProtectedTags gets all protected tags of the repository
func GetProtectedTags(repoID int64) ([]*ProtectedTag, error) { func GetProtectedTags(ctx context.Context, repoID int64) ([]*ProtectedTag, error) {
tags := make([]*ProtectedTag, 0) tags := make([]*ProtectedTag, 0)
return tags, db.GetEngine(db.DefaultContext).Find(&tags, &ProtectedTag{RepoID: repoID}) return tags, db.GetEngine(ctx).Find(&tags, &ProtectedTag{RepoID: repoID})
} }
// GetProtectedTagByID gets the protected tag with the specific id // GetProtectedTagByID gets the protected tag with the specific id
@ -112,7 +113,7 @@ func GetProtectedTagByID(id int64) (*ProtectedTag, error) {
// IsUserAllowedToControlTag checks if a user can control the specific tag. // IsUserAllowedToControlTag checks if a user can control the specific tag.
// It returns true if the tag name is not protected or the user is allowed to control it. // It returns true if the tag name is not protected or the user is allowed to control it.
func IsUserAllowedToControlTag(tags []*ProtectedTag, tagName string, userID int64) (bool, error) { func IsUserAllowedToControlTag(ctx context.Context, tags []*ProtectedTag, tagName string, userID int64) (bool, error) {
isAllowed := true isAllowed := true
for _, tag := range tags { for _, tag := range tags {
err := tag.EnsureCompiledPattern() err := tag.EnsureCompiledPattern()
@ -124,7 +125,7 @@ func IsUserAllowedToControlTag(tags []*ProtectedTag, tagName string, userID int6
continue continue
} }
isAllowed, err = IsUserAllowedModifyTag(tag, userID) isAllowed, err = IsUserAllowedModifyTag(ctx, tag, userID)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -7,6 +7,7 @@ package git_test
import ( import (
"testing" "testing"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
@ -17,29 +18,29 @@ func TestIsUserAllowed(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pt := &git_model.ProtectedTag{} pt := &git_model.ProtectedTag{}
allowed, err := git_model.IsUserAllowedModifyTag(pt, 1) allowed, err := git_model.IsUserAllowedModifyTag(db.DefaultContext, pt, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, allowed) assert.False(t, allowed)
pt = &git_model.ProtectedTag{ pt = &git_model.ProtectedTag{
AllowlistUserIDs: []int64{1}, AllowlistUserIDs: []int64{1},
} }
allowed, err = git_model.IsUserAllowedModifyTag(pt, 1) allowed, err = git_model.IsUserAllowedModifyTag(db.DefaultContext, pt, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, allowed) assert.True(t, allowed)
allowed, err = git_model.IsUserAllowedModifyTag(pt, 2) allowed, err = git_model.IsUserAllowedModifyTag(db.DefaultContext, pt, 2)
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, allowed) assert.False(t, allowed)
pt = &git_model.ProtectedTag{ pt = &git_model.ProtectedTag{
AllowlistTeamIDs: []int64{1}, AllowlistTeamIDs: []int64{1},
} }
allowed, err = git_model.IsUserAllowedModifyTag(pt, 1) allowed, err = git_model.IsUserAllowedModifyTag(db.DefaultContext, pt, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, allowed) assert.False(t, allowed)
allowed, err = git_model.IsUserAllowedModifyTag(pt, 2) allowed, err = git_model.IsUserAllowedModifyTag(db.DefaultContext, pt, 2)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, allowed) assert.True(t, allowed)
@ -47,11 +48,11 @@ func TestIsUserAllowed(t *testing.T) {
AllowlistUserIDs: []int64{1}, AllowlistUserIDs: []int64{1},
AllowlistTeamIDs: []int64{1}, AllowlistTeamIDs: []int64{1},
} }
allowed, err = git_model.IsUserAllowedModifyTag(pt, 1) allowed, err = git_model.IsUserAllowedModifyTag(db.DefaultContext, pt, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, allowed) assert.True(t, allowed)
allowed, err = git_model.IsUserAllowedModifyTag(pt, 2) allowed, err = git_model.IsUserAllowedModifyTag(db.DefaultContext, pt, 2)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, allowed) assert.True(t, allowed)
} }
@ -135,7 +136,7 @@ func TestIsUserAllowedToControlTag(t *testing.T) {
} }
for n, c := range cases { for n, c := range cases {
isAllowed, err := git_model.IsUserAllowedToControlTag(protectedTags, c.name, c.userid) isAllowed, err := git_model.IsUserAllowedToControlTag(db.DefaultContext, protectedTags, c.name, c.userid)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, c.allowed, isAllowed, "case %d: error should match", n) assert.Equal(t, c.allowed, isAllowed, "case %d: error should match", n)
} }
@ -157,7 +158,7 @@ func TestIsUserAllowedToControlTag(t *testing.T) {
} }
for n, c := range cases { for n, c := range cases {
isAllowed, err := git_model.IsUserAllowedToControlTag(protectedTags, c.name, c.userid) isAllowed, err := git_model.IsUserAllowedToControlTag(db.DefaultContext, protectedTags, c.name, c.userid)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, c.allowed, isAllowed, "case %d: error should match", n) assert.Equal(t, c.allowed, isAllowed, "case %d: error should match", n)
} }

View File

@ -48,9 +48,10 @@ func (issue *Issue) LoadAssignees(ctx context.Context) (err error) {
// GetAssigneeIDsByIssue returns the IDs of users assigned to an issue // GetAssigneeIDsByIssue returns the IDs of users assigned to an issue
// but skips joining with `user` for performance reasons. // but skips joining with `user` for performance reasons.
// User permissions must be verified elsewhere if required. // User permissions must be verified elsewhere if required.
func GetAssigneeIDsByIssue(issueID int64) ([]int64, error) { func GetAssigneeIDsByIssue(ctx context.Context, issueID int64) ([]int64, error) {
userIDs := make([]int64, 0, 5) userIDs := make([]int64, 0, 5)
return userIDs, db.GetEngine(db.DefaultContext).Table("issue_assignees"). return userIDs, db.GetEngine(ctx).
Table("issue_assignees").
Cols("assignee_id"). Cols("assignee_id").
Where("issue_id = ?", issueID). Where("issue_id = ?", issueID).
Distinct("assignee_id"). Distinct("assignee_id").
@ -64,7 +65,7 @@ func IsUserAssignedToIssue(ctx context.Context, issue *Issue, user *user_model.U
// ToggleIssueAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it. // ToggleIssueAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
func ToggleIssueAssignee(issue *Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *Comment, err error) { func ToggleIssueAssignee(issue *Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *Comment, err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
@ -151,7 +152,7 @@ func toggleUserAssignee(ctx context.Context, issue *Issue, assigneeID int64) (re
} }
// MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs // MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) { func MakeIDsFromAPIAssigneesToAdd(ctx context.Context, oneAssignee string, multipleAssignees []string) (assigneeIDs []int64, err error) {
var requestAssignees []string var requestAssignees []string
// Keeping the old assigning method for compatibility reasons // Keeping the old assigning method for compatibility reasons
@ -165,7 +166,7 @@ func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string
} }
// Get the IDs of all assignees // Get the IDs of all assignees
assigneeIDs, err = user_model.GetUserIDsByNames(requestAssignees, false) assigneeIDs, err = user_model.GetUserIDsByNames(ctx, requestAssignees, false)
return assigneeIDs, err return assigneeIDs, err
} }

View File

@ -71,22 +71,22 @@ func TestMakeIDsFromAPIAssigneesToAdd(t *testing.T) {
_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
IDs, err := issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{""}) IDs, err := issues_model.MakeIDsFromAPIAssigneesToAdd(db.DefaultContext, "", []string{""})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, []int64{}, IDs) assert.Equal(t, []int64{}, IDs)
_, err = issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{"none_existing_user"}) _, err = issues_model.MakeIDsFromAPIAssigneesToAdd(db.DefaultContext, "", []string{"none_existing_user"})
assert.Error(t, err) assert.Error(t, err)
IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("user1", []string{"user1"}) IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd(db.DefaultContext, "user1", []string{"user1"})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, []int64{1}, IDs) assert.Equal(t, []int64{1}, IDs)
IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("user2", []string{""}) IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd(db.DefaultContext, "user2", []string{""})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, []int64{2}, IDs) assert.Equal(t, []int64{2}, IDs)
IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{"user1", "user2"}) IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd(db.DefaultContext, "", []string{"user1", "user2"})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, []int64{1, 2}, IDs) assert.Equal(t, []int64{1, 2}, IDs)
} }

View File

@ -309,13 +309,8 @@ type PushActionContent struct {
CommitIDs []string `json:"commit_ids"` CommitIDs []string `json:"commit_ids"`
} }
// LoadIssue loads issue from database // LoadIssue loads the issue reference for the comment
func (c *Comment) LoadIssue() (err error) { func (c *Comment) LoadIssue(ctx context.Context) (err error) {
return c.LoadIssueCtx(db.DefaultContext)
}
// LoadIssueCtx loads issue from database
func (c *Comment) LoadIssueCtx(ctx context.Context) (err error) {
if c.Issue != nil { if c.Issue != nil {
return nil return nil
} }
@ -350,7 +345,8 @@ func (c *Comment) AfterLoad(session *xorm.Session) {
} }
} }
func (c *Comment) loadPoster(ctx context.Context) (err error) { // LoadPoster loads comment poster
func (c *Comment) LoadPoster(ctx context.Context) (err error) {
if c.PosterID <= 0 || c.Poster != nil { if c.PosterID <= 0 || c.Poster != nil {
return nil return nil
} }
@ -381,7 +377,7 @@ func (c *Comment) AfterDelete() {
// HTMLURL formats a URL-string to the issue-comment // HTMLURL formats a URL-string to the issue-comment
func (c *Comment) HTMLURL() string { func (c *Comment) HTMLURL() string {
err := c.LoadIssue() err := c.LoadIssue(db.DefaultContext)
if err != nil { // Silently dropping errors :unamused: if err != nil { // Silently dropping errors :unamused:
log.Error("LoadIssue(%d): %v", c.IssueID, err) log.Error("LoadIssue(%d): %v", c.IssueID, err)
return "" return ""
@ -410,7 +406,7 @@ func (c *Comment) HTMLURL() string {
// APIURL formats a API-string to the issue-comment // APIURL formats a API-string to the issue-comment
func (c *Comment) APIURL() string { func (c *Comment) APIURL() string {
err := c.LoadIssue() err := c.LoadIssue(db.DefaultContext)
if err != nil { // Silently dropping errors :unamused: if err != nil { // Silently dropping errors :unamused:
log.Error("LoadIssue(%d): %v", c.IssueID, err) log.Error("LoadIssue(%d): %v", c.IssueID, err)
return "" return ""
@ -426,7 +422,7 @@ func (c *Comment) APIURL() string {
// IssueURL formats a URL-string to the issue // IssueURL formats a URL-string to the issue
func (c *Comment) IssueURL() string { func (c *Comment) IssueURL() string {
err := c.LoadIssue() err := c.LoadIssue(db.DefaultContext)
if err != nil { // Silently dropping errors :unamused: if err != nil { // Silently dropping errors :unamused:
log.Error("LoadIssue(%d): %v", c.IssueID, err) log.Error("LoadIssue(%d): %v", c.IssueID, err)
return "" return ""
@ -446,7 +442,7 @@ func (c *Comment) IssueURL() string {
// PRURL formats a URL-string to the pull-request // PRURL formats a URL-string to the pull-request
func (c *Comment) PRURL() string { func (c *Comment) PRURL() string {
err := c.LoadIssue() err := c.LoadIssue(db.DefaultContext)
if err != nil { // Silently dropping errors :unamused: if err != nil { // Silently dropping errors :unamused:
log.Error("LoadIssue(%d): %v", c.IssueID, err) log.Error("LoadIssue(%d): %v", c.IssueID, err)
return "" return ""
@ -521,10 +517,10 @@ func (c *Comment) LoadProject() error {
} }
// LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone // LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone
func (c *Comment) LoadMilestone() error { func (c *Comment) LoadMilestone(ctx context.Context) error {
if c.OldMilestoneID > 0 { if c.OldMilestoneID > 0 {
var oldMilestone Milestone var oldMilestone Milestone
has, err := db.GetEngine(db.DefaultContext).ID(c.OldMilestoneID).Get(&oldMilestone) has, err := db.GetEngine(ctx).ID(c.OldMilestoneID).Get(&oldMilestone)
if err != nil { if err != nil {
return err return err
} else if has { } else if has {
@ -534,7 +530,7 @@ func (c *Comment) LoadMilestone() error {
if c.MilestoneID > 0 { if c.MilestoneID > 0 {
var milestone Milestone var milestone Milestone
has, err := db.GetEngine(db.DefaultContext).ID(c.MilestoneID).Get(&milestone) has, err := db.GetEngine(ctx).ID(c.MilestoneID).Get(&milestone)
if err != nil { if err != nil {
return err return err
} else if has { } else if has {
@ -544,19 +540,14 @@ func (c *Comment) LoadMilestone() error {
return nil return nil
} }
// LoadPoster loads comment poster
func (c *Comment) LoadPoster() error {
return c.loadPoster(db.DefaultContext)
}
// LoadAttachments loads attachments (it never returns error, the error during `GetAttachmentsByCommentIDCtx` is ignored) // LoadAttachments loads attachments (it never returns error, the error during `GetAttachmentsByCommentIDCtx` is ignored)
func (c *Comment) LoadAttachments() error { func (c *Comment) LoadAttachments(ctx context.Context) error {
if len(c.Attachments) > 0 { if len(c.Attachments) > 0 {
return nil return nil
} }
var err error var err error
c.Attachments, err = repo_model.GetAttachmentsByCommentID(db.DefaultContext, c.ID) c.Attachments, err = repo_model.GetAttachmentsByCommentID(ctx, c.ID)
if err != nil { if err != nil {
log.Error("getAttachmentsByCommentID[%d]: %v", c.ID, err) log.Error("getAttachmentsByCommentID[%d]: %v", c.ID, err)
} }
@ -565,7 +556,7 @@ func (c *Comment) LoadAttachments() error {
// UpdateAttachments update attachments by UUIDs for the comment // UpdateAttachments update attachments by UUIDs for the comment
func (c *Comment) UpdateAttachments(uuids []string) error { func (c *Comment) UpdateAttachments(uuids []string) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -598,7 +589,7 @@ func (c *Comment) LoadAssigneeUserAndTeam() error {
c.Assignee = user_model.NewGhostUser() c.Assignee = user_model.NewGhostUser()
} }
} else if c.AssigneeTeamID > 0 && c.AssigneeTeam == nil { } else if c.AssigneeTeamID > 0 && c.AssigneeTeam == nil {
if err = c.LoadIssue(); err != nil { if err = c.LoadIssue(db.DefaultContext); err != nil {
return err return err
} }
@ -740,7 +731,7 @@ func (c *Comment) UnsignedLine() uint64 {
// CodeCommentURL returns the url to a comment in code // CodeCommentURL returns the url to a comment in code
func (c *Comment) CodeCommentURL() string { func (c *Comment) CodeCommentURL() string {
err := c.LoadIssue() err := c.LoadIssue(db.DefaultContext)
if err != nil { // Silently dropping errors :unamused: if err != nil { // Silently dropping errors :unamused:
log.Error("LoadIssue(%d): %v", c.IssueID, err) log.Error("LoadIssue(%d): %v", c.IssueID, err)
return "" return ""
@ -1003,7 +994,7 @@ type CreateCommentOptions struct {
// CreateComment creates comment of issue or commit. // CreateComment creates comment of issue or commit.
func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) { func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1135,7 +1126,7 @@ func CountComments(opts *FindCommentsOptions) (int64, error) {
// UpdateComment updates information of comment. // UpdateComment updates information of comment.
func UpdateComment(c *Comment, doer *user_model.User) error { func UpdateComment(c *Comment, doer *user_model.User) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -1145,7 +1136,7 @@ func UpdateComment(c *Comment, doer *user_model.User) error {
if _, err := sess.ID(c.ID).AllCols().Update(c); err != nil { if _, err := sess.ID(c.ID).AllCols().Update(c); err != nil {
return err return err
} }
if err := c.LoadIssueCtx(ctx); err != nil { if err := c.LoadIssue(ctx); err != nil {
return err return err
} }
if err := c.AddCrossReferences(ctx, doer, true); err != nil { if err := c.AddCrossReferences(ctx, doer, true); err != nil {
@ -1245,7 +1236,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
return nil, err return nil, err
} }
if err := CommentList(comments).loadPosters(ctx); err != nil { if err := CommentList(comments).LoadPosters(ctx); err != nil {
return nil, err return nil, err
} }
@ -1363,11 +1354,11 @@ func CreateAutoMergeComment(ctx context.Context, typ CommentType, pr *PullReques
if typ != CommentTypePRScheduledToAutoMerge && typ != CommentTypePRUnScheduledToAutoMerge { if typ != CommentTypePRScheduledToAutoMerge && typ != CommentTypePRUnScheduledToAutoMerge {
return nil, fmt.Errorf("comment type %d cannot be used to create an auto merge comment", typ) return nil, fmt.Errorf("comment type %d cannot be used to create an auto merge comment", typ)
} }
if err = pr.LoadIssueCtx(ctx); err != nil { if err = pr.LoadIssue(ctx); err != nil {
return return
} }
if err = pr.LoadBaseRepoCtx(ctx); err != nil { if err = pr.LoadBaseRepo(ctx); err != nil {
return return
} }
@ -1512,18 +1503,18 @@ func (c *Comment) GetExternalName() string { return c.OriginalAuthor }
func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID } func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID }
// CountCommentTypeLabelWithEmptyLabel count label comments with empty label // CountCommentTypeLabelWithEmptyLabel count label comments with empty label
func CountCommentTypeLabelWithEmptyLabel() (int64, error) { func CountCommentTypeLabelWithEmptyLabel(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Count(new(Comment)) return db.GetEngine(ctx).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Count(new(Comment))
} }
// FixCommentTypeLabelWithEmptyLabel count label comments with empty label // FixCommentTypeLabelWithEmptyLabel count label comments with empty label
func FixCommentTypeLabelWithEmptyLabel() (int64, error) { func FixCommentTypeLabelWithEmptyLabel(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Delete(new(Comment)) return db.GetEngine(ctx).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Delete(new(Comment))
} }
// CountCommentTypeLabelWithOutsideLabels count label comments with outside label // CountCommentTypeLabelWithOutsideLabels count label comments with outside label
func CountCommentTypeLabelWithOutsideLabels() (int64, error) { func CountCommentTypeLabelWithOutsideLabels(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Where("comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))", CommentTypeLabel). return db.GetEngine(ctx).Where("comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))", CommentTypeLabel).
Table("comment"). Table("comment").
Join("inner", "label", "label.id = comment.label_id"). Join("inner", "label", "label.id = comment.label_id").
Join("inner", "issue", "issue.id = comment.issue_id "). Join("inner", "issue", "issue.id = comment.issue_id ").
@ -1532,8 +1523,8 @@ func CountCommentTypeLabelWithOutsideLabels() (int64, error) {
} }
// FixCommentTypeLabelWithOutsideLabels count label comments with outside label // FixCommentTypeLabelWithOutsideLabels count label comments with outside label
func FixCommentTypeLabelWithOutsideLabels() (int64, error) { func FixCommentTypeLabelWithOutsideLabels(ctx context.Context) (int64, error) {
res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM comment WHERE comment.id IN ( res, err := db.GetEngine(ctx).Exec(`DELETE FROM comment WHERE comment.id IN (
SELECT il_too.id FROM ( SELECT il_too.id FROM (
SELECT com.id SELECT com.id
FROM comment AS com FROM comment AS com

View File

@ -24,7 +24,8 @@ func (comments CommentList) getPosterIDs() []int64 {
return posterIDs.Values() return posterIDs.Values()
} }
func (comments CommentList) loadPosters(ctx context.Context) error { // LoadPosters loads posters
func (comments CommentList) LoadPosters(ctx context.Context) error {
if len(comments) == 0 { if len(comments) == 0 {
return nil return nil
} }
@ -277,7 +278,8 @@ func (comments CommentList) Issues() IssueList {
return issueList return issueList
} }
func (comments CommentList) loadIssues(ctx context.Context) error { // LoadIssues loads issues of comments
func (comments CommentList) LoadIssues(ctx context.Context) error {
if len(comments) == 0 { if len(comments) == 0 {
return nil return nil
} }
@ -382,7 +384,8 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error {
return nil return nil
} }
func (comments CommentList) loadAttachments(ctx context.Context) (err error) { // LoadAttachments loads attachments
func (comments CommentList) LoadAttachments(ctx context.Context) (err error) {
if len(comments) == 0 { if len(comments) == 0 {
return nil return nil
} }
@ -476,7 +479,7 @@ func (comments CommentList) loadReviews(ctx context.Context) error { //nolint
// loadAttributes loads all attributes // loadAttributes loads all attributes
func (comments CommentList) loadAttributes(ctx context.Context) (err error) { func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
if err = comments.loadPosters(ctx); err != nil { if err = comments.LoadPosters(ctx); err != nil {
return return
} }
@ -496,7 +499,7 @@ func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
return return
} }
if err = comments.loadAttachments(ctx); err != nil { if err = comments.LoadAttachments(ctx); err != nil {
return return
} }
@ -504,7 +507,7 @@ func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
return return
} }
if err = comments.loadIssues(ctx); err != nil { if err = comments.LoadIssues(ctx); err != nil {
return return
} }
@ -520,18 +523,3 @@ func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
func (comments CommentList) LoadAttributes() error { func (comments CommentList) LoadAttributes() error {
return comments.loadAttributes(db.DefaultContext) return comments.loadAttributes(db.DefaultContext)
} }
// LoadAttachments loads attachments
func (comments CommentList) LoadAttachments() error {
return comments.loadAttachments(db.DefaultContext)
}
// LoadPosters loads posters
func (comments CommentList) LoadPosters() error {
return comments.loadPosters(db.DefaultContext)
}
// LoadIssues loads issues of comments
func (comments CommentList) LoadIssues() error {
return comments.loadIssues(db.DefaultContext)
}

View File

@ -129,7 +129,7 @@ const (
// CreateIssueDependency creates a new dependency for an issue // CreateIssueDependency creates a new dependency for an issue
func CreateIssueDependency(user *user_model.User, issue, dep *Issue) error { func CreateIssueDependency(user *user_model.User, issue, dep *Issue) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -170,7 +170,7 @@ func CreateIssueDependency(user *user_model.User, issue, dep *Issue) error {
// RemoveIssueDependency removes a dependency from an issue // RemoveIssueDependency removes a dependency from an issue
func RemoveIssueDependency(user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) { func RemoveIssueDependency(user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -241,11 +241,7 @@ func (issue *Issue) LoadLabels(ctx context.Context) (err error) {
} }
// LoadPoster loads poster // LoadPoster loads poster
func (issue *Issue) LoadPoster() error { func (issue *Issue) LoadPoster(ctx context.Context) (err error) {
return issue.loadPoster(db.DefaultContext)
}
func (issue *Issue) loadPoster(ctx context.Context) (err error) {
if issue.Poster == nil { if issue.Poster == nil {
issue.Poster, err = user_model.GetUserByIDCtx(ctx, issue.PosterID) issue.Poster, err = user_model.GetUserByIDCtx(ctx, issue.PosterID)
if err != nil { if err != nil {
@ -261,7 +257,8 @@ func (issue *Issue) loadPoster(ctx context.Context) (err error) {
return err return err
} }
func (issue *Issue) loadPullRequest(ctx context.Context) (err error) { // LoadPullRequest loads pull request info
func (issue *Issue) LoadPullRequest(ctx context.Context) (err error) {
if issue.IsPull && issue.PullRequest == nil { if issue.IsPull && issue.PullRequest == nil {
issue.PullRequest, err = GetPullRequestByIssueID(ctx, issue.ID) issue.PullRequest, err = GetPullRequestByIssueID(ctx, issue.ID)
if err != nil { if err != nil {
@ -275,18 +272,13 @@ func (issue *Issue) loadPullRequest(ctx context.Context) (err error) {
return nil return nil
} }
// LoadPullRequest loads pull request info
func (issue *Issue) LoadPullRequest() error {
return issue.loadPullRequest(db.DefaultContext)
}
func (issue *Issue) loadComments(ctx context.Context) (err error) { func (issue *Issue) loadComments(ctx context.Context) (err error) {
return issue.loadCommentsByType(ctx, CommentTypeUnknown) return issue.loadCommentsByType(ctx, CommentTypeUnknown)
} }
// LoadDiscussComments loads discuss comments // LoadDiscussComments loads discuss comments
func (issue *Issue) LoadDiscussComments() error { func (issue *Issue) LoadDiscussComments(ctx context.Context) error {
return issue.loadCommentsByType(db.DefaultContext, CommentTypeComment) return issue.loadCommentsByType(ctx, CommentTypeComment)
} }
func (issue *Issue) loadCommentsByType(ctx context.Context, tp CommentType) (err error) { func (issue *Issue) loadCommentsByType(ctx context.Context, tp CommentType) (err error) {
@ -357,7 +349,8 @@ func (issue *Issue) loadForeignReference(ctx context.Context) (err error) {
return nil return nil
} }
func (issue *Issue) loadMilestone(ctx context.Context) (err error) { // LoadMilestone load milestone of this issue.
func (issue *Issue) LoadMilestone(ctx context.Context) (err error) {
if (issue.Milestone == nil || issue.Milestone.ID != issue.MilestoneID) && issue.MilestoneID > 0 { if (issue.Milestone == nil || issue.Milestone.ID != issue.MilestoneID) && issue.MilestoneID > 0 {
issue.Milestone, err = GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) issue.Milestone, err = GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID)
if err != nil && !IsErrMilestoneNotExist(err) { if err != nil && !IsErrMilestoneNotExist(err) {
@ -373,7 +366,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return return
} }
if err = issue.loadPoster(ctx); err != nil { if err = issue.LoadPoster(ctx); err != nil {
return return
} }
@ -381,7 +374,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return return
} }
if err = issue.loadMilestone(ctx); err != nil { if err = issue.LoadMilestone(ctx); err != nil {
return return
} }
@ -393,7 +386,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return return
} }
if err = issue.loadPullRequest(ctx); err != nil && !IsErrPullRequestNotExist(err) { if err = issue.LoadPullRequest(ctx); err != nil && !IsErrPullRequestNotExist(err) {
// It is possible pull request is not yet created. // It is possible pull request is not yet created.
return err return err
} }
@ -425,11 +418,6 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return issue.loadReactions(ctx) return issue.loadReactions(ctx)
} }
// LoadMilestone load milestone of this issue.
func (issue *Issue) LoadMilestone() error {
return issue.loadMilestone(db.DefaultContext)
}
// GetIsRead load the `IsRead` field of the issue // GetIsRead load the `IsRead` field of the issue
func (issue *Issue) GetIsRead(userID int64) error { func (issue *Issue) GetIsRead(userID int64) error {
issueUser := &IssueUser{IssueID: issue.ID, UID: userID} issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
@ -540,7 +528,7 @@ func clearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User)
// ClearIssueLabels removes all issue labels as the given user. // ClearIssueLabels removes all issue labels as the given user.
// Triggers appropriate WebHooks, if any. // Triggers appropriate WebHooks, if any.
func ClearIssueLabels(issue *Issue, doer *user_model.User) (err error) { func ClearIssueLabels(issue *Issue, doer *user_model.User) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -548,7 +536,7 @@ func ClearIssueLabels(issue *Issue, doer *user_model.User) (err error) {
if err := issue.LoadRepo(ctx); err != nil { if err := issue.LoadRepo(ctx); err != nil {
return err return err
} else if err = issue.loadPullRequest(ctx); err != nil { } else if err = issue.LoadPullRequest(ctx); err != nil {
return err return err
} }
@ -588,7 +576,7 @@ func (ts labelSorter) Swap(i, j int) {
// ReplaceIssueLabels removes all current labels and add new labels to the issue. // ReplaceIssueLabels removes all current labels and add new labels to the issue.
// Triggers appropriate WebHooks, if any. // Triggers appropriate WebHooks, if any.
func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) { func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -751,7 +739,7 @@ func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User,
if err := issue.LoadRepo(ctx); err != nil { if err := issue.LoadRepo(ctx); err != nil {
return nil, err return nil, err
} }
if err := issue.loadPoster(ctx); err != nil { if err := issue.LoadPoster(ctx); err != nil {
return nil, err return nil, err
} }
@ -760,7 +748,7 @@ func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User,
// ChangeIssueTitle changes the title of this issue, as the given user. // ChangeIssueTitle changes the title of this issue, as the given user.
func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err error) { func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -794,7 +782,7 @@ func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err
// ChangeIssueRef changes the branch of this issue, as the given user. // ChangeIssueRef changes the branch of this issue, as the given user.
func ChangeIssueRef(issue *Issue, doer *user_model.User, oldRef string) (err error) { func ChangeIssueRef(issue *Issue, doer *user_model.User, oldRef string) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -844,7 +832,7 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *
// UpdateIssueAttachments update attachments by UUIDs for the issue // UpdateIssueAttachments update attachments by UUIDs for the issue
func UpdateIssueAttachments(issueID int64, uuids []string) (err error) { func UpdateIssueAttachments(issueID int64, uuids []string) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -864,7 +852,7 @@ func UpdateIssueAttachments(issueID int64, uuids []string) (err error) {
// ChangeIssueContent changes issue content, as the given user. // ChangeIssueContent changes issue content, as the given user.
func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (err error) { func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -1027,7 +1015,7 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
return fmt.Errorf("find all labels [label_ids: %v]: %w", opts.LabelIDs, err) return fmt.Errorf("find all labels [label_ids: %v]: %w", opts.LabelIDs, err)
} }
if err = opts.Issue.loadPoster(ctx); err != nil { if err = opts.Issue.LoadPoster(ctx); err != nil {
return err return err
} }
@ -1069,7 +1057,7 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
// NewIssue creates new issue with labels for repository. // NewIssue creates new issue with labels for repository.
func NewIssue(repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) { func NewIssue(repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -1505,10 +1493,9 @@ func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Sess
} }
// CountIssuesByRepo map from repoID to number of issues matching the options // CountIssuesByRepo map from repoID to number of issues matching the options
func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) { func CountIssuesByRepo(ctx context.Context, opts *IssuesOptions) (map[int64]int64, error) {
e := db.GetEngine(db.DefaultContext) sess := db.GetEngine(ctx).
Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSessionNoLimit(sess) opts.setupSessionNoLimit(sess)
@ -1551,10 +1538,9 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
} }
// Issues returns a list of issues by given conditions. // Issues returns a list of issues by given conditions.
func Issues(opts *IssuesOptions) ([]*Issue, error) { func Issues(ctx context.Context, opts *IssuesOptions) ([]*Issue, error) {
e := db.GetEngine(db.DefaultContext) sess := db.GetEngine(ctx).
Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSessionWithLimit(sess) opts.setupSessionWithLimit(sess)
sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID) sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID)
@ -1572,11 +1558,11 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
} }
// CountIssues number return of issues by given conditions. // CountIssues number return of issues by given conditions.
func CountIssues(opts *IssuesOptions) (int64, error) { func CountIssues(ctx context.Context, opts *IssuesOptions) (int64, error) {
e := db.GetEngine(db.DefaultContext) sess := db.GetEngine(ctx).
Select("COUNT(issue.id) AS count").
sess := e.Select("COUNT(issue.id) AS count").Table("issue") Table("issue").
sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id") Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSessionNoLimit(sess) opts.setupSessionNoLimit(sess)
return sess.Count() return sess.Count()
@ -1585,9 +1571,10 @@ func CountIssues(opts *IssuesOptions) (int64, error) {
// GetParticipantsIDsByIssueID returns the IDs of all users who participated in comments of an issue, // GetParticipantsIDsByIssueID returns the IDs of all users who participated in comments of an issue,
// but skips joining with `user` for performance reasons. // but skips joining with `user` for performance reasons.
// User permissions must be verified elsewhere if required. // User permissions must be verified elsewhere if required.
func GetParticipantsIDsByIssueID(issueID int64) ([]int64, error) { func GetParticipantsIDsByIssueID(ctx context.Context, issueID int64) ([]int64, error) {
userIDs := make([]int64, 0, 5) userIDs := make([]int64, 0, 5)
return userIDs, db.GetEngine(db.DefaultContext).Table("comment"). return userIDs, db.GetEngine(ctx).
Table("comment").
Cols("poster_id"). Cols("poster_id").
Where("issue_id = ?", issueID). Where("issue_id = ?", issueID).
And("type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview). And("type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview).
@ -1986,7 +1973,7 @@ func SearchIssueIDsByKeyword(ctx context.Context, kw string, repoIDs []int64, li
// If the issue status is changed a statusChangeComment is returned // If the issue status is changed a statusChangeComment is returned
// similarly if the title is changed the titleChanged bool is set to true // similarly if the title is changed the titleChanged bool is set to true
func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment *Comment, titleChanged bool, err error) { func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment *Comment, titleChanged bool, err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
@ -2044,7 +2031,7 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us
if issue.DeadlineUnix == deadlineUnix { if issue.DeadlineUnix == deadlineUnix {
return nil return nil
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -2426,8 +2413,9 @@ func (issue *Issue) GetExternalName() string { return issue.OriginalAuthor }
func (issue *Issue) GetExternalID() int64 { return issue.OriginalAuthorID } func (issue *Issue) GetExternalID() int64 { return issue.OriginalAuthorID }
// CountOrphanedIssues count issues without a repo // CountOrphanedIssues count issues without a repo
func CountOrphanedIssues() (int64, error) { func CountOrphanedIssues(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Table("issue"). return db.GetEngine(ctx).
Table("issue").
Join("LEFT", "repository", "issue.repo_id=repository.id"). Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}). Where(builder.IsNull{"repository.id"}).
Select("COUNT(`issue`.`id`)"). Select("COUNT(`issue`.`id`)").
@ -2435,35 +2423,31 @@ func CountOrphanedIssues() (int64, error) {
} }
// DeleteOrphanedIssues delete issues without a repo // DeleteOrphanedIssues delete issues without a repo
func DeleteOrphanedIssues() error { func DeleteOrphanedIssues(ctx context.Context) error {
ctx, committer, err := db.TxContext() var attachmentPaths []string
err := db.AutoTx(ctx, func(ctx context.Context) error {
var ids []int64
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
Find(&ids); err != nil {
return err
}
for i := range ids {
paths, err := DeleteIssuesByRepoID(ctx, ids[i])
if err != nil {
return err
}
attachmentPaths = append(attachmentPaths, paths...)
}
return nil
})
if err != nil { if err != nil {
return err return err
} }
defer committer.Close()
var ids []int64
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
Find(&ids); err != nil {
return err
}
var attachmentPaths []string
for i := range ids {
paths, err := DeleteIssuesByRepoID(ctx, ids[i])
if err != nil {
return err
}
attachmentPaths = append(attachmentPaths, paths...)
}
if err := committer.Commit(); err != nil {
return err
}
committer.Close()
// Remove issue attachment files. // Remove issue attachment files.
for i := range attachmentPaths { for i := range attachmentPaths {

View File

@ -9,7 +9,7 @@ import "code.gitea.io/gitea/models/db"
// RecalculateIssueIndexForRepo create issue_index for repo if not exist and // RecalculateIssueIndexForRepo create issue_index for repo if not exist and
// update it based on highest index of existing issues assigned to a repo // update it based on highest index of existing issues assigned to a repo
func RecalculateIssueIndexForRepo(repoID int64) error { func RecalculateIssueIndexForRepo(repoID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -34,7 +34,8 @@ func (issues IssueList) getRepoIDs() []int64 {
return repoIDs.Values() return repoIDs.Values()
} }
func (issues IssueList) loadRepositories(ctx context.Context) ([]*repo_model.Repository, error) { // LoadRepositories loads issues' all repositories
func (issues IssueList) LoadRepositories(ctx context.Context) ([]*repo_model.Repository, error) {
if len(issues) == 0 { if len(issues) == 0 {
return nil, nil return nil, nil
} }
@ -73,11 +74,6 @@ func (issues IssueList) loadRepositories(ctx context.Context) ([]*repo_model.Rep
return repo_model.ValuesRepository(repoMaps), nil return repo_model.ValuesRepository(repoMaps), nil
} }
// LoadRepositories loads issues' all repositories
func (issues IssueList) LoadRepositories() ([]*repo_model.Repository, error) {
return issues.loadRepositories(db.DefaultContext)
}
func (issues IssueList) getPosterIDs() []int64 { func (issues IssueList) getPosterIDs() []int64 {
posterIDs := make(container.Set[int64], len(issues)) posterIDs := make(container.Set[int64], len(issues))
for _, issue := range issues { for _, issue := range issues {
@ -317,7 +313,8 @@ func (issues IssueList) getPullIssueIDs() []int64 {
return ids return ids
} }
func (issues IssueList) loadPullRequests(ctx context.Context) error { // LoadPullRequests loads pull requests
func (issues IssueList) LoadPullRequests(ctx context.Context) error {
issuesIDs := issues.getPullIssueIDs() issuesIDs := issues.getPullIssueIDs()
if len(issuesIDs) == 0 { if len(issuesIDs) == 0 {
return nil return nil
@ -361,7 +358,8 @@ func (issues IssueList) loadPullRequests(ctx context.Context) error {
return nil return nil
} }
func (issues IssueList) loadAttachments(ctx context.Context) (err error) { // LoadAttachments loads attachments
func (issues IssueList) LoadAttachments(ctx context.Context) (err error) {
if len(issues) == 0 { if len(issues) == 0 {
return nil return nil
} }
@ -513,8 +511,8 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) {
// loadAttributes loads all attributes, expect for attachments and comments // loadAttributes loads all attributes, expect for attachments and comments
func (issues IssueList) loadAttributes(ctx context.Context) error { func (issues IssueList) loadAttributes(ctx context.Context) error {
if _, err := issues.loadRepositories(ctx); err != nil { if _, err := issues.LoadRepositories(ctx); err != nil {
return fmt.Errorf("issue.loadAttributes: loadRepositories: %w", err) return fmt.Errorf("issue.loadAttributes: LoadRepositories: %w", err)
} }
if err := issues.loadPosters(ctx); err != nil { if err := issues.loadPosters(ctx); err != nil {
@ -537,7 +535,7 @@ func (issues IssueList) loadAttributes(ctx context.Context) error {
return fmt.Errorf("issue.loadAttributes: loadAssignees: %w", err) return fmt.Errorf("issue.loadAttributes: loadAssignees: %w", err)
} }
if err := issues.loadPullRequests(ctx); err != nil { if err := issues.LoadPullRequests(ctx); err != nil {
return fmt.Errorf("issue.loadAttributes: loadPullRequests: %w", err) return fmt.Errorf("issue.loadAttributes: loadPullRequests: %w", err)
} }
@ -554,24 +552,14 @@ func (issues IssueList) LoadAttributes() error {
return issues.loadAttributes(db.DefaultContext) return issues.loadAttributes(db.DefaultContext)
} }
// LoadAttachments loads attachments
func (issues IssueList) LoadAttachments() error {
return issues.loadAttachments(db.DefaultContext)
}
// LoadComments loads comments // LoadComments loads comments
func (issues IssueList) LoadComments() error { func (issues IssueList) LoadComments(ctx context.Context) error {
return issues.loadComments(db.DefaultContext, builder.NewCond()) return issues.loadComments(ctx, builder.NewCond())
} }
// LoadDiscussComments loads discuss comments // LoadDiscussComments loads discuss comments
func (issues IssueList) LoadDiscussComments() error { func (issues IssueList) LoadDiscussComments(ctx context.Context) error {
return issues.loadComments(db.DefaultContext, builder.Eq{"comment.type": CommentTypeComment}) return issues.loadComments(ctx, builder.Eq{"comment.type": CommentTypeComment})
}
// LoadPullRequests loads pull requests
func (issues IssueList) LoadPullRequests() error {
return issues.loadPullRequests(db.DefaultContext)
} }
// GetApprovalCounts returns a map of issue ID to slice of approval counts // GetApprovalCounts returns a map of issue ID to slice of approval counts

View File

@ -7,6 +7,7 @@ package issues_test
import ( import (
"testing" "testing"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -23,7 +24,7 @@ func TestIssueList_LoadRepositories(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}), unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}),
} }
repos, err := issueList.LoadRepositories() repos, err := issueList.LoadRepositories(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, repos, 2) assert.Len(t, repos, 2)
for _, issue := range issueList { for _, issue := range issueList {

View File

@ -40,7 +40,7 @@ func updateIssueLock(opts *IssueLockOptions, lock bool) error {
commentType = CommentTypeUnlock commentType = CommentTypeUnlock
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -61,11 +61,11 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 {
} }
// LoadIssuesFromBoard load issues assigned to this board // LoadIssuesFromBoard load issues assigned to this board
func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) { func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) {
issueList := make([]*Issue, 0, 10) issueList := make([]*Issue, 0, 10)
if b.ID != 0 { if b.ID != 0 {
issues, err := Issues(&IssuesOptions{ issues, err := Issues(ctx, &IssuesOptions{
ProjectBoardID: b.ID, ProjectBoardID: b.ID,
ProjectID: b.ProjectID, ProjectID: b.ProjectID,
SortType: "project-column-sorting", SortType: "project-column-sorting",
@ -77,7 +77,7 @@ func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) {
} }
if b.Default { if b.Default {
issues, err := Issues(&IssuesOptions{ issues, err := Issues(ctx, &IssuesOptions{
ProjectBoardID: -1, // Issues without ProjectBoardID ProjectBoardID: -1, // Issues without ProjectBoardID
ProjectID: b.ProjectID, ProjectID: b.ProjectID,
SortType: "project-column-sorting", SortType: "project-column-sorting",
@ -88,7 +88,7 @@ func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) {
issueList = append(issueList, issues...) issueList = append(issueList, issues...)
} }
if err := IssueList(issueList).LoadComments(); err != nil { if err := IssueList(issueList).LoadComments(ctx); err != nil {
return nil, err return nil, err
} }
@ -96,10 +96,10 @@ func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) {
} }
// LoadIssuesFromBoardList load issues assigned to the boards // LoadIssuesFromBoardList load issues assigned to the boards
func LoadIssuesFromBoardList(bs project_model.BoardList) (map[int64]IssueList, error) { func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (map[int64]IssueList, error) {
issuesMap := make(map[int64]IssueList, len(bs)) issuesMap := make(map[int64]IssueList, len(bs))
for i := range bs { for i := range bs {
il, err := LoadIssuesFromBoard(bs[i]) il, err := LoadIssuesFromBoard(ctx, bs[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -110,7 +110,7 @@ func LoadIssuesFromBoardList(bs project_model.BoardList) (map[int64]IssueList, e
// ChangeProjectAssign changes the project associated with an issue // ChangeProjectAssign changes the project associated with an issue
func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64) error { func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -166,7 +166,7 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U
// MoveIssueAcrossProjectBoards move a card from one board to another // MoveIssueAcrossProjectBoards move a card from one board to another
func MoveIssueAcrossProjectBoards(issue *Issue, board *project_model.Board) error { func MoveIssueAcrossProjectBoards(issue *Issue, board *project_model.Board) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -189,7 +189,7 @@ func TestIssues(t *testing.T) {
[]int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests []int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
}, },
} { } {
issues, err := issues_model.Issues(&test.Opts) issues, err := issues_model.Issues(db.DefaultContext, &test.Opts)
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, issues, len(test.ExpectedIssueIDs)) { if assert.Len(t, issues, len(test.ExpectedIssueIDs)) {
for i, issue := range issues { for i, issue := range issues {
@ -556,7 +556,7 @@ func TestLoadTotalTrackedTime(t *testing.T) {
func TestCountIssues(t *testing.T) { func TestCountIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
count, err := issues_model.CountIssues(&issues_model.IssuesOptions{}) count, err := issues_model.CountIssues(db.DefaultContext, &issues_model.IssuesOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 17, count) assert.EqualValues(t, 17, count)
} }

View File

@ -235,7 +235,7 @@ func (c *Comment) AddCrossReferences(stdCtx context.Context, doer *user_model.Us
if c.Type != CommentTypeCode && c.Type != CommentTypeComment { if c.Type != CommentTypeCode && c.Type != CommentTypeComment {
return nil return nil
} }
if err := c.LoadIssueCtx(stdCtx); err != nil { if err := c.LoadIssue(stdCtx); err != nil {
return err return err
} }
ctx := &crossReferencesContext{ ctx := &crossReferencesContext{

View File

@ -131,7 +131,7 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu
r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}) r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo})
d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}) d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer})
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
defer committer.Close() defer committer.Close()
@ -174,7 +174,7 @@ func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *i
i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue}) i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue})
c := &issues_model.Comment{Type: issues_model.CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content} c := &issues_model.Comment{Type: issues_model.CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content}
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
assert.NoError(t, err) assert.NoError(t, err)
defer committer.Close() defer committer.Close()
err = db.Insert(ctx, c) err = db.Insert(ctx, c)

View File

@ -116,8 +116,8 @@ func (label *Label) CalOpenIssues() {
} }
// CalOpenOrgIssues calculates the open issues of a label for a specific repo // CalOpenOrgIssues calculates the open issues of a label for a specific repo
func (label *Label) CalOpenOrgIssues(repoID, labelID int64) { func (label *Label) CalOpenOrgIssues(ctx context.Context, repoID, labelID int64) {
counts, _ := CountIssuesByRepo(&IssuesOptions{ counts, _ := CountIssuesByRepo(ctx, &IssuesOptions{
RepoID: repoID, RepoID: repoID,
LabelIDs: []int64{labelID}, LabelIDs: []int64{labelID},
IsClosed: util.OptionalBoolFalse, IsClosed: util.OptionalBoolFalse,
@ -232,7 +232,7 @@ func NewLabel(ctx context.Context, label *Label) error {
// NewLabels creates new labels // NewLabels creates new labels
func NewLabels(labels ...*Label) error { func NewLabels(labels ...*Label) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -267,7 +267,7 @@ func DeleteLabel(id, labelID int64) error {
return err return err
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -395,9 +395,9 @@ func BuildLabelNamesIssueIDsCondition(labelNames []string) *builder.Builder {
// GetLabelsInRepoByIDs returns a list of labels by IDs in given repository, // GetLabelsInRepoByIDs returns a list of labels by IDs in given repository,
// it silently ignores label IDs that do not belong to the repository. // it silently ignores label IDs that do not belong to the repository.
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) { func GetLabelsInRepoByIDs(ctx context.Context, repoID int64, labelIDs []int64) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs)) labels := make([]*Label, 0, len(labelIDs))
return labels, db.GetEngine(db.DefaultContext). return labels, db.GetEngine(ctx).
Where("repo_id = ?", repoID). Where("repo_id = ?", repoID).
In("id", labelIDs). In("id", labelIDs).
Asc("name"). Asc("name").
@ -498,9 +498,9 @@ func GetLabelIDsInOrgByNames(orgID int64, labelNames []string) ([]int64, error)
// GetLabelsInOrgByIDs returns a list of labels by IDs in given organization, // GetLabelsInOrgByIDs returns a list of labels by IDs in given organization,
// it silently ignores label IDs that do not belong to the organization. // it silently ignores label IDs that do not belong to the organization.
func GetLabelsInOrgByIDs(orgID int64, labelIDs []int64) ([]*Label, error) { func GetLabelsInOrgByIDs(ctx context.Context, orgID int64, labelIDs []int64) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs)) labels := make([]*Label, 0, len(labelIDs))
return labels, db.GetEngine(db.DefaultContext). return labels, db.GetEngine(ctx).
Where("org_id = ?", orgID). Where("org_id = ?", orgID).
In("id", labelIDs). In("id", labelIDs).
Asc("name"). Asc("name").
@ -627,7 +627,7 @@ func NewIssueLabel(issue *Issue, label *Label, doer *user_model.User) (err error
return nil return nil
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -676,7 +676,7 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *us
// NewIssueLabels creates a list of issue-label relations. // NewIssueLabels creates a list of issue-label relations.
func NewIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) { func NewIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -746,13 +746,13 @@ func DeleteLabelsByRepoID(ctx context.Context, repoID int64) error {
} }
// CountOrphanedLabels return count of labels witch are broken and not accessible via ui anymore // CountOrphanedLabels return count of labels witch are broken and not accessible via ui anymore
func CountOrphanedLabels() (int64, error) { func CountOrphanedLabels(ctx context.Context) (int64, error) {
noref, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Count() noref, err := db.GetEngine(ctx).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Count()
if err != nil { if err != nil {
return 0, err return 0, err
} }
norepo, err := db.GetEngine(db.DefaultContext).Table("label"). norepo, err := db.GetEngine(ctx).Table("label").
Where(builder.And( Where(builder.And(
builder.Gt{"repo_id": 0}, builder.Gt{"repo_id": 0},
builder.NotIn("repo_id", builder.Select("id").From("repository")), builder.NotIn("repo_id", builder.Select("id").From("repository")),
@ -762,7 +762,7 @@ func CountOrphanedLabels() (int64, error) {
return 0, err return 0, err
} }
noorg, err := db.GetEngine(db.DefaultContext).Table("label"). noorg, err := db.GetEngine(ctx).Table("label").
Where(builder.And( Where(builder.And(
builder.Gt{"org_id": 0}, builder.Gt{"org_id": 0},
builder.NotIn("org_id", builder.Select("id").From("user")), builder.NotIn("org_id", builder.Select("id").From("user")),
@ -776,14 +776,14 @@ func CountOrphanedLabels() (int64, error) {
} }
// DeleteOrphanedLabels delete labels witch are broken and not accessible via ui anymore // DeleteOrphanedLabels delete labels witch are broken and not accessible via ui anymore
func DeleteOrphanedLabels() error { func DeleteOrphanedLabels(ctx context.Context) error {
// delete labels with no reference // delete labels with no reference
if _, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Delete(new(Label)); err != nil { if _, err := db.GetEngine(ctx).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Delete(new(Label)); err != nil {
return err return err
} }
// delete labels with none existing repos // delete labels with none existing repos
if _, err := db.GetEngine(db.DefaultContext). if _, err := db.GetEngine(ctx).
Where(builder.And( Where(builder.And(
builder.Gt{"repo_id": 0}, builder.Gt{"repo_id": 0},
builder.NotIn("repo_id", builder.Select("id").From("repository")), builder.NotIn("repo_id", builder.Select("id").From("repository")),
@ -793,7 +793,7 @@ func DeleteOrphanedLabels() error {
} }
// delete labels with none existing orgs // delete labels with none existing orgs
if _, err := db.GetEngine(db.DefaultContext). if _, err := db.GetEngine(ctx).
Where(builder.And( Where(builder.And(
builder.Gt{"org_id": 0}, builder.Gt{"org_id": 0},
builder.NotIn("org_id", builder.Select("id").From("user")), builder.NotIn("org_id", builder.Select("id").From("user")),
@ -806,23 +806,23 @@ func DeleteOrphanedLabels() error {
} }
// CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore // CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore
func CountOrphanedIssueLabels() (int64, error) { func CountOrphanedIssueLabels(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Table("issue_label"). return db.GetEngine(ctx).Table("issue_label").
NotIn("label_id", builder.Select("id").From("label")). NotIn("label_id", builder.Select("id").From("label")).
Count() Count()
} }
// DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore // DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore
func DeleteOrphanedIssueLabels() error { func DeleteOrphanedIssueLabels(ctx context.Context) error {
_, err := db.GetEngine(db.DefaultContext). _, err := db.GetEngine(ctx).
NotIn("label_id", builder.Select("id").From("label")). NotIn("label_id", builder.Select("id").From("label")).
Delete(IssueLabel{}) Delete(IssueLabel{})
return err return err
} }
// CountIssueLabelWithOutsideLabels count label comments with outside label // CountIssueLabelWithOutsideLabels count label comments with outside label
func CountIssueLabelWithOutsideLabels() (int64, error) { func CountIssueLabelWithOutsideLabels(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.Expr("(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)")). return db.GetEngine(ctx).Where(builder.Expr("(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)")).
Table("issue_label"). Table("issue_label").
Join("inner", "label", "issue_label.label_id = label.id "). Join("inner", "label", "issue_label.label_id = label.id ").
Join("inner", "issue", "issue.id = issue_label.issue_id "). Join("inner", "issue", "issue.id = issue_label.issue_id ").
@ -831,8 +831,8 @@ func CountIssueLabelWithOutsideLabels() (int64, error) {
} }
// FixIssueLabelWithOutsideLabels fix label comments with outside label // FixIssueLabelWithOutsideLabels fix label comments with outside label
func FixIssueLabelWithOutsideLabels() (int64, error) { func FixIssueLabelWithOutsideLabels(ctx context.Context) (int64, error) {
res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM issue_label WHERE issue_label.id IN ( res, err := db.GetEngine(ctx).Exec(`DELETE FROM issue_label WHERE issue_label.id IN (
SELECT il_too.id FROM ( SELECT il_too.id FROM (
SELECT il_too_too.id SELECT il_too_too.id
FROM issue_label AS il_too_too FROM issue_label AS il_too_too

View File

@ -121,7 +121,7 @@ func TestGetLabelInRepoByID(t *testing.T) {
func TestGetLabelsInRepoByIDs(t *testing.T) { func TestGetLabelsInRepoByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := issues_model.GetLabelsInRepoByIDs(1, []int64{1, 2, unittest.NonexistentID}) labels, err := issues_model.GetLabelsInRepoByIDs(db.DefaultContext, 1, []int64{1, 2, unittest.NonexistentID})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, labels, 2) { if assert.Len(t, labels, 2) {
assert.EqualValues(t, 1, labels[0].ID) assert.EqualValues(t, 1, labels[0].ID)
@ -212,7 +212,7 @@ func TestGetLabelInOrgByID(t *testing.T) {
func TestGetLabelsInOrgByIDs(t *testing.T) { func TestGetLabelsInOrgByIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
labels, err := issues_model.GetLabelsInOrgByIDs(3, []int64{3, 4, unittest.NonexistentID}) labels, err := issues_model.GetLabelsInOrgByIDs(db.DefaultContext, 3, []int64{3, 4, unittest.NonexistentID})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, labels, 2) { if assert.Len(t, labels, 2) {
assert.EqualValues(t, 3, labels[0].ID) assert.EqualValues(t, 3, labels[0].ID)
@ -370,7 +370,7 @@ func TestDeleteIssueLabel(t *testing.T) {
} }
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
defer committer.Close() defer committer.Close()
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, issues_model.DeleteIssueLabel(ctx, issue, label, doer)) assert.NoError(t, issues_model.DeleteIssueLabel(ctx, issue, label, doer))

View File

@ -111,7 +111,7 @@ func (m *Milestone) State() api.StateType {
// NewMilestone creates new milestone of repository. // NewMilestone creates new milestone of repository.
func NewMilestone(m *Milestone) (err error) { func NewMilestone(m *Milestone) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -161,7 +161,7 @@ func GetMilestoneByRepoIDANDName(repoID int64, name string) (*Milestone, error)
// UpdateMilestone updates information of given milestone. // UpdateMilestone updates information of given milestone.
func UpdateMilestone(m *Milestone, oldIsClosed bool) error { func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -219,7 +219,7 @@ func UpdateMilestoneCounters(ctx context.Context, id int64) error {
// ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo. // ChangeMilestoneStatusByRepoIDAndID changes a milestone open/closed status if the milestone ID is in the repo.
func ChangeMilestoneStatusByRepoIDAndID(repoID, milestoneID int64, isClosed bool) error { func ChangeMilestoneStatusByRepoIDAndID(repoID, milestoneID int64, isClosed bool) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -246,7 +246,7 @@ func ChangeMilestoneStatusByRepoIDAndID(repoID, milestoneID int64, isClosed bool
// ChangeMilestoneStatus changes the milestone open/closed status. // ChangeMilestoneStatus changes the milestone open/closed status.
func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -290,7 +290,7 @@ func DeleteMilestoneByRepoID(repoID, id int64) error {
return err return err
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -205,8 +205,8 @@ func DeletePullsByBaseRepoID(ctx context.Context, repoID int64) error {
} }
// MustHeadUserName returns the HeadRepo's username if failed return blank // MustHeadUserName returns the HeadRepo's username if failed return blank
func (pr *PullRequest) MustHeadUserName() string { func (pr *PullRequest) MustHeadUserName(ctx context.Context) string {
if err := pr.LoadHeadRepo(); err != nil { if err := pr.LoadHeadRepo(ctx); err != nil {
if !repo_model.IsErrRepoNotExist(err) { if !repo_model.IsErrRepoNotExist(err) {
log.Error("LoadHeadRepo: %v", err) log.Error("LoadHeadRepo: %v", err)
} else { } else {
@ -220,8 +220,9 @@ func (pr *PullRequest) MustHeadUserName() string {
return pr.HeadRepo.OwnerName return pr.HeadRepo.OwnerName
} }
// LoadAttributes loads pull request attributes from database
// Note: don't try to get Issue because will end up recursive querying. // Note: don't try to get Issue because will end up recursive querying.
func (pr *PullRequest) loadAttributes(ctx context.Context) (err error) { func (pr *PullRequest) LoadAttributes(ctx context.Context) (err error) {
if pr.HasMerged && pr.Merger == nil { if pr.HasMerged && pr.Merger == nil {
pr.Merger, err = user_model.GetUserByIDCtx(ctx, pr.MergerID) pr.Merger, err = user_model.GetUserByIDCtx(ctx, pr.MergerID)
if user_model.IsErrUserNotExist(err) { if user_model.IsErrUserNotExist(err) {
@ -235,13 +236,8 @@ func (pr *PullRequest) loadAttributes(ctx context.Context) (err error) {
return nil return nil
} }
// LoadAttributes loads pull request attributes from database // LoadHeadRepo loads the head repository
func (pr *PullRequest) LoadAttributes() error { func (pr *PullRequest) LoadHeadRepo(ctx context.Context) (err error) {
return pr.loadAttributes(db.DefaultContext)
}
// LoadHeadRepoCtx loads the head repository
func (pr *PullRequest) LoadHeadRepoCtx(ctx context.Context) (err error) {
if !pr.isHeadRepoLoaded && pr.HeadRepo == nil && pr.HeadRepoID > 0 { if !pr.isHeadRepoLoaded && pr.HeadRepo == nil && pr.HeadRepoID > 0 {
if pr.HeadRepoID == pr.BaseRepoID { if pr.HeadRepoID == pr.BaseRepoID {
if pr.BaseRepo != nil { if pr.BaseRepo != nil {
@ -262,18 +258,8 @@ func (pr *PullRequest) LoadHeadRepoCtx(ctx context.Context) (err error) {
return nil return nil
} }
// LoadHeadRepo loads the head repository
func (pr *PullRequest) LoadHeadRepo() error {
return pr.LoadHeadRepoCtx(db.DefaultContext)
}
// LoadBaseRepo loads the target repository // LoadBaseRepo loads the target repository
func (pr *PullRequest) LoadBaseRepo() error { func (pr *PullRequest) LoadBaseRepo(ctx context.Context) (err error) {
return pr.LoadBaseRepoCtx(db.DefaultContext)
}
// LoadBaseRepoCtx loads the target repository
func (pr *PullRequest) LoadBaseRepoCtx(ctx context.Context) (err error) {
if pr.BaseRepo != nil { if pr.BaseRepo != nil {
return nil return nil
} }
@ -296,12 +282,7 @@ func (pr *PullRequest) LoadBaseRepoCtx(ctx context.Context) (err error) {
} }
// LoadIssue loads issue information from database // LoadIssue loads issue information from database
func (pr *PullRequest) LoadIssue() (err error) { func (pr *PullRequest) LoadIssue(ctx context.Context) (err error) {
return pr.LoadIssueCtx(db.DefaultContext)
}
// LoadIssueCtx loads issue information from database
func (pr *PullRequest) LoadIssueCtx(ctx context.Context) (err error) {
if pr.Issue != nil { if pr.Issue != nil {
return nil return nil
} }
@ -368,7 +349,7 @@ func (pr *PullRequest) getReviewedByLines(writer io.Writer) error {
return nil return nil
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -392,7 +373,7 @@ func (pr *PullRequest) getReviewedByLines(writer io.Writer) error {
break break
} }
if err := review.loadReviewer(ctx); err != nil && !user_model.IsErrUserNotExist(err) { if err := review.LoadReviewer(ctx); err != nil && !user_model.IsErrUserNotExist(err) {
log.Error("Unable to LoadReviewer[%d] for PR ID %d : %v", review.ReviewerID, pr.ID, err) log.Error("Unable to LoadReviewer[%d] for PR ID %d : %v", review.ReviewerID, pr.ID, err)
return err return err
} else if review.Reviewer == nil { } else if review.Reviewer == nil {
@ -458,7 +439,7 @@ func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
} }
pr.Issue = nil pr.Issue = nil
if err := pr.LoadIssueCtx(ctx); err != nil { if err := pr.LoadIssue(ctx); err != nil {
return false, err return false, err
} }
@ -498,7 +479,7 @@ func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
// NewPullRequest creates new pull request with labels for repository. // NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) { func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(outerCtx)
if err != nil { if err != nil {
return err return err
} }
@ -541,9 +522,9 @@ func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue
// GetUnmergedPullRequest returns a pull request that is open and has not been merged // GetUnmergedPullRequest returns a pull request that is open and has not been merged
// by given head/base and repo/branch. // by given head/base and repo/branch.
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string, flow PullRequestFlow) (*PullRequest, error) { func GetUnmergedPullRequest(ctx context.Context, headRepoID, baseRepoID int64, headBranch, baseBranch string, flow PullRequestFlow) (*PullRequest, error) {
pr := new(PullRequest) pr := new(PullRequest)
has, err := db.GetEngine(db.DefaultContext). has, err := db.GetEngine(ctx).
Where("head_repo_id=? AND head_branch=? AND base_repo_id=? AND base_branch=? AND has_merged=? AND flow = ? AND issue.is_closed=?", Where("head_repo_id=? AND head_branch=? AND base_repo_id=? AND base_branch=? AND has_merged=? AND flow = ? AND issue.is_closed=?",
headRepoID, headBranch, baseRepoID, baseBranch, false, flow, false). headRepoID, headBranch, baseRepoID, baseBranch, false, flow, false).
Join("INNER", "issue", "issue.id=pull_request.issue_id"). Join("INNER", "issue", "issue.id=pull_request.issue_id").
@ -588,10 +569,10 @@ func GetPullRequestByIndex(ctx context.Context, repoID, index int64) (*PullReque
return nil, ErrPullRequestNotExist{0, 0, 0, repoID, "", ""} return nil, ErrPullRequestNotExist{0, 0, 0, repoID, "", ""}
} }
if err = pr.loadAttributes(ctx); err != nil { if err = pr.LoadAttributes(ctx); err != nil {
return nil, err return nil, err
} }
if err = pr.LoadIssueCtx(ctx); err != nil { if err = pr.LoadIssue(ctx); err != nil {
return nil, err return nil, err
} }
@ -607,7 +588,7 @@ func GetPullRequestByID(ctx context.Context, id int64) (*PullRequest, error) {
} else if !has { } else if !has {
return nil, ErrPullRequestNotExist{id, 0, 0, 0, "", ""} return nil, ErrPullRequestNotExist{id, 0, 0, 0, "", ""}
} }
return pr, pr.loadAttributes(ctx) return pr, pr.LoadAttributes(ctx)
} }
// GetPullRequestByIssueIDWithNoAttributes returns pull request with no attributes loaded by given issue ID. // GetPullRequestByIssueIDWithNoAttributes returns pull request with no attributes loaded by given issue ID.
@ -634,7 +615,7 @@ func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest,
} else if !has { } else if !has {
return nil, ErrPullRequestNotExist{0, issueID, 0, 0, "", ""} return nil, ErrPullRequestNotExist{0, issueID, 0, 0, "", ""}
} }
return pr, pr.loadAttributes(ctx) return pr, pr.LoadAttributes(ctx)
} }
// GetAllUnmergedAgitPullRequestByPoster get all unmerged agit flow pull request // GetAllUnmergedAgitPullRequestByPoster get all unmerged agit flow pull request
@ -664,14 +645,15 @@ func (pr *PullRequest) UpdateCols(cols ...string) error {
} }
// UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged // UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged
func (pr *PullRequest) UpdateColsIfNotMerged(cols ...string) error { func (pr *PullRequest) UpdateColsIfNotMerged(ctx context.Context, cols ...string) error {
_, err := db.GetEngine(db.DefaultContext).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr) _, err := db.GetEngine(ctx).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
return err return err
} }
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title // IsWorkInProgress determine if the Pull Request is a Work In Progress by its title
// Issue must be set before this method can be called.
func (pr *PullRequest) IsWorkInProgress() bool { func (pr *PullRequest) IsWorkInProgress() bool {
if err := pr.LoadIssue(); err != nil { if err := pr.LoadIssue(db.DefaultContext); err != nil {
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return false return false
} }
@ -695,8 +677,8 @@ func (pr *PullRequest) IsFilesConflicted() bool {
// GetWorkInProgressPrefix returns the prefix used to mark the pull request as a work in progress. // GetWorkInProgressPrefix returns the prefix used to mark the pull request as a work in progress.
// It returns an empty string when none were found // It returns an empty string when none were found
func (pr *PullRequest) GetWorkInProgressPrefix() string { func (pr *PullRequest) GetWorkInProgressPrefix(ctx context.Context) string {
if err := pr.LoadIssue(); err != nil { if err := pr.LoadIssue(ctx); err != nil {
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return "" return ""
} }
@ -739,7 +721,7 @@ func GetPullRequestsByHeadBranch(ctx context.Context, headBranch string, headRep
// GetBaseBranchHTMLURL returns the HTML URL of the base branch // GetBaseBranchHTMLURL returns the HTML URL of the base branch
func (pr *PullRequest) GetBaseBranchHTMLURL() string { func (pr *PullRequest) GetBaseBranchHTMLURL() string {
if err := pr.LoadBaseRepo(); err != nil { if err := pr.LoadBaseRepo(db.DefaultContext); err != nil {
log.Error("LoadBaseRepo: %v", err) log.Error("LoadBaseRepo: %v", err)
return "" return ""
} }
@ -755,7 +737,7 @@ func (pr *PullRequest) GetHeadBranchHTMLURL() string {
return "" return ""
} }
if err := pr.LoadHeadRepo(); err != nil { if err := pr.LoadHeadRepo(db.DefaultContext); err != nil {
log.Error("LoadHeadRepo: %v", err) log.Error("LoadHeadRepo: %v", err)
return "" return ""
} }

View File

@ -79,7 +79,7 @@ func CanMaintainerWriteToBranch(p access_model.Permission, branch string, user *
for _, pr := range prs { for _, pr := range prs {
if pr.AllowMaintainerEdit { if pr.AllowMaintainerEdit {
err = pr.LoadBaseRepo() err = pr.LoadBaseRepo(db.DefaultContext)
if err != nil { if err != nil {
continue continue
} }

View File

@ -17,7 +17,7 @@ import (
func TestPullRequest_LoadAttributes(t *testing.T) { func TestPullRequest_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadAttributes()) assert.NoError(t, pr.LoadAttributes(db.DefaultContext))
assert.NotNil(t, pr.Merger) assert.NotNil(t, pr.Merger)
assert.Equal(t, pr.MergerID, pr.Merger.ID) assert.Equal(t, pr.MergerID, pr.Merger.ID)
} }
@ -25,10 +25,10 @@ func TestPullRequest_LoadAttributes(t *testing.T) {
func TestPullRequest_LoadIssue(t *testing.T) { func TestPullRequest_LoadIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadIssue()) assert.NoError(t, pr.LoadIssue(db.DefaultContext))
assert.NotNil(t, pr.Issue) assert.NotNil(t, pr.Issue)
assert.Equal(t, int64(2), pr.Issue.ID) assert.Equal(t, int64(2), pr.Issue.ID)
assert.NoError(t, pr.LoadIssue()) assert.NoError(t, pr.LoadIssue(db.DefaultContext))
assert.NotNil(t, pr.Issue) assert.NotNil(t, pr.Issue)
assert.Equal(t, int64(2), pr.Issue.ID) assert.Equal(t, int64(2), pr.Issue.ID)
} }
@ -36,10 +36,10 @@ func TestPullRequest_LoadIssue(t *testing.T) {
func TestPullRequest_LoadBaseRepo(t *testing.T) { func TestPullRequest_LoadBaseRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadBaseRepo()) assert.NoError(t, pr.LoadBaseRepo(db.DefaultContext))
assert.NotNil(t, pr.BaseRepo) assert.NotNil(t, pr.BaseRepo)
assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID)
assert.NoError(t, pr.LoadBaseRepo()) assert.NoError(t, pr.LoadBaseRepo(db.DefaultContext))
assert.NotNil(t, pr.BaseRepo) assert.NotNil(t, pr.BaseRepo)
assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID)
} }
@ -47,7 +47,7 @@ func TestPullRequest_LoadBaseRepo(t *testing.T) {
func TestPullRequest_LoadHeadRepo(t *testing.T) { func TestPullRequest_LoadHeadRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadHeadRepo()) assert.NoError(t, pr.LoadHeadRepo(db.DefaultContext))
assert.NotNil(t, pr.HeadRepo) assert.NotNil(t, pr.HeadRepo)
assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID) assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID)
} }
@ -96,11 +96,11 @@ func TestPullRequestsOldest(t *testing.T) {
func TestGetUnmergedPullRequest(t *testing.T) { func TestGetUnmergedPullRequest(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr, err := issues_model.GetUnmergedPullRequest(1, 1, "branch2", "master", issues_model.PullRequestFlowGithub) pr, err := issues_model.GetUnmergedPullRequest(db.DefaultContext, 1, 1, "branch2", "master", issues_model.PullRequestFlowGithub)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(2), pr.ID) assert.Equal(t, int64(2), pr.ID)
_, err = issues_model.GetUnmergedPullRequest(1, 9223372036854775807, "branch1", "master", issues_model.PullRequestFlowGithub) _, err = issues_model.GetUnmergedPullRequest(db.DefaultContext, 1, 9223372036854775807, "branch1", "master", issues_model.PullRequestFlowGithub)
assert.Error(t, err) assert.Error(t, err)
assert.True(t, issues_model.IsErrPullRequestNotExist(err)) assert.True(t, issues_model.IsErrPullRequestNotExist(err))
} }
@ -228,7 +228,7 @@ func TestPullRequest_IsWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
pr.LoadIssue() pr.LoadIssue(db.DefaultContext)
assert.False(t, pr.IsWorkInProgress()) assert.False(t, pr.IsWorkInProgress())
@ -243,16 +243,16 @@ func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
pr.LoadIssue() pr.LoadIssue(db.DefaultContext)
assert.Empty(t, pr.GetWorkInProgressPrefix()) assert.Empty(t, pr.GetWorkInProgressPrefix(db.DefaultContext))
original := pr.Issue.Title original := pr.Issue.Title
pr.Issue.Title = "WIP: " + original pr.Issue.Title = "WIP: " + original
assert.Equal(t, "WIP:", pr.GetWorkInProgressPrefix()) assert.Equal(t, "WIP:", pr.GetWorkInProgressPrefix(db.DefaultContext))
pr.Issue.Title = "[wip] " + original pr.Issue.Title = "[wip] " + original
assert.Equal(t, "[wip]", pr.GetWorkInProgressPrefix()) assert.Equal(t, "[wip]", pr.GetWorkInProgressPrefix(db.DefaultContext))
} }
func TestDeleteOrphanedObjects(t *testing.T) { func TestDeleteOrphanedObjects(t *testing.T) {
@ -264,11 +264,11 @@ func TestDeleteOrphanedObjects(t *testing.T) {
_, err = db.GetEngine(db.DefaultContext).Insert(&issues_model.PullRequest{IssueID: 1000}, &issues_model.PullRequest{IssueID: 1001}, &issues_model.PullRequest{IssueID: 1003}) _, err = db.GetEngine(db.DefaultContext).Insert(&issues_model.PullRequest{IssueID: 1000}, &issues_model.PullRequest{IssueID: 1001}, &issues_model.PullRequest{IssueID: 1003})
assert.NoError(t, err) assert.NoError(t, err)
orphaned, err := db.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") orphaned, err := db.CountOrphanedObjects(db.DefaultContext, "pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 3, orphaned) assert.EqualValues(t, 3, orphaned)
err = db.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") err = db.DeleteOrphanedObjects(db.DefaultContext, "pull_request", "issue", "pull_request.issue_id=issue.id")
assert.NoError(t, err) assert.NoError(t, err)
countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{}) countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{})

View File

@ -224,7 +224,7 @@ func CreateReaction(opts *ReactionOptions) (*Reaction, error) {
return nil, ErrForbiddenIssueReaction{opts.Type} return nil, ErrForbiddenIssueReaction{opts.Type}
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -154,7 +154,8 @@ func (r *Review) loadIssue(ctx context.Context) (err error) {
return err return err
} }
func (r *Review) loadReviewer(ctx context.Context) (err error) { // LoadReviewer loads reviewer
func (r *Review) LoadReviewer(ctx context.Context) (err error) {
if r.ReviewerID == 0 || r.Reviewer != nil { if r.ReviewerID == 0 || r.Reviewer != nil {
return return
} }
@ -162,7 +163,8 @@ func (r *Review) loadReviewer(ctx context.Context) (err error) {
return err return err
} }
func (r *Review) loadReviewerTeam(ctx context.Context) (err error) { // LoadReviewerTeam loads reviewer team
func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) {
if r.ReviewerTeamID == 0 || r.ReviewerTeam != nil { if r.ReviewerTeamID == 0 || r.ReviewerTeam != nil {
return return
} }
@ -171,16 +173,6 @@ func (r *Review) loadReviewerTeam(ctx context.Context) (err error) {
return err return err
} }
// LoadReviewer loads reviewer
func (r *Review) LoadReviewer() error {
return r.loadReviewer(db.DefaultContext)
}
// LoadReviewerTeam loads reviewer team
func (r *Review) LoadReviewerTeam() error {
return r.loadReviewerTeam(db.DefaultContext)
}
// LoadAttributes loads all attributes except CodeComments // LoadAttributes loads all attributes except CodeComments
func (r *Review) LoadAttributes(ctx context.Context) (err error) { func (r *Review) LoadAttributes(ctx context.Context) (err error) {
if err = r.loadIssue(ctx); err != nil { if err = r.loadIssue(ctx); err != nil {
@ -189,10 +181,10 @@ func (r *Review) LoadAttributes(ctx context.Context) (err error) {
if err = r.LoadCodeComments(ctx); err != nil { if err = r.LoadCodeComments(ctx); err != nil {
return return
} }
if err = r.loadReviewer(ctx); err != nil { if err = r.LoadReviewer(ctx); err != nil {
return return
} }
if err = r.loadReviewerTeam(ctx); err != nil { if err = r.LoadReviewerTeam(ctx); err != nil {
return return
} }
return err return err
@ -374,7 +366,7 @@ func IsContentEmptyErr(err error) bool {
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, content, commitID string, stale bool, attachmentUUIDs []string) (*Review, *Comment, error) { func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, content, commitID string, stale bool, attachmentUUIDs []string) (*Review, *Comment, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -622,7 +614,7 @@ func DismissReview(review *Review, isDismiss bool) (err error) {
// InsertReviews inserts review and review comments // InsertReviews inserts review and review comments
func InsertReviews(reviews []*Review) error { func InsertReviews(reviews []*Review) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -664,7 +656,7 @@ func InsertReviews(reviews []*Review) error {
// AddReviewRequest add a review request from one reviewer // AddReviewRequest add a review request from one reviewer
func AddReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { func AddReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -719,7 +711,7 @@ func AddReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment,
// RemoveReviewRequest remove a review request from one reviewer // RemoveReviewRequest remove a review request from one reviewer
func RemoveReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { func RemoveReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -772,7 +764,7 @@ func RemoveReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Commen
// AddTeamReviewRequest add a review request from one team // AddTeamReviewRequest add a review request from one team
func AddTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) { func AddTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -831,7 +823,7 @@ func AddTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_
// RemoveTeamReviewRequest remove a review request from one team // RemoveTeamReviewRequest remove a review request from one team
func RemoveTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) { func RemoveTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -949,7 +941,7 @@ func CanMarkConversation(issue *Issue, doer *user_model.User) (permResult bool,
// DeleteReview delete a review and it's code comments // DeleteReview delete a review and it's code comments
func DeleteReview(r *Review) error { func DeleteReview(r *Review) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -135,7 +135,7 @@ func TestGetReviewersByIssueID(t *testing.T) {
allReviews, err := issues_model.GetReviewersByIssueID(issue.ID) allReviews, err := issues_model.GetReviewersByIssueID(issue.ID)
for _, reviewer := range allReviews { for _, reviewer := range allReviews {
assert.NoError(t, reviewer.LoadReviewer()) assert.NoError(t, reviewer.LoadReviewer(db.DefaultContext))
} }
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, allReviews, 3) { if assert.Len(t, allReviews, 3) {

View File

@ -261,7 +261,7 @@ func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss
// CancelStopwatch removes the given stopwatch and logs it into issue's timeline. // CancelStopwatch removes the given stopwatch and logs it into issue's timeline.
func CancelStopwatch(user *user_model.User, issue *Issue) error { func CancelStopwatch(user *user_model.User, issue *Issue) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -149,7 +149,7 @@ func GetTrackedSeconds(ctx context.Context, opts FindTrackedTimesOptions) (track
// AddTime will add the given time (in seconds) to the issue // AddTime will add the given time (in seconds) to the issue
func AddTime(user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { func AddTime(user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -220,7 +220,7 @@ func TotalTimes(options *FindTrackedTimesOptions) (map[*user_model.User]string,
// DeleteIssueUserTimes deletes times for issue // DeleteIssueUserTimes deletes times for issue
func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error { func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -257,7 +257,7 @@ func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error {
// DeleteTime delete a specific Time // DeleteTime delete a specific Time
func DeleteTime(t *TrackedTime) error { func DeleteTime(t *TrackedTime) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -20,7 +20,7 @@ func InsertMilestones(ms ...*issues_model.Milestone) (err error) {
return nil return nil
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -42,7 +42,7 @@ func InsertMilestones(ms ...*issues_model.Milestone) (err error) {
// InsertIssues insert issues to database // InsertIssues insert issues to database
func InsertIssues(issues ...*issues_model.Issue) error { func InsertIssues(issues ...*issues_model.Issue) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -105,7 +105,7 @@ func InsertIssueComments(comments []*issues_model.Comment) error {
issueIDs.Add(comment.IssueID) issueIDs.Add(comment.IssueID)
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -137,7 +137,7 @@ func InsertIssueComments(comments []*issues_model.Comment) error {
// InsertPullRequests inserted pull requests // InsertPullRequests inserted pull requests
func InsertPullRequests(prs ...*issues_model.PullRequest) error { func InsertPullRequests(prs ...*issues_model.PullRequest) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -157,7 +157,7 @@ func InsertPullRequests(prs ...*issues_model.PullRequest) error {
// InsertReleases migrates release // InsertReleases migrates release
func InsertReleases(rels ...*repo_model.Release) error { func InsertReleases(rels ...*repo_model.Release) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -6,6 +6,7 @@
package migrations package migrations
import ( import (
"context"
"fmt" "fmt"
"os" "os"
@ -23,6 +24,7 @@ import (
"code.gitea.io/gitea/models/migrations/v1_7" "code.gitea.io/gitea/models/migrations/v1_7"
"code.gitea.io/gitea/models/migrations/v1_8" "code.gitea.io/gitea/models/migrations/v1_8"
"code.gitea.io/gitea/models/migrations/v1_9" "code.gitea.io/gitea/models/migrations/v1_9"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -437,6 +439,8 @@ var migrations = []Migration{
NewMigration("Alter package_version.metadata_json to LONGTEXT", v1_19.AlterPackageVersionMetadataToLongText), NewMigration("Alter package_version.metadata_json to LONGTEXT", v1_19.AlterPackageVersionMetadataToLongText),
// v233 -> v234 // v233 -> v234
NewMigration("Add header_authorization_encrypted column to webhook table", v1_19.AddHeaderAuthorizationEncryptedColWebhook), NewMigration("Add header_authorization_encrypted column to webhook table", v1_19.AddHeaderAuthorizationEncryptedColWebhook),
// v234 -> v235
NewMigration("Add package cleanup rule table", v1_19.CreatePackageCleanupRuleTable),
} }
// GetCurrentDBVersion returns the current db version // GetCurrentDBVersion returns the current db version
@ -527,6 +531,13 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
return nil return nil
} }
// Some migration tasks depend on the git command
if git.DefaultContext == nil {
if err = git.InitSimple(context.Background()); err != nil {
return err
}
}
// Migrate // Migrate
for i, m := range migrations[v-minDBVersion:] { for i, m := range migrations[v-minDBVersion:] {
log.Info("Migration[%d]: %s", v+int64(i), m.Description()) log.Info("Migration[%d]: %s", v+int64(i), m.Description())

View File

@ -0,0 +1,29 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package v1_19 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func CreatePackageCleanupRuleTable(x *xorm.Engine) error {
type PackageCleanupRule struct {
ID int64 `xorm:"pk autoincr"`
Enabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
OwnerID int64 `xorm:"UNIQUE(s) INDEX NOT NULL DEFAULT 0"`
Type string `xorm:"UNIQUE(s) INDEX NOT NULL"`
KeepCount int `xorm:"NOT NULL DEFAULT 0"`
KeepPattern string `xorm:"NOT NULL DEFAULT ''"`
RemoveDays int `xorm:"NOT NULL DEFAULT 0"`
RemovePattern string `xorm:"NOT NULL DEFAULT ''"`
MatchFullName bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL DEFAULT 0"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL DEFAULT 0"`
}
return x.Sync2(new(PackageCleanupRule))
}

View File

@ -99,7 +99,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
// RemoveOrgUser removes user from given organization. // RemoveOrgUser removes user from given organization.
func RemoveOrgUser(orgID, userID int64) error { func RemoveOrgUser(orgID, userID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -76,7 +76,7 @@ func addAllRepositories(ctx context.Context, t *organization.Team) error {
// AddAllRepositories adds all repositories to the team // AddAllRepositories adds all repositories to the team
func AddAllRepositories(t *organization.Team) (err error) { func AddAllRepositories(t *organization.Team) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -95,7 +95,7 @@ func RemoveAllRepositories(t *organization.Team) (err error) {
return nil return nil
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -219,7 +219,7 @@ func RemoveRepository(t *organization.Team, repoID int64) error {
return err return err
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -263,7 +263,7 @@ func NewTeam(t *organization.Team) (err error) {
return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName} return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -308,7 +308,7 @@ func UpdateTeam(t *organization.Team, authChanged, includeAllChanged bool) (err
t.Description = t.Description[:255] t.Description = t.Description[:255]
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -375,7 +375,7 @@ func UpdateTeam(t *organization.Team, authChanged, includeAllChanged bool) (err
// DeleteTeam deletes given team. // DeleteTeam deletes given team.
// It's caller's responsibility to assign organization ID. // It's caller's responsibility to assign organization ID.
func DeleteTeam(t *organization.Team) error { func DeleteTeam(t *organization.Team) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -460,7 +460,7 @@ func AddTeamMember(team *organization.Team, userID int64) error {
return err return err
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -598,7 +598,7 @@ func removeTeamMember(ctx context.Context, team *organization.Team, userID int64
// RemoveTeamMember removes member from given team of given organization. // RemoveTeamMember removes member from given team of given organization.
func RemoveTeamMember(team *organization.Team, userID int64) error { func RemoveTeamMember(team *organization.Team, userID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -143,7 +143,7 @@ func TestDeleteTeam(t *testing.T) {
// check that team members don't have "leftover" access to repos // check that team members don't have "leftover" access to repos
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
accessMode, err := access_model.AccessLevel(user, repo) accessMode, err := access_model.AccessLevel(db.DefaultContext, user, repo)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, accessMode < perm.AccessModeWrite) assert.True(t, accessMode < perm.AccessModeWrite)
} }

View File

@ -277,7 +277,7 @@ func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
org.NumMembers = 1 org.NumMembers = 1
org.Type = user_model.UserTypeOrganization org.Type = user_model.UserTypeOrganization
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -458,8 +458,9 @@ func CountOrgs(opts FindOrgOptions) (int64, error) {
// HasOrgOrUserVisible tells if the given user can see the given org or user // HasOrgOrUserVisible tells if the given user can see the given org or user
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool { func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
// Not SignedUser // If user is nil, it's an anonymous user/request.
if user == nil { // The Ghost user is handled like an anonymous user.
if user == nil || user.IsGhost() {
return orgOrUser.Visibility == structs.VisibleTypePublic return orgOrUser.Visibility == structs.VisibleTypePublic
} }
@ -564,7 +565,7 @@ func AddOrgUser(orgID, uid int64) error {
return err return err
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -32,7 +32,7 @@ func getUnitsByTeamID(ctx context.Context, teamID int64) (units []*TeamUnit, err
// UpdateTeamUnits updates a teams's units // UpdateTeamUnits updates a teams's units
func UpdateTeamUnits(team *Team, units []TeamUnit) (err error) { func UpdateTeamUnits(team *Team, units []TeamUnit) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -45,6 +45,21 @@ const (
TypeVagrant Type = "vagrant" TypeVagrant Type = "vagrant"
) )
var TypeList = []Type{
TypeComposer,
TypeConan,
TypeContainer,
TypeGeneric,
TypeHelm,
TypeMaven,
TypeNpm,
TypeNuGet,
TypePub,
TypePyPI,
TypeRubyGems,
TypeVagrant,
}
// Name gets the name of the package type // Name gets the name of the package type
func (pt Type) Name() string { func (pt Type) Name() string {
switch pt { switch pt {

View File

@ -62,6 +62,13 @@ func GetBlobByID(ctx context.Context, blobID int64) (*PackageBlob, error) {
return pb, nil return pb, nil
} }
// ExistPackageBlobWithSHA returns if a package blob exists with the provided sha
func ExistPackageBlobWithSHA(ctx context.Context, blobSha256 string) (bool, error) {
return db.GetEngine(ctx).Exist(&PackageBlob{
HashSHA256: blobSha256,
})
}
// FindExpiredUnreferencedBlobs gets all blobs without associated files older than the specific duration // FindExpiredUnreferencedBlobs gets all blobs without associated files older than the specific duration
func FindExpiredUnreferencedBlobs(ctx context.Context, olderThan time.Duration) ([]*PackageBlob, error) { func FindExpiredUnreferencedBlobs(ctx context.Context, olderThan time.Duration) ([]*PackageBlob, error) {
pbs := make([]*PackageBlob, 0, 10) pbs := make([]*PackageBlob, 0, 10)

View File

@ -0,0 +1,110 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package packages
import (
"context"
"errors"
"fmt"
"regexp"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)
var ErrPackageCleanupRuleNotExist = errors.New("Package blob does not exist")
func init() {
db.RegisterModel(new(PackageCleanupRule))
}
// PackageCleanupRule represents a rule which describes when to clean up package versions
type PackageCleanupRule struct {
ID int64 `xorm:"pk autoincr"`
Enabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
OwnerID int64 `xorm:"UNIQUE(s) INDEX NOT NULL DEFAULT 0"`
Type Type `xorm:"UNIQUE(s) INDEX NOT NULL"`
KeepCount int `xorm:"NOT NULL DEFAULT 0"`
KeepPattern string `xorm:"NOT NULL DEFAULT ''"`
KeepPatternMatcher *regexp.Regexp `xorm:"-"`
RemoveDays int `xorm:"NOT NULL DEFAULT 0"`
RemovePattern string `xorm:"NOT NULL DEFAULT ''"`
RemovePatternMatcher *regexp.Regexp `xorm:"-"`
MatchFullName bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL DEFAULT 0"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL DEFAULT 0"`
}
func (pcr *PackageCleanupRule) CompiledPattern() error {
if pcr.KeepPatternMatcher != nil || pcr.RemovePatternMatcher != nil {
return nil
}
if pcr.KeepPattern != "" {
var err error
pcr.KeepPatternMatcher, err = regexp.Compile(fmt.Sprintf(`(?i)\A%s\z`, pcr.KeepPattern))
if err != nil {
return err
}
}
if pcr.RemovePattern != "" {
var err error
pcr.RemovePatternMatcher, err = regexp.Compile(fmt.Sprintf(`(?i)\A%s\z`, pcr.RemovePattern))
if err != nil {
return err
}
}
return nil
}
func InsertCleanupRule(ctx context.Context, pcr *PackageCleanupRule) (*PackageCleanupRule, error) {
return pcr, db.Insert(ctx, pcr)
}
func GetCleanupRuleByID(ctx context.Context, id int64) (*PackageCleanupRule, error) {
pcr := &PackageCleanupRule{}
has, err := db.GetEngine(ctx).ID(id).Get(pcr)
if err != nil {
return nil, err
}
if !has {
return nil, ErrPackageCleanupRuleNotExist
}
return pcr, nil
}
func UpdateCleanupRule(ctx context.Context, pcr *PackageCleanupRule) error {
_, err := db.GetEngine(ctx).ID(pcr.ID).AllCols().Update(pcr)
return err
}
func GetCleanupRulesByOwner(ctx context.Context, ownerID int64) ([]*PackageCleanupRule, error) {
pcrs := make([]*PackageCleanupRule, 0, 10)
return pcrs, db.GetEngine(ctx).Where("owner_id = ?", ownerID).Find(&pcrs)
}
func DeleteCleanupRuleByID(ctx context.Context, ruleID int64) error {
_, err := db.GetEngine(ctx).ID(ruleID).Delete(&PackageCleanupRule{})
return err
}
func HasOwnerCleanupRuleForPackageType(ctx context.Context, ownerID int64, packageType Type) (bool, error) {
return db.GetEngine(ctx).
Where("owner_id = ? AND type = ?", ownerID, packageType).
Exist(&PackageCleanupRule{})
}
func IterateEnabledCleanupRules(ctx context.Context, callback func(context.Context, *PackageCleanupRule) error) error {
return db.Iterate(
ctx,
builder.Eq{"enabled": true},
callback,
)
}

View File

@ -320,6 +320,15 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P
return pvs, count, err return pvs, count, err
} }
// ExistVersion checks if a version matching the search options exist
func ExistVersion(ctx context.Context, opts *PackageSearchOptions) (bool, error) {
return db.GetEngine(ctx).
Where(opts.toConds()).
Table("package_version").
Join("INNER", "package", "package.id = package_version.package_id").
Exist(new(PackageVersion))
}
// CountVersions counts all versions of packages matching the search options // CountVersions counts all versions of packages matching the search options
func CountVersions(ctx context.Context, opts *PackageSearchOptions) (int64, error) { func CountVersions(ctx context.Context, opts *PackageSearchOptions) (int64, error) {
return db.GetEngine(ctx). return db.GetEngine(ctx).

View File

@ -36,34 +36,34 @@ func TestAccessLevel(t *testing.T) {
// org. owned private repo // org. owned private repo
repo24 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24}) repo24 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24})
level, err := access_model.AccessLevel(user2, repo1) level, err := access_model.AccessLevel(db.DefaultContext, user2, repo1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, perm_model.AccessModeOwner, level) assert.Equal(t, perm_model.AccessModeOwner, level)
level, err = access_model.AccessLevel(user2, repo3) level, err = access_model.AccessLevel(db.DefaultContext, user2, repo3)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, perm_model.AccessModeOwner, level) assert.Equal(t, perm_model.AccessModeOwner, level)
level, err = access_model.AccessLevel(user5, repo1) level, err = access_model.AccessLevel(db.DefaultContext, user5, repo1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, perm_model.AccessModeRead, level) assert.Equal(t, perm_model.AccessModeRead, level)
level, err = access_model.AccessLevel(user5, repo3) level, err = access_model.AccessLevel(db.DefaultContext, user5, repo3)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, perm_model.AccessModeNone, level) assert.Equal(t, perm_model.AccessModeNone, level)
// restricted user has no access to a public repo // restricted user has no access to a public repo
level, err = access_model.AccessLevel(user29, repo1) level, err = access_model.AccessLevel(db.DefaultContext, user29, repo1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, perm_model.AccessModeNone, level) assert.Equal(t, perm_model.AccessModeNone, level)
// ... unless he's a collaborator // ... unless he's a collaborator
level, err = access_model.AccessLevel(user29, repo4) level, err = access_model.AccessLevel(db.DefaultContext, user29, repo4)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, perm_model.AccessModeWrite, level) assert.Equal(t, perm_model.AccessModeWrite, level)
// ... or a team member // ... or a team member
level, err = access_model.AccessLevel(user29, repo24) level, err = access_model.AccessLevel(db.DefaultContext, user29, repo24)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, perm_model.AccessModeRead, level) assert.Equal(t, perm_model.AccessModeRead, level)
} }

View File

@ -326,17 +326,13 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the // AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
// user does not have access. // user does not have access.
func AccessLevel(user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint
return AccessLevelUnit(user, repo, unit.TypeCode) return AccessLevelUnit(ctx, user, repo, unit.TypeCode)
} }
// AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the // AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the
// user does not have access. // user does not have access.
func AccessLevelUnit(user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint
return accessLevelUnit(db.DefaultContext, user, repo, unitType)
}
func accessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) {
perm, err := GetUserRepoPermission(ctx, repo, user) perm, err := GetUserRepoPermission(ctx, repo, user)
if err != nil { if err != nil {
return perm_model.AccessModeNone, err return perm_model.AccessModeNone, err
@ -346,7 +342,7 @@ func accessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_mode
// HasAccessUnit returns true if user has testMode to the unit of the repository // HasAccessUnit returns true if user has testMode to the unit of the repository
func HasAccessUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type, testMode perm_model.AccessMode) (bool, error) { func HasAccessUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type, testMode perm_model.AccessMode) (bool, error) {
mode, err := accessLevelUnit(ctx, user, repo, unitType) mode, err := AccessLevelUnit(ctx, user, repo, unitType)
return testMode <= mode, err return testMode <= mode, err
} }

View File

@ -133,7 +133,7 @@ func NewBoard(board *Board) error {
// DeleteBoardByID removes all issues references to the project board. // DeleteBoardByID removes all issues references to the project board.
func DeleteBoardByID(boardID int64) error { func DeleteBoardByID(boardID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -78,7 +78,7 @@ func (p *Project) NumOpenIssues() int {
// MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column // MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column
func MoveIssuesOnProjectBoard(board *Board, sortedIssueIDs map[int64]int64) error { func MoveIssuesOnProjectBoard(board *Board, sortedIssueIDs map[int64]int64) error {
return db.WithTx(func(ctx context.Context) error { return db.WithTx(db.DefaultContext, func(ctx context.Context) error {
sess := db.GetEngine(ctx) sess := db.GetEngine(ctx)
issueIDs := make([]int64, 0, len(sortedIssueIDs)) issueIDs := make([]int64, 0, len(sortedIssueIDs))

View File

@ -180,7 +180,7 @@ func NewProject(p *Project) error {
return errors.New("project type is not valid") return errors.New("project type is not valid")
} }
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -248,7 +248,7 @@ func updateRepositoryProjectCount(ctx context.Context, repoID int64) error {
// ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed // ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed
func ChangeProjectStatusByRepoIDAndID(repoID, projectID int64, isClosed bool) error { func ChangeProjectStatusByRepoIDAndID(repoID, projectID int64, isClosed bool) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -272,7 +272,7 @@ func ChangeProjectStatusByRepoIDAndID(repoID, projectID int64, isClosed bool) er
// ChangeProjectStatus toggle a project between opened and closed // ChangeProjectStatus toggle a project between opened and closed
func ChangeProjectStatus(p *Project, isClosed bool) error { func ChangeProjectStatus(p *Project, isClosed bool) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -301,7 +301,7 @@ func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error {
// DeleteProjectByID deletes a project from a repository. // DeleteProjectByID deletes a project from a repository.
func DeleteProjectByID(id int64) error { func DeleteProjectByID(id int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -45,7 +45,7 @@ func Init() error {
// DeleteRepository deletes a repository for a user or organization. // DeleteRepository deletes a repository for a user or organization.
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock) // make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
func DeleteRepository(doer *user_model.User, uid, repoID int64) error { func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -569,7 +569,7 @@ func UpdateRepoStats(ctx context.Context, id int64) error {
} }
func updateUserStarNumbers(users []user_model.User) error { func updateUserStarNumbers(users []user_model.User) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -7,11 +7,14 @@ package repo
import ( import (
"context" "context"
"fmt" "fmt"
"strconv"
"strings"
"time" "time"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@ -44,6 +47,28 @@ func (archiver *RepoArchiver) RelativePath() string {
return fmt.Sprintf("%d/%s/%s.%s", archiver.RepoID, archiver.CommitID[:2], archiver.CommitID, archiver.Type.String()) return fmt.Sprintf("%d/%s/%s.%s", archiver.RepoID, archiver.CommitID[:2], archiver.CommitID, archiver.Type.String())
} }
// repoArchiverForRelativePath takes a relativePath created from (archiver *RepoArchiver) RelativePath() and creates a shell repoArchiver struct representing it
func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) {
parts := strings.SplitN(relativePath, "/", 3)
if len(parts) != 3 {
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
}
repoID, err := strconv.ParseInt(parts[0], 10, 64)
if err != nil {
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
}
nameExts := strings.SplitN(parts[2], ".", 2)
if len(nameExts) != 2 {
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
}
return &RepoArchiver{
RepoID: repoID,
CommitID: parts[1] + nameExts[0],
Type: git.ToArchiveType(nameExts[1]),
}, nil
}
var delRepoArchiver = new(RepoArchiver) var delRepoArchiver = new(RepoArchiver)
// DeleteRepoArchiver delete archiver // DeleteRepoArchiver delete archiver
@ -65,6 +90,17 @@ func GetRepoArchiver(ctx context.Context, repoID int64, tp git.ArchiveType, comm
return nil, nil return nil, nil
} }
// ExistsRepoArchiverWithStoragePath checks if there is a RepoArchiver for a given storage path
func ExistsRepoArchiverWithStoragePath(ctx context.Context, storagePath string) (bool, error) {
// We need to invert the path provided func (archiver *RepoArchiver) RelativePath() above
archiver, err := repoArchiverForRelativePath(storagePath)
if err != nil {
return false, err
}
return db.GetEngine(ctx).Exist(archiver)
}
// AddRepoArchiver adds an archiver // AddRepoArchiver adds an archiver
func AddRepoArchiver(ctx context.Context, archiver *RepoArchiver) error { func AddRepoArchiver(ctx context.Context, archiver *RepoArchiver) error {
_, err := db.GetEngine(ctx).Insert(archiver) _, err := db.GetEngine(ctx).Insert(archiver)

View File

@ -122,9 +122,9 @@ func GetAttachmentsByUUIDs(ctx context.Context, uuids []string) ([]*Attachment,
return attachments, db.GetEngine(ctx).In("uuid", uuids).Find(&attachments) return attachments, db.GetEngine(ctx).In("uuid", uuids).Find(&attachments)
} }
// ExistAttachmentsByUUID returns true if attachment is exist by given UUID // ExistAttachmentsByUUID returns true if attachment exists with the given UUID
func ExistAttachmentsByUUID(uuid string) (bool, error) { func ExistAttachmentsByUUID(ctx context.Context, uuid string) (bool, error) {
return db.GetEngine(db.DefaultContext).Where("`uuid`=?", uuid).Exist(new(Attachment)) return db.GetEngine(ctx).Where("`uuid`=?", uuid).Exist(new(Attachment))
} }
// GetAttachmentsByIssueID returns all attachments of an issue. // GetAttachmentsByIssueID returns all attachments of an issue.
@ -226,20 +226,20 @@ func UpdateAttachment(ctx context.Context, atta *Attachment) error {
} }
// DeleteAttachmentsByRelease deletes all attachments associated with the given release. // DeleteAttachmentsByRelease deletes all attachments associated with the given release.
func DeleteAttachmentsByRelease(releaseID int64) error { func DeleteAttachmentsByRelease(ctx context.Context, releaseID int64) error {
_, err := db.GetEngine(db.DefaultContext).Where("release_id = ?", releaseID).Delete(&Attachment{}) _, err := db.GetEngine(ctx).Where("release_id = ?", releaseID).Delete(&Attachment{})
return err return err
} }
// CountOrphanedAttachments returns the number of bad attachments // CountOrphanedAttachments returns the number of bad attachments
func CountOrphanedAttachments() (int64, error) { func CountOrphanedAttachments(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))"). return db.GetEngine(ctx).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))").
Count(new(Attachment)) Count(new(Attachment))
} }
// DeleteOrphanedAttachments delete all bad attachments // DeleteOrphanedAttachments delete all bad attachments
func DeleteOrphanedAttachments() error { func DeleteOrphanedAttachments(ctx context.Context) error {
_, err := db.GetEngine(db.DefaultContext).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))"). _, err := db.GetEngine(ctx).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))").
Delete(new(Attachment)) Delete(new(Attachment))
return err return err
} }

View File

@ -24,6 +24,13 @@ func (repo *Repository) CustomAvatarRelativePath() string {
return repo.Avatar return repo.Avatar
} }
// ExistsWithAvatarAtStoragePath returns true if there is a user with this Avatar
func ExistsWithAvatarAtStoragePath(ctx context.Context, storagePath string) (bool, error) {
// See func (repo *Repository) CustomAvatarRelativePath()
// repo.Avatar is used directly as the storage path - therefore we can check for existence directly using the path
return db.GetEngine(ctx).Where("`avatar`=?", storagePath).Exist(new(Repository))
}
// RelAvatarLink returns a relative link to the repository's avatar. // RelAvatarLink returns a relative link to the repository's avatar.
func (repo *Repository) RelAvatarLink() string { func (repo *Repository) RelAvatarLink() string {
return repo.relAvatarLink(db.DefaultContext) return repo.relAvatarLink(db.DefaultContext)

View File

@ -138,7 +138,7 @@ func ChangeCollaborationAccessModeCtx(ctx context.Context, repo *Repository, uid
// ChangeCollaborationAccessMode sets new access mode for the collaboration. // ChangeCollaborationAccessMode sets new access mode for the collaboration.
func ChangeCollaborationAccessMode(repo *Repository, uid int64, mode perm.AccessMode) error { func ChangeCollaborationAccessMode(repo *Repository, uid int64, mode perm.AccessMode) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -110,7 +110,7 @@ func GetTopLanguageStats(repo *Repository, limit int) (LanguageStatList, error)
// UpdateLanguageStats updates the language statistics for repository // UpdateLanguageStats updates the language statistics for repository
func UpdateLanguageStats(repo *Repository, commitID string, stats map[string]int64) error { func UpdateLanguageStats(repo *Repository, commitID string, stats map[string]int64) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }
@ -182,7 +182,7 @@ func UpdateLanguageStats(repo *Repository, commitID string, stats map[string]int
// CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo) // CopyLanguageStat Copy originalRepo language stat information to destRepo (use for forked repo)
func CopyLanguageStat(originalRepo, destRepo *Repository) error { func CopyLanguageStat(originalRepo, destRepo *Repository) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -120,9 +120,9 @@ func GetPushMirrorsByRepoID(ctx context.Context, repoID int64, listOptions db.Li
} }
// GetPushMirrorsSyncedOnCommit returns push-mirrors for this repo that should be updated by new commits // GetPushMirrorsSyncedOnCommit returns push-mirrors for this repo that should be updated by new commits
func GetPushMirrorsSyncedOnCommit(repoID int64) ([]*PushMirror, error) { func GetPushMirrorsSyncedOnCommit(ctx context.Context, repoID int64) ([]*PushMirror, error) {
mirrors := make([]*PushMirror, 0, 10) mirrors := make([]*PushMirror, 0, 10)
return mirrors, db.GetEngine(db.DefaultContext). return mirrors, db.GetEngine(ctx).
Where("repo_id=? AND sync_on_commit=?", repoID, true). Where("repo_id=? AND sync_on_commit=?", repoID, true).
Find(&mirrors) Find(&mirrors)
} }

View File

@ -90,7 +90,8 @@ func init() {
db.RegisterModel(new(Release)) db.RegisterModel(new(Release))
} }
func (r *Release) loadAttributes(ctx context.Context) error { // LoadAttributes load repo and publisher attributes for a release
func (r *Release) LoadAttributes(ctx context.Context) error {
var err error var err error
if r.Repo == nil { if r.Repo == nil {
r.Repo, err = GetRepositoryByIDCtx(ctx, r.RepoID) r.Repo, err = GetRepositoryByIDCtx(ctx, r.RepoID)
@ -111,11 +112,6 @@ func (r *Release) loadAttributes(ctx context.Context) error {
return GetReleaseAttachments(ctx, r) return GetReleaseAttachments(ctx, r)
} }
// LoadAttributes load repo and publisher attributes for a release
func (r *Release) LoadAttributes() error {
return r.loadAttributes(db.DefaultContext)
}
// APIURL the api url for a release. release must have attributes loaded // APIURL the api url for a release. release must have attributes loaded
func (r *Release) APIURL() string { func (r *Release) APIURL() string {
return r.Repo.APIURL() + "/releases/" + strconv.FormatInt(r.ID, 10) return r.Repo.APIURL() + "/releases/" + strconv.FormatInt(r.ID, 10)
@ -241,8 +237,8 @@ func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
} }
// GetReleasesByRepoID returns a list of releases of repository. // GetReleasesByRepoID returns a list of releases of repository.
func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, error) { func GetReleasesByRepoID(ctx context.Context, repoID int64, opts FindReleasesOptions) ([]*Release, error) {
sess := db.GetEngine(db.DefaultContext). sess := db.GetEngine(ctx).
Desc("created_unix", "id"). Desc("created_unix", "id").
Where(opts.toConds(repoID)) Where(opts.toConds(repoID))
@ -381,8 +377,8 @@ func SortReleases(rels []*Release) {
} }
// DeleteReleaseByID deletes a release from database by given ID. // DeleteReleaseByID deletes a release from database by given ID.
func DeleteReleaseByID(id int64) error { func DeleteReleaseByID(ctx context.Context, id int64) error {
_, err := db.GetEngine(db.DefaultContext).ID(id).Delete(new(Release)) _, err := db.GetEngine(ctx).ID(id).Delete(new(Release))
return err return err
} }

View File

@ -236,14 +236,6 @@ func (repo *Repository) AfterLoad() {
repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects
} }
// MustOwner always returns a valid *user_model.User object to avoid
// conceptually impossible error handling.
// It creates a fake object that contains error details
// when error occurs.
func (repo *Repository) MustOwner() *user_model.User {
return repo.mustOwner(db.DefaultContext)
}
// LoadAttributes loads attributes of the repository. // LoadAttributes loads attributes of the repository.
func (repo *Repository) LoadAttributes(ctx context.Context) error { func (repo *Repository) LoadAttributes(ctx context.Context) error {
// Load owner // Load owner
@ -403,7 +395,11 @@ func (repo *Repository) GetOwner(ctx context.Context) (err error) {
return err return err
} }
func (repo *Repository) mustOwner(ctx context.Context) *user_model.User { // MustOwner always returns a valid *user_model.User object to avoid
// conceptually impossible error handling.
// It creates a fake object that contains error details
// when error occurs.
func (repo *Repository) MustOwner(ctx context.Context) *user_model.User {
if err := repo.GetOwner(ctx); err != nil { if err := repo.GetOwner(ctx); err != nil {
return &user_model.User{ return &user_model.User{
Name: "error", Name: "error",
@ -438,7 +434,7 @@ func (repo *Repository) ComposeMetas() map[string]string {
} }
} }
repo.MustOwner() repo.MustOwner(db.DefaultContext)
if repo.Owner.IsOrganization() { if repo.Owner.IsOrganization() {
teams := make([]string, 0, 5) teams := make([]string, 0, 5)
_ = db.GetEngine(db.DefaultContext).Table("team_repo"). _ = db.GetEngine(db.DefaultContext).Table("team_repo").
@ -792,13 +788,13 @@ func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed
} }
// CountNullArchivedRepository counts the number of repositories with is_archived is null // CountNullArchivedRepository counts the number of repositories with is_archived is null
func CountNullArchivedRepository() (int64, error) { func CountNullArchivedRepository(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(Repository)) return db.GetEngine(ctx).Where(builder.IsNull{"is_archived"}).Count(new(Repository))
} }
// FixNullArchivedRepository sets is_archived to false where it is null // FixNullArchivedRepository sets is_archived to false where it is null
func FixNullArchivedRepository() (int64, error) { func FixNullArchivedRepository(ctx context.Context) (int64, error) {
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&Repository{ return db.GetEngine(ctx).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&Repository{
IsArchived: false, IsArchived: false,
}) })
} }

View File

@ -518,14 +518,13 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
// SearchRepository returns repositories based on search options, // SearchRepository returns repositories based on search options,
// it returns results in given range and number of total results. // it returns results in given range and number of total results.
func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) { func SearchRepository(ctx context.Context, opts *SearchRepoOptions) (RepositoryList, int64, error) {
cond := SearchRepositoryCondition(opts) cond := SearchRepositoryCondition(opts)
return SearchRepositoryByCondition(opts, cond, true) return SearchRepositoryByCondition(ctx, opts, cond, true)
} }
// SearchRepositoryByCondition search repositories by condition // SearchRepositoryByCondition search repositories by condition
func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) { func SearchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
ctx := db.DefaultContext
sess, count, err := searchRepositoryByCondition(ctx, opts, cond) sess, count, err := searchRepositoryByCondition(ctx, opts, cond)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -652,9 +651,9 @@ func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) bu
// SearchRepositoryByName takes keyword and part of repository name to search, // SearchRepositoryByName takes keyword and part of repository name to search,
// it returns results in given range and number of total results. // it returns results in given range and number of total results.
func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, error) { func SearchRepositoryByName(ctx context.Context, opts *SearchRepoOptions) (RepositoryList, int64, error) {
opts.IncludeDescription = false opts.IncludeDescription = false
return SearchRepository(opts) return SearchRepository(ctx, opts)
} }
// SearchRepositoryIDs takes keyword and part of repository name to search, // SearchRepositoryIDs takes keyword and part of repository name to search,

View File

@ -20,7 +20,7 @@ func TestSearchRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
// test search public repository on explore page // test search public repository on explore page
repos, count, err := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{ repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -35,7 +35,7 @@ func TestSearchRepository(t *testing.T) {
} }
assert.Equal(t, int64(1), count) assert.Equal(t, int64(1), count)
repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{ repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -49,7 +49,7 @@ func TestSearchRepository(t *testing.T) {
assert.Len(t, repos, 2) assert.Len(t, repos, 2)
// test search private repository on explore page // test search private repository on explore page
repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{ repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -65,7 +65,7 @@ func TestSearchRepository(t *testing.T) {
} }
assert.Equal(t, int64(1), count) assert.Equal(t, int64(1), count)
repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{ repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -80,14 +80,14 @@ func TestSearchRepository(t *testing.T) {
assert.Len(t, repos, 3) assert.Len(t, repos, 3)
// Test non existing owner // Test non existing owner
repos, count, err = repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID}) repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, repos) assert.Empty(t, repos)
assert.Equal(t, int64(0), count) assert.Equal(t, int64(0), count)
// Test search within description // Test search within description
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{ repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -104,7 +104,7 @@ func TestSearchRepository(t *testing.T) {
assert.Equal(t, int64(1), count) assert.Equal(t, int64(1), count)
// Test NOT search within description // Test NOT search within description
repos, count, err = repo_model.SearchRepository(&repo_model.SearchRepoOptions{ repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
@ -277,7 +277,7 @@ func TestSearchRepository(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) { t.Run(testCase.name, func(t *testing.T) {
repos, count, err := repo_model.SearchRepositoryByName(testCase.opts) repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(testCase.count), count) assert.Equal(t, int64(testCase.count), count)
@ -377,7 +377,7 @@ func TestSearchRepositoryByTopicName(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) { t.Run(testCase.name, func(t *testing.T) {
_, count, err := repo_model.SearchRepositoryByName(testCase.opts) _, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(testCase.count), count) assert.Equal(t, int64(testCase.count), count)
}) })

View File

@ -241,7 +241,7 @@ func UpdateRepoUnit(unit *RepoUnit) error {
// UpdateRepositoryUnits updates a repository's units // UpdateRepositoryUnits updates a repository's units
func UpdateRepositoryUnits(repo *Repository, units []RepoUnit, deleteUnitTypes []unit.Type) (err error) { func UpdateRepositoryUnits(repo *Repository, units []RepoUnit, deleteUnitTypes []unit.Type) (err error) {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

View File

@ -26,7 +26,7 @@ func init() {
// StarRepo or unstar repository. // StarRepo or unstar repository.
func StarRepo(userID, repoID int64, star bool) error { func StarRepo(userID, repoID int64, star bool) error {
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext(db.DefaultContext)
if err != nil { if err != nil {
return err return err
} }

Some files were not shown because too many files have changed in this diff Show More