mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-15 07:20:26 +00:00
Compare commits
161 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27c5d9ac0f | ||
|
|
69fc06217d | ||
|
|
df248fbccd | ||
|
|
9c9485d1df | ||
|
|
8634b38383 | ||
|
|
206224aff5 | ||
|
|
7902524927 | ||
|
|
a3870d7955 | ||
|
|
9cf4962d95 | ||
|
|
2c3f0c4250 | ||
|
|
ff2cbd6824 | ||
|
|
85e12097a4 | ||
|
|
72d35af26a | ||
|
|
eac5cb9a64 | ||
|
|
a2f9fb501f | ||
|
|
a1bd21d45e | ||
|
|
2e6fc8aaf0 | ||
|
|
62c73cdd8e | ||
|
|
c7b4e90106 | ||
|
|
021d8b198f | ||
|
|
71df8827a9 | ||
|
|
3c6b708c0f | ||
|
|
1a3cdde2ed | ||
|
|
37cbf7f519 | ||
|
|
3c91d267ec | ||
|
|
0f2e6034ce | ||
|
|
b566753e47 | ||
|
|
14957b42bc | ||
|
|
c502e2b1e3 | ||
|
|
d8cba03e16 | ||
|
|
e7a5e0a82b | ||
|
|
f602b5f5ed | ||
|
|
d52756b879 | ||
|
|
a72cffddfa | ||
|
|
beed05d2f7 | ||
|
|
9421d22215 | ||
|
|
4123ace6c4 | ||
|
|
211376cd9a | ||
|
|
164c02b010 | ||
|
|
1452c3ae70 | ||
|
|
197220c2ee | ||
|
|
d1d90aa9c1 | ||
|
|
fc57758c87 | ||
|
|
f4581e0f23 | ||
|
|
912ffa2dbd | ||
|
|
e5a4f83bea | ||
|
|
fa3073044a | ||
|
|
fe55c0e76c | ||
|
|
816a37f576 | ||
|
|
155acecb4b | ||
|
|
2b0ec87644 | ||
|
|
388436d500 | ||
|
|
ea4f733de8 | ||
|
|
d4bb6cde65 | ||
|
|
cd9ddac459 | ||
|
|
d9896e4de6 | ||
|
|
6ee6dc2eb0 | ||
|
|
230ccf5276 | ||
|
|
d0dd0928be | ||
|
|
aca1775dae | ||
|
|
456be57bb7 | ||
|
|
b72473d114 | ||
|
|
dbdbd0f5e6 | ||
|
|
f4f3a3e0f3 | ||
|
|
6b6e9da3cd | ||
|
|
41c3c5bb80 | ||
|
|
677de6419b | ||
|
|
8ef91fa1cc | ||
|
|
b3c7fbcce3 | ||
|
|
3515a9c365 | ||
|
|
e50f9ff165 | ||
|
|
131fc0db9c | ||
|
|
382ee8ce34 | ||
|
|
ce0e17989b | ||
|
|
84240ce3a2 | ||
|
|
a59481d3d9 | ||
|
|
3ad19e7335 | ||
|
|
032b0bbeda | ||
|
|
9347d06de5 | ||
|
|
7410ef5b9f | ||
|
|
8aff5ab18b | ||
|
|
68f39ad66b | ||
|
|
4bb2d68a10 | ||
|
|
4e1468a1b5 | ||
|
|
ddf0265b5c | ||
|
|
3aaef3b3dd | ||
|
|
097680a64d | ||
|
|
bdc7c65173 | ||
|
|
f9e2ccb108 | ||
|
|
39a5f838c7 | ||
|
|
da0df4b5d9 | ||
|
|
c3fe2a5e34 | ||
|
|
191b309486 | ||
|
|
4d7dfe38bf | ||
|
|
186664b881 | ||
|
|
1d1a62187a | ||
|
|
ec28d5885d | ||
|
|
f33e2d1efd | ||
|
|
964288a4a8 | ||
|
|
f3028a7768 | ||
|
|
16f98ebaec | ||
|
|
ca46a3f68b | ||
|
|
16ee36b023 | ||
|
|
e147d8d805 | ||
|
|
cd00d61b91 | ||
|
|
0f20b2e51a | ||
|
|
a3a52251ad | ||
|
|
46d0192f5c | ||
|
|
745134a89f | ||
|
|
04132b338d | ||
|
|
527c1a4fda | ||
|
|
48bb631f20 | ||
|
|
92ea9b7055 | ||
|
|
d912a9b21f | ||
|
|
b042992694 | ||
|
|
f962b32177 | ||
|
|
5c9a2e91f4 | ||
|
|
915c436d95 | ||
|
|
686f780673 | ||
|
|
659f1fc0c6 | ||
|
|
f87ec19130 | ||
|
|
47b9fdc590 | ||
|
|
90ab3b1940 | ||
|
|
0b7e6ff363 | ||
|
|
440f38913e | ||
|
|
3556875c51 | ||
|
|
f5603d2210 | ||
|
|
ca3166ddba | ||
|
|
1ca9cbb7c2 | ||
|
|
590795f592 | ||
|
|
168dfbb70b | ||
|
|
bade14ee69 | ||
|
|
8e083c9f3e | ||
|
|
342a54a142 | ||
|
|
a53aa04f5d | ||
|
|
40e4f6f354 | ||
|
|
abab629d90 | ||
|
|
626929eaa3 | ||
|
|
a604f85c60 | ||
|
|
ee42a69b3a | ||
|
|
766104acae | ||
|
|
180ebee6de | ||
|
|
400c17e290 | ||
|
|
15f891abd7 | ||
|
|
8514af643d | ||
|
|
763547f43f | ||
|
|
a89978a207 | ||
|
|
c6c51dcde6 | ||
|
|
462fe3819b | ||
|
|
8dff8ba7c2 | ||
|
|
5a131275c1 | ||
|
|
6907601529 | ||
|
|
fcb22b1a47 | ||
|
|
650252f851 | ||
|
|
cd0afc4f90 | ||
|
|
dd75d0957d | ||
|
|
83da3ae68c | ||
|
|
44102c47d4 | ||
|
|
fed7d64861 | ||
|
|
6df7514417 | ||
|
|
0861a01192 |
1954 changed files with 37016 additions and 94292 deletions
|
|
@ -19,10 +19,10 @@ forgejo.org/models/auth
|
|||
forgejo.org/models/db
|
||||
TruncateBeans
|
||||
TruncateBeansCascade
|
||||
InTransaction
|
||||
DumpTables
|
||||
GetTableNames
|
||||
extendBeansForCascade
|
||||
IsErrNameActivityPubInvalid
|
||||
|
||||
forgejo.org/models/dbfs
|
||||
file.renameTo
|
||||
|
|
@ -58,6 +58,7 @@ forgejo.org/models/user
|
|||
IsErrUserSettingIsNotExist
|
||||
GetUserAllSettings
|
||||
DeleteUserSetting
|
||||
GetFederatedUser
|
||||
|
||||
forgejo.org/modules/activitypub
|
||||
NewContext
|
||||
|
|
@ -87,6 +88,7 @@ forgejo.org/modules/eventsource
|
|||
Event.String
|
||||
|
||||
forgejo.org/modules/forgefed
|
||||
NewForgeFollow
|
||||
NewForgeUndoLike
|
||||
ForgeUndoLike.UnmarshalJSON
|
||||
ForgeUndoLike.Validate
|
||||
|
|
@ -132,9 +134,6 @@ forgejo.org/modules/json
|
|||
StdJSON.Indent
|
||||
|
||||
forgejo.org/modules/log
|
||||
eventWriterBuffer.Close
|
||||
eventWriterBuffer.Write
|
||||
eventWriterBuffer.GetString
|
||||
NewEventWriterBuffer
|
||||
|
||||
forgejo.org/modules/markup
|
||||
|
|
@ -225,6 +224,9 @@ forgejo.org/routers/web/org
|
|||
forgejo.org/services/context
|
||||
GetPrivateContext
|
||||
|
||||
forgejo.org/services/federation
|
||||
FollowRemoteActor
|
||||
|
||||
forgejo.org/services/notify
|
||||
UnregisterNotifier
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "forgejo-dev",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.26-trixie",
|
||||
"name": "Gitea DevContainer",
|
||||
"image": "mcr.microsoft.com/devcontainers/go:1.25-trixie",
|
||||
"features": {
|
||||
// installs nodejs into container
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ insert_final_newline = false
|
|||
[options/locale/locale_*.ini]
|
||||
insert_final_newline = false
|
||||
|
||||
# Weblate is configured to use one tab for indention
|
||||
# Weblate JSON output defaults to four spaces
|
||||
[options/locale_next/locale_*.json]
|
||||
indent_style = tab
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
|
|
|||
|
|
@ -13,18 +13,7 @@ body:
|
|||
- Please speak English, as this is the language all maintainers can speak and write.
|
||||
- Be civil, and follow the [Forgejo Code of Conduct](https://codeberg.org/forgejo/code-of-conduct).
|
||||
- Take a moment to [check if a similar problem has already been discussed in the past.](https://codeberg.org/forgejo/forgejo/issues?q=&type=all&labels=78137). Feel free to add your own experience there, if applicable.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### New workflow
|
||||
|
||||
We are currently experimenting with a new workflow to manage issues and better understand your problems and needs. This is step 1 of the workflow: Please try to focus on explaining the problem you are facing, which could be a bug in the code, a moment of confusion, or a need that you have.
|
||||
|
||||
We do not expect anything from Forgejo users after creating a problem report, but we appreciate if you stay responsive to further questions. If you want, you can also participate in a discussion for solutions.
|
||||
|
||||
Forgejo contributors will review your report, try to understand how important it is to you and other Forgejo users, and suggest potential solutions. In a next step, solutions can be documented and implemented.
|
||||
|
||||
If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback.
|
||||
- We are currently experimenting with a new workflow to understand your problems and requests. Please try to focus on explaining the problem you are facing, which could be a bug in the code, a moment of confusion, or a missing feature. We'll think about solutions to the problem at a later step. If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback.
|
||||
- type: dropdown
|
||||
id: can-reproduce
|
||||
attributes:
|
||||
|
|
@ -41,18 +30,15 @@ body:
|
|||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: About your usage of Forgejo
|
||||
label: Your usage of Forgejo
|
||||
description: |
|
||||
Please provide a brief description of your usage of Forgejo. There is no clear guideline on how much you need to share here. You can be brief, but we value the insights you provide us to better understand your use case. Thank you very much!
|
||||
Please provide a description of your usage of Forgejo. To better understand your problem, it will be relevant to know in which environment you use Forgejo and if you have performed specific configuration.
|
||||
<details><summary>Further instructions</summary>
|
||||
|
||||
* When reporting problems with certain functionality, you should include related information. Examples:
|
||||
* When reporting an issue with setting up an identity provider, it is useful to know if you use Forgejo in a 10-users non-profit / start up, or if you are talking about a school / university with several thousands of users.
|
||||
* When describing confusion, it will be relevant to know your skill level and background ("New, but used a similar product", "In my role as a project manager ..."), so we know for which target audience we need to design the functionality.
|
||||
* When reporting workflow issues or needs, it will be useful to know how large your project is, how many team members you have, which skill level we are talking about etc. For example, we might choose a different design depending on whether a feature is only for professional developers or for hobby coders.
|
||||
* If you want, we always appreciate generic information about your Forgejo usage to help us understand your usage and make better decisions. For example:
|
||||
* Your personal relation to Forgejo and user role ("I'm new to Forgejo, but used a comparable product called …", "In my role as a designer, …").
|
||||
* If you already explained your usage of Forgejo elsewhere (e.g. in another issue or in a user research repository), feel free to just drop a link.
|
||||
* If you report a bug with a certain functionality, it will be relevant to learn about related configuration ("This is how I configured my identity provider", "This is how my Forgejo Actions runner is set up").
|
||||
* Please elaborate on your personal relation to Forgejo and user role ("I'm new to Forgejo, but used a comparable product called …", "In my role as a designer, …").
|
||||
* If you feel that Forgejo needs a change in functionality, please describe in which environment you want to use it, so that we can understand for which audience the complexity needs to appeal to ("We're a group of friends with no technical / professional background and use a personal Forgejo instance to prepare our first libre game").
|
||||
* If you already explained your usage of Forgejo elsewhere (e.g. in another issue or in a user research repository), you can put a link here.
|
||||
|
||||
</details>
|
||||
validations:
|
||||
|
|
@ -62,7 +48,7 @@ body:
|
|||
attributes:
|
||||
label: Problem description
|
||||
description: |
|
||||
Please describe your problem as a first-hand experience. Try to focus on the problem. You do not have to find a solution, you can leave this to us.
|
||||
Please describe your problem as a first-hand experience. Try to focus on the problem. Finding a solution will happen in a next step.
|
||||
<details><summary>Further instructions</summary>
|
||||
|
||||
* Start by explaining what you want to achieve ("I wanted to find an issue to work on").
|
||||
|
|
@ -89,7 +75,7 @@ body:
|
|||
id: forgejo-ver
|
||||
attributes:
|
||||
label: Forgejo Version
|
||||
description: Forgejo version (or commit reference) your instance is running or that you used to reproduce the bug on Forgejo Next.
|
||||
description: Forgejo version (or commit reference) your instance is running
|
||||
- type: textarea
|
||||
id: versions
|
||||
attributes:
|
||||
|
|
@ -98,10 +84,3 @@ body:
|
|||
Please include details to help us understand your problem: browser engine and version (for UI issues), operating system and version running Forgejo, database engine and version, deployment methods and relevant third-party packages (e.g. renderers, identity providers)
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Solutions
|
||||
|
||||
*Accepted solutions to address this problem will go here*
|
||||
visible: [content]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name: "Step 2: Enhancement"
|
||||
description: "[Advanced users only] Suggest a solution to one or multiple problems that have already been reported (see step 1)."
|
||||
description: Suggest a solution to one or multiple problems that have already been reported (see step 1).
|
||||
title: "enh: "
|
||||
labels: ["enhancement/feature"]
|
||||
body:
|
||||
|
|
@ -8,28 +8,19 @@ body:
|
|||
value: |
|
||||
- Please speak English, as this is the language all maintainers can speak and write.
|
||||
- Be civil, and follow the [Forgejo Code of Conduct](https://codeberg.org/forgejo/code-of-conduct).
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### New workflow
|
||||
|
||||
We are currently experimenting with a new workflow to manage issues and better understand your problems and needs. This is step 2 of the workflow, which is intended for Forgejo contributors: If you just want to raise a problem or need you have, please refer to step 1.
|
||||
|
||||
This step allows to document a solution to one or multiple problems. It allows developers to focus on actionable implementation tasks without the clutter of previous discussions or triaging work.
|
||||
|
||||
If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback.
|
||||
- We are currently experimenting with a new workflow to understand your problems and requests. This is step 2 of a two-step-process. The goal is to discuss potential solutions to already-reported problems. If you want to learn more about the background of our workflow, feel invited to read and participate in [the discussion that led to the current approach](https://codeberg.org/forgejo/discussions/issues/415). We are looking forward to your feedback.
|
||||
- type: textarea
|
||||
id: problems
|
||||
attributes:
|
||||
label: Existing problems this enhancement addresses
|
||||
description: Only list the issue numbers of the **existing** problems that your proposal addresses. **Do not add new descriptions.** If you haven't previously described a problem, please [complete step one of the workflow](https://codeberg.org/forgejo/forgejo/issues/new?template=.forgejo%2fissue_template%2fproblem.yaml) and describe the problem you'd like to solve first.
|
||||
description: List the issue numbers of the reported problems that this enhancement addresses. If you haven't previously described a problem, please [complete step one of the workflow](https://codeberg.org/forgejo/forgejo/issues/new?template=.forgejo%2fissue_template%2fproblem.yaml) and describe the problem you'd like to solve first.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Enhancement description
|
||||
description: Describe the changes you suggest for Forgejo and explain how they address the problems.
|
||||
description: As concisely as possible, describe the changes you suggest for Forgejo and explain how they address the problems.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🔓 Security Reports
|
||||
url: mailto:security@forgejo.org
|
||||
|
|
|
|||
|
|
@ -10,22 +10,13 @@ labels:
|
|||
|
||||
## Checklist
|
||||
|
||||
The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. All work and communication must conform to Forgejo's [AI Agreement](https://codeberg.org/forgejo/governance/src/branch/main/AIAgreement.md). There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).
|
||||
The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).
|
||||
|
||||
### Tests for Go changes
|
||||
|
||||
(can be removed for JavaScript changes)
|
||||
### Tests
|
||||
|
||||
- I added test coverage for Go changes...
|
||||
- [ ] in their respective `*_test.go` for unit tests.
|
||||
- [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
|
||||
- I ran...
|
||||
- [ ] `make pr-go` before pushing
|
||||
|
||||
### Tests for JavaScript changes
|
||||
|
||||
(can be removed for Go changes)
|
||||
|
||||
- I added test coverage for JavaScript changes...
|
||||
- [ ] in `web_src/js/*.test.js` if it can be unit tested.
|
||||
- [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).
|
||||
|
|
@ -37,9 +28,6 @@ The [contributor guide](https://forgejo.org/docs/next/contributor/) contains inf
|
|||
|
||||
### Release notes
|
||||
|
||||
- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
|
||||
- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.
|
||||
|
||||
*The decision if the pull request will be shown in the release notes is up to the mergers / release team.*
|
||||
|
||||
The content of the `release-notes/<pull request number>.md` file will serve as the basis for the release notes. If the file does not exist, the title of the pull request will be used instead.
|
||||
- [ ] I do not want this change to show in the release notes.
|
||||
- [ ] I want the title to show in the release notes with a link to this pull request.
|
||||
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ runs:
|
|||
steps:
|
||||
- run: |
|
||||
su forgejo -c 'make deps-backend'
|
||||
- uses: https://data.forgejo.org/actions/cache@v5
|
||||
- uses: https://data.forgejo.org/actions/cache@v4
|
||||
id: cache-backend
|
||||
with:
|
||||
path: ${{github.workspace}}/gitea
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ runs:
|
|||
|
||||
- name: "Restore Go dependencies from cache or mark for later caching"
|
||||
id: cache-deps
|
||||
uses: https://data.forgejo.org/actions/cache@v5
|
||||
uses: https://data.forgejo.org/actions/cache@v4
|
||||
with:
|
||||
key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod', 'Makefile') }}
|
||||
restore-keys: |
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ jobs:
|
|||
cat <<'EOF'
|
||||
${{ toJSON(github) }}
|
||||
EOF
|
||||
- uses: https://data.forgejo.org/actions/git-backporting@v4.9.1
|
||||
- uses: https://data.forgejo.org/actions/git-backporting@v4.8.7
|
||||
with:
|
||||
target-branch-pattern: "^backport/(?<target>(v.*))$"
|
||||
strategy: ort
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
name: Integration tests for the release process
|
||||
enable-email-notifications: true
|
||||
|
||||
env:
|
||||
FORGEJO_VERSION: 11.0.14 # renovate: datasource=docker depName=data.forgejo.org/forgejo/forgejo
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
|
|
@ -29,14 +26,14 @@ jobs:
|
|||
if: vars.ROLE == 'forgejo-coding'
|
||||
runs-on: lxc-bookworm
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
|
||||
- id: forgejo
|
||||
uses: https://data.forgejo.org/actions/setup-forgejo@v3.1.11
|
||||
uses: https://data.forgejo.org/actions/setup-forgejo@v3.1.7
|
||||
with:
|
||||
user: root
|
||||
password: admin1234
|
||||
image-version: ${{ env.FORGEJO_VERSION }}
|
||||
image-version: 1.21
|
||||
lxc-ip-prefix: 10.0.9
|
||||
|
||||
- name: publish the forgejo release
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
# root is used for testing, allow it
|
||||
if: vars.ROLE == 'forgejo-integration' || github.repository_owner == 'root'
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ jobs:
|
|||
|
||||
- name: cache node_modules
|
||||
id: node
|
||||
uses: https://data.forgejo.org/actions/cache@v5
|
||||
uses: https://data.forgejo.org/actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
|
|
@ -164,7 +164,7 @@ jobs:
|
|||
|
||||
- name: build container & release
|
||||
if: ${{ secrets.TOKEN != '' }}
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.6.0
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.5.1
|
||||
with:
|
||||
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||
|
|
@ -183,7 +183,7 @@ jobs:
|
|||
|
||||
- name: build rootless container
|
||||
if: ${{ secrets.TOKEN != '' }}
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.6.0
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.5.1
|
||||
with:
|
||||
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||
|
|
@ -227,14 +227,11 @@ jobs:
|
|||
curl -sS -X DELETE $url/api/v1/repos/forgejo-experimental/forgejo/releases/tags/$tag > /dev/null
|
||||
curl -sS -X DELETE $url/api/v1/repos/forgejo-experimental/forgejo/tags/$tag > /dev/null
|
||||
fi
|
||||
# actions/checkout@v6 sets http.https://codeberg.org/.extraheader with the automatic token. Get rid of it so
|
||||
# it does not prevent using the token that has write permissions. As of @v6, it is stored in
|
||||
# $RUNNER_TEMP/git-credentials-(uuid).config and included in the repo via
|
||||
# includeif.gitdir:...=$RUNNER_TEMP/git-credentials-(uuid).config. Since we don't need these credentials
|
||||
# anymore we can just remove the generated config file.
|
||||
rm -f $RUNNER_TEMP/git-credentials-*
|
||||
# actions/checkout@v3 sets http.https://codeberg.org/.extraheader with the automatic token.
|
||||
# Get rid of it so it does not prevent using the token that has write permissions
|
||||
git config --local --unset http.https://codeberg.org/.extraheader
|
||||
if test -f .git/shallow ; then
|
||||
echo "unexpected .git/shallow file is present"
|
||||
echo "unexptected .git/shallow file is present"
|
||||
echo "it suggests a checkout --depth X was used which may prevent pushing the commit"
|
||||
echo "it happens when actions/checkout is called without depth: 0"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
container:
|
||||
image: data.forgejo.org/oci/node:24-bookworm
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
show-progress: 'false'
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
image: registry.redict.io/redict:7.3.6-scratch
|
||||
options: --tmpfs /data:noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
with:
|
||||
repository: ${{ inputs.repository }}
|
||||
ref: ${{ inputs.ref }}
|
||||
|
|
@ -83,7 +83,7 @@ jobs:
|
|||
TEST_MINIO_ENDPOINT: minio:9000
|
||||
TEST_LDAP: 1
|
||||
TEST_REDIS_SERVER: cacher:6379
|
||||
- uses: https://data.forgejo.org/forgejo/upload-artifact@v5
|
||||
- uses: https://code.forgejo.org/forgejo/upload-artifact@v4
|
||||
with:
|
||||
name: coverage
|
||||
path: ${{ forge.workspace }}/coverage/merged
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ jobs:
|
|||
runs-on: lxc-bookworm
|
||||
if: vars.DOER != '' && vars.FORGEJO != '' && vars.TO_OWNER != '' && vars.FROM_OWNER != '' && secrets.TOKEN != ''
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
|
||||
- name: copy & sign
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.6.0
|
||||
uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.5.1
|
||||
with:
|
||||
from-forgejo: ${{ vars.FORGEJO }}
|
||||
to-forgejo: ${{ vars.FORGEJO }}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ on:
|
|||
|
||||
env:
|
||||
RNA_WORKDIR: /srv/rna
|
||||
RNA_VERSION: v1.7.0 # renovate: datasource=forgejo-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
RNA_VERSION: v1.4.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
|
||||
jobs:
|
||||
release-notes:
|
||||
|
|
@ -15,9 +15,9 @@ jobs:
|
|||
container:
|
||||
image: 'data.forgejo.org/oci/ci:1'
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
|
||||
- uses: https://data.forgejo.org/actions/cache@v5
|
||||
- uses: https://data.forgejo.org/actions/cache@v4
|
||||
with:
|
||||
key: rna-${{ env.RNA_VERSION }}
|
||||
path: ${{ env.RNA_WORKDIR }}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@ on:
|
|||
- labeled
|
||||
|
||||
env:
|
||||
RNA_VERSION: v1.7.0 # renovate: datasource=forgejo-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
RNA_VERSION: v1.4.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
|
||||
jobs:
|
||||
release-notes:
|
||||
if: ( vars.ROLE == 'forgejo-coding' ) && contains(github.event.pull_request.labels.*.name, 'worth a release-note')
|
||||
runs-on: docker
|
||||
container:
|
||||
image: 'data.forgejo.org/oci/ci:1'
|
||||
image: 'data.forgejo.org/oci/node:24-bookworm'
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
|
||||
- name: event
|
||||
run: |
|
||||
|
|
@ -28,12 +28,17 @@ jobs:
|
|||
${{ toJSON(github.event) }}
|
||||
EOF
|
||||
|
||||
- name: install release-notes-assistant
|
||||
- uses: https://data.forgejo.org/actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: false
|
||||
|
||||
- name: apt install jq
|
||||
run: |
|
||||
set -x
|
||||
wget -O /usr/local/bin/rna https://code.forgejo.org/forgejo/release-notes-assistant/releases/download/${{ env.RNA_VERSION}}/release-notes-assistant
|
||||
chmod +x /usr/local/bin/rna
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get -q install -y -qq jq
|
||||
|
||||
- name: release-notes-assistant preview
|
||||
run: |
|
||||
rna --config .release-notes-assistant.yaml --storage pr --storage-location ${{ github.event.pull_request.number }} --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} preview ${{ github.event.pull_request.number }}
|
||||
go run code.forgejo.org/forgejo/release-notes-assistant@$RNA_VERSION --config .release-notes-assistant.yaml --storage pr --storage-location ${{ github.event.pull_request.number }} --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} preview ${{ github.event.pull_request.number }}
|
||||
|
|
|
|||
79
.forgejo/workflows/renovate.yml
Normal file
79
.forgejo/workflows/renovate.yml
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#
|
||||
# Runs every 2 hours, but Renovate is limited to create new PR before 4am.
|
||||
# See renovate.json for more settings.
|
||||
# Automerge is enabled for Renovate PR's but need to be approved before.
|
||||
#
|
||||
name: renovate
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- renovate/** # self-test updates
|
||||
paths:
|
||||
- .forgejo/workflows/renovate.yml
|
||||
schedule:
|
||||
- cron: '0 0/2 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
RENOVATE_DRY_RUN: ${{ (github.event_name != 'schedule' && github.ref_name != github.event.repository.default_branch) && 'full' || '' }}
|
||||
RENOVATE_REPOSITORIES: ${{ github.repository }}
|
||||
# fix because 10.0.0-58-7e1df53+gitea-1.22.0 < 10.0.0 for semver
|
||||
# and codeberg api returns such versions from `git describe --tags`
|
||||
# RENOVATE_X_PLATFORM_VERSION: 10.0.0+gitea-1.22.0 currently not needed
|
||||
|
||||
jobs:
|
||||
renovate:
|
||||
if: vars.ROLE == 'forgejo-coding' && secrets.RENOVATE_TOKEN != ''
|
||||
|
||||
runs-on: docker
|
||||
container:
|
||||
image: data.forgejo.org/renovate/renovate:42.39.2
|
||||
|
||||
steps:
|
||||
- name: Load renovate repo cache
|
||||
uses: https://data.forgejo.org/actions/cache/restore@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
|
||||
with:
|
||||
path: |
|
||||
.tmp/cache/renovate/repository
|
||||
.tmp/cache/renovate/renovate-cache-sqlite
|
||||
.tmp/osv
|
||||
key: repo-cache-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
repo-cache-
|
||||
|
||||
- name: Run renovate
|
||||
run: renovate
|
||||
env:
|
||||
GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }}
|
||||
LOG_LEVEL: debug
|
||||
RENOVATE_BASE_DIR: ${{ github.workspace }}/.tmp
|
||||
RENOVATE_ENDPOINT: ${{ github.server_url }}
|
||||
RENOVATE_PLATFORM: forgejo
|
||||
RENOVATE_REPOSITORY_CACHE: 'enabled'
|
||||
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
|
||||
RENOVATE_GIT_AUTHOR: 'Renovate Bot <forgejo-renovate-action@forgejo.org>'
|
||||
RENOVATE_CONFIG_FILE_NAMES: '[".forgejo/renovate.json"]'
|
||||
|
||||
RENOVATE_X_SQLITE_PACKAGE_CACHE: true
|
||||
|
||||
GIT_AUTHOR_NAME: 'Renovate Bot'
|
||||
GIT_AUTHOR_EMAIL: 'forgejo-renovate-action@forgejo.org'
|
||||
GIT_COMMITTER_NAME: 'Renovate Bot'
|
||||
GIT_COMMITTER_EMAIL: 'forgejo-renovate-action@forgejo.org'
|
||||
|
||||
OSV_OFFLINE_ROOT_DIR: ${{ github.workspace }}/.tmp/osv
|
||||
|
||||
# use direct connection for these domains for renovate go datasource instead of the go proxy
|
||||
# allows faster lookups
|
||||
GONOPROXY: code.forgejo.org
|
||||
|
||||
- name: Save renovate repo cache
|
||||
if: always() && env.RENOVATE_DRY_RUN != 'full'
|
||||
uses: https://data.forgejo.org/actions/cache/save@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
|
||||
with:
|
||||
path: |
|
||||
.tmp/cache/renovate/repository
|
||||
.tmp/cache/renovate/renovate-cache-sqlite
|
||||
.tmp/osv
|
||||
key: repo-cache-${{ github.run_id }}
|
||||
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
image: 'data.forgejo.org/oci/node:24-trixie'
|
||||
options: --tmpfs /tmp:exec,noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: install git 2.34.1 and git-lfs 3.0.2
|
||||
uses: ./.forgejo/workflows-composite/install-minimum-git-version
|
||||
|
|
@ -53,7 +53,7 @@ jobs:
|
|||
image: 'data.forgejo.org/oci/node:24-trixie'
|
||||
options: --tmpfs /tmp:exec,noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: install git 2.34.1 and git-lfs 3.0.2
|
||||
uses: ./.forgejo/workflows-composite/install-minimum-git-version
|
||||
|
|
@ -85,7 +85,7 @@ jobs:
|
|||
MARIADB_DATABASE: testgitea
|
||||
options: --tmpfs /var/lib/mysql:noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: install dependencies
|
||||
run: apt-get update -qq && apt-get -q install -qq -y git-lfs
|
||||
|
|
|
|||
|
|
@ -22,12 +22,10 @@ jobs:
|
|||
cat <<'EOF'
|
||||
${{ toJSON(github) }}
|
||||
EOF
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
# DO NOT add checks here, but rather in the makefile
|
||||
- run: su forgejo -c './tools/cimake.sh pr-go'
|
||||
# this will re-run the backend target also contained in pr-go, but
|
||||
# a re-build is insignificant
|
||||
- run: su forgejo -c 'make deps-backend deps-tools'
|
||||
- run: su forgejo -c 'make --always-make -j$(nproc) lint-backend tidy-check swagger-check lint-swagger fmt-check swagger-validate' # ensure the "go-licenses" make target runs
|
||||
- uses: ./.forgejo/workflows-composite/build-backend
|
||||
frontend-checks:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
|
|
@ -36,17 +34,14 @@ jobs:
|
|||
image: 'data.forgejo.org/oci/node:24-trixie'
|
||||
options: --tmpfs /tmp:exec,noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: https://data.forgejo.org/actions/setup-node@v6
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
|
||||
- run: make deps-frontend
|
||||
- run: make lint-frontend
|
||||
- run: make checks-frontend
|
||||
- name: make test-frontend-coverage
|
||||
run: |
|
||||
- run: |
|
||||
# Usage of `dayjs` can be impacted by local system timezone and can be sensitive to DST differences; since
|
||||
# frontend tests are very short they're run twice with varying DST rules to reduce regression risk.
|
||||
TZ=Europe/Berlin make test-frontend-coverage
|
||||
|
|
@ -58,8 +53,8 @@ jobs:
|
|||
run: |
|
||||
apt-get update -qq
|
||||
apt-get -q install -qq -y zstd
|
||||
- name: 'Cache frontend build for playwright testing'
|
||||
uses: https://data.forgejo.org/actions/cache/save@v5
|
||||
- name: "Cache frontend build for playwright testing"
|
||||
uses: https://data.forgejo.org/actions/cache/save@v4
|
||||
with:
|
||||
path: ${{github.workspace}}/public/assets
|
||||
key: frontend-build-${{ github.sha }}
|
||||
|
|
@ -76,7 +71,7 @@ jobs:
|
|||
options: --tmpfs /bitnami/elasticsearch/data
|
||||
env:
|
||||
discovery.type: single-node
|
||||
ES_JAVA_OPTS: '-Xms512m -Xmx512m'
|
||||
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
||||
minio:
|
||||
image: data.forgejo.org/oci/bitnami/minio:2024.8.17
|
||||
options: >-
|
||||
|
|
@ -86,7 +81,7 @@ jobs:
|
|||
MINIO_ROOT_USER: 123456
|
||||
MINIO_ROOT_PASSWORD: 12345678
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: test release-notes-assistant.sh
|
||||
run: |
|
||||
|
|
@ -109,17 +104,17 @@ jobs:
|
|||
image: 'data.forgejo.org/oci/playwright:latest'
|
||||
options: --tmpfs /tmp:exec,noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 20
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: 'Restore frontend build'
|
||||
uses: https://data.forgejo.org/actions/cache/restore@v5
|
||||
- name: "Restore frontend build"
|
||||
uses: https://data.forgejo.org/actions/cache/restore@v4
|
||||
id: cache-frontend
|
||||
with:
|
||||
path: ${{github.workspace}}/public/assets
|
||||
key: frontend-build-${{ github.sha }}
|
||||
- name: 'Build frontend (if not cached)'
|
||||
- name: "Build frontend (if not cached)"
|
||||
if: steps.cache-frontend.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
su forgejo -c 'make deps-frontend frontend'
|
||||
|
|
@ -144,7 +139,7 @@ jobs:
|
|||
RUN_ALL: ${{steps.run-all.all}}
|
||||
- name: Upload test artifacts on failure
|
||||
if: failure()
|
||||
uses: https://data.forgejo.org/forgejo/upload-artifact@v5
|
||||
uses: https://data.forgejo.org/forgejo/upload-artifact@v4
|
||||
with:
|
||||
name: test-artifacts.zip
|
||||
path: tests/e2e/test-artifacts/
|
||||
|
|
@ -177,9 +172,9 @@ jobs:
|
|||
image: ${{ matrix.cacher.image }}
|
||||
options: ${{ matrix.cacher.options }}
|
||||
env:
|
||||
ALLOW_EMPTY_PASSWORD: 'yes' # redis & valkey will immediately shutdown with no defined password unless overridden
|
||||
ALLOW_EMPTY_PASSWORD: "yes" # redis & valkey will immediately shutdown with no defined password unless overridden
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- uses: ./.forgejo/workflows-composite/build-backend
|
||||
- run: |
|
||||
|
|
@ -208,7 +203,7 @@ jobs:
|
|||
MYSQL_EXTRA_FLAGS: --innodb-adaptive-flushing=OFF --innodb-buffer-pool-size=4G --innodb-log-buffer-size=128M --innodb-flush-log-at-trx-commit=0 --innodb-flush-log-at-timeout=30 --innodb-flush-method=nosync --innodb-fsync-threshold=1000000000 --disable-log-bin
|
||||
options: --tmpfs /bitnami/mysql/data:noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: install dependencies
|
||||
run: apt-get update -qq && apt-get -q install -qq -y git-lfs
|
||||
|
|
@ -246,7 +241,7 @@ jobs:
|
|||
POSTGRESQL_EXTRA_FLAGS: -c full_page_writes=off
|
||||
options: --tmpfs /bitnami/postgresql
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: install dependencies
|
||||
run: apt-get update -qq && apt-get -q install -qq -y git-lfs
|
||||
|
|
@ -268,7 +263,7 @@ jobs:
|
|||
image: 'data.forgejo.org/oci/node:24-trixie'
|
||||
options: --tmpfs /tmp:exec,noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- name: install dependencies
|
||||
run: apt-get update -qq && apt-get -q install -qq -y git-lfs
|
||||
|
|
@ -296,20 +291,7 @@ jobs:
|
|||
image: 'data.forgejo.org/oci/node:24-trixie'
|
||||
options: --tmpfs /tmp:exec,noatime
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- uses: https://data.forgejo.org/actions/checkout@v5
|
||||
- uses: ./.forgejo/workflows-composite/setup-env
|
||||
- run: su forgejo -c 'make deps-backend deps-tools'
|
||||
- run: su forgejo -c 'make security-check'
|
||||
semgrep:
|
||||
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
|
||||
name: semgrep/ci
|
||||
runs-on: docker
|
||||
container:
|
||||
image: 'data.forgejo.org/oci/semgrep:latest'
|
||||
steps:
|
||||
- run: apk add nodejs # required for actions/checkout
|
||||
- uses: https://data.forgejo.org/actions/checkout@v6
|
||||
- name: self-check semgrep rules
|
||||
run: semgrep --test .semgrep/tests/ --config .semgrep/config/
|
||||
- name: semgrep ci
|
||||
run: semgrep ci --config .semgrep/config/ --metrics=off
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -107,7 +107,6 @@ cpu.out
|
|||
/.air
|
||||
/.go-licenses
|
||||
/.cur-deadcode-out
|
||||
/.deadcode.diff
|
||||
|
||||
# Files and folders that were previously generated
|
||||
/public/assets/img/webpack
|
||||
|
|
|
|||
232
.golangci.yml
232
.golangci.yml
|
|
@ -15,7 +15,6 @@ linters:
|
|||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- modernize
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- revive
|
||||
|
|
@ -26,7 +25,6 @@ linters:
|
|||
- unused
|
||||
- usetesting
|
||||
- wastedassign
|
||||
- nilnil
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
|
|
@ -46,25 +44,6 @@ linters:
|
|||
desc: use forgejo.org/modules/git instead, see https://codeberg.org/forgejo/forgejo/pulls/4941
|
||||
- pkg: gopkg.in/yaml.v3
|
||||
desc: use go.yaml.in/yaml instead, see https://codeberg.org/forgejo/forgejo/pulls/8956
|
||||
migration-isolation:
|
||||
list-mode: lax
|
||||
files:
|
||||
- "**/models/forgejo_migrations/**"
|
||||
deny:
|
||||
- pkg: "forgejo.org/models"
|
||||
desc: >
|
||||
Migrations must not import application models. Application models will be the most recent schema for
|
||||
Forgejo, while migrations will be operating against the database schema that existed when they were
|
||||
authored.
|
||||
- pkg: "forgejo.org/services"
|
||||
desc: >
|
||||
Migrations must not import application services. Application services will reference application
|
||||
models which will use the most recent schema for Forgejo, while migrations will be operating against the
|
||||
database schema that existed when they were authored.
|
||||
allow:
|
||||
- "forgejo.org/models/db"
|
||||
- "forgejo.org/models/gitea_migrations/base"
|
||||
- "forgejo.org/models/gitea_migrations/test"
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
|
|
@ -134,8 +113,6 @@ linters:
|
|||
disable:
|
||||
- error-is-as
|
||||
- go-require
|
||||
nilnil:
|
||||
only-two: false
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
|
|
@ -186,215 +163,6 @@ linters:
|
|||
- linters:
|
||||
- staticcheck
|
||||
text: "(ST1005|ST1003|QF1001):"
|
||||
|
||||
# TODO: eventually remove this section entirely
|
||||
- path: cmd/admin_auth_ldap_test.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: cmd/admin_auth_oauth_test.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: cmd/admin_auth_pam_test.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: cmd/cmd.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: cmd/forgejo/actions.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/actions/run.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/actions/task.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/activities/action_list.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/asymkey/gpg_key_object_verification.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/auth/oauth2.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/db/collation.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/dbfs/dbfile.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/forgejo_migrations_legacy/v32.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/forgejo_migrations_legacy/v32_test.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/db/context.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/git/branch_list.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/git/lfs_lock.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/git/protected_branch.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/git/protected_tag.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/issues/issue.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/issues/issue_xref.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/issues/review.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/organization/org_user.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/quota/rule.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/repo/archiver.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/repo/fork.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/repo/topic.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/user/email_address.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/user/list.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/user/user.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: models/repo/repo.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/git/commit.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/git/foreachref/parser.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/git/last_commit_cache.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/git/log_name_status.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/graceful/net_unix.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/indexer/internal/bleve/util.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/indexer/issues/util.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/optional/serialization.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: modules/setting/storage.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/api/packages/chef/auth.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/api/packages/container/auth.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/api/packages/nuget/auth.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/api/packages/swift/swift.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/web/auth/oauth.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/web/repo/compare.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/web/repo/release.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/web/repo/setting/runners.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/web/repo/setting/secrets.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/web/repo/setting/variables.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/actions/context.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/actions/task.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/actions/trust.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/contexttest/context_tests.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/gitdiff/csv.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/issue/assignee.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: routers/api/packages/conan/auth.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/issue/commit.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/issue/issue.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/migrations/onedev.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/packages/cargo/index.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/pull/check.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/pull/comment.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/pull/merge.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/pull/review.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/remote/promote.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/repository/archiver/archiver.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/repository/generate_repo_commit.go
|
||||
linters:
|
||||
- nilnil
|
||||
- path: services/repository/repository.go
|
||||
linters:
|
||||
- nilnil
|
||||
paths:
|
||||
- node_modules
|
||||
- public
|
||||
|
|
|
|||
17
.mockery.yml
17
.mockery.yml
|
|
@ -1,17 +0,0 @@
|
|||
formatter: gofmt
|
||||
template: testify
|
||||
packages:
|
||||
forgejo.org/modules/nosql:
|
||||
config:
|
||||
filename: mocks.go # make mocks public so that external packages can use
|
||||
forgejo.org/services/auth/method:
|
||||
forgejo.org/services/authz:
|
||||
config:
|
||||
filename: authorization_reducer_mock.go # make mocks public so that external packages can use
|
||||
code.forgejo.org/go-chi/cache:
|
||||
interfaces:
|
||||
Cache:
|
||||
config:
|
||||
pkgname: cache
|
||||
dir: modules/cache
|
||||
filename: mocks.go # make mocks public, not `_test.go`, so that external packages can mock caching
|
||||
|
|
@ -1 +1 @@
|
|||
24.15.0
|
||||
24.12.0
|
||||
|
|
@ -7,8 +7,7 @@ branch-from-version: 'v%[1]d.%[2]d/forgejo'
|
|||
tag-from-version: 'v%[1]d.%[2]d.%[3]d'
|
||||
supported-release-count: 3
|
||||
branch-known:
|
||||
# replace with v15 when v11 becomes EOL
|
||||
- 'v11.0/forgejo'
|
||||
- 'v7.0/forgejo'
|
||||
cleanup-line: 'sed -Ee "s/^(feat|fix):\s*//g" -e "s/^\[WIP\] //" -e "s/^WIP: //" -e "s;\[(UI|BUG|FEAT|v.*?/forgejo)\]\s*;;g"'
|
||||
render-header: |
|
||||
|
||||
|
|
|
|||
|
|
@ -1,111 +0,0 @@
|
|||
rules:
|
||||
- id: forgejo-api-use-resource-SearchRepoOptions
|
||||
patterns:
|
||||
- pattern: |
|
||||
repo_model.SearchRepoOptions{...}
|
||||
- pattern-not: |
|
||||
repo_model.SearchRepoOptions{
|
||||
...,
|
||||
AuthorizationReducer: ctx.Reducer,
|
||||
...
|
||||
}
|
||||
languages:
|
||||
- go
|
||||
message: >
|
||||
SearchRepoOptions does not take into account fine-grained access token limitations. Include the
|
||||
AuthorizationReducer field.
|
||||
severity: ERROR
|
||||
paths:
|
||||
include:
|
||||
- "/routers/api/**/*.go"
|
||||
|
||||
- id: forgejo-api-use-resource-SearchRepoOptions
|
||||
patterns:
|
||||
- pattern: |
|
||||
organization.SearchTeamRepoOptions{...}
|
||||
- pattern-not: |
|
||||
organization.SearchTeamRepoOptions{
|
||||
...,
|
||||
AuthorizationReducer: ctx.Reducer,
|
||||
...
|
||||
}
|
||||
languages:
|
||||
- go
|
||||
message: >
|
||||
SearchTeamRepoOptions does not take into account fine-grained access token limitations. Include the
|
||||
AuthorizationReducer field.
|
||||
severity: ERROR
|
||||
paths:
|
||||
include:
|
||||
- "/routers/api/**/*.go"
|
||||
|
||||
- id: forgejo-api-use-resource-GetUserRepoPermission
|
||||
patterns:
|
||||
- pattern: |
|
||||
$X.GetUserRepoPermission($CTX, $REPO, $DOER)
|
||||
- metavariable-type:
|
||||
metavariable: $CTX
|
||||
types:
|
||||
- "*context.APIContext"
|
||||
languages:
|
||||
- go
|
||||
message: >
|
||||
GetUserRepoPermission does not take into account fine-grained access token limitations. Use
|
||||
GetUserRepoPermissionWithReducer.
|
||||
fix: |
|
||||
$X.GetUserRepoPermissionWithReducer($CTX, $REPO, $DOER, $CTX.Reducer)
|
||||
severity: ERROR
|
||||
|
||||
- id: forgejo-api-suspicious-GetUserRepoPermission
|
||||
patterns:
|
||||
- pattern: $X.GetUserRepoPermission($CTX, $REPO, $DOER)
|
||||
- pattern-not: # don't match if identical to forgejo-api-use-resource-GetUserRepoPermission
|
||||
patterns:
|
||||
- pattern: |
|
||||
$X.GetUserRepoPermission($CTX, $REPO, $DOER)
|
||||
- metavariable-type:
|
||||
metavariable: $CTX
|
||||
types:
|
||||
- "*context.APIContext"
|
||||
languages:
|
||||
- go
|
||||
message: >
|
||||
API code is accessing GetUserRepoPermission which does not take into account fine-grained access token
|
||||
limitations. Should this use GetUserRepoPermissionWithReducer?
|
||||
severity: ERROR
|
||||
paths:
|
||||
include:
|
||||
- "/routers/api/**/*.go"
|
||||
|
||||
- id: forgejo-api-direct-IsAdmin-check
|
||||
patterns:
|
||||
- pattern: |
|
||||
ctx.Doer.IsAdmin
|
||||
languages:
|
||||
- go
|
||||
message: |
|
||||
ctx.Doer.IsAdmin does not take into account limited API access tokens. Use ctx.IsUserSiteAdmin() instead.
|
||||
fix: |
|
||||
ctx.IsUserSiteAdmin()
|
||||
severity: ERROR
|
||||
paths:
|
||||
include:
|
||||
- "/routers/api/**/*.go"
|
||||
|
||||
- id: forgejo-api-direct-repo-Admin-check
|
||||
patterns:
|
||||
- pattern: |
|
||||
ctx.Repo.IsAdmin()
|
||||
- pattern: |
|
||||
ctx.Repo.IsOwner()
|
||||
languages:
|
||||
- go
|
||||
message: |
|
||||
ctx.Repo.IsAdmin/IsOwner() does not take into account limited API access tokens. Use ctx.IsUserRepoAdmin() instead.
|
||||
fix: |
|
||||
ctx.IsUserRepoAdmin()
|
||||
severity: ERROR
|
||||
paths:
|
||||
include:
|
||||
- "/routers/api/**/*.go"
|
||||
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
rules:
|
||||
- id: forgejo-switch-empty-case
|
||||
pattern-either:
|
||||
- pattern: |-
|
||||
switch $_ {
|
||||
case $_:
|
||||
}
|
||||
- patterns:
|
||||
- pattern: |-
|
||||
switch {
|
||||
case $_:
|
||||
}
|
||||
languages:
|
||||
- go
|
||||
severity: ERROR
|
||||
message: >
|
||||
switch has a case block with no content. This is treated as "break" by Go, but developers may confuse it for
|
||||
"fallthrough". To fix this error, disambiguate by using "break" or "fallthrough".
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
rules:
|
||||
- id: forgejo-logic-suspicious-OwnerID-check
|
||||
pattern: |-
|
||||
$X.OwnerID > 0
|
||||
languages:
|
||||
- go
|
||||
severity: ERROR
|
||||
message: >
|
||||
Many resources like comments or runners cannot only be owned by regular users, which have positive IDs, but also
|
||||
by predefined system users like Ghost or Forgejo Actions that have negative IDs. In those cases, ownership checks
|
||||
should only exclude 0: `OwnerID != 0`.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
rules:
|
||||
- id: xorm-sync-missing-ignore-drop-indices
|
||||
patterns:
|
||||
- pattern-either:
|
||||
- pattern: |
|
||||
$X.Sync(...)
|
||||
- pattern: |
|
||||
$X.SyncWithOptions($OPTS, ...)
|
||||
- pattern-not: |
|
||||
$X.SyncWithOptions(xorm.SyncOptions{..., IgnoreDropIndices: true, ...}, ...)
|
||||
- metavariable-type:
|
||||
metavariable: $X
|
||||
types:
|
||||
- "*xorm.Engine"
|
||||
- "*xorm.Session"
|
||||
paths:
|
||||
exclude:
|
||||
- /models/gitea_migrations/**/*.go
|
||||
- /models/forgejo_migrations_legacy/**/*.go
|
||||
languages:
|
||||
- go
|
||||
message: |
|
||||
xorm Sync operation may drop indices if used on an incomplete bean definition for an existing table. Use SyncWithOptions with IgnoreDropIndices: true instead.
|
||||
severity: ERROR
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 The Gitea Authors.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
access_model "forgejo.org/models/perm/access"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
api "forgejo.org/modules/structs"
|
||||
"forgejo.org/routers/api/v1/utils"
|
||||
"forgejo.org/services/context"
|
||||
"forgejo.org/services/convert"
|
||||
)
|
||||
|
||||
// ListForks list a repository's forks
|
||||
func ListForks(ctx *context.APIContext) {
|
||||
forks, total, err := repo_model.GetForks(ctx, ctx.Repo.Repository, ctx.Doer, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetForks", err)
|
||||
return
|
||||
}
|
||||
apiForks := make([]*api.Repository, len(forks))
|
||||
for i, fork := range forks {
|
||||
// ruleid:forgejo-api-use-resource-GetUserRepoPermission
|
||||
permission, err := access_model.GetUserRepoPermissionWithReducer(ctx, fork, ctx.Doer, ctx.Reducer)
|
||||
// ok:forgejo-api-use-resource-GetUserRepoPermission
|
||||
permission, err := access_model.GetUserRepoPermissionWithReducer(ctx, fork, ctx.Doer, ctx.Reducer)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
apiForks[i] = convert.ToRepo(ctx, fork, permission)
|
||||
}
|
||||
}
|
||||
|
||||
// getStarredRepos returns the repos that the user with the specified userID has
|
||||
// starred
|
||||
func getStarredRepos(ctx std_context.Context, user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, error) {
|
||||
starredRepos, err := repo_model.GetStarredRepos(ctx, user.ID, private, listOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repos := make([]*api.Repository, len(starredRepos))
|
||||
for i, starred := range starredRepos {
|
||||
// ruleid:forgejo-api-suspicious-GetUserRepoPermission
|
||||
permission, err := access_model.GetUserRepoPermission(ctx, starred, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repos[i] = convert.ToRepo(ctx, starred, permission)
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2020 The Gitea Authors.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
access_model "forgejo.org/models/perm/access"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
api "forgejo.org/modules/structs"
|
||||
"forgejo.org/routers/api/v1/utils"
|
||||
"forgejo.org/services/context"
|
||||
"forgejo.org/services/convert"
|
||||
)
|
||||
|
||||
// ListForks list a repository's forks
|
||||
func ListForks(ctx *context.APIContext) {
|
||||
forks, total, err := repo_model.GetForks(ctx, ctx.Repo.Repository, ctx.Doer, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetForks", err)
|
||||
return
|
||||
}
|
||||
apiForks := make([]*api.Repository, len(forks))
|
||||
for i, fork := range forks {
|
||||
// ruleid:forgejo-api-use-resource-GetUserRepoPermission
|
||||
permission, err := access_model.GetUserRepoPermission(ctx, fork, ctx.Doer)
|
||||
// ok:forgejo-api-use-resource-GetUserRepoPermission
|
||||
permission, err := access_model.GetUserRepoPermissionWithReducer(ctx, fork, ctx.Doer, ctx.Reducer)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
apiForks[i] = convert.ToRepo(ctx, fork, permission)
|
||||
}
|
||||
}
|
||||
|
||||
// getStarredRepos returns the repos that the user with the specified userID has
|
||||
// starred
|
||||
func getStarredRepos(ctx std_context.Context, user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, error) {
|
||||
starredRepos, err := repo_model.GetStarredRepos(ctx, user.ID, private, listOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repos := make([]*api.Repository, len(starredRepos))
|
||||
for i, starred := range starredRepos {
|
||||
// ruleid:forgejo-api-suspicious-GetUserRepoPermission
|
||||
permission, err := access_model.GetUserRepoPermission(ctx, starred, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repos[i] = convert.ToRepo(ctx, starred, permission)
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
|
||||
"forgejo.org/modules/setting"
|
||||
)
|
||||
|
||||
func setPortEmptyCaseBad(port string) error {
|
||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
|
||||
setting.HTTPPort = port
|
||||
|
||||
// ruleid:forgejo-switch-empty-case
|
||||
switch setting.Protocol {
|
||||
case setting.HTTPUnix:
|
||||
case setting.FCGI:
|
||||
case setting.FCGIUnix:
|
||||
default:
|
||||
defaultLocalURL := string(setting.Protocol) + "://"
|
||||
}
|
||||
|
||||
// ok:forgejo-switch-empty-case
|
||||
switch setting.Protocol {
|
||||
case setting.HTTPUnix:
|
||||
break
|
||||
case setting.FCGI:
|
||||
break
|
||||
case setting.FCGIUnix:
|
||||
break
|
||||
default:
|
||||
defaultLocalURL := string(setting.Protocol) + "://"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import "xorm.io/builder"
|
||||
|
||||
type FindRunJobOptions struct {
|
||||
RepoID int64
|
||||
OwnerID int64
|
||||
}
|
||||
|
||||
func (opts FindRunJobOptions) Bad() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
// ruleid:forgejo-logic-suspicious-OwnerID-check
|
||||
if opts.OwnerID > 0 {
|
||||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func (opts FindRunJobOptions) Good() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
// ok:forgejo-logic-suspicious-OwnerID-check
|
||||
if opts.OwnerID != 0 {
|
||||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package forgejo_migrations
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"forgejo.org/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type ActionUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 `xorm:"INDEX UNIQUE(action_user_index) REFERENCES(user, id)"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(action_user_index) REFERENCES(repository, id)"`
|
||||
|
||||
TrustedWithPullRequests bool
|
||||
|
||||
LastAccess timeutil.TimeStamp `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
func testSyncBad1(x *xorm.Engine) error {
|
||||
// ruleid:xorm-sync-missing-ignore-drop-indices
|
||||
return x.Sync(new(ActionUser))
|
||||
}
|
||||
|
||||
func testSyncBad2(x *xorm.Engine) error {
|
||||
// ruleid:xorm-sync-missing-ignore-drop-indices
|
||||
_, err = x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: false}, bean)
|
||||
return err
|
||||
}
|
||||
|
||||
func testSyncGood1(x *xorm.Engine) error {
|
||||
// ok:xorm-sync-missing-ignore-drop-indices
|
||||
_, err = x.SyncWithOptions(xorm.SyncOptions{IgnoreDropIndices: true}, bean)
|
||||
return err
|
||||
}
|
||||
|
||||
func testSyncGood2(x *fs.File) error {
|
||||
// ok:xorm-sync-missing-ignore-drop-indices
|
||||
_, err = x.Sync()
|
||||
return err
|
||||
}
|
||||
|
|
@ -51,12 +51,3 @@ modules/structs/.* @Cyborus
|
|||
routers/api/v1/.* @Cyborus
|
||||
routers/api/forgejo/.* @Cyborus
|
||||
tests/integration/api_.* @Cyborus
|
||||
|
||||
# Federation code, requires care to be taken with regards to interoperability
|
||||
# and backwards compatibility due to the way signatures work.
|
||||
services/federation/.* @famfo @0xllx0
|
||||
modules/forgefed/.* @famfo @0xllx0
|
||||
models/forgefed/.* @famfo @0xllx0
|
||||
routers/api/v1/activitypub/.* @famfo @0xllx0
|
||||
tests/integration/api_activitypub_.* @famfo @0xllx0
|
||||
tests/integration/activitypub_.* @famfo @0xllx0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
|
||||
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.26-alpine3.23 AS build-env
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.25-alpine3.23 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx
|
||||
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.26-alpine3.23 AS build-env
|
||||
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.25-alpine3.23 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct}
|
||||
|
|
@ -86,8 +86,8 @@ RUN addgroup \
|
|||
-G git \
|
||||
git
|
||||
|
||||
RUN mkdir -p /var/lib/gitea
|
||||
RUN chown git:git /var/lib/gitea
|
||||
RUN mkdir -p /var/lib/gitea /etc/gitea
|
||||
RUN chown git:git /var/lib/gitea /etc/gitea
|
||||
|
||||
COPY --from=build-env /tmp/local /
|
||||
RUN cd /usr/local/bin ; ln -s gitea forgejo
|
||||
|
|
@ -103,9 +103,13 @@ ENV GITEA_CUSTOM=/var/lib/gitea/custom
|
|||
ENV GITEA_TEMP=/tmp/gitea
|
||||
ENV TMPDIR=/tmp/gitea
|
||||
|
||||
# Legacy config file for backwards compatibility
|
||||
# TODO: remove on next major version release
|
||||
ENV GITEA_APP_INI_LEGACY=/etc/gitea/app.ini
|
||||
|
||||
ENV GITEA_APP_INI=${GITEA_CUSTOM}/conf/app.ini
|
||||
ENV HOME="/var/lib/gitea/git"
|
||||
VOLUME ["/var/lib/gitea"]
|
||||
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
||||
WORKDIR /var/lib/gitea
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/docker-entrypoint.sh"]
|
||||
|
|
|
|||
64
Makefile
64
Makefile
|
|
@ -37,18 +37,17 @@ endif
|
|||
XGO_VERSION := go-1.21.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.6.1 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.6.0 # renovate: datasource=go
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.6.2 # renovate: datasource=go
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.2 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1 # renovate: datasource=go
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses/v2@v2.0.1 # renovate: datasource=go
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
|
||||
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.45.0 # renovate: datasource=go
|
||||
ERRORTYPE_PACKAGE ?= fillmore-labs.com/errortype@v0.0.11 # renovate: datasource=go
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@43.170.20 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
MOCKERY_PACKAGE ?= github.com/vektra/mockery/v3@v3.7.0 # renovate: datasource=go
|
||||
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.39.0 # renovate: datasource=go
|
||||
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.6.0 # renovate: datasource=go
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@42.39.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
|
||||
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
|
||||
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
|
||||
|
|
@ -245,7 +244,7 @@ help:
|
|||
@echo " - generate-license update license files"
|
||||
@echo " - generate-gitignore update gitignore files"
|
||||
@echo " - generate-manpage generate manpage"
|
||||
@echo " - generate-mockery generate mockery files"
|
||||
@echo " - generate-gomock generate gomock files"
|
||||
@echo " - generate-forgejo-api generate the forgejo API from spec"
|
||||
@echo " - forgejo-api-validate check if the forgejo API matches the specs"
|
||||
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||
|
|
@ -323,7 +322,7 @@ git-check:
|
|||
node-check:
|
||||
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
|
||||
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | sed 's:-.*::' | tr '.' ' ');))
|
||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
|
||||
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||
echo "Forgejo requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
|
|
@ -466,7 +465,7 @@ lint-swagger: node_modules
|
|||
|
||||
.PHONY: lint-renovate
|
||||
lint-renovate: node_modules
|
||||
npx --yes --package $(RENOVATE_NPM_PACKAGE) -- renovate-config-validator --no-global .forgejo/renovate.json > .lint-renovate 2>&1 || true
|
||||
npx --yes --package $(RENOVATE_NPM_PACKAGE) -- renovate-config-validator > .lint-renovate 2>&1 || true
|
||||
@if grep --quiet --extended-regexp -e '^( ERROR:)' .lint-renovate ; then cat .lint-renovate ; rm .lint-renovate ; exit 1 ; fi
|
||||
@rm .lint-renovate
|
||||
|
||||
|
|
@ -486,23 +485,10 @@ RUN_DEADCODE = $(GO) run $(DEADCODE_PACKAGE) -generated=false -f='{{println .Pat
|
|||
|
||||
.PHONY: lint-go
|
||||
lint-go:
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS) \
|
||||
|| (code=$$?; echo "Please run 'make lint-go-fix' and commit the result"; exit $${code})
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS)
|
||||
$(RUN_DEADCODE) > .cur-deadcode-out
|
||||
@$(DIFF) .deadcode-out .cur-deadcode-out >.deadcode.diff || true
|
||||
@if grep -qE '^[+][^+]' .deadcode.diff ; then \
|
||||
cat .deadcode.diff ; \
|
||||
echo "Looks like you added dead code, please evaluate and remove or use it."; \
|
||||
echo "If you are sure the dead code should stay around, please run 'make lint-go-fix',"; \
|
||||
echo "commit the result and explain the reason in the commit message / PR description."; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if grep -qE '^[-][^-]' .deadcode.diff ; then \
|
||||
cat .deadcode.diff ; \
|
||||
echo "Looks like you removed dead code. Thank you!"; \
|
||||
echo "Run 'make lint-go-fix' and commit the result to accept."; \
|
||||
fi
|
||||
$(GO) run $(ERRORTYPE_PACKAGE) ./...
|
||||
@$(DIFF) .deadcode-out .cur-deadcode-out \
|
||||
|| (code=$$?; echo "Please run 'make lint-go-fix' and commit the result"; exit $${code})
|
||||
|
||||
.PHONY: lint-go-fix
|
||||
lint-go-fix:
|
||||
|
|
@ -534,11 +520,6 @@ security-check:
|
|||
tsc: node_modules
|
||||
npx tsc --noEmit
|
||||
|
||||
# target for PRs to be pushed. Mandatory to succeed in CI
|
||||
.PHONY: pr-go
|
||||
pr-go: deps-backend deps-tools lint-backend tidy-check swagger-check lint-swagger fmt-check swagger-validate
|
||||
TAGS=bindata $(MAKE) backend
|
||||
|
||||
###
|
||||
# Development and testing targets
|
||||
###
|
||||
|
|
@ -562,12 +543,12 @@ test: test-frontend test-backend
|
|||
.PHONY: test-backend
|
||||
test-backend: | compute-go-test-packages
|
||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@TZ=UTC GITEA_ROOT="$(CURDIR)" $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
|
||||
@TZ=UTC $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
|
||||
|
||||
.PHONY: test-remote-cacher
|
||||
test-remote-cacher:
|
||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
GITEA_ROOT="$(CURDIR)" $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_REMOTE_CACHER_PACKAGES)
|
||||
@$(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_REMOTE_CACHER_PACKAGES)
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules
|
||||
|
|
@ -592,7 +573,7 @@ test-check:
|
|||
.PHONY: test\#%
|
||||
test\#%: | compute-go-test-packages
|
||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@TZ=UTC GITEA_ROOT="$(CURDIR)" $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES)
|
||||
@TZ=UTC $(GOTEST) $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES)
|
||||
|
||||
coverage-merge:
|
||||
rm -fr coverage/merged ; mkdir -p coverage/merged
|
||||
|
|
@ -968,13 +949,16 @@ deps-tools:
|
|||
$(GO) install $(XGO_PACKAGE)
|
||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
||||
$(GO) install $(GOVULNCHECK_PACKAGE)
|
||||
$(GO) install $(ERRORTYPE_PACKAGE)
|
||||
$(GO) install $(MOCKERY_PACKAGE)
|
||||
$(GO) install $(GOMOCK_PACKAGE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
@touch node_modules
|
||||
|
||||
.venv: poetry.lock
|
||||
poetry install
|
||||
@touch .venv
|
||||
|
||||
.PHONY: fomantic
|
||||
fomantic:
|
||||
rm -rf $(FOMANTIC_WORK_DIR)/build
|
||||
|
|
@ -1024,9 +1008,9 @@ generate-license:
|
|||
generate-gitignore:
|
||||
$(GO) run build/generate-gitignores.go
|
||||
|
||||
.PHONY: generate-mockery
|
||||
generate-mockery:
|
||||
$(GO) run $(MOCKERY_PACKAGE)
|
||||
.PHONY: generate-gomock
|
||||
generate-gomock:
|
||||
$(GO) run $(GOMOCK_PACKAGE) -package mock -destination ./modules/queue/mock/redisuniversalclient.go forgejo.org/modules/nosql RedisClient
|
||||
|
||||
.PHONY: generate-images
|
||||
generate-images: | node_modules
|
||||
|
|
|
|||
99
assets/go-licenses.json
generated
99
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
|
|
@ -6,12 +6,39 @@ translation_meta.test
|
|||
# this also gets instantiated as a Messenger once
|
||||
repo.migrate.migrating_failed.error
|
||||
|
||||
# models/asymkey/gpg_key_object_verification.go: $ObjectVerification.Reason
|
||||
# unfortunately, it is non-trivial to parse all the occurences
|
||||
gpg.error.extract_sign
|
||||
gpg.error.failed_retrieval_gpg_keys
|
||||
gpg.error.generate_hash
|
||||
gpg.error.no_committer_account
|
||||
|
||||
# models/system/notice.go: func (n *Notice) TrStr() string
|
||||
admin.notices.type_1
|
||||
admin.notices.type_2
|
||||
|
||||
# modules/setting/ui.go
|
||||
themes.names.
|
||||
|
||||
# services/context/context.go
|
||||
relativetime.
|
||||
|
||||
# templates/repo/issue/view_content.tmpl: indirection via $closeTranslationKey
|
||||
repo.issues.close
|
||||
repo.pulls.close
|
||||
|
||||
# templates/repo/issue/view_content/comments.tmpl: indirection via $refTr
|
||||
repo.issues.ref_closing_from
|
||||
repo.issues.ref_issue_from
|
||||
repo.issues.ref_pull_from
|
||||
repo.issues.ref_reopening_from
|
||||
|
||||
# templates/repo/issue/view_content/comments.tmpl: ctx.Locale.Tr (printf "projects.type-%d.display_name" .OldProject.Type)
|
||||
projects.
|
||||
projects.type-1.display_name
|
||||
projects.type-2.display_name
|
||||
projects.type-3.display_name
|
||||
|
||||
# templates/repo/settings/webhook/link_menu.tmpl, templates/webhook/new.tmpl: repo.settings.web_hook_name_
|
||||
# tests/integration/repo_archive_text_test.go
|
||||
repo.settings.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import (
|
|||
"strings"
|
||||
|
||||
llu "forgejo.org/build/lint-locale-usage"
|
||||
lluAsymKey "forgejo.org/models/asymkey/lint-locale-usage"
|
||||
lluUnit "forgejo.org/models/unit/lint-locale-usage"
|
||||
lluMigrate "forgejo.org/services/migrations/lint-locale-usage"
|
||||
)
|
||||
|
|
@ -37,172 +36,142 @@ func HandleGoFile(handler llu.Handler, fname string, src any) error {
|
|||
}
|
||||
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
return HandleGoNode(handler, fset, fname, n)
|
||||
})
|
||||
// search for function calls of the form `anything.Tr(any-string-lit, ...)`
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleGoNode(handler llu.Handler, fset *token.FileSet, fname string, n ast.Node) bool {
|
||||
// search for function calls of the form `anything.Tr(any-string-lit, ...)`
|
||||
|
||||
switch n2 := n.(type) {
|
||||
case *ast.CallExpr:
|
||||
if len(n2.Args) == 0 {
|
||||
return true
|
||||
}
|
||||
funSel, ok := n2.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
ltf, ok := handler.LocaleTrFunctions[funSel.Sel.Name]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
var gotUnexpectedInvoke *int
|
||||
|
||||
for _, argNum := range ltf {
|
||||
if len(n2.Args) <= int(argNum) {
|
||||
argc := len(n2.Args)
|
||||
gotUnexpectedInvoke = &argc
|
||||
} else {
|
||||
handler.HandleGoTrArgument(fset, n2.Args[int(argNum)], "")
|
||||
switch n2 := n.(type) {
|
||||
case *ast.CallExpr:
|
||||
if len(n2.Args) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if gotUnexpectedInvoke != nil {
|
||||
handler.OnUnexpectedInvoke(fset, funSel.Sel.NamePos, funSel.Sel.Name, *gotUnexpectedInvoke)
|
||||
}
|
||||
|
||||
case *ast.CompositeLit:
|
||||
if strings.HasSuffix(fname, "models/unit/unit.go") {
|
||||
lluUnit.HandleCompositeUnit(handler, fset, n2)
|
||||
} else if strings.Contains(fname, "models/asymkey/") {
|
||||
lluAsymKey.HandleCompositeErrorReason(handler, fset, n2)
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
if matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, "llu:returnsTrKeyWeak"); matchInsPrefix != nil {
|
||||
results := n2.Type.Results.List
|
||||
if len(results) != 1 {
|
||||
handler.OnWarning(fset, n2.Type.Func, fmt.Sprintf("function %s has unexpected return type; expected single return value", n2.Name.Name))
|
||||
funSel, ok := n2.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
ast.Inspect(n2.Body, func(n ast.Node) bool {
|
||||
// search for return stmts
|
||||
// TODO: what about nested functions?
|
||||
if ret, ok := n.(*ast.ReturnStmt); ok {
|
||||
for _, res := range ret.Results {
|
||||
ast.Inspect(res, func(n ast.Node) bool {
|
||||
if expr, ok := n.(ast.Expr); ok {
|
||||
handler.HandleGoTrArgument(fset, expr, *matchInsPrefix)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return false
|
||||
ltf, ok := handler.LocaleTrFunctions[funSel.Sel.Name]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
var gotUnexpectedInvoke *int
|
||||
|
||||
for _, argNum := range ltf {
|
||||
if len(n2.Args) <= int(argNum) {
|
||||
argc := len(n2.Args)
|
||||
gotUnexpectedInvoke = &argc
|
||||
} else {
|
||||
handler.HandleGoTrArgument(fset, n2.Args[int(argNum)], "")
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
if matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, "llu:returnsTrKey"); matchInsPrefix != nil {
|
||||
results := n2.Type.Results.List
|
||||
if len(results) != 1 {
|
||||
handler.OnWarning(fset, n2.Type.Func, fmt.Sprintf("function %s has unexpected return type; expected single return value", n2.Name.Name))
|
||||
return true
|
||||
}
|
||||
|
||||
ast.Inspect(n2.Body, func(n ast.Node) bool {
|
||||
// search for return stmts
|
||||
if ret, ok := n.(*ast.ReturnStmt); ok {
|
||||
for _, res := range ret.Results {
|
||||
handler.HandleGoTrArgument(fset, res, *matchInsPrefix)
|
||||
}
|
||||
return false
|
||||
} else if _, ok := n.(*ast.FuncDecl); ok {
|
||||
ast.Inspect(n, func(n2 ast.Node) bool {
|
||||
return HandleGoNode(handler, fset, fname, n2)
|
||||
})
|
||||
// don't search inside nested functions for return stmts
|
||||
return false
|
||||
if gotUnexpectedInvoke != nil {
|
||||
handler.OnUnexpectedInvoke(fset, funSel.Sel.NamePos, funSel.Sel.Name, *gotUnexpectedInvoke)
|
||||
}
|
||||
|
||||
case *ast.CompositeLit:
|
||||
if strings.HasSuffix(fname, "models/unit/unit.go") {
|
||||
lluUnit.HandleCompositeUnit(handler, fset, n2)
|
||||
}
|
||||
|
||||
case *ast.FuncDecl:
|
||||
matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, "llu:returnsTrKey")
|
||||
if matchInsPrefix != nil {
|
||||
results := n2.Type.Results.List
|
||||
if len(results) != 1 {
|
||||
handler.OnWarning(fset, n2.Type.Func, fmt.Sprintf("function %s has unexpected return type; expected single return value", n2.Name.Name))
|
||||
return true
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
if strings.HasSuffix(fname, "services/migrations/migrate.go") {
|
||||
lluMigrate.HandleMessengerInFunc(handler, fset, n2)
|
||||
}
|
||||
return true
|
||||
case *ast.GenDecl:
|
||||
switch n2.Tok {
|
||||
case token.CONST, token.VAR:
|
||||
matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, " llu:TrKeys")
|
||||
if matchInsPrefix == nil {
|
||||
return true
|
||||
}
|
||||
for _, spec := range n2.Specs {
|
||||
// interpret all contained strings as message IDs
|
||||
ast.Inspect(spec, func(n ast.Node) bool {
|
||||
if argLit, ok := n.(*ast.BasicLit); ok {
|
||||
handler.HandleGoTrBasicLit(fset, argLit, *matchInsPrefix)
|
||||
ast.Inspect(n2.Body, func(n ast.Node) bool {
|
||||
// search for return stmts
|
||||
// TODO: what about nested functions?
|
||||
if ret, ok := n.(*ast.ReturnStmt); ok {
|
||||
for _, res := range ret.Results {
|
||||
ast.Inspect(res, func(n ast.Node) bool {
|
||||
if expr, ok := n.(ast.Expr); ok {
|
||||
handler.HandleGoTrArgument(fset, expr, *matchInsPrefix)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
case token.TYPE:
|
||||
// modules/web/middleware/binding.go:Validate uses the convention that structs
|
||||
// entries can have tags.
|
||||
// In particular, `locale:$msgid` should be handled; any fields with `form:-` shouldn't.
|
||||
// Problem: we don't know which structs are forms, actually.
|
||||
|
||||
for _, spec := range n2.Specs {
|
||||
tspec := spec.(*ast.TypeSpec)
|
||||
structNode, ok := tspec.Type.(*ast.StructType)
|
||||
if !ok || !(strings.HasSuffix(tspec.Name.Name, "Form") ||
|
||||
(tspec.Doc != nil &&
|
||||
slices.ContainsFunc(tspec.Doc.List, func(c *ast.Comment) bool {
|
||||
return c.Text == "// swagger:model"
|
||||
}))) {
|
||||
continue
|
||||
if strings.HasSuffix(fname, "services/migrations/migrate.go") {
|
||||
lluMigrate.HandleMessengerInFunc(handler, fset, n2)
|
||||
}
|
||||
return true
|
||||
case *ast.GenDecl:
|
||||
switch n2.Tok {
|
||||
case token.CONST, token.VAR:
|
||||
matchInsPrefix := handler.HandleGoCommentGroup(fset, n2.Doc, " llu:TrKeys")
|
||||
if matchInsPrefix == nil {
|
||||
return true
|
||||
}
|
||||
for _, field := range structNode.Fields.List {
|
||||
if field.Names == nil {
|
||||
for _, spec := range n2.Specs {
|
||||
// interpret all contained strings as message IDs
|
||||
ast.Inspect(spec, func(n ast.Node) bool {
|
||||
if argLit, ok := n.(*ast.BasicLit); ok {
|
||||
handler.HandleGoTrBasicLit(fset, argLit, *matchInsPrefix)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
case token.TYPE:
|
||||
// modules/web/middleware/binding.go:Validate uses the convention that structs
|
||||
// entries can have tags.
|
||||
// In particular, `locale:$msgid` should be handled; any fields with `form:-` shouldn't.
|
||||
// Problem: we don't know which structs are forms, actually.
|
||||
|
||||
for _, spec := range n2.Specs {
|
||||
tspec := spec.(*ast.TypeSpec)
|
||||
structNode, ok := tspec.Type.(*ast.StructType)
|
||||
if !ok || !(strings.HasSuffix(tspec.Name.Name, "Form") ||
|
||||
(tspec.Doc != nil &&
|
||||
slices.ContainsFunc(tspec.Doc.List, func(c *ast.Comment) bool {
|
||||
return c.Text == "// swagger:model"
|
||||
}))) {
|
||||
continue
|
||||
}
|
||||
if len(field.Names) != 1 {
|
||||
handler.OnWarning(fset, field.Type.Pos(), "unsupported multiple field names")
|
||||
continue
|
||||
}
|
||||
msgidPos := field.Names[0].NamePos
|
||||
msgid := "form." + field.Names[0].Name
|
||||
if field.Tag != nil && field.Tag.Kind == token.STRING {
|
||||
rawTag, err := strconv.Unquote(field.Tag.Value)
|
||||
if err != nil {
|
||||
handler.OnWarning(fset, field.Tag.ValuePos, "invalid tag value encountered")
|
||||
for _, field := range structNode.Fields.List {
|
||||
if field.Names == nil {
|
||||
continue
|
||||
}
|
||||
tag := reflect.StructTag(rawTag)
|
||||
if tag.Get("form") == "-" {
|
||||
if len(field.Names) != 1 {
|
||||
handler.OnWarning(fset, field.Type.Pos(), "unsupported multiple field names")
|
||||
continue
|
||||
}
|
||||
tmp := tag.Get("locale")
|
||||
if len(tmp) != 0 {
|
||||
msgidPos = field.Tag.ValuePos
|
||||
msgid = tmp
|
||||
msgidPos := field.Names[0].NamePos
|
||||
msgid := "form." + field.Names[0].Name
|
||||
if field.Tag != nil && field.Tag.Kind == token.STRING {
|
||||
rawTag, err := strconv.Unquote(field.Tag.Value)
|
||||
if err != nil {
|
||||
handler.OnWarning(fset, field.Tag.ValuePos, "invalid tag value encountered")
|
||||
continue
|
||||
}
|
||||
tag := reflect.StructTag(rawTag)
|
||||
if tag.Get("form") == "-" {
|
||||
continue
|
||||
}
|
||||
tmp := tag.Get("locale")
|
||||
if len(tmp) != 0 {
|
||||
msgidPos = field.Tag.ValuePos
|
||||
msgid = tmp
|
||||
}
|
||||
}
|
||||
handler.OnMsgid(fset, msgidPos, msgid, true)
|
||||
}
|
||||
handler.OnMsgid(fset, msgidPos, msgid, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return true
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import (
|
|||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
|
|
@ -45,57 +44,12 @@ type StringTrie interface {
|
|||
|
||||
type StringTrieMap map[string]StringTrie
|
||||
|
||||
func printfPatternToRegex(key string) (string, bool) {
|
||||
parts := strings.Split(key, "%")
|
||||
if len(parts) < 2 {
|
||||
return key, false
|
||||
}
|
||||
var pattern strings.Builder
|
||||
pattern.WriteString("^")
|
||||
pattern.WriteString(parts[0])
|
||||
skip := false
|
||||
for _, part := range parts[1:] {
|
||||
if skip {
|
||||
skip = false
|
||||
continue
|
||||
}
|
||||
if len(part) == 0 {
|
||||
// "%%"
|
||||
pattern.WriteString("%")
|
||||
continue
|
||||
}
|
||||
switch part[0] {
|
||||
case 'd':
|
||||
pattern.WriteString("[0-9]+")
|
||||
default:
|
||||
pattern.WriteString("[A-Za-z0-9]*")
|
||||
}
|
||||
pattern.WriteString(part[1:])
|
||||
}
|
||||
pattern.WriteString("$")
|
||||
return pattern.String(), true
|
||||
}
|
||||
|
||||
func (m StringTrieMap) Matches(key []string) bool {
|
||||
if len(key) == 0 || m == nil {
|
||||
return true
|
||||
}
|
||||
value, ok := m[key[0]]
|
||||
if !ok {
|
||||
for altKey, value := range m {
|
||||
// TODO: cache mapping $printfFormatString -> $regexpCompileOutput
|
||||
pattern, found := printfPatternToRegex(altKey)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
matched, err := regexp.MatchString(pattern, key[0])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unable to compile regexp '%s': %s", pattern, err.Error()))
|
||||
}
|
||||
if matched && (value == nil || value.Matches(key[1:])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if value == nil {
|
||||
|
|
@ -147,7 +101,7 @@ func ParseAllowedMaskedUsages(fname string, usedMsgids container.Set[string], al
|
|||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
if linePrefix, found := strings.CutSuffix(line, "."); found || strings.Contains(line, "%") {
|
||||
if linePrefix, found := strings.CutSuffix(line, "."); found {
|
||||
allowedMaskedPrefixes.Insert(strings.Split(linePrefix, "."))
|
||||
} else {
|
||||
if !chkMsgid(line) {
|
||||
|
|
@ -191,14 +145,9 @@ func Usage() {
|
|||
|
||||
fmt.Fprintf(outp, "\nSpecial Go doc comments:\n")
|
||||
for _, i := range []string{
|
||||
"//llu:returnsTrKeyWeak",
|
||||
"\tcan be used in front of functions to indicate",
|
||||
"\tthat the function returns message IDs (allows nesting inside complicated function calls)",
|
||||
"\tWARNING: this currently doesn't support nested functions properly",
|
||||
"",
|
||||
"//llu:returnsTrKey",
|
||||
"\tcan be used in front of functions to indicate",
|
||||
"\tthat the function returns message IDs (doesn't allow nesting inside complicated function calls)",
|
||||
"\tthat the function returns message IDs",
|
||||
"\tWARNING: this currently doesn't support nested functions properly",
|
||||
"",
|
||||
"//llu:returnsTrKeySuffix prefix.",
|
||||
|
|
@ -311,10 +260,6 @@ func main() {
|
|||
}
|
||||
|
||||
handler := llu.Handler{
|
||||
OnMsgidPattern: func(fset *token.FileSet, pos token.Pos, msgidPattern string) {
|
||||
msgidPatternSplit := strings.Split(msgidPattern, ".")
|
||||
allowedMaskedPrefixes.Insert(msgidPatternSplit)
|
||||
},
|
||||
OnMsgidPrefix: func(fset *token.FileSet, pos token.Pos, msgidPrefix string, truncated bool) {
|
||||
msgidPrefixSplit := strings.Split(msgidPrefix, ".")
|
||||
if !truncated {
|
||||
|
|
@ -325,10 +270,6 @@ func main() {
|
|||
}
|
||||
},
|
||||
OnMsgid: func(fset *token.FileSet, pos token.Pos, msgid string, weak bool) {
|
||||
if strings.Contains(msgid, "%") {
|
||||
fmt.Printf("%s:\tunexpected msgid pattern: %s\n", fset.Position(pos).String(), msgid)
|
||||
return
|
||||
}
|
||||
if !msgids.Contains(msgid) {
|
||||
if weak && allowWeakMissingMsgids {
|
||||
return
|
||||
|
|
@ -361,7 +302,7 @@ func main() {
|
|||
if name == "docker" || name == ".git" || name == "node_modules" {
|
||||
return fs.SkipDir
|
||||
}
|
||||
} else if name == "bindata.go" || fpath == "modules/translation/i18n/i18n_test.go" || fpath == "modules/translation/i18n/i18n_ini_test.go" {
|
||||
} else if name == "bindata.go" || fpath == "modules/translation/i18n/i18n_test.go" {
|
||||
// skip false positives
|
||||
} else if strings.HasSuffix(name, ".go") {
|
||||
onError(HandleGoFile(handler, fpath, nil))
|
||||
|
|
|
|||
|
|
@ -34,14 +34,12 @@ func (handler Handler) HandleGoTrBasicLit(fset *token.FileSet, argLit *ast.Basic
|
|||
}
|
||||
|
||||
func (handler Handler) HandleGoTrArgument(fset *token.FileSet, n ast.Expr, prefix string) {
|
||||
switch n := n.(type) {
|
||||
case *ast.BasicLit:
|
||||
handler.HandleGoTrBasicLit(fset, n, prefix)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
if n.Op != token.ADD {
|
||||
if argLit, ok := n.(*ast.BasicLit); ok {
|
||||
handler.HandleGoTrBasicLit(fset, argLit, prefix)
|
||||
} else if argBinExpr, ok := n.(*ast.BinaryExpr); ok {
|
||||
if argBinExpr.Op != token.ADD {
|
||||
// pass
|
||||
} else if argLit, ok := n.X.(*ast.BasicLit); ok && argLit.Kind == token.STRING {
|
||||
} else if argLit, ok := argBinExpr.X.(*ast.BasicLit); ok && argLit.Kind == token.STRING {
|
||||
// extract string content
|
||||
arg, err := strconv.Unquote(argLit.Value)
|
||||
if err != nil {
|
||||
|
|
@ -55,39 +53,6 @@ func (handler Handler) HandleGoTrArgument(fset *token.FileSet, n ast.Expr, prefi
|
|||
}
|
||||
handler.OnMsgidPrefix(fset, argLit.ValuePos, prep, trunc)
|
||||
}
|
||||
|
||||
case *ast.CallExpr:
|
||||
if selExpr, ok := n.Fun.(*ast.SelectorExpr); ok {
|
||||
if xIdent, xok := selExpr.X.(*ast.Ident); !xok || xIdent.Name != "fmt" {
|
||||
return
|
||||
}
|
||||
if selExpr.Sel.Name != "Sprintf" {
|
||||
handler.OnWarning(fset, selExpr.Sel.NamePos, fmt.Sprintf("unexpected formatting function encountered: %s", selExpr.Sel.Name))
|
||||
return
|
||||
}
|
||||
if len(n.Args) == 0 {
|
||||
handler.OnWarning(fset, selExpr.Sel.NamePos, fmt.Sprintf("unexpected formatting function invocation (no arguments) of '%s'", selExpr.Sel.Name))
|
||||
return
|
||||
}
|
||||
|
||||
if argLit, ok := n.Args[0].(*ast.BasicLit); ok && argLit.Kind == token.STRING {
|
||||
// extract string content
|
||||
arg, err := strconv.Unquote(argLit.Value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if strings.Contains(arg, " ") {
|
||||
handler.OnWarning(fset, argLit.ValuePos, fmt.Sprintf(
|
||||
"formatting function invocation of '%s' with weird msgid format string: %s",
|
||||
selExpr.Sel.Name,
|
||||
arg,
|
||||
))
|
||||
return
|
||||
}
|
||||
// found interesting strings
|
||||
handler.OnMsgidPattern(fset, argLit.ValuePos, prefix+arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,13 +60,9 @@ func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.N
|
|||
|
||||
case tmplParser.NodeField:
|
||||
nodeField := nodeCommand.Args[0].(*tmplParser.FieldNode)
|
||||
if len(nodeField.Ident) != 2 || nodeField.Ident[0] != "locale" {
|
||||
if len(nodeField.Ident) != 2 || !(nodeField.Ident[0] == "locale" || nodeField.Ident[0] == "Locale") {
|
||||
return
|
||||
}
|
||||
resolvedPos := fset.PositionFor(token.Pos(nodeCommand.Pos), false)
|
||||
if !strings.Contains(resolvedPos.Filename, "templates/mail/") {
|
||||
handler.OnWarning(fset, token.Pos(nodeCommand.Pos), "encountered unexpected .locale usage")
|
||||
}
|
||||
funcname = nodeField.Ident[1]
|
||||
|
||||
case tmplParser.NodeVariable:
|
||||
|
|
@ -150,12 +146,16 @@ func (handler Handler) handleTemplateMsgid(fset *token.FileSet, node tmplParser.
|
|||
handler.OnMsgid(fset, stringPos, msgidPrefix, false)
|
||||
} else {
|
||||
if nodeIdent.Ident == "printf" {
|
||||
// found interesting strings
|
||||
if !(strings.HasSuffix(msgidPrefix, ".%s") && strings.Count(msgidPrefix, "%") == 1) {
|
||||
handler.OnMsgidPattern(fset, stringPos, msgidPrefix)
|
||||
parts := strings.SplitN(msgidPrefix, "%", 2)
|
||||
if len(parts) != 2 {
|
||||
handler.OnWarning(
|
||||
fset,
|
||||
stringPos,
|
||||
fmt.Sprintf("unsupported invocation of locate function (format string doesn't match \"prefix%%smth\" pattern): %s", nodeString.String()),
|
||||
)
|
||||
return
|
||||
}
|
||||
msgidPrefix = strings.TrimSuffix(msgidPrefix, "%s")
|
||||
msgidPrefix = parts[0]
|
||||
}
|
||||
|
||||
msgidPrefixFin, truncated := PrepareMsgidPrefix(msgidPrefix)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ func InitLocaleTrFunctions() map[string][]uint {
|
|||
type Handler struct {
|
||||
OnMsgid func(fset *token.FileSet, pos token.Pos, msgid string, weak bool)
|
||||
OnMsgidPrefix func(fset *token.FileSet, pos token.Pos, msgidPrefix string, truncated bool)
|
||||
OnMsgidPattern func(fset *token.FileSet, pos token.Pos, msgidPattern string)
|
||||
OnUnexpectedInvoke func(fset *token.FileSet, pos token.Pos, funcname string, argc int)
|
||||
OnWarning func(fset *token.FileSet, pos token.Pos, msg string)
|
||||
LocaleTrFunctions map[string][]uint
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ func subcmdRegenerate() *cli.Command {
|
|||
Name: "regenerate",
|
||||
Usage: "Regenerate specific files",
|
||||
Commands: []*cli.Command{
|
||||
microcmdRegenHooks,
|
||||
microcmdRegenKeys,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,36 @@ import (
|
|||
"context"
|
||||
|
||||
asymkey_model "forgejo.org/models/asymkey"
|
||||
"forgejo.org/modules/graceful"
|
||||
repo_service "forgejo.org/services/repository"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
var microcmdRegenKeys = &cli.Command{
|
||||
Name: "keys",
|
||||
Usage: "Regenerate authorized_keys file",
|
||||
Before: noDanglingArgs,
|
||||
Action: runRegenerateKeys,
|
||||
var (
|
||||
microcmdRegenHooks = &cli.Command{
|
||||
Name: "hooks",
|
||||
Usage: "Regenerate git-hooks",
|
||||
Before: noDanglingArgs,
|
||||
Action: runRegenerateHooks,
|
||||
}
|
||||
|
||||
microcmdRegenKeys = &cli.Command{
|
||||
Name: "keys",
|
||||
Usage: "Regenerate authorized_keys file",
|
||||
Before: noDanglingArgs,
|
||||
Action: runRegenerateKeys,
|
||||
}
|
||||
)
|
||||
|
||||
func runRegenerateHooks(ctx context.Context, c *cli.Command) error {
|
||||
ctx, cancel := installSignals(ctx)
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||
}
|
||||
|
||||
func runRegenerateKeys(ctx context.Context, c *cli.Command) error {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ func subcmdUser() *cli.Command {
|
|||
microcmdUserChangePassword(),
|
||||
microcmdUserDelete(),
|
||||
microcmdUserGenerateAccessToken(),
|
||||
microcmdUserCreateAuthorizedIntegration(),
|
||||
microcmdUserMustChangePassword(),
|
||||
microcmdUserResetMFA(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -205,15 +205,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
|
|||
|
||||
// create the access token
|
||||
if accessTokenScope != "" {
|
||||
t := &auth_model.AccessToken{
|
||||
Name: accessTokenName,
|
||||
UID: u.ID,
|
||||
Scope: accessTokenScope,
|
||||
|
||||
// maintain legacy behaviour until new CLI options are added -- token has access to all resources, is not
|
||||
// fine-grained
|
||||
ResourceAllRepos: true,
|
||||
}
|
||||
t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
|
||||
if err := auth_model.NewAccessToken(ctx, t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,10 +86,6 @@ func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
|
|||
}
|
||||
t.Scope = accessTokenScope
|
||||
|
||||
// maintain legacy behaviour until new CLI options are added -- token has access to all resources, is not
|
||||
// fine-grained
|
||||
t.ResourceAllRepos = true
|
||||
|
||||
// create the token
|
||||
if err := auth_model.NewAccessToken(ctx, t); err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -1,270 +0,0 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/repo"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/json"
|
||||
"forgejo.org/services/authz"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func microcmdUserCreateAuthorizedIntegration() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "create-authorized-integration",
|
||||
Description: `Creates an authorized integration. Authorized integrations allow Forgejo to
|
||||
receive JWTs from external sources, validate their claims against
|
||||
user-defined rules, and grant access to Forgejo's API on behalf of a user.
|
||||
|
||||
The issuer may be set to "urn:forgejo:authorized-integrations:actions"
|
||||
to support JWTs from the local instance's Forgejo Actions, utilizing the
|
||||
enable-openid-connect flag in a workflow.`,
|
||||
|
||||
// `--claim-in sub=v1,v2,v3` needs to be parsed as a single parameter so that we can comma-split the value into
|
||||
// an array. To accomplish this, we disable urfave 's slice flag separator, which would cause this to be
|
||||
// treated as "sub=v1", "v2=?", and "v3=?", resulting in an error of missing values.
|
||||
DisableSliceFlagSeparator: true,
|
||||
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "username",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "Username",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "Name of the authorized integration for later identification",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "description",
|
||||
Usage: "Optional description for the authorized integration",
|
||||
},
|
||||
|
||||
// JWT validation:
|
||||
&cli.StringFlag{
|
||||
Name: "issuer",
|
||||
Usage: `JWT issuer ('iss' claim), example: https://forgejo.example.org/api/actions`,
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringMapFlag{
|
||||
Name: "claim-eq",
|
||||
Value: map[string]string{},
|
||||
Usage: `Zero-or-more claim equality checks, formatted as claim=value, example: "actor=someuser"`,
|
||||
},
|
||||
&cli.StringMapFlag{
|
||||
Name: "claim-in",
|
||||
Value: map[string]string{},
|
||||
Usage: `Zero-or-more claim equality in list checks, formatted as claim=value1,value2,... example: "actor=user1,user2"`,
|
||||
},
|
||||
&cli.StringMapFlag{
|
||||
Name: "claim-glob",
|
||||
Value: map[string]string{},
|
||||
Usage: `Zero-or-more claim glob checks, formatted as claim=value, example: "sub=repo:forgejo/*:pull_request"`,
|
||||
},
|
||||
&cli.StringMapFlag{
|
||||
Name: "claim-glob-in",
|
||||
Value: map[string]string{},
|
||||
Usage: `Zero-or-more claim glob in list checks, formatted as claim=va*ue1,va*ue2,... example: "sub=repo:*/*:pull_request,repo:*/*:refs:*"`,
|
||||
},
|
||||
// nested claim support omitted for now -- pretty complex for a CLI
|
||||
|
||||
// Permissions available on successful auth:
|
||||
&cli.StringSliceFlag{
|
||||
Name: "scope",
|
||||
Value: []string{"all"},
|
||||
Usage: `One-or-more scopes to apply to access token, examples: "all", "read:issue", "write:repository"`,
|
||||
},
|
||||
&cli.StringSliceFlag{
|
||||
Name: "repo",
|
||||
Value: []string{"all"},
|
||||
Usage: `Zero-or-more specific repositories that can be accessed, or "all" to allow access to all repositories, example: "owner1/repo1"`,
|
||||
},
|
||||
},
|
||||
Before: noDanglingArgs,
|
||||
Action: runCreateAuthorizedIntegration,
|
||||
}
|
||||
}
|
||||
|
||||
func runCreateAuthorizedIntegration(ctx context.Context, c *cli.Command) error {
|
||||
if !c.IsSet("username") {
|
||||
return errors.New("you must provide a username to generate a token for")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals(ctx)
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := user_model.GetUserByName(ctx, c.String("username"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ai := &auth_model.AuthorizedIntegration{
|
||||
UserID: user.ID,
|
||||
Name: c.String("name"),
|
||||
Description: c.String("description"),
|
||||
UI: auth_model.AuthorizedIntegrationUIGeneric,
|
||||
}
|
||||
|
||||
var rules []auth_model.ClaimRule
|
||||
ai.Issuer = c.String("issuer")
|
||||
for claim, value := range c.StringMap("claim-eq") {
|
||||
rules = append(rules, auth_model.ClaimRule{
|
||||
Claim: claim,
|
||||
Comparison: auth_model.ClaimEqual,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
for claim, value := range c.StringMap("claim-in") {
|
||||
values := []string{}
|
||||
for s := range strings.SplitSeq(value, ",") {
|
||||
values = append(values, strings.TrimSpace(s))
|
||||
}
|
||||
rules = append(rules, auth_model.ClaimRule{
|
||||
Claim: claim,
|
||||
Comparison: auth_model.ClaimIn,
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
for claim, value := range c.StringMap("claim-glob") {
|
||||
rules = append(rules, auth_model.ClaimRule{
|
||||
Claim: claim,
|
||||
Comparison: auth_model.ClaimGlob,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
for claim, value := range c.StringMap("claim-glob-in") {
|
||||
values := []string{}
|
||||
for s := range strings.SplitSeq(value, ",") {
|
||||
values = append(values, strings.TrimSpace(s))
|
||||
}
|
||||
rules = append(rules, auth_model.ClaimRule{
|
||||
Claim: claim,
|
||||
Comparison: auth_model.ClaimGlobIn,
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
ai.ClaimRules = &auth_model.ClaimRules{Rules: rules}
|
||||
|
||||
scopes := strings.Join(c.StringSlice("scope"), ",")
|
||||
accessTokenScope, err := auth_model.AccessTokenScope(scopes).Normalize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid access token scope provided: %w", err)
|
||||
}
|
||||
ai.Scope = accessTokenScope
|
||||
|
||||
allRepos := false
|
||||
repos := []*repo.Repository{}
|
||||
for _, repoName := range c.StringSlice("repo") {
|
||||
if repoName == "all" {
|
||||
allRepos = true
|
||||
} else {
|
||||
split := strings.Split(repoName, "/")
|
||||
if len(split) != 2 {
|
||||
return fmt.Errorf("invalid repo name: %q", split)
|
||||
}
|
||||
owner := split[0]
|
||||
name := split[1]
|
||||
repo, err := repo.GetRepositoryByOwnerAndName(ctx, owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repos = append(repos, repo)
|
||||
}
|
||||
}
|
||||
ai.ResourceAllRepos = allRepos
|
||||
|
||||
rr := make([]*auth_model.AuthorizedIntegResourceRepo, len(repos))
|
||||
for i := range repos {
|
||||
rr[i] = &auth_model.AuthorizedIntegResourceRepo{RepoID: repos[i].ID}
|
||||
}
|
||||
if err := authz.ValidateAuthorizedIntegration(ai, rr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := auth_model.InsertAuthorizedIntegration(ctx, ai); err != nil {
|
||||
return err
|
||||
}
|
||||
if !allRepos {
|
||||
if err := auth_model.InsertAuthorizedIntegrationResourceRepos(ctx, ai.ID, rr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type ClaimRuleDescription struct {
|
||||
Description string `json:"description"`
|
||||
Claim string `json:"claim"`
|
||||
Comparison auth_model.ClaimComparison `json:"compare"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Values []string `json:"values,omitempty"`
|
||||
}
|
||||
output := struct {
|
||||
Message string `json:"message"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Issuer string `json:"issuer"`
|
||||
Audience string `json:"audience"`
|
||||
ClaimRules []ClaimRuleDescription `json:"claim_rules"`
|
||||
}{
|
||||
Message: "Authorized integration was successfully created.",
|
||||
Name: ai.Name,
|
||||
Description: ai.Description,
|
||||
Issuer: ai.Issuer,
|
||||
Audience: ai.Audience,
|
||||
}
|
||||
for _, cr := range ai.ClaimRules.Rules {
|
||||
var description string
|
||||
switch cr.Comparison {
|
||||
case auth_model.ClaimEqual:
|
||||
description = fmt.Sprintf("%q = %q", cr.Claim, cr.Value)
|
||||
case auth_model.ClaimIn:
|
||||
description = fmt.Sprintf("%q in %q", cr.Claim, cr.Values)
|
||||
case auth_model.ClaimGlob:
|
||||
description = fmt.Sprintf("%q matches %q", cr.Claim, cr.Value)
|
||||
case auth_model.ClaimGlobIn:
|
||||
description = fmt.Sprintf("%q matches in %q", cr.Claim, cr.Values)
|
||||
}
|
||||
output.ClaimRules = append(output.ClaimRules, ClaimRuleDescription{
|
||||
Description: description,
|
||||
Claim: cr.Claim,
|
||||
Comparison: cr.Comparison,
|
||||
Value: cr.Value,
|
||||
Values: cr.Values,
|
||||
})
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var indent bytes.Buffer
|
||||
if err := json.Indent(&indent, raw, "", " "); err != nil {
|
||||
return err
|
||||
}
|
||||
os.Stdout.Write(indent.Bytes())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -150,8 +150,8 @@ func runCert(ctx context.Context, c *cli.Command) error {
|
|||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
hosts := strings.SplitSeq(c.String("host"), ",")
|
||||
for h := range hosts {
|
||||
hosts := strings.Split(c.String("host"), ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
|
|
|
|||
22
cmd/cmd.go
22
cmd/cmd.go
|
|
@ -1,5 +1,4 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package cmd provides subcommands to the gitea binary - such as "web" or
|
||||
|
|
@ -90,9 +89,26 @@ If this is the intended configuration file complete the [database] section.`, se
|
|||
return nil
|
||||
}
|
||||
|
||||
// installSignals returns a context that's cancelled on the SIGINT and SIGTERM signals or if the passed ctx is cancelled.
|
||||
func installSignals(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||
return signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
go func() {
|
||||
// install notify
|
||||
signalChannel := make(chan os.Signal, 1)
|
||||
|
||||
signal.Notify(
|
||||
signalChannel,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
)
|
||||
select {
|
||||
case <-signalChannel:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
cancel()
|
||||
signal.Reset()
|
||||
}()
|
||||
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_installSignals(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skipf("Windows does not terminate in an awaitable manner")
|
||||
return
|
||||
}
|
||||
|
||||
for _, s := range []syscall.Signal{syscall.SIGTERM, syscall.SIGINT} {
|
||||
t.Run(fmt.Sprintf("Context is terminated on %s", s), func(t *testing.T) {
|
||||
// Register the signal handler. context.Background() is chosen deliberately,
|
||||
// because unlike t.Context(), we can be sure that it's not cancelled by a
|
||||
// different handler.
|
||||
ctx, cancel := installSignals(context.Background())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
// Send the signal in the background.
|
||||
go syscall.Kill(syscall.Getpid(), s)
|
||||
|
||||
select {
|
||||
case <-time.Tick(time.Second * 10):
|
||||
t.Fatalf("Context not cancelled via signal after 10 seconds")
|
||||
case <-ctx.Done():
|
||||
t.Logf("Context was cancelled")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
42
cmd/dump.go
42
cmd/dump.go
|
|
@ -12,7 +12,6 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -84,9 +83,11 @@ func (o outputType) Join() string {
|
|||
}
|
||||
|
||||
func (o *outputType) Set(value string) error {
|
||||
if slices.Contains(o.Enum, value) {
|
||||
o.selected = value
|
||||
return nil
|
||||
for _, enum := range o.Enum {
|
||||
if enum == value {
|
||||
o.selected = value
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("allowed values are %s", o.Join())
|
||||
|
|
@ -112,10 +113,7 @@ func getArchiverByType(outType string) (archives.ArchiverAsync, error) {
|
|||
var archiver archives.ArchiverAsync
|
||||
switch outType {
|
||||
case "zip":
|
||||
archiver = archives.Zip{
|
||||
Compression: 8,
|
||||
SelectiveCompression: false,
|
||||
}
|
||||
archiver = archives.Zip{}
|
||||
case "tar":
|
||||
archiver = archives.Tar{}
|
||||
case "tar.sz":
|
||||
|
|
@ -252,8 +250,8 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error {
|
|||
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
|
||||
} else {
|
||||
for _, suffix := range outputTypeEnum.Enum {
|
||||
if before, ok := strings.CutSuffix(fileName, "."+suffix); ok {
|
||||
fileName = before
|
||||
if strings.HasSuffix(fileName, "."+suffix) {
|
||||
fileName = strings.TrimSuffix(fileName, "."+suffix)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -332,12 +330,14 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error {
|
|||
go dumpDatabase(ctx, archiveJobs, &wg, verbose)
|
||||
|
||||
if len(setting.CustomConf) > 0 {
|
||||
wg.Go(func() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
||||
if err := addFile(archiveJobs, "app.ini", setting.CustomConf, verbose); err != nil {
|
||||
fatal("Failed to include specified app.ini: %v", err)
|
||||
}
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
|
||||
|
|
@ -361,13 +361,15 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error {
|
|||
if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
|
||||
log.Info("Skipping attachment data")
|
||||
} else {
|
||||
wg.Go(func() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
|
||||
return addObject(archiveJobs, object, path.Join("data", "attachments", objPath), verbose)
|
||||
}); err != nil {
|
||||
fatal("Failed to dump attachments: %v", err)
|
||||
}
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
|
||||
|
|
@ -375,13 +377,15 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error {
|
|||
} else if !setting.Packages.Enabled {
|
||||
log.Info("Package registry not enabled - skipping")
|
||||
} else {
|
||||
wg.Go(func() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := storage.Packages.IterateObjects("", func(objPath string, object storage.Object) error {
|
||||
return addObject(archiveJobs, object, path.Join("data", "packages", objPath), verbose)
|
||||
}); err != nil {
|
||||
fatal("Failed to dump packages: %v", err)
|
||||
}
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
||||
|
|
@ -395,11 +399,13 @@ func runDump(stdCtx context.Context, ctx *cli.Command) error {
|
|||
log.Error("Failed to check if %s exists: %v", setting.Log.RootPath, err)
|
||||
}
|
||||
if isExist {
|
||||
wg.Go(func() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := addRecursiveExclude(archiveJobs, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil {
|
||||
fatal("Failed to include log: %v", err)
|
||||
}
|
||||
})
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,8 +143,8 @@ func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error {
|
|||
opts.PullRequests = true
|
||||
opts.ReleaseAssets = true
|
||||
} else {
|
||||
units := strings.SplitSeq(ctx.String("units"), ",")
|
||||
for unit := range units {
|
||||
units := strings.Split(ctx.String("units"), ",")
|
||||
for _, unit := range units {
|
||||
switch strings.ToLower(strings.TrimSpace(unit)) {
|
||||
case "":
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -102,11 +102,6 @@ func SubcmdActionsRegister(ctx context.Context) *cli.Command {
|
|||
Value: "",
|
||||
Usage: "version of the runner (not required since v1.21)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "ephemeral",
|
||||
Value: false,
|
||||
Usage: "instruct Forgejo to permanently unregister this runner after it has run one job",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +172,6 @@ func RunRegister(ctx context.Context, cli *cli.Command) error {
|
|||
scope := cli.String("scope")
|
||||
name := cli.String("name")
|
||||
version := cli.String("version")
|
||||
ephemeral := cli.Bool("ephemeral")
|
||||
labels, err := getLabels(cli)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -205,7 +199,7 @@ func RunRegister(ctx context.Context, cli *cli.Command) error {
|
|||
return err
|
||||
}
|
||||
|
||||
runner, err := actions_model.RegisterRunner(ctx, owner, repo, secret, labels, name, version, ephemeral)
|
||||
runner, err := actions_model.RegisterRunner(ctx, owner, repo, secret, labels, name, version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while registering runner: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
)
|
||||
|
||||
func CmdF3(ctx context.Context) *cli.Command {
|
||||
ctx = f3_logger.ContextSetLogger(ctx, util.NewF3Logger(nil, log.GetLogger(log.DEFAULT)))
|
||||
return &cli.Command{
|
||||
Name: "f3",
|
||||
Usage: "F3",
|
||||
|
|
@ -37,9 +38,7 @@ func SubcmdF3Mirror(ctx context.Context) *cli.Command {
|
|||
mirrorCmd := f3_cmd.CreateCmdMirror()
|
||||
mirrorCmd.Before = prepareWorkPathAndCustomConf(ctx)
|
||||
f3Action := mirrorCmd.Action
|
||||
mirrorCmd.Action = func(ctx context.Context, cli *cli.Command) error {
|
||||
return runMirror(ctx, cli, f3Action)
|
||||
}
|
||||
mirrorCmd.Action = func(ctx context.Context, cli *cli.Command) error { return runMirror(ctx, cli, f3Action) }
|
||||
return mirrorCmd
|
||||
}
|
||||
|
||||
|
|
@ -68,8 +67,6 @@ func runMirror(ctx context.Context, c *cli.Command, action cli.ActionFunc) error
|
|||
if err := models.Init(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx = f3_logger.ContextSetLogger(ctx, util.NewF3Logger(nil, log.GetLogger(log.DEFAULT)))
|
||||
}
|
||||
|
||||
err := action(ctx, c)
|
||||
|
|
|
|||
|
|
@ -126,9 +126,7 @@ func installSignals(ctx context.Context) (context.Context, context.CancelFunc) {
|
|||
)
|
||||
select {
|
||||
case <-signalChannel:
|
||||
break
|
||||
case <-ctx.Done():
|
||||
break
|
||||
}
|
||||
cancel()
|
||||
signal.Reset()
|
||||
|
|
|
|||
|
|
@ -568,7 +568,7 @@ Forgejo or set your environment appropriately.`, "")
|
|||
hookOptions.RefFullNames = make([]git.RefName, 0, hookBatchSize)
|
||||
|
||||
for {
|
||||
// note: pktLineTypeUnknown means pktLineTypeFlush and pktLineTypeData all allowed
|
||||
// note: pktLineTypeUnknow means pktLineTypeFlush and pktLineTypeData all allowed
|
||||
rs, err = readPktLine(ctx, reader, pktLineTypeUnknown)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ func runSendMail(ctx context.Context, c *cli.Command) error {
|
|||
}
|
||||
|
||||
subject := c.String("title")
|
||||
confirmSkipped := c.Bool("force")
|
||||
confirmSkiped := c.Bool("force")
|
||||
body := c.String("content")
|
||||
|
||||
if !confirmSkipped {
|
||||
if !confirmSkiped {
|
||||
if len(body) == 0 {
|
||||
fmt.Print("warning: Content is empty")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ func appGlobalFlags() []cli.Flag {
|
|||
func prepareSubcommandWithConfig(command *cli.Command, globalFlags func() []cli.Flag) {
|
||||
command.Flags = append(globalFlags(), command.Flags...)
|
||||
command.Action = prepareWorkPathAndCustomConf(command.Action)
|
||||
command.HideHelp = true
|
||||
if command.Name != "help" {
|
||||
command.Commands = append(command.Commands, cmdHelp())
|
||||
}
|
||||
|
|
@ -198,6 +199,7 @@ func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmd
|
|||
}
|
||||
app.Flags = append(app.Flags, cli.VersionFlag)
|
||||
app.Flags = append(app.Flags, globalFlags()...)
|
||||
app.HideHelp = true // use our own help action to show helps (with more information like default config)
|
||||
app.Before = PrepareConsoleLoggerLevel(log.INFO)
|
||||
for i := range subCmdWithConfig {
|
||||
prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -63,12 +62,7 @@ func runTestApp(app *cli.Command, args ...string) (runResult, error) {
|
|||
}
|
||||
|
||||
func TestCliCmd(t *testing.T) {
|
||||
path, err := os.Executable()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defaultWorkPath := filepath.Dir(path)
|
||||
defaultWorkPath := filepath.Dir(setting.AppPath)
|
||||
defaultCustomPath := filepath.Join(defaultWorkPath, "custom")
|
||||
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
|
||||
|
||||
|
|
@ -114,11 +108,6 @@ func TestCliCmd(t *testing.T) {
|
|||
cmd: "./gitea test-cmd --config /tmp/app-other.ini",
|
||||
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
|
||||
},
|
||||
{
|
||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
||||
cmd: "./gitea forgejo-cli --help",
|
||||
exp: "(subcommand help template)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
|
|
|||
|
|
@ -290,9 +290,10 @@ func runServ(ctx context.Context, c *cli.Command) error {
|
|||
Op: lfsVerb,
|
||||
UserID: results.UserID,
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// Sign and get the complete encoded token as a string using the secret
|
||||
tokenString, err := setting.LFS.SigningKey.JWT(claims)
|
||||
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
||||
if err != nil {
|
||||
return fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,6 +164,8 @@ func serveInstall(_ context.Context, ctx *cli.Command) error {
|
|||
}
|
||||
|
||||
func serveInstalled(_ context.Context, ctx *cli.Command) error {
|
||||
setting.InitCfgProvider(setting.CustomConf)
|
||||
setting.LoadCommonSettings()
|
||||
setting.MustInstalled()
|
||||
|
||||
showWebStartupMessage("Prepare to run web server")
|
||||
|
|
@ -276,11 +278,8 @@ func setPort(port string) error {
|
|||
|
||||
switch setting.Protocol {
|
||||
case setting.HTTPUnix:
|
||||
break
|
||||
case setting.FCGI:
|
||||
break
|
||||
case setting.FCGIUnix:
|
||||
break
|
||||
default:
|
||||
defaultLocalURL := string(setting.Protocol) + "://"
|
||||
if setting.HTTPAddr == "0.0.0.0" {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ After=network.target
|
|||
# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that
|
||||
# LimitNOFILE=524288:524288
|
||||
RestartSec=2s
|
||||
Type=notify
|
||||
Type=simple
|
||||
User=git
|
||||
Group=git
|
||||
WorkingDirectory=/var/lib/forgejo/
|
||||
|
|
|
|||
|
|
@ -313,9 +313,6 @@ RUN_USER = ; git
|
|||
;LFS_START_SERVER = false
|
||||
;;
|
||||
;;
|
||||
;; see JWT_* under [oauth2]
|
||||
;LFS_JWT_SIGNING_ALGORITHM = HS256
|
||||
;LFS_JWT_SIGNING_PRIVATE_KEY_FILE = jwt/lfs_private.pem
|
||||
;; LFS authentication secret, change this yourself
|
||||
;LFS_JWT_SECRET =
|
||||
;;
|
||||
|
|
@ -420,8 +417,8 @@ DB_TYPE = sqlite3
|
|||
;; Database connection max idle time, 0 prevents closing due to idle time.
|
||||
;CONN_MAX_IDLETIME = 0
|
||||
;;
|
||||
;; Database maximum number of open connections. Ensure you only increase the value if your database server is configured to handle the amount of open connections accordingly.
|
||||
;MAX_OPEN_CONNS = 30
|
||||
;; Database maximum number of open connections, default is 100 which is the lowest default from Postgres (MariaDB + MySQL default to 151). Ensure you only increase the value if you configured your database server accordingly.
|
||||
;MAX_OPEN_CONNS = 100
|
||||
;;
|
||||
;; Whether execute database models migrations automatically
|
||||
;AUTO_MIGRATION = true
|
||||
|
|
@ -460,7 +457,7 @@ INTERNAL_TOKEN =
|
|||
;GLOBAL_TWO_FACTOR_REQUIREMENT = none
|
||||
;;
|
||||
;; Name of cookie used to store authentication information.
|
||||
;COOKIE_REMEMBER_NAME = persistent
|
||||
;COOKIE_REMEMBER_NAME = gitea_incredible
|
||||
;;
|
||||
;; Reverse proxy authentication header name of user name, email, and full name
|
||||
;REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
||||
|
|
@ -547,7 +544,6 @@ ENABLED = true
|
|||
;; Private key file path used to sign OAuth2 tokens. The path is relative to APP_DATA_PATH.
|
||||
;; This setting is only needed if JWT_SIGNING_ALGORITHM is set to RS256, RS384, RS512, ES256, ES384 or ES512.
|
||||
;; The file must contain a RSA or ECDSA private key in the PKCS8 format. If no key exists a 4096 bit key will be created for you.
|
||||
;; XXX jwt/ is a misnomer, it should rather be oauth2/, because we use many JWTs
|
||||
;JWT_SIGNING_PRIVATE_KEY_FILE = jwt/private.pem
|
||||
;;
|
||||
;; OAuth2 authentication secret for access and refresh tokens, change this yourself to a unique string. CLI generate option is helpful in this case. https://forgejo.org/docs/latest/admin/command-line/#generate-secret
|
||||
|
|
@ -1066,7 +1062,7 @@ LEVEL = Info
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
;; List of file extensions for which lines should be wrapped in the CodeMirror editor
|
||||
;; List of file extensions for which lines should be wrapped in the Monaco editor
|
||||
;; Separate extensions with a comma. To line wrap files without an extension, just put a comma
|
||||
;LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,.livemd,
|
||||
|
||||
|
|
@ -1136,6 +1132,9 @@ LEVEL = Info
|
|||
;; If an squash commit's comment should be populated with the commit messages of the squashed commits
|
||||
;POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES = false
|
||||
;;
|
||||
;; Add co-authored-by and co-committed-by trailers if committer does not match author
|
||||
;ADD_CO_COMMITTER_TRAILERS = true
|
||||
;;
|
||||
;; Retarget child pull requests to the parent pull request branch target on merge of parent pull request. It only works on merged PRs where the head and base branch target the same repo.
|
||||
;RETARGET_CHILDREN_ON_MERGE = true
|
||||
|
||||
|
|
@ -1562,17 +1561,15 @@ LEVEL = Info
|
|||
;DEFAULT_EMAIL_NOTIFICATIONS = enabled
|
||||
;; Send an email to all admins when a new user signs up to inform the admins about this act. Options: true, false
|
||||
;SEND_NOTIFICATION_EMAIL_ON_NEW_USER = false
|
||||
;; Disabled features for users, could be "deletion", "manage_ssh_keys","manage_gpg_keys", "manage_password" more features can be disabled in future
|
||||
;; Disabled features for users, could be "deletion", "manage_ssh_keys","manage_gpg_keys" more features can be disabled in future
|
||||
;; - deletion: a user cannot delete their own account
|
||||
;; - manage_ssh_keys: a user cannot configure ssh keys
|
||||
;; - manage_gpg_keys: a user cannot configure gpg keys
|
||||
;; - manage_password: a user cannot configure their password
|
||||
;USER_DISABLED_FEATURES =
|
||||
;; Comma separated list of disabled features ONLY if the user has an external login type (eg. LDAP, Oauth, etc.), could be `deletion`, `manage_ssh_keys`, `manage_gpg_keys`, `manage_password`. This setting is independent from `USER_DISABLED_FEATURES` and supplements its behavior.
|
||||
;; Comma separated list of disabled features ONLY if the user has an external login type (eg. LDAP, Oauth, etc.), could be `deletion`, `manage_ssh_keys`, `manage_gpg_keys`. This setting is independent from `USER_DISABLED_FEATURES` and supplements its behavior.
|
||||
;; - deletion: a user cannot delete their own account
|
||||
;; - manage_ssh_keys: a user cannot configure ssh keys
|
||||
;; - manage_gpg_keys: a user cannot configure gpg keys
|
||||
;; - manage_password: a user cannot configure their password
|
||||
;;EXTERNAL_USER_DISABLE_FEATURES =
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
@ -1899,7 +1896,7 @@ LEVEL = Info
|
|||
;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_.
|
||||
;;
|
||||
;; Session cookie name
|
||||
;COOKIE_NAME = session
|
||||
;COOKIE_NAME = i_like_gitea
|
||||
;;
|
||||
;; If you use session in https only: true or false. If not set, it defaults to `true` if the ROOT_URL is an HTTPS URL.
|
||||
;COOKIE_SECURE =
|
||||
|
|
@ -2459,15 +2456,6 @@ LEVEL = Info
|
|||
;; Enable/Disable RSS/Atom feed
|
||||
;ENABLE_FEED = true
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[pwa]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Enable standalone mode; this allows PWA enabled browsers to "install" the website as an progressive web app,
|
||||
;; by setting the https://developer.mozilla.org/en-US/docs/Web/Manifest/display property to "standalone".
|
||||
;STANDALONE = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[markup]
|
||||
|
|
@ -2555,7 +2543,7 @@ LEVEL = Info
|
|||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[f3]
|
||||
;[F3]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;
|
||||
|
|
@ -2788,21 +2776,12 @@ LEVEL = Info
|
|||
;ABANDONED_JOB_TIMEOUT = 24h
|
||||
;; Strings committers can place inside a commit message or PR title to skip executing the corresponding actions workflow
|
||||
;SKIP_WORKFLOW_STRINGS = [skip ci],[ci skip],[no ci],[skip actions],[actions skip]
|
||||
;; Limit on inputs for manual / workflow_dispatch triggers, default is 100
|
||||
;LIMIT_DISPATCH_INPUTS = 100
|
||||
;; Limit on inputs for manual / workflow_dispatch triggers, default is 10
|
||||
;LIMIT_DISPATCH_INPUTS = 10
|
||||
;; Support queuing workflow jobs, by setting `concurrency.group` & `concurrency.cancel-in-progress: false`, can increase
|
||||
;; server and database workload due to more complex database queries and more frequent server task querying; this
|
||||
;; feature can be disabled to reduce performance impact
|
||||
;CONCURRENCY_GROUP_QUEUE_ENABLED = true
|
||||
;; Algorithm used to sign ID tokens. Valid values: RS256, RS384, RS512, ES256, ES384, ES512, EdDSA.
|
||||
;; RS256 will ensure compatibility with all relying parties.
|
||||
;; If a different algorithm is chosen, verify that relying parties of interest support the signing algorithm.
|
||||
;ID_TOKEN_SIGNING_ALGORITHM = RS256
|
||||
;; Private key file path used to sign ID tokens. The path is relative to APP_DATA_PATH.
|
||||
;; The file must contain an RSA or ECDSA private key in the PKCS8 format. If no key exists, a key will be created for you.
|
||||
;ID_TOKEN_SIGNING_PRIVATE_KEY_FILE = actions_id_token/private.pem
|
||||
;; Lifetime of ID tokens generated by the actions `/idtoken` endpoint in seconds.
|
||||
;ID_TOKEN_EXPIRATION_TIME = 3600
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
@ -2823,30 +2802,3 @@ LEVEL = Info
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; storage type
|
||||
;STORAGE_TYPE = local
|
||||
|
||||
;; Authorized integrations are a capability for users to define external systems which can generate JWTs that Forgejo
|
||||
;; will trust in order to perform API access on behalf of that user. While validating a JWT from an external system,
|
||||
;; Forgejo makes outgoing HTTP requests to the JWT issuer.
|
||||
; [authorized_integration]
|
||||
;; Timeout for HTTP requests to remote servers. Default is 10 seconds.
|
||||
;REQUEST_TIMEOUT = 10s
|
||||
;
|
||||
;; Allowed domains for authorized integrations. Default is blank which means all domains will be allowed (except local
|
||||
;; networks, see ALLOW_LOCALNETWORKS).
|
||||
;; Multiple domains can be separated by commas.
|
||||
;; Wildcards are supported: "github.com, *.github.com"
|
||||
;ALLOWED_DOMAINS =
|
||||
;
|
||||
;; Blocklist for authorized integrations, default is blank.
|
||||
;; Multiple domains can be separated by commas.
|
||||
;; Wildcards are supported: "github.com, *.github.com"
|
||||
;BLOCKED_DOMAINS =
|
||||
;
|
||||
;; Allow private addresses defined by RFC 1918, RFC 1122, RFC 4632 and RFC 4291.
|
||||
;; Default is false.
|
||||
;; If a domain is allowed by ALLOWED_DOMAINS, this option will be ignored.
|
||||
;ALLOW_LOCALNETWORKS = false
|
||||
;
|
||||
;; Remote requests are cached after being received for the cache time-to-live (TTL). Default is 10 minutes.
|
||||
;; Caching uses the configured adapter in the [cache] config section.
|
||||
;CACHE_TTL = 10m
|
||||
|
|
|
|||
|
|
@ -13,5 +13,10 @@ fi
|
|||
if [ $# -gt 0 ]; then
|
||||
exec "$@"
|
||||
else
|
||||
# TODO: remove on next major version release
|
||||
# Honour legacy config file if existing
|
||||
if [ -f ${GITEA_APP_INI_LEGACY} ]; then
|
||||
GITEA_APP_INI=${GITEA_APP_INI_LEGACY}
|
||||
fi
|
||||
exec /usr/local/bin/gitea -c ${GITEA_APP_INI} web
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -11,10 +11,22 @@ mkdir -p ${GITEA_CUSTOM} && chmod 0700 ${GITEA_CUSTOM}
|
|||
mkdir -p ${GITEA_TEMP} && chmod 0700 ${GITEA_TEMP}
|
||||
if [ ! -w ${GITEA_TEMP} ]; then echo "${GITEA_TEMP} is not writable"; exit 1; fi
|
||||
|
||||
# Prepare config file
|
||||
# TODO: remove on next major version release
|
||||
# Honour legacy config file if existing, but inform the user
|
||||
if [ -f ${GITEA_APP_INI_LEGACY} ] && [ ${GITEA_APP_INI} != ${GITEA_APP_INI_LEGACY} ]; then
|
||||
GITEA_APP_INI_DEFAULT=/var/lib/gitea/custom/conf/app.ini
|
||||
echo -e \
|
||||
"\033[33mWARNING\033[0m: detected configuration file in deprecated default path ${GITEA_APP_INI_LEGACY}." \
|
||||
"The new default is ${GITEA_APP_INI_DEFAULT}. To remove this warning, choose one of the options:\n" \
|
||||
"* Move ${GITEA_APP_INI_LEGACY} to ${GITEA_APP_INI_DEFAULT} (or to \$GITEA_APP_INI if you want to override this variable)\n" \
|
||||
"* Explicitly override GITEA_APP_INI=${GITEA_APP_INI_LEGACY} in the container environment"
|
||||
GITEA_APP_INI=${GITEA_APP_INI_LEGACY}
|
||||
fi
|
||||
|
||||
#Prepare config file
|
||||
if [ ! -f ${GITEA_APP_INI} ]; then
|
||||
|
||||
# Prepare config file folder
|
||||
#Prepare config file folder
|
||||
GITEA_APP_INI_DIR=$(dirname ${GITEA_APP_INI})
|
||||
mkdir -p ${GITEA_APP_INI_DIR} && chmod 0700 ${GITEA_APP_INI_DIR}
|
||||
if [ ! -w ${GITEA_APP_INI_DIR} ]; then echo "${GITEA_APP_INI_DIR} is not writable"; exit 1; fi
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
# And place the original in /usr/lib/gitea with working files in /data/gitea
|
||||
GITEA="/app/gitea/gitea"
|
||||
WORK_DIR="/var/lib/gitea"
|
||||
APP_INI="/var/lib/gitea/custom/conf/app.ini"
|
||||
APP_INI="/etc/gitea/app.ini"
|
||||
|
||||
APP_INI_SET=""
|
||||
for i in "$@"; do
|
||||
|
|
|
|||
|
|
@ -1145,7 +1145,6 @@ export default tseslint.config(
|
|||
|
||||
'playwright/prefer-comparison-matcher': [2],
|
||||
'playwright/prefer-equality-matcher': [2],
|
||||
'playwright/prefer-locator': [0],
|
||||
'playwright/prefer-native-locators': [2],
|
||||
'playwright/prefer-to-contain': [2],
|
||||
'playwright/prefer-to-have-length': [2],
|
||||
|
|
|
|||
6
flake.lock
generated
6
flake.lock
generated
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1777954456,
|
||||
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
|
||||
"lastModified": 1762977756,
|
||||
"narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
|
||||
"rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
194
go.mod
194
go.mod
|
|
@ -1,37 +1,37 @@
|
|||
module forgejo.org
|
||||
|
||||
go 1.26.0
|
||||
go 1.25.0
|
||||
|
||||
toolchain go1.26.3
|
||||
toolchain go1.25.9
|
||||
|
||||
require (
|
||||
code.forgejo.org/f3/gof3/v3 v3.11.15
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20260301104140-add494e31dab
|
||||
code.forgejo.org/forgejo/actions-proto v0.7.0
|
||||
code.forgejo.org/f3/gof3/v3 v3.11.1
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
code.forgejo.org/forgejo/actions-proto v0.6.0
|
||||
code.forgejo.org/forgejo/go-rpmutils v1.0.0
|
||||
code.forgejo.org/forgejo/levelqueue v1.0.0
|
||||
code.forgejo.org/forgejo/reply v1.0.2
|
||||
code.forgejo.org/forgejo/runner/v12 v12.10.1
|
||||
code.forgejo.org/forgejo/runner/v12 v12.6.4
|
||||
code.forgejo.org/go-chi/binding v1.0.1
|
||||
code.forgejo.org/go-chi/cache v1.0.1
|
||||
code.forgejo.org/go-chi/captcha v1.0.2
|
||||
code.forgejo.org/go-chi/session v1.0.4
|
||||
code.forgejo.org/go-chi/session v1.0.2
|
||||
code.gitea.io/sdk/gitea v0.21.0
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.2
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.1
|
||||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
connectrpc.com/connect v1.19.2
|
||||
connectrpc.com/connect v1.19.1
|
||||
github.com/42wim/httpsig v1.2.3
|
||||
github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/ProtonMail/go-crypto v1.4.1
|
||||
github.com/PuerkitoBio/goquery v1.12.0
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0
|
||||
github.com/alecthomas/chroma/v2 v2.23.1
|
||||
github.com/ProtonMail/go-crypto v1.3.0
|
||||
github.com/PuerkitoBio/goquery v1.10.3
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
||||
github.com/alecthomas/chroma/v2 v2.20.0
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.6.0
|
||||
github.com/blevesearch/bleve/v2 v2.5.6
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8
|
||||
github.com/caddyserver/certmagic v0.25.3
|
||||
github.com/caddyserver/certmagic v0.24.0
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
github.com/djherbis/nio/v3 v3.0.1
|
||||
|
|
@ -41,77 +41,78 @@ require (
|
|||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.4
|
||||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/gdgvda/cron v0.7.0
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/go-ap/activitypub v0.0.0-20260208110334-902f6cf8c2cc
|
||||
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
|
||||
github.com/go-ap/jsonld v0.0.0-20251216162253-e38fa664ea77
|
||||
github.com/go-chi/chi/v5 v5.2.5
|
||||
github.com/go-chi/chi/v5 v5.2.4
|
||||
github.com/go-chi/cors v1.2.2
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.6
|
||||
github.com/go-enry/go-enry/v2 v2.9.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.12
|
||||
github.com/go-openapi/spec v0.22.3
|
||||
github.com/go-sql-driver/mysql v1.10.0
|
||||
github.com/go-webauthn/webauthn v0.16.5
|
||||
github.com/go-openapi/spec v0.22.1
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/go-webauthn/webauthn v0.14.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/go-github/v81 v81.0.0
|
||||
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc
|
||||
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/hashicorp/go-version v1.8.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/inbucket/html2text v1.0.0
|
||||
github.com/jackc/pgx/v5 v5.9.2
|
||||
github.com/inbucket/html2text v0.9.0
|
||||
github.com/jackc/pgx/v5 v5.7.6
|
||||
github.com/jhillyerd/enmime/v2 v2.2.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/klauspost/compress v1.18.6
|
||||
github.com/klauspost/cpuid/v2 v2.3.0
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/mattn/go-isatty v0.0.21
|
||||
github.com/mattn/go-sqlite3 v1.14.44
|
||||
github.com/meilisearch/meilisearch-go v0.36.2
|
||||
github.com/klauspost/compress v1.18.3
|
||||
github.com/klauspost/cpuid/v2 v2.2.11
|
||||
github.com/markbates/goth v1.80.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.34
|
||||
github.com/meilisearch/meilisearch-go v0.34.0
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/minio/minio-go/v7 v7.1.0
|
||||
github.com/minio/minio-go/v7 v7.0.95
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/niklasfasching/go-org v1.9.1
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/redis/go-redis/v9 v9.19.0
|
||||
github.com/redis/go-redis/v9 v9.17.2
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
|
||||
github.com/sergi/go-diff v1.4.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/urfave/cli/v3 v3.9.0
|
||||
github.com/valyala/fastjson v1.6.10
|
||||
github.com/urfave/cli/v3 v3.5.0
|
||||
github.com/valyala/fastjson v1.6.7
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.8.2
|
||||
github.com/yuin/goldmark v1.7.13
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
gitlab.com/gitlab-org/api/client-go v0.143.2
|
||||
go.uber.org/mock v0.6.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/crypto v0.51.0
|
||||
golang.org/x/image v0.40.0
|
||||
golang.org/x/net v0.54.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
golang.org/x/image v0.38.0
|
||||
golang.org/x/net v0.51.0
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/sys v0.44.0
|
||||
golang.org/x/text v0.37.0
|
||||
golang.org/x/sys v0.41.0
|
||||
golang.org/x/text v0.35.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
mvdan.cc/xurls/v2 v2.5.0
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.9
|
||||
)
|
||||
|
|
@ -119,46 +120,46 @@ require (
|
|||
require (
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 // indirect
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.1 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.14.5 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.2 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.3.11 // indirect
|
||||
github.com/blevesearch/geo v0.2.5 // indirect
|
||||
github.com/blevesearch/go-faiss v1.1.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.22.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.2.11 // indirect
|
||||
github.com/blevesearch/geo v0.2.4 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.26 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.2.0 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.4.7 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
github.com/blevesearch/vellum v1.2.0 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.3 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.3.4 // indirect
|
||||
github.com/blevesearch/zapx/v17 v17.1.2 // indirect
|
||||
github.com/blevesearch/vellum v1.1.0 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.4.2 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.2.7 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.1 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
|
||||
github.com/caddyserver/zerossl v0.1.5 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb // indirect
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||
|
|
@ -166,32 +167,35 @@ require (
|
|||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20260208110149-e1b309365966 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.8.0 // indirect
|
||||
github.com/go-git/go-git/v5 v5.17.1 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/go-webauthn/x v0.2.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.3 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.3 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.3 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.3 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.3 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.3 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.3 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.3 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3 // indirect
|
||||
github.com/go-webauthn/x v0.1.25 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/go-tpm v0.9.8 // indirect
|
||||
github.com/google/go-tpm v0.9.5 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
|
|
@ -200,21 +204,23 @@ require (
|
|||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/libdns/libdns v1.1.1 // indirect
|
||||
github.com/lib/pq v1.11.2 // indirect
|
||||
github.com/libdns/libdns v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/markbates/going v1.0.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.17 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.6 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.2 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/crc64nvme v1.1.1 // indirect
|
||||
github.com/minio/crc64nvme v1.0.2 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minlz v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
|
|
@ -225,7 +231,6 @@ require (
|
|||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
github.com/olekukonko/tablewriter v1.0.7 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.34.1 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
|
@ -235,28 +240,27 @@ require (
|
|||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rhysd/actionlint v1.7.10 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tinylib/msgp v1.6.4 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/assert v1.3.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
github.com/zeebo/xxh3 v1.1.0 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.1 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
@ -269,4 +273,4 @@ replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-2024121
|
|||
|
||||
replace git.sr.ht/~mariusor/go-xsd-duration => code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078
|
||||
|
||||
replace xorm.io/xorm v1.3.9 => code.forgejo.org/xorm/xorm v1.3.9-forgejo.12
|
||||
replace xorm.io/xorm v1.3.9 => code.forgejo.org/xorm/xorm v1.3.9-forgejo.8
|
||||
|
|
|
|||
402
go.sum
402
go.sum
|
|
@ -16,12 +16,12 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
|
|||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
code.forgejo.org/f3/gof3/v3 v3.11.15 h1:/EzJWRQUhVVia1EmCWRZHCuW8qdAX1DEXY0M1n0mECc=
|
||||
code.forgejo.org/f3/gof3/v3 v3.11.15/go.mod h1:k99wl7QiI9LjS3qEd1RXLdcZPE3wZP5gkVhy8/WhzJ4=
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20260301104140-add494e31dab h1:g/uf/tNOvCdGL9EuYV9EJQglEH8n572P1ZwH4lBBkjI=
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20260301104140-add494e31dab/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM=
|
||||
code.forgejo.org/forgejo/actions-proto v0.7.0 h1:0UA8AIOIWEiMLQvBbnZ6GQ9reh6BbM15Ep8x+oeTCY8=
|
||||
code.forgejo.org/forgejo/actions-proto v0.7.0/go.mod h1:+444hHBs9/qDh5X/AedaTv0Egj3vd/EXP93vg9zFV2E=
|
||||
code.forgejo.org/f3/gof3/v3 v3.11.1 h1:c0vE8XvqpbXuSv8gzttn96k5T2FQi0u9bYnux46qSAs=
|
||||
code.forgejo.org/f3/gof3/v3 v3.11.1/go.mod h1:1p2UKrqZiwxKneQF2DKrMnc403YIgR/lfcfvadZtmDs=
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU=
|
||||
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM=
|
||||
code.forgejo.org/forgejo/actions-proto v0.6.0 h1:dw1Dogk9A4V/yrLVkhe9dSZPsqNAIkI1kCXPSqG3tZA=
|
||||
code.forgejo.org/forgejo/actions-proto v0.6.0/go.mod h1:+444hHBs9/qDh5X/AedaTv0Egj3vd/EXP93vg9zFV2E=
|
||||
code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M=
|
||||
code.forgejo.org/forgejo/go-rpmutils v1.0.0/go.mod h1:cg+VbgLXfrDPza9T+kBsMb3TVmmzPN4XseT6gDGLSUk=
|
||||
code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:RArF5AsF9LH4nEoJxqRxcP5r8hhRfWcId84G82YbqzA=
|
||||
|
|
@ -30,8 +30,8 @@ code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/Uf
|
|||
code.forgejo.org/forgejo/levelqueue v1.0.0/go.mod h1:fmG6zhVuqim2rxSFOoasgXO8V2W/k9U31VVYqLIRLhQ=
|
||||
code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ=
|
||||
code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U=
|
||||
code.forgejo.org/forgejo/runner/v12 v12.10.1 h1:qRKjWItVDc2lEMl3jGlKyFpqgX4xHeOdEyl/irO/Nk8=
|
||||
code.forgejo.org/forgejo/runner/v12 v12.10.1/go.mod h1:A51GyZJlril5cIpVMvOn3NqE8upOE6ePjwC6s31kRHk=
|
||||
code.forgejo.org/forgejo/runner/v12 v12.6.4 h1:nhYj2wSj5BVKxcF0bRtMt/A9iGkxHPFJiIui+T/4mrc=
|
||||
code.forgejo.org/forgejo/runner/v12 v12.6.4/go.mod h1:34ATLtcxtOgjAJiINaJvBoNJiKpL1hGn0kt+gk+zdyk=
|
||||
code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA=
|
||||
code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY=
|
||||
|
|
@ -40,27 +40,25 @@ code.forgejo.org/go-chi/cache v1.0.1 h1:w6IsDcPbeEnEYZn7M2HJe3/3/Ehtcw/72VjcVK7+
|
|||
code.forgejo.org/go-chi/cache v1.0.1/go.mod h1:K3aQSyRIN4xiuqV1kanfQ6O4ToDpzDpY3bNOyGjFe3U=
|
||||
code.forgejo.org/go-chi/captcha v1.0.2 h1:vyHDPXkpjDv8bLO9NqtWzZayzstD/WpJ5xwEkAaqZGQ=
|
||||
code.forgejo.org/go-chi/captcha v1.0.2/go.mod h1:lxiPLcJ76UCZHoH31/Wbum4GUi2NgjfFZLrJkKv1lLE=
|
||||
code.forgejo.org/go-chi/session v1.0.4 h1:WQ1NaVxcCpxYwCliEGypKclZnOCjh3p1fk8XciJc62U=
|
||||
code.forgejo.org/go-chi/session v1.0.4/go.mod h1:+sSTiomM5C8AUPtxZyTENIbcTz22kcVottKO0lnmDRk=
|
||||
code.forgejo.org/xorm/xorm v1.3.9-forgejo.12 h1:EodlU2MGu/EeAdUpEOe+X4cU/qlnJol1ucJ0sHUCM1o=
|
||||
code.forgejo.org/xorm/xorm v1.3.9-forgejo.12/go.mod h1:ozQINrM8b7uYFMBB/w19nUTTLcda3RKTQ8HspocVKdg=
|
||||
code.forgejo.org/go-chi/session v1.0.2 h1:pG+AXre9L9VXJmTaADXkmeEPuRalhmBXyv6tG2Rvjcc=
|
||||
code.forgejo.org/go-chi/session v1.0.2/go.mod h1:HnEGyBny7WPzCiVLP2vzL5ssma+3gCSl/vLpuVNYrqc=
|
||||
code.forgejo.org/xorm/xorm v1.3.9-forgejo.8 h1:dsSKm2nus0NhHsqYxeuB3Gldk6TtlusD1CBGV6V1SS0=
|
||||
code.forgejo.org/xorm/xorm v1.3.9-forgejo.8/go.mod h1:A7sFd3BFmRp20h6drSsCXgQRQdF8Vz8HuCSrzFS3m90=
|
||||
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
|
||||
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
|
||||
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
|
||||
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.2 h1:nkTdaghZb6I0oGYFhTLSALhA2ShgkPnYAJryE5IE9+0=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.2/go.mod h1:/Z+3DHSrefCzzN5ePkGjVYKFErRimoeUf694Gz8Pn/Y=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.1 h1:qnujLH4/Yk/CFtFMmtjozbdV6Ry5G3Q/E/mLlWm/gQI=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.1/go.mod h1:/Z+3DHSrefCzzN5ePkGjVYKFErRimoeUf694Gz8Pn/Y=
|
||||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 h1:r9uq8StaSHYKJ8DklR9Xy+E9c40G1Z8yj5TRGi8L6+4=
|
||||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0/go.mod h1:IK1OlR6APjVB3E9tuYGvf0qXMrwP+TrzcHS5rf4wffQ=
|
||||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 h1:I512jiIeXDC4//2BeSPrRM2ZS4wpBKUaPeTPxakMNGA=
|
||||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0/go.mod h1:SNHomXNW88o1pFfLHpD4KsCZLfcr4z5dm+xcX5SV10A=
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY=
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
|
||||
connectrpc.com/connect v1.19.2 h1:McQ83FGdzL+t60peksi0gXC7MQ/iLKgLduAnThbM0mo=
|
||||
connectrpc.com/connect v1.19.2/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
|
||||
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
|
||||
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
|
||||
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
|
||||
|
|
@ -73,24 +71,24 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+
|
|||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo=
|
||||
github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.14.5 h1:ckd0o545JqDPeVJDgeFoaM21eBixUnlWfYgjE5VnyWw=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.14.5/go.mod h1:eq4wdNXxtJIS/oikeCzdX1rBzek7ANzbth041hrU8Q4=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0 h1:tgjwQrDH5m6jIYB7kac5IQZmfUzQNseac/e3H4VoCNE=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0/go.mod h1:1HmmMEVsr+0R1QWahSeMJkjSkq6CYAZu1aIbYSpfJ4o=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 h1:cSXom2MoKJ9KPPw29RoZtHvUETY4F4n/kXl8m9btnQ0=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y87l8VsHiiwhb3cgdyn68mX40s7NT6PA=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
|
||||
github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
|
|
@ -103,48 +101,47 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP
|
|||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bits-and-blooms/bitset v1.24.2 h1:M7/NzVbsytmtfHbumG+K2bremQPMJuqv1JD3vOaFxp0=
|
||||
github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
|
||||
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/blevesearch/bleve/v2 v2.6.0 h1:Cyd3dd4q5tCbOV8MnKUVRUDYMHOir9xn12NZzXVSEd4=
|
||||
github.com/blevesearch/bleve/v2 v2.6.0/go.mod h1:gLmI8lWgHgrIYf7UpUX7JISI1CaqC6VScu46mHThuAY=
|
||||
github.com/blevesearch/bleve_index_api v1.3.11 h1:x29vbV8OjWfLcrDVd7Lr1q+BkLNS0JWNEig0MCVnKH4=
|
||||
github.com/blevesearch/bleve_index_api v1.3.11/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko=
|
||||
github.com/blevesearch/geo v0.2.5 h1:yJg9FX1oRwLnjXSXF+ECHfXFTF4diF02Ca/qUGVjJhE=
|
||||
github.com/blevesearch/geo v0.2.5/go.mod h1:Jhq7WE2K6mJTx1xS44M2pUO6Io+wjCSHh1+co3YOgH4=
|
||||
github.com/blevesearch/go-faiss v1.1.0 h1:xM7Jc0ZUCv5lssG9Ohj3Jv0SdTpxcUABU1dDt9XVsc4=
|
||||
github.com/blevesearch/go-faiss v1.1.0/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
|
||||
github.com/blevesearch/bleve/v2 v2.5.6 h1:YdixQmOUuZHojQRe8Te7BY2cRirbzpbcpybAFs0m2DI=
|
||||
github.com/blevesearch/bleve/v2 v2.5.6/go.mod h1:t5WoESS5TDteTdnjhhvpA1BpLYErOBX2IQViTMLK7wo=
|
||||
github.com/blevesearch/bleve_index_api v1.2.11 h1:bXQ54kVuwP8hdrXUSOnvTQfgK0KI1+f9A0ITJT8tX1s=
|
||||
github.com/blevesearch/bleve_index_api v1.2.11/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
|
||||
github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk=
|
||||
github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
|
||||
github.com/blevesearch/go-faiss v1.0.26 h1:4dRLolFgjPyjkaXwff4NfbZFdE/dfywbzDqporeQvXI=
|
||||
github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
|
||||
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
|
||||
github.com/blevesearch/mmap-go v1.2.0 h1:l33nNKPFcBjJUMwem6sAYJPUzhUCABoK9FxZDGiFNBI=
|
||||
github.com/blevesearch/mmap-go v1.2.0/go.mod h1:Vd6+20GBhEdwJnU1Xohgt88XCD/CTWcqbCNxkZpyBo0=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.4.7 h1:GlMzW08hcsM3DnLUxhyF/1PcDal1qtvvIuytuph5djw=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.4.7/go.mod h1://IJ7tG3QCf0cWW/aVSXqy77tc1AvLu3fcJLYEvOAFs=
|
||||
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
|
||||
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc=
|
||||
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
|
||||
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
|
||||
github.com/blevesearch/vellum v1.2.0 h1:xkDiOEsHc2t3Cp0NsNZZ36pvc130sCzcGKOPMzXe+e0=
|
||||
github.com/blevesearch/vellum v1.2.0/go.mod h1:uEcfBJz7mAOf0Kvq6qoEKQQkLODBF46SINYNkZNae4k=
|
||||
github.com/blevesearch/zapx/v11 v11.4.3 h1:PTZOO5loKpHC/x/GzmPZNa9cw7GZIQxd5qRjwij9tHY=
|
||||
github.com/blevesearch/zapx/v11 v11.4.3/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
|
||||
github.com/blevesearch/zapx/v12 v12.4.3 h1:eElXvAaAX4m04t//CGBQAtHNPA+Q6A1hHZVrN3LSFYo=
|
||||
github.com/blevesearch/zapx/v12 v12.4.3/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
|
||||
github.com/blevesearch/zapx/v13 v13.4.3 h1:qsdhRhaSpVnqDFlRiH9vG5+KJ+dE7KAW9WyZz/KXAiE=
|
||||
github.com/blevesearch/zapx/v13 v13.4.3/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
|
||||
github.com/blevesearch/zapx/v14 v14.4.3 h1:GY4Hecx0C6UTmiNC2pKdeA2rOKiLR5/rwpU9WR51dgM=
|
||||
github.com/blevesearch/zapx/v14 v14.4.3/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
|
||||
github.com/blevesearch/zapx/v15 v15.4.3 h1:iJiMJOHrz216jyO6lS0m9RTCEkprUnzvqAI2lc/0/CU=
|
||||
github.com/blevesearch/zapx/v15 v15.4.3/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
|
||||
github.com/blevesearch/zapx/v16 v16.3.4 h1:hDAqA8qusZTNbPEL7//w5P65UZ2de6yhSeUaTbp0Po0=
|
||||
github.com/blevesearch/zapx/v16 v16.3.4/go.mod h1:zqkPPqs9GS9FzVWzCO3Wf1X044yWAV17+4zb+FTiEHg=
|
||||
github.com/blevesearch/zapx/v17 v17.1.2 h1:avbOk2igaASNoiy0BE/jPgcxAnRI2PGeydeP4hg7Ikk=
|
||||
github.com/blevesearch/zapx/v17 v17.1.2/go.mod h1:WQObxKrqUX7cd0G1GMvDfc/bmZzQvoy7APOPimx7DiI=
|
||||
github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
|
||||
github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
|
||||
github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs=
|
||||
github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
|
||||
github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE=
|
||||
github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
|
||||
github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks=
|
||||
github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
|
||||
github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0=
|
||||
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
|
||||
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
|
||||
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
|
||||
github.com/blevesearch/zapx/v16 v16.2.7 h1:xcgFRa7f/tQXOwApVq7JWgPYSlzyUMmkuYa54tMDuR0=
|
||||
github.com/blevesearch/zapx/v16 v16.2.7/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
|
|
@ -164,10 +161,10 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
|||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8 h1:QN/daUob6cmK8GcdKnwn9+YTlPr1vNj+oeAIiJK6fPc=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.8/go.mod h1:+k1KVKROZocrTLsEQ9PEf9A+8+X8uaVV5iO1ZIOwKYM=
|
||||
github.com/caddyserver/certmagic v0.25.3 h1:mGf5ba8F7xA4c5jfDZZbK2buY1VEkbnwpMDixaju94A=
|
||||
github.com/caddyserver/certmagic v0.25.3/go.mod h1:YVs43D5+H/Dckt4bTga1KSO/xYfFBfVZainGDywYPAA=
|
||||
github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE=
|
||||
github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/caddyserver/certmagic v0.24.0 h1:EfXTWpxHAUKgDfOj6MHImJN8Jm4AMFfMT6ITuKhrDF0=
|
||||
github.com/caddyserver/certmagic v0.24.0/go.mod h1:xPT7dC1DuHHnS2yuEQCEyks+b89sUkMENh8dJF+InLE=
|
||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||
|
|
@ -194,6 +191,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
|||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o=
|
||||
github.com/djherbis/buffer v1.2.0 h1:PH5Dd2ss0C7CRRhQCZ2u7MssF+No9ide8Ye71nPHcrQ=
|
||||
github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQqiAiUyE=
|
||||
|
|
@ -250,29 +249,28 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
|
|||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gdgvda/cron v0.7.0 h1:LFPZUTbCb5ZpzYxavbQDDbjd6nwTwkiNUWyulOdlY2I=
|
||||
github.com/gdgvda/cron v0.7.0/go.mod h1:caBF+mzTZGtQqFE05T1m6u9OmCASY3EK51XAICf3wio=
|
||||
github.com/go-ap/activitypub v0.0.0-20260208110334-902f6cf8c2cc h1:yLe7YJhK+XNjNV4SqDxAjpWAgft+KU+XwKZS4AKEUV0=
|
||||
github.com/go-ap/activitypub v0.0.0-20260208110334-902f6cf8c2cc/go.mod h1:jUs8eczo1EAT4ByRpZ4mQmNvjarw9eNf7Nm5udpMRhY=
|
||||
github.com/go-ap/errors v0.0.0-20260208110149-e1b309365966 h1:tV+3kZgqFMKVUf+JPKBV400ISM8440+6y/SQCS0WZwQ=
|
||||
github.com/go-ap/errors v0.0.0-20260208110149-e1b309365966/go.mod h1:zkp58Q5yXpCxZbh3d0GDvwqiYclfVuHEHjc9SZKAj6I=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 h1:j2TrkUG/NATGi/EQS+MvEoF79CxiRUmT16ErFroNcKI=
|
||||
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9/go.mod h1:cJ9Ye0ZNSMN7RzZDBRY3E+8M3Bpf/R1JX22Ir9yX6WI=
|
||||
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 h1:I2nuhyVI/48VXoRCCZR2hYBgnSXa+EuDJf/VyX06TC0=
|
||||
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7/go.mod h1:5x8a6P/dhmMGFxWLcyYlyOuJ2lRNaHGhRv+yu8BaTSI=
|
||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
|
||||
github.com/go-ap/jsonld v0.0.0-20251216162253-e38fa664ea77 h1:yHAmoR6avNy84PlLmjHt1z9flAp2Qs2ens5QDE/CNWk=
|
||||
github.com/go-ap/jsonld v0.0.0-20251216162253-e38fa664ea77/go.mod h1:4h93IBxgfnE/DEleMLgJ/XCeu/RtQ+MUh3ucANseeXA=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4=
|
||||
github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
|
||||
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
|
||||
github.com/go-enry/go-enry/v2 v2.9.6 h1:np63eOtMV56zfYDHnFVgpEVOk8fr2kmylcMnAZUDbSs=
|
||||
github.com/go-enry/go-enry/v2 v2.9.6/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
|
||||
github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY=
|
||||
github.com/go-enry/go-enry/v2 v2.9.2/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
|
||||
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
|
||||
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
|
|
@ -282,51 +280,53 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI
|
|||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
|
||||
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
|
||||
github.com/go-git/go-git/v5 v5.17.1 h1:WnljyxIzSj9BRRUlnmAU35ohDsjRK0EKmL0evDqi5Jk=
|
||||
github.com/go-git/go-git/v5 v5.17.1/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=
|
||||
github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=
|
||||
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
|
||||
github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/jsonpointer v0.22.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8=
|
||||
github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo=
|
||||
github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc=
|
||||
github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4=
|
||||
github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k=
|
||||
github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA=
|
||||
github.com/go-openapi/swag/conv v0.25.3 h1:PcB18wwfba7MN5BVlBIV+VxvUUeC2kEuCEyJ2/t2X7E=
|
||||
github.com/go-openapi/swag/conv v0.25.3/go.mod h1:n4Ibfwhn8NJnPXNRhBO5Cqb9ez7alBR40JS4rbASUPU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.3 h1:U20VKDS74HiPaLV7UZkztpyVOw3JNVsit+w+gTXRj0A=
|
||||
github.com/go-openapi/swag/jsonname v0.25.3/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.3 h1:kV7wer79KXUM4Ea4tBdAVTU842Rg6tWstX3QbM4fGdw=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.3/go.mod h1:ILcKqe4HC1VEZmJx51cVuZQ6MF8QvdfXsQfiaCs0z9o=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.3 h1:/i3E9hBujtXfHy91rjtwJ7Fgv5TuDHgnSrYjhFxwxOw=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.3/go.mod h1:8kYfCR2rHyOj25HVvxL5Nm8wkfzggddgjZm6RgjT8Ao=
|
||||
github.com/go-openapi/swag/loading v0.25.3 h1:Nn65Zlzf4854MY6Ft0JdNrtnHh2bdcS/tXckpSnOb2Y=
|
||||
github.com/go-openapi/swag/loading v0.25.3/go.mod h1:xajJ5P4Ang+cwM5gKFrHBgkEDWfLcsAKepIuzTmOb/c=
|
||||
github.com/go-openapi/swag/stringutils v0.25.3 h1:nAmWq1fUTWl/XiaEPwALjp/8BPZJun70iDHRNq/sH6w=
|
||||
github.com/go-openapi/swag/stringutils v0.25.3/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.3 h1:2w4mEEo7DQt3V4veWMZw0yTPQibiL3ri2fdDV4t2TQc=
|
||||
github.com/go-openapi/swag/typeutils v0.25.3/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3 h1:LKTJjCn/W1ZfMec0XDL4Vxh8kyAnv1orH5F2OREDUrg=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3/go.mod h1:Y7QN6Wc5DOBXK14/xeo1cQlq0EA0wvLoSv13gDQoCao=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
|
||||
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-webauthn/webauthn v0.16.5 h1:x+vADHlaiIjta23kGhtwyCIlB5mayKx6SBlpwQ5NF9A=
|
||||
github.com/go-webauthn/webauthn v0.16.5/go.mod h1:mQC6L0lZ5Kiu35G70zeB2WnrW4+vbHjR8Koq4HdVaMg=
|
||||
github.com/go-webauthn/x v0.2.3 h1:8oArS+Rc1SWFLXhE17KZNx258Z4kUSyaDgsSncCO5RA=
|
||||
github.com/go-webauthn/x v0.2.3/go.mod h1:tM04GF3V6VYq79AZMl7vbj4q6pz9r7L2criWRzbWhPk=
|
||||
github.com/go-webauthn/webauthn v0.14.0 h1:ZLNPUgPcDlAeoxe+5umWG/tEeCoQIDr7gE2Zx2QnhL0=
|
||||
github.com/go-webauthn/webauthn v0.14.0/go.mod h1:QZzPFH3LJ48u5uEPAu+8/nWJImoLBWM7iAH/kSVSo6k=
|
||||
github.com/go-webauthn/x v0.1.25 h1:g/0noooIGcz/yCVqebcFgNnGIgBlJIccS+LYAa+0Z88=
|
||||
github.com/go-webauthn/x v0.1.25/go.mod h1:ieblaPY1/BVCV0oQTsA/VAo08/TWayQuJuo5Q+XxmTY=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
|
|
@ -340,8 +340,10 @@ github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7w
|
|||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
|
|
@ -367,12 +369,12 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
|||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
|
@ -386,10 +388,8 @@ github.com/google/go-github/v81 v81.0.0 h1:hTLugQRxSLD1Yei18fk4A5eYjOGLUBKAl/VCq
|
|||
github.com/google/go-github/v81 v81.0.0/go.mod h1:upyjaybucIbBIuxgJS7YLOZGziyvvJ92WX6WEBNE3sM=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
|
||||
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
|
||||
github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU=
|
||||
github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
|
|
@ -398,8 +398,8 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI
|
|||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw=
|
||||
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
|
||||
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8 h1:3DsUAV+VNEQa2CUVLxCY3f87278uWfIDhJnbdvDjvmE=
|
||||
github.com/google/pprof v0.0.0-20251114195745-4902fdda35c8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
|
|
@ -439,16 +439,18 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI
|
|||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||
github.com/inbucket/html2text v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs=
|
||||
github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU=
|
||||
github.com/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+qks=
|
||||
github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
|
||||
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
|
|
@ -475,14 +477,12 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
|||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
||||
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
|
|
@ -497,49 +497,49 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU=
|
||||
github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U=
|
||||
github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ=
|
||||
github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
|
||||
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
|
||||
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/libdns/libdns v1.0.0 h1:IvYaz07JNz6jUQ4h/fv2R4sVnRnm77J/aOuC9B+TQTA=
|
||||
github.com/libdns/libdns v1.0.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE=
|
||||
github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o=
|
||||
github.com/markbates/goth v1.82.0 h1:8j/c34AjBSTNzO7zTsOyP5IYCQCMBTRBHAbBt/PI0bQ=
|
||||
github.com/markbates/goth v1.82.0/go.mod h1:/DRlcq0pyqkKToyZjsL2KgiA1zbF1HIjE7u2uC79rUk=
|
||||
github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8=
|
||||
github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
|
||||
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.17 h1:78v8ZlW0bP43XfmAfPsdXcoNCelfMHsDmd/pkENfrjQ=
|
||||
github.com/mattn/go-runewidth v0.0.17/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8=
|
||||
github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/meilisearch/meilisearch-go v0.36.2 h1:MYaMPCpdLh2aYPt+zK+19mLoA4dfBY3S1L7T0FADCjU=
|
||||
github.com/meilisearch/meilisearch-go v0.36.2/go.mod h1:hWcR0MuWLSzHfbz9GGzIr3s9rnXLm1jqkmHkJPbUSvM=
|
||||
github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk=
|
||||
github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY=
|
||||
github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
|
||||
github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/meilisearch/meilisearch-go v0.34.0 h1:P+Ohdx4/PCxXaoI5wNi0LMwPkuiNrF/kGIzBrKYS4tw=
|
||||
github.com/meilisearch/meilisearch-go v0.34.0/go.mod h1:cUVJZ2zMqTvvwIMEEAdsWH+zrHsrLpAw6gm8Lt1MXK0=
|
||||
github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc=
|
||||
github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
||||
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
|
||||
github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.1.0 h1:QEt5IStDpxgGjEdtOgpiZ5QhmSl3ax7qy61vi2SwHO8=
|
||||
github.com/minio/minio-go/v7 v7.1.0/go.mod h1:Dm7WS1AgLmBa0NcQD6SeJnJf+K/EUW3GR7Ks6olB3OA=
|
||||
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
|
||||
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
|
||||
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
|
||||
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -595,8 +595,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
|
|
@ -606,8 +606,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
|
|||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k=
|
||||
github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||
github.com/redis/go-redis/v9 v9.17.2 h1:P2EGsA4qVIM3Pp+aPocCJ7DguDHhqrXNhVcEp4ViluI=
|
||||
github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rhysd/actionlint v1.7.10 h1:FL3XIEs72G4/++168vlv5FKOWMSWvWIQw1kBCadyOcM=
|
||||
|
|
@ -655,15 +655,16 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
|||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
|
||||
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
|
||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c=
|
||||
github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4=
|
||||
github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE=
|
||||
github.com/urfave/cli/v3 v3.5.0 h1:qCuFMmdayTF3zmjG8TSsoBzrDqszNrklYg2x3g4MSgw=
|
||||
github.com/urfave/cli/v3 v3.5.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM=
|
||||
github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
|
|
@ -673,8 +674,8 @@ github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBz
|
|||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
|
||||
github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
||||
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
|
|
@ -683,8 +684,6 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
|||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
gitlab.com/gitlab-org/api/client-go v0.143.2 h1:tfmUW8u+G/DGKOB/FDR0c06f0RVUAEe0ym8WpLoiHXI=
|
||||
gitlab.com/gitlab-org/api/client-go v0.143.2/go.mod h1:gJn5yLx9vYGXr73Yv0ueHWCVl+fL8iUOgJFxC7qV+iM=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
|
|
@ -702,8 +701,8 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
|||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
|
|
@ -723,8 +722,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
|||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
@ -733,12 +732,12 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
|
|||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8=
|
||||
golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=
|
||||
golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
|
||||
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
|
@ -760,8 +759,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -792,15 +791,15 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -850,8 +849,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
|
@ -861,8 +860,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
@ -876,12 +875,12 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
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.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
|
@ -911,8 +910,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
@ -973,6 +972,8 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
|||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
@ -982,6 +983,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
|
|
@ -991,16 +993,16 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
|
||||
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
|
||||
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
|
||||
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/sqlite v1.50.0 h1:eMowQSWLK0MeiQTdmz3lqoF5dqclujdlIKeJA11+7oM=
|
||||
modernc.org/sqlite v1.50.0/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
|
||||
mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
|
||||
mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=
|
||||
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
|
||||
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
|
||||
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
-
|
||||
id: 100
|
||||
attempt: 3
|
||||
runner_id: 12345678
|
||||
status: 5 # StatusWaiting
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: false
|
||||
token_hash: a1
|
||||
token_salt: eeeeeeee
|
||||
token_last_eight: eeeeeeee
|
||||
log_filename: artifact-test2/2f/47.log
|
||||
log_in_storage: true
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: false
|
||||
runner_request_key: 0a7e017d-4201-4b34-8cf4-de0f431893a4
|
||||
-
|
||||
id: 101
|
||||
attempt: 3
|
||||
runner_id: 12345678
|
||||
status: 5 # StatusWaiting
|
||||
repo_id: 4
|
||||
owner_id: 1
|
||||
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||
is_fork_pull_request: false
|
||||
token_hash: a2
|
||||
token_salt: eeeeeeee
|
||||
token_last_eight: eeeeeeee
|
||||
log_filename: artifact-test2/2f/47.log
|
||||
log_in_storage: true
|
||||
log_length: 707
|
||||
log_size: 90179
|
||||
log_expired: false
|
||||
runner_request_key: 0a7e017d-4201-4b34-8cf4-de0f431893a4
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
- id: 719931
|
||||
uuid: "9e0eb762-cbdf-4725-9c90-4298568a2a77"
|
||||
name: "runner-1"
|
||||
version: "dev"
|
||||
owner_id: 3 # Owned by org3
|
||||
repo_id: 0
|
||||
description: "A superb runner"
|
||||
agent_labels: ["debian", "gpu"]
|
||||
deleted: 0
|
||||
- id: 719932
|
||||
uuid: "b0a0a168-1b08-4365-95dd-e65040ca384d"
|
||||
name: "runner-2"
|
||||
version: "11.3.1"
|
||||
owner_id: 2 # Owned by user2
|
||||
repo_id: 0
|
||||
description: "An exclusive runner"
|
||||
agent_labels: ["docker"]
|
||||
deleted: 0
|
||||
- id: 719933
|
||||
uuid: "974f9caf-ee64-4022-a56b-67b28ea06a0d"
|
||||
name: "runner-3"
|
||||
version: "12.2.0"
|
||||
owner_id: 0
|
||||
repo_id: 0
|
||||
description: "A runner for everyone"
|
||||
agent_labels: ["docker"]
|
||||
deleted: 0
|
||||
- id: 719934
|
||||
uuid: "022650a8-e999-4a3d-afb0-21f75233798b"
|
||||
name: "runner-4"
|
||||
version: "12.1.0"
|
||||
owner_id: 0
|
||||
repo_id: 32 # owned by org3
|
||||
description: ""
|
||||
agent_labels: ["debian"]
|
||||
deleted: 0
|
||||
- id: 719935
|
||||
uuid: "f3031695-87ea-41cf-8d48-21948e83ee17"
|
||||
name: "runner-5"
|
||||
version: "12.7.0"
|
||||
owner_id: 0
|
||||
repo_id: 36 # owned by user2
|
||||
description: ""
|
||||
agent_labels: ["arch"]
|
||||
deleted: 0
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
- id: 719931
|
||||
uuid: "9e0eb762-cbdf-4725-9c90-4298568a2a77"
|
||||
name: "runner-1"
|
||||
version: "dev"
|
||||
owner_id: 3 # Owned by org3
|
||||
repo_id: 0
|
||||
description: "A superb runner"
|
||||
agent_labels: ["debian", "gpu"]
|
||||
deleted: 0
|
||||
- id: 719932
|
||||
uuid: "b0a0a168-1b08-4365-95dd-e65040ca384d"
|
||||
name: "runner-2"
|
||||
version: "11.3.1"
|
||||
owner_id: 2 # Owned by user2
|
||||
repo_id: 0
|
||||
description: "An exclusive runner"
|
||||
agent_labels: ["docker"]
|
||||
deleted: 0
|
||||
- id: 719933
|
||||
uuid: "974f9caf-ee64-4022-a56b-67b28ea06a0d"
|
||||
name: "runner-3"
|
||||
version: "12.2.0"
|
||||
owner_id: 0
|
||||
repo_id: 0
|
||||
description: "A runner for everyone"
|
||||
agent_labels: ["docker"]
|
||||
deleted: 0
|
||||
- id: 719934
|
||||
uuid: "022650a8-e999-4a3d-afb0-21f75233798b"
|
||||
name: "runner-4"
|
||||
version: "12.1.0"
|
||||
owner_id: 0
|
||||
repo_id: 32
|
||||
description: ""
|
||||
agent_labels: ["debian"]
|
||||
deleted: 0
|
||||
|
|
@ -9,7 +9,6 @@ package actions
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
|
|
@ -89,13 +88,6 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa
|
|||
return artifact, nil
|
||||
}
|
||||
|
||||
// IsV4 reports whether the artifact was uploaded via the v4 backend.
|
||||
// The v4 backend stores the whole artifact as a single zip file;
|
||||
// v1-v3 stores each file as a separate row.
|
||||
func (a *ActionArtifact) IsV4() bool {
|
||||
return a.ArtifactName+".zip" == a.ArtifactPath && a.ContentEncoding == "application/zip"
|
||||
}
|
||||
|
||||
func getArtifactByNameAndPath(ctx context.Context, runID int64, name, fpath string) (*ActionArtifact, error) {
|
||||
var art ActionArtifact
|
||||
has, err := db.GetEngine(ctx).Where("run_id = ? AND artifact_name = ? AND artifact_path = ?", runID, name, fpath).Get(&art)
|
||||
|
|
@ -158,32 +150,11 @@ type ActionArtifactMeta struct {
|
|||
Status ArtifactStatus
|
||||
}
|
||||
|
||||
// AggregatedArtifact is the aggregated view of a logical artifact
|
||||
// (one or more rows sharing the same run_id + artifact_name), used by the
|
||||
// public API to represent a single artifact to clients.
|
||||
type AggregatedArtifact struct {
|
||||
ID int64 `xorm:"id"`
|
||||
RunID int64 `xorm:"run_id"`
|
||||
RepoID int64 `xorm:"-"`
|
||||
ArtifactName string `xorm:"artifact_name"`
|
||||
FileSize int64 `xorm:"file_size"`
|
||||
Status ArtifactStatus `xorm:"status"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created_unix"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated_unix"`
|
||||
ExpiredUnix timeutil.TimeStamp `xorm:"expired_unix"`
|
||||
}
|
||||
|
||||
// APIDownloadURL returns the download URL for this artifact under the given
|
||||
// repository API URL prefix (e.g. "https://host/api/v1/repos/owner/name").
|
||||
func (a *AggregatedArtifact) APIDownloadURL(repoAPIURL string) string {
|
||||
return fmt.Sprintf("%s/actions/artifacts/%d/zip", repoAPIURL, a.ID)
|
||||
}
|
||||
|
||||
// ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run
|
||||
func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtifactMeta, error) {
|
||||
arts := make([]*ActionArtifactMeta, 0, 10)
|
||||
return arts, db.GetEngine(ctx).Table("action_artifact").
|
||||
Where(builder.Eq{"run_id": runID}.And(builder.In("status", ArtifactStatusUploadConfirmed, ArtifactStatusExpired))).
|
||||
Where("run_id=? AND (status=? OR status=?)", runID, ArtifactStatusUploadConfirmed, ArtifactStatusExpired).
|
||||
GroupBy("artifact_name").
|
||||
Select("artifact_name, sum(file_size) as file_size, max(status) as status").
|
||||
Find(&arts)
|
||||
|
|
@ -221,94 +192,3 @@ func SetArtifactDeleted(ctx context.Context, artifactID int64) error {
|
|||
_, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)})
|
||||
return err
|
||||
}
|
||||
|
||||
// SetArtifactsOfRunDeleted marks all artifacts of the given run as deleted.
|
||||
func SetArtifactsOfRunDeleted(ctx context.Context, runID int64) error {
|
||||
_, err := db.GetEngine(ctx).
|
||||
Where("run_id=?", runID).
|
||||
Cols("status").
|
||||
Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)})
|
||||
return err
|
||||
}
|
||||
|
||||
// aggregatedArtifactConds returns the common WHERE clause used by aggregated
|
||||
// artifact queries: restrict to visible statuses and apply the caller's filters.
|
||||
// The Status field on opts is ignored — visibility is fixed to UploadConfirmed/Expired.
|
||||
func aggregatedArtifactConds(opts FindArtifactsOptions) builder.Cond {
|
||||
opts.Status = 0
|
||||
return opts.ToConds().And(builder.In("status", ArtifactStatusUploadConfirmed, ArtifactStatusExpired))
|
||||
}
|
||||
|
||||
const aggregatedArtifactSelect = "min(id) as id, run_id, artifact_name, sum(file_size) as file_size, max(status) as status, min(created_unix) as created_unix, max(updated_unix) as updated_unix, max(expired_unix) as expired_unix"
|
||||
|
||||
// ListAggregatedArtifacts returns paginated aggregated artifacts.
|
||||
// Each result represents one logical artifact: a (run_id, artifact_name) group,
|
||||
// with ID = MIN(id), FileSize = SUM(file_size), Status = MAX(status), and
|
||||
// timestamps aggregated accordingly. Status filter in opts is ignored; results
|
||||
// are always restricted to UploadConfirmed and Expired statuses.
|
||||
func ListAggregatedArtifacts(ctx context.Context, opts FindArtifactsOptions) ([]*AggregatedArtifact, int64, error) {
|
||||
cond := aggregatedArtifactConds(opts)
|
||||
|
||||
var countKeys []struct {
|
||||
ID int64 `xorm:"id"`
|
||||
}
|
||||
if err := db.GetEngine(ctx).Table("action_artifact").
|
||||
Where(cond).
|
||||
GroupBy("run_id, artifact_name").
|
||||
Select("min(id) as id").
|
||||
Find(&countKeys); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
total := int64(len(countKeys))
|
||||
|
||||
sess := db.GetEngine(ctx).Table("action_artifact").
|
||||
Where(cond).
|
||||
GroupBy("run_id, artifact_name").
|
||||
Select(aggregatedArtifactSelect).
|
||||
OrderBy("id DESC")
|
||||
|
||||
capacity := 10
|
||||
if opts.PageSize > 0 {
|
||||
sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
|
||||
capacity = opts.PageSize
|
||||
}
|
||||
|
||||
arts := make([]*AggregatedArtifact, 0, capacity)
|
||||
return arts, total, sess.Find(&arts)
|
||||
}
|
||||
|
||||
// GetAggregatedArtifactByID returns the aggregated artifact by its canonical ID
|
||||
// (MIN(id) of the group), scoped to the given repository. Returns util.ErrNotExist
|
||||
// when the ID does not exist, is not canonical for its group, or does not belong to repoID.
|
||||
// The repoID scoping is performed in the query so callers don't need a follow-up check.
|
||||
func GetAggregatedArtifactByID(ctx context.Context, repoID, artifactID int64) (*AggregatedArtifact, error) {
|
||||
var art ActionArtifact
|
||||
has, err := db.GetEngine(ctx).Where(builder.Eq{"id": artifactID, "repo_id": repoID}).Get(&art)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, util.ErrNotExist
|
||||
}
|
||||
|
||||
cond := aggregatedArtifactConds(FindArtifactsOptions{
|
||||
RunID: art.RunID,
|
||||
ArtifactName: art.ArtifactName,
|
||||
})
|
||||
|
||||
meta := new(AggregatedArtifact)
|
||||
has, err = db.GetEngine(ctx).Table("action_artifact").
|
||||
Where(cond).
|
||||
GroupBy("run_id, artifact_name").
|
||||
Select(aggregatedArtifactSelect).
|
||||
Get(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has || meta.ID != artifactID {
|
||||
return nil, util.ErrNotExist
|
||||
}
|
||||
|
||||
meta.RepoID = art.RepoID
|
||||
return meta, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
gouuid "github.com/google/uuid"
|
||||
)
|
||||
|
||||
func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels *[]string, name, version string, ephemeral bool) (*ActionRunner, error) {
|
||||
func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels *[]string, name, version string) (*ActionRunner, error) {
|
||||
uuid, err := gouuid.FromBytes([]byte(token[:16]))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gouuid.FromBytes %v", err)
|
||||
|
|
@ -60,12 +60,11 @@ func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, la
|
|||
//
|
||||
name, _ = util.SplitStringAtByteN(name, 255)
|
||||
|
||||
cols := []string{"name", "owner_id", "repo_id", "version", "ephemeral"}
|
||||
cols := []string{"name", "owner_id", "repo_id", "version"}
|
||||
runner.Name = name
|
||||
runner.OwnerID = ownerID
|
||||
runner.RepoID = repoID
|
||||
runner.Version = version
|
||||
runner.Ephemeral = ephemeral
|
||||
if labels != nil {
|
||||
runner.AgentLabels = *labels
|
||||
cols = append(cols, "agent_labels")
|
||||
|
|
|
|||
|
|
@ -22,11 +22,9 @@ func TestActions_RegisterRunner_Token(t *testing.T) {
|
|||
labels := []string{}
|
||||
name := "runner"
|
||||
version := "v1.2.3"
|
||||
ephemeral := true
|
||||
runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version, ephemeral)
|
||||
runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, name, runner.Name)
|
||||
assert.True(t, runner.Ephemeral)
|
||||
|
||||
assert.Equal(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d")
|
||||
}
|
||||
|
|
@ -46,7 +44,7 @@ func TestActions_RegisterRunner_TokenUpdate(t *testing.T) {
|
|||
"the initial token should match the runner's secret",
|
||||
)
|
||||
|
||||
RegisterRunner(db.DefaultContext, before.OwnerID, before.RepoID, newToken, nil, before.Name, before.Version, false)
|
||||
RegisterRunner(db.DefaultContext, before.OwnerID, before.RepoID, newToken, nil, before.Name, before.Version)
|
||||
|
||||
after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID})
|
||||
|
||||
|
|
@ -68,11 +66,10 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) {
|
|||
token := "0123456789012345678901234567890123456789"
|
||||
name := "runner"
|
||||
version := "v1.2.3"
|
||||
ephemeral := true
|
||||
labels := []string{"woop", "doop"}
|
||||
labelsCopy := labels // labels may be affected by the tested function so we copy them
|
||||
|
||||
runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version, ephemeral)
|
||||
runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, &labels, name, version)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that the returned record has been updated, except for the labels
|
||||
|
|
@ -81,7 +78,6 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) {
|
|||
assert.Equal(t, name, runner.Name)
|
||||
assert.Equal(t, version, runner.Version)
|
||||
assert.Equal(t, labelsCopy, runner.AgentLabels)
|
||||
assert.Equal(t, ephemeral, runner.Ephemeral)
|
||||
|
||||
// Check that whatever is in the DB has been updated, except for the labels
|
||||
after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID})
|
||||
|
|
@ -90,7 +86,6 @@ func TestActions_RegisterRunner_CreateWithLabels(t *testing.T) {
|
|||
assert.Equal(t, name, after.Name)
|
||||
assert.Equal(t, version, after.Version)
|
||||
assert.Equal(t, labelsCopy, after.AgentLabels)
|
||||
assert.Equal(t, ephemeral, after.Ephemeral)
|
||||
}
|
||||
|
||||
func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) {
|
||||
|
|
@ -100,9 +95,8 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) {
|
|||
token := "0123456789012345678901234567890123456789"
|
||||
name := "runner"
|
||||
version := "v1.2.3"
|
||||
ephemeral := true
|
||||
|
||||
runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, nil, name, version, ephemeral)
|
||||
runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, nil, name, version)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that the returned record has been updated, except for the labels
|
||||
|
|
@ -111,7 +105,6 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) {
|
|||
assert.Equal(t, name, runner.Name)
|
||||
assert.Equal(t, version, runner.Version)
|
||||
assert.Equal(t, []string{}, runner.AgentLabels)
|
||||
assert.Equal(t, ephemeral, runner.Ephemeral)
|
||||
|
||||
// Check that whatever is in the DB has been updated, except for the labels
|
||||
after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: runner.ID})
|
||||
|
|
@ -120,7 +113,6 @@ func TestActions_RegisterRunner_CreateWithoutLabels(t *testing.T) {
|
|||
assert.Equal(t, name, after.Name)
|
||||
assert.Equal(t, version, after.Version)
|
||||
assert.Equal(t, []string{}, after.AgentLabels)
|
||||
assert.Equal(t, ephemeral, after.Ephemeral)
|
||||
}
|
||||
|
||||
func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) {
|
||||
|
|
@ -133,11 +125,10 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) {
|
|||
newRepoID := int64(1)
|
||||
newName := "rennur"
|
||||
newVersion := "v4.5.6"
|
||||
ephemeral := true
|
||||
newLabels := []string{"warp", "darp"}
|
||||
labelsCopy := newLabels // labels may be affected by the tested function so we copy them
|
||||
|
||||
runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, &newLabels, newName, newVersion, ephemeral)
|
||||
runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, &newLabels, newName, newVersion)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that the returned record has been updated
|
||||
|
|
@ -146,7 +137,6 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) {
|
|||
assert.Equal(t, newName, runner.Name)
|
||||
assert.Equal(t, newVersion, runner.Version)
|
||||
assert.Equal(t, labelsCopy, runner.AgentLabels)
|
||||
assert.Equal(t, ephemeral, runner.Ephemeral)
|
||||
|
||||
// Check that whatever is in the DB has been updated
|
||||
after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID})
|
||||
|
|
@ -155,7 +145,6 @@ func TestActions_RegisterRunner_UpdateWithLabels(t *testing.T) {
|
|||
assert.Equal(t, newName, after.Name)
|
||||
assert.Equal(t, newVersion, after.Version)
|
||||
assert.Equal(t, labelsCopy, after.AgentLabels)
|
||||
assert.Equal(t, ephemeral, after.Ephemeral)
|
||||
}
|
||||
|
||||
func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) {
|
||||
|
|
@ -168,9 +157,8 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) {
|
|||
newRepoID := int64(1)
|
||||
newName := "rennur"
|
||||
newVersion := "v4.5.6"
|
||||
ephemeral := true
|
||||
|
||||
runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, nil, newName, newVersion, ephemeral)
|
||||
runner, err := RegisterRunner(db.DefaultContext, newOwnerID, newRepoID, token, nil, newName, newVersion)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that the returned record has been updated, except for the labels
|
||||
|
|
@ -179,7 +167,6 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) {
|
|||
assert.Equal(t, newName, runner.Name)
|
||||
assert.Equal(t, newVersion, runner.Version)
|
||||
assert.Equal(t, before.AgentLabels, runner.AgentLabels)
|
||||
assert.Equal(t, ephemeral, runner.Ephemeral)
|
||||
|
||||
// Check that whatever is in the DB has been updated, except for the labels
|
||||
after := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: recordID})
|
||||
|
|
@ -188,5 +175,4 @@ func TestActions_RegisterRunner_UpdateWithoutLabels(t *testing.T) {
|
|||
assert.Equal(t, newName, after.Name)
|
||||
assert.Equal(t, newVersion, after.Version)
|
||||
assert.Equal(t, before.AgentLabels, after.AgentLabels)
|
||||
assert.Equal(t, ephemeral, after.Ephemeral)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,6 @@ const (
|
|||
ErrorCodeIncompleteRunsOnMissingOutput
|
||||
ErrorCodeIncompleteRunsOnMissingMatrixDimension
|
||||
ErrorCodeIncompleteRunsOnUnknownCause
|
||||
ErrorCodeIncompleteWithMissingJob
|
||||
ErrorCodeIncompleteWithMissingOutput
|
||||
ErrorCodeIncompleteWithMissingMatrixDimension
|
||||
ErrorCodeIncompleteWithUnknownCause
|
||||
ErrorCodeUnknownJobInNeeds
|
||||
)
|
||||
|
||||
func TranslatePreExecutionError(lang translation.Locale, run *ActionRun) string {
|
||||
|
|
@ -62,16 +57,6 @@ func TranslatePreExecutionError(lang translation.Locale, run *ActionRun) string
|
|||
return lang.TrString("actions.workflow.incomplete_runson_missing_matrix_dimension", run.PreExecutionErrorDetails...)
|
||||
case ErrorCodeIncompleteRunsOnUnknownCause:
|
||||
return lang.TrString("actions.workflow.incomplete_runson_unknown_cause", run.PreExecutionErrorDetails...)
|
||||
case ErrorCodeIncompleteWithMissingJob:
|
||||
return lang.TrString("actions.workflow.incomplete_with_missing_job", run.PreExecutionErrorDetails...)
|
||||
case ErrorCodeIncompleteWithMissingOutput:
|
||||
return lang.TrString("actions.workflow.incomplete_with_missing_output", run.PreExecutionErrorDetails...)
|
||||
case ErrorCodeIncompleteWithMissingMatrixDimension:
|
||||
return lang.TrString("actions.workflow.incomplete_with_missing_matrix_dimension", run.PreExecutionErrorDetails...)
|
||||
case ErrorCodeIncompleteWithUnknownCause:
|
||||
return lang.TrString("actions.workflow.incomplete_with_unknown_cause", run.PreExecutionErrorDetails...)
|
||||
case ErrorCodeUnknownJobInNeeds:
|
||||
return lang.TrString("actions.workflow.unknown_job_in_needs", run.PreExecutionErrorDetails...)
|
||||
}
|
||||
return fmt.Sprintf("<unsupported error: code=%v details=%#v", run.PreExecutionErrorCode, run.PreExecutionErrorDetails)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func TestTranslatePreExecutionError(t *testing.T) {
|
|||
PreExecutionErrorCode: ErrorCodeIncompleteMatrixMissingOutput,
|
||||
PreExecutionErrorDetails: []any{"blocked_job", "other_job", "some_output"},
|
||||
},
|
||||
expected: "Unable to evaluate `strategy.matrix` of job blocked_job: job other_job is missing output some_output.",
|
||||
expected: "Unable to evaluate `strategy.matrix` of job blocked_job: job other_job does not have an output some_output.",
|
||||
},
|
||||
{
|
||||
name: "ErrorCodeIncompleteMatrixMissingJob",
|
||||
|
|
@ -84,7 +84,7 @@ func TestTranslatePreExecutionError(t *testing.T) {
|
|||
PreExecutionErrorCode: ErrorCodeIncompleteRunsOnMissingOutput,
|
||||
PreExecutionErrorDetails: []any{"blocked_job", "other_job", "some_output"},
|
||||
},
|
||||
expected: "Unable to evaluate `runs-on` of job blocked_job: job other_job is missing output some_output.",
|
||||
expected: "Unable to evaluate `runs-on` of job blocked_job: job other_job does not have an output some_output.",
|
||||
},
|
||||
{
|
||||
name: "ErrorCodeIncompleteRunsOnMissingJob",
|
||||
|
|
@ -110,38 +110,6 @@ func TestTranslatePreExecutionError(t *testing.T) {
|
|||
},
|
||||
expected: "Unable to evaluate `runs-on` of job blocked_job: unknown error.",
|
||||
},
|
||||
{
|
||||
name: "ErrorCodeIncompleteWithMissingOutput",
|
||||
run: &ActionRun{
|
||||
PreExecutionErrorCode: ErrorCodeIncompleteWithMissingOutput,
|
||||
PreExecutionErrorDetails: []any{"blocked_job", "other_job", "some_output"},
|
||||
},
|
||||
expected: "Unable to evaluate `with` of job blocked_job: job other_job is missing output some_output.",
|
||||
},
|
||||
{
|
||||
name: "ErrorCodeIncompleteWithMissingJob",
|
||||
run: &ActionRun{
|
||||
PreExecutionErrorCode: ErrorCodeIncompleteWithMissingJob,
|
||||
PreExecutionErrorDetails: []any{"blocked_job", "other_job", "needs-1, needs-2"},
|
||||
},
|
||||
expected: "Unable to evaluate `with` of job blocked_job: job other_job is not in the `needs` list of job blocked_job (needs-1, needs-2).",
|
||||
},
|
||||
{
|
||||
name: "ErrorCodeIncompleteWithMissingMatrixDimension",
|
||||
run: &ActionRun{
|
||||
PreExecutionErrorCode: ErrorCodeIncompleteWithMissingMatrixDimension,
|
||||
PreExecutionErrorDetails: []any{"blocked_job", "platfurm"},
|
||||
},
|
||||
expected: "Unable to evaluate `with` of job blocked_job: matrix dimension platfurm does not exist.",
|
||||
},
|
||||
{
|
||||
name: "ErrorCodeIncompleteWithUnknownCause",
|
||||
run: &ActionRun{
|
||||
PreExecutionErrorCode: ErrorCodeIncompleteWithUnknownCause,
|
||||
PreExecutionErrorDetails: []any{"blocked_job"},
|
||||
},
|
||||
expected: "Unable to evaluate `with` of job blocked_job: unknown error.",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -104,21 +104,6 @@ func (run *ActionRun) Link() string {
|
|||
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.Link(), run.Index)
|
||||
}
|
||||
|
||||
func (run *ActionRun) CommitLink() string {
|
||||
if run.Repo == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/commit/%s", run.Repo.Link(), run.CommitSHA)
|
||||
}
|
||||
|
||||
// WorkflowPath returns the path in the git repo to the workflow file that this run was based on
|
||||
func (run *ActionRun) WorkflowPath() string {
|
||||
if run.WorkflowDirectory == "" {
|
||||
return run.WorkflowID
|
||||
}
|
||||
return run.WorkflowDirectory + "/" + run.WorkflowID
|
||||
}
|
||||
|
||||
// RefLink return the url of run's ref
|
||||
func (run *ActionRun) RefLink() string {
|
||||
refName := git.RefName(run.Ref)
|
||||
|
|
@ -153,9 +138,7 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
|||
|
||||
if run.TriggerUser == nil {
|
||||
u, err := user_model.GetPossibleUserByID(ctx, run.TriggerUserID)
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
u = user_model.NewGhostUser()
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
run.TriggerUser = u
|
||||
|
|
@ -225,65 +208,6 @@ func (run *ActionRun) SetDefaultConcurrencyGroup() {
|
|||
))
|
||||
}
|
||||
|
||||
func (run *ActionRun) FindOuterWorkflowCall(ctx context.Context, innerCall *ActionRunJob) (*ActionRunJob, error) {
|
||||
allJobs, err := GetRunJobsByRunID(ctx, run.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure to get run jobs: %w", err)
|
||||
}
|
||||
if innerCall.workflowPayloadDecoded == nil || innerCall.workflowPayloadDecoded.Metadata.WorkflowCallParent == "" {
|
||||
return nil, errors.New("invalid state for FindOuterWorkflowCall")
|
||||
}
|
||||
parent := innerCall.workflowPayloadDecoded.Metadata.WorkflowCallParent
|
||||
for _, job := range allJobs {
|
||||
if job.ID == innerCall.ID {
|
||||
continue
|
||||
}
|
||||
swf, err := job.DecodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if swf.Metadata.WorkflowCallID == parent {
|
||||
return job, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no workflow call with ID %s found in run %d", parent, run.ID)
|
||||
}
|
||||
|
||||
func (run *ActionRun) IsScheduledRun() bool {
|
||||
return run.TriggerEvent == "schedule"
|
||||
}
|
||||
|
||||
func (run *ActionRun) IsDispatchedRun() bool {
|
||||
return run.TriggerEvent == "workflow_dispatch"
|
||||
}
|
||||
|
||||
// IsValid indicates whether this ActionRun is valid and can be run.
|
||||
func (run *ActionRun) IsValid() bool {
|
||||
return run.PreExecutionErrorCode == 0 && run.PreExecutionError == ""
|
||||
}
|
||||
|
||||
// CanBeRerun indicates whether this ActionRun can be rerun.
|
||||
func (run *ActionRun) CanBeRerun() bool {
|
||||
if !run.IsValid() {
|
||||
return false
|
||||
}
|
||||
return run.Status.IsDone()
|
||||
}
|
||||
|
||||
func (run *ActionRun) PrepareNextAttempt() error {
|
||||
if run.Status != StatusUnknown && !run.Status.IsDone() {
|
||||
return fmt.Errorf("cannot prepare next attempt because run %d is active: %s", run.ID, run.Status.String())
|
||||
}
|
||||
|
||||
run.PreviousDuration = run.Duration()
|
||||
|
||||
run.Status = StatusWaiting
|
||||
run.Started = 0
|
||||
run.Stopped = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionsCountOpenCacheKey(repoID int64) string {
|
||||
return fmt.Sprintf("Actions:CountOpenActionRuns:%d", repoID)
|
||||
}
|
||||
|
|
@ -347,7 +271,7 @@ func UpdateRunApprovalByID(ctx context.Context, id int64, approval ApprovalType,
|
|||
func GetRunsNotDoneByRepoIDAndPullRequestPosterID(ctx context.Context, repoID, pullRequestPosterID int64) ([]*ActionRun, error) {
|
||||
var runs []*ActionRun
|
||||
// performance relies on indexes on repo_id and status
|
||||
if err := db.GetEngine(ctx).Where("repo_id=? AND pull_request_poster_id=?", repoID, pullRequestPosterID).And(builder.In("status", PendingStatuses())).Find(&runs); err != nil {
|
||||
if err := db.GetEngine(ctx).Where("repo_id=? AND pull_request_poster_id=?", repoID, pullRequestPosterID).And(builder.In("status", []Status{StatusUnknown, StatusWaiting, StatusRunning, StatusBlocked})).Find(&runs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return runs, nil
|
||||
|
|
@ -356,7 +280,7 @@ func GetRunsNotDoneByRepoIDAndPullRequestPosterID(ctx context.Context, repoID, p
|
|||
func GetRunsNotDoneByRepoIDAndPullRequestID(ctx context.Context, repoID, pullRequestID int64) ([]*ActionRun, error) {
|
||||
var runs []*ActionRun
|
||||
// performance relies on indexes on repo_id and status
|
||||
if err := db.GetEngine(ctx).Where("repo_id=? AND pull_request_id=?", repoID, pullRequestID).And(builder.In("status", PendingStatuses())).Find(&runs); err != nil {
|
||||
if err := db.GetEngine(ctx).Where("repo_id=? AND pull_request_id=?", repoID, pullRequestID).And(builder.In("status", []Status{StatusUnknown, StatusWaiting, StatusRunning, StatusBlocked})).Find(&runs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return runs, nil
|
||||
|
|
@ -366,11 +290,11 @@ func GetRunsNotDoneByRepoIDAndPullRequestID(ctx context.Context, repoID, pullReq
|
|||
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
||||
// We don't have to send the ActionRunNowDone notification here because there are no runs that start in a not done status.
|
||||
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
ctx, commiter, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
defer commiter.Close()
|
||||
|
||||
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
||||
if err != nil {
|
||||
|
|
@ -397,7 +321,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
|
|||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
return commiter.Commit()
|
||||
}
|
||||
|
||||
// Adds `ActionRunJob` instances from `SingleWorkflows` to an existing ActionRun.
|
||||
|
|
@ -418,7 +342,7 @@ func InsertRunJobs(ctx context.Context, run *ActionRun, jobs []*jobparser.Single
|
|||
}
|
||||
payload, _ = v.Marshal()
|
||||
|
||||
if len(needs) > 0 || run.NeedApproval || v.IncompleteMatrix || v.IncompleteRunsOn || v.IncompleteWith {
|
||||
if len(needs) > 0 || run.NeedApproval || v.IncompleteMatrix || v.IncompleteRunsOn {
|
||||
status = StatusBlocked
|
||||
} else {
|
||||
status = StatusWaiting
|
||||
|
|
@ -427,8 +351,7 @@ func InsertRunJobs(ctx context.Context, run *ActionRun, jobs []*jobparser.Single
|
|||
name, _ = util.SplitStringAtByteN(job.Name, 255)
|
||||
runsOn = job.RunsOn()
|
||||
}
|
||||
|
||||
runJob := &ActionRunJob{
|
||||
runJobs = append(runJobs, &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RepoID: run.RepoID,
|
||||
OwnerID: run.OwnerID,
|
||||
|
|
@ -439,12 +362,8 @@ func InsertRunJobs(ctx context.Context, run *ActionRun, jobs []*jobparser.Single
|
|||
JobID: id,
|
||||
Needs: needs,
|
||||
RunsOn: runsOn,
|
||||
}
|
||||
if err := runJob.PrepareNextAttempt(status); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runJobs = append(runJobs, runJob)
|
||||
Status: status,
|
||||
})
|
||||
}
|
||||
|
||||
if len(runJobs) > 0 {
|
||||
|
|
@ -612,11 +531,4 @@ func ComputeRunStatus(ctx context.Context, runID int64) (run *ActionRun, columns
|
|||
return run, columns, nil
|
||||
}
|
||||
|
||||
// DeleteRun removes the given run. It is the caller's responsibility to handle the run's dependencies like artifacts or
|
||||
// jobs. Nothing happens if the run does not exist.
|
||||
func DeleteRun(ctx context.Context, runID int64) error {
|
||||
_, err := db.GetEngine(ctx).Delete(&ActionRun{ID: runID})
|
||||
return err
|
||||
}
|
||||
|
||||
type ActionRunIndex db.ResourceIndex
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import (
|
|||
"forgejo.org/modules/util"
|
||||
|
||||
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
|
||||
gouuid "github.com/google/uuid"
|
||||
"go.yaml.in/yaml/v3"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
|
@ -31,7 +30,6 @@ type ActionRunJob struct {
|
|||
IsForkPullRequest bool
|
||||
Name string `xorm:"VARCHAR(255)"`
|
||||
Attempt int64
|
||||
Handle string `xorm:"unique"`
|
||||
WorkflowPayload []byte
|
||||
JobID string `xorm:"VARCHAR(255)"` // job id in workflow, not job's id
|
||||
Needs []string `xorm:"JSON TEXT"`
|
||||
|
|
@ -110,12 +108,6 @@ func (job *ActionRunJob) LoadAttributes(ctx context.Context) error {
|
|||
return job.Run.LoadAttributes(ctx)
|
||||
}
|
||||
|
||||
// IsRequestedByRunner returns true if this attempt of this ActionRunJob was explicitly requested by the runner or if
|
||||
// the runner expressed no preference.
|
||||
func (job *ActionRunJob) IsRequestedByRunner(handle *string) bool {
|
||||
return handle == nil || job.Handle == *handle
|
||||
}
|
||||
|
||||
func (job *ActionRunJob) ItRunsOn(labels []string) bool {
|
||||
if len(labels) == 0 || len(job.RunsOn) == 0 {
|
||||
return false
|
||||
|
|
@ -125,34 +117,6 @@ func (job *ActionRunJob) ItRunsOn(labels []string) bool {
|
|||
return labelSet.IsSubset(job.RunsOn)
|
||||
}
|
||||
|
||||
func (job *ActionRunJob) PrepareNextAttempt(initialStatus Status) error {
|
||||
if job.Status != StatusUnknown && !job.Status.IsDone() {
|
||||
return fmt.Errorf("cannot prepare next attempt because job %d is active: %s", job.ID, job.Status.String())
|
||||
}
|
||||
|
||||
job.Attempt++
|
||||
job.Started = 0
|
||||
job.Stopped = 0
|
||||
job.TaskID = 0
|
||||
job.Handle = gouuid.New().String()
|
||||
job.Status = initialStatus
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanBeRerun answers whether this ActionRunJob can be rerun. Returns true if it is done and the Run it belongs to
|
||||
// is valid. Returns false in all other cases.
|
||||
func (job *ActionRunJob) CanBeRerun(ctx context.Context) (bool, error) {
|
||||
if err := job.LoadRun(ctx); err != nil {
|
||||
return false, fmt.Errorf("cannot load run %d of job %d: %w", job.RunID, job.ID, err)
|
||||
}
|
||||
|
||||
if !job.Run.IsValid() {
|
||||
return false, nil
|
||||
}
|
||||
return job.Status.IsDone(), nil
|
||||
}
|
||||
|
||||
func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) {
|
||||
var job ActionRunJob
|
||||
has, err := db.GetEngine(ctx).Where("id=?", id).Get(&job)
|
||||
|
|
@ -270,9 +234,7 @@ var AggregateJobStatus = func(jobs []*ActionRunJob) Status {
|
|||
}
|
||||
}
|
||||
|
||||
// Retrieves the parsed workflow for this specific job. This field is often accessed multiple times in succession, so
|
||||
// the parsed content is cached in-memory on the `ActionRunJob` instance.
|
||||
func (job *ActionRunJob) DecodeWorkflowPayload() (*jobparser.SingleWorkflow, error) {
|
||||
func (job *ActionRunJob) decodeWorkflowPayload() (*jobparser.SingleWorkflow, error) {
|
||||
if job.workflowPayloadDecoded != nil {
|
||||
return job.workflowPayloadDecoded, nil
|
||||
}
|
||||
|
|
@ -296,8 +258,8 @@ func (job *ActionRunJob) ClearCachedWorkflowPayload() {
|
|||
// Checks whether the target job is an `(incomplete matrix)` job that will be blocked until the matrix is complete, and
|
||||
// then regenerated and deleted. If it is incomplete, and if the information is available, the specific job and/or
|
||||
// output that causes it to be incomplete will be returned as well.
|
||||
func (job *ActionRunJob) HasIncompleteMatrix() (bool, *jobparser.IncompleteNeeds, error) {
|
||||
jobWorkflow, err := job.DecodeWorkflowPayload()
|
||||
func (job *ActionRunJob) IsIncompleteMatrix() (bool, *jobparser.IncompleteNeeds, error) {
|
||||
jobWorkflow, err := job.decodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return false, nil, fmt.Errorf("failure decoding workflow payload: %w", err)
|
||||
}
|
||||
|
|
@ -306,69 +268,10 @@ func (job *ActionRunJob) HasIncompleteMatrix() (bool, *jobparser.IncompleteNeeds
|
|||
|
||||
// Checks whether the target job has a `runs-on` field with an expression that requires an input from another job. The
|
||||
// job will be blocked until the other job is complete, and then regenerated and deleted.
|
||||
func (job *ActionRunJob) HasIncompleteRunsOn() (bool, *jobparser.IncompleteNeeds, *jobparser.IncompleteMatrix, error) {
|
||||
jobWorkflow, err := job.DecodeWorkflowPayload()
|
||||
func (job *ActionRunJob) IsIncompleteRunsOn() (bool, *jobparser.IncompleteNeeds, *jobparser.IncompleteMatrix, error) {
|
||||
jobWorkflow, err := job.decodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return false, nil, nil, fmt.Errorf("failure decoding workflow payload: %w", err)
|
||||
}
|
||||
return jobWorkflow.IncompleteRunsOn, jobWorkflow.IncompleteRunsOnNeeds, jobWorkflow.IncompleteRunsOnMatrix, nil
|
||||
}
|
||||
|
||||
// Check whether the target job was generated as a result of expanding a reusable workflow.
|
||||
func (job *ActionRunJob) IsWorkflowCallInnerJob() (bool, error) {
|
||||
jobWorkflow, err := job.DecodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failure decoding workflow payload: %w", err)
|
||||
}
|
||||
return jobWorkflow.Metadata.WorkflowCallParent != "", nil
|
||||
}
|
||||
|
||||
// Check whether this job is a caller of a reusable workflow -- in other words, the real work done in this job is in
|
||||
// spawned child jobs, not this job.
|
||||
func (job *ActionRunJob) IsWorkflowCallOuterJob() (bool, error) {
|
||||
jobWorkflow, err := job.DecodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failure decoding workflow payload: %w", err)
|
||||
}
|
||||
return jobWorkflow.Metadata.WorkflowCallID != "", nil
|
||||
}
|
||||
|
||||
// Checks whether the target job has a `with` field with an expression that requires an input from another job. The job
|
||||
// will be blocked until the other job is complete, and then regenerated and deleted.
|
||||
func (job *ActionRunJob) HasIncompleteWith() (bool, *jobparser.IncompleteNeeds, *jobparser.IncompleteMatrix, error) {
|
||||
jobWorkflow, err := job.DecodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return false, nil, nil, fmt.Errorf("failure decoding workflow payload: %w", err)
|
||||
}
|
||||
return jobWorkflow.IncompleteWith, jobWorkflow.IncompleteWithNeeds, jobWorkflow.IncompleteWithMatrix, nil
|
||||
}
|
||||
|
||||
// EnableOpenIDConnect checks whether the job allows for ID token generation.
|
||||
func (job *ActionRunJob) EnableOpenIDConnect() (bool, error) {
|
||||
jobWorkflow, err := job.DecodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failure decoding workflow payload: %w", err)
|
||||
}
|
||||
return jobWorkflow.EnableOpenIDConnect, nil
|
||||
}
|
||||
|
||||
// AllNeedsExist checks whether this ActionRunJob's Needs can theoretically be met by comparing them with the supplied
|
||||
// list of all job IDs that part of a particular workflow run. Returns the list of unknown job IDs found in Needs
|
||||
// alongside an indicator whether the check was successful.
|
||||
func (job *ActionRunJob) AllNeedsExist(allExistingJobIDs container.Set[string]) ([]string, bool) {
|
||||
unknownJobIDs := []string{}
|
||||
for _, need := range job.Needs {
|
||||
if !allExistingJobIDs.Contains(need) {
|
||||
unknownJobIDs = append(unknownJobIDs, need)
|
||||
}
|
||||
}
|
||||
|
||||
return unknownJobIDs, len(unknownJobIDs) == 0
|
||||
}
|
||||
|
||||
// DeleteJob removes the given job. Removing all associated tasks is up to the caller. If the given job does not exist,
|
||||
// nothing happens.
|
||||
func DeleteJob(ctx context.Context, jobID int64) error {
|
||||
_, err := db.GetEngine(ctx).Delete(&ActionRunJob{ID: jobID})
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,6 @@ func (jobs ActionJobList) GetRunIDs() []int64 {
|
|||
})
|
||||
}
|
||||
|
||||
func (jobs ActionJobList) GetJobIDs() container.Set[string] {
|
||||
jobIDs := container.SetOf[string]()
|
||||
for _, job := range jobs {
|
||||
jobIDs.Add(job.JobID)
|
||||
}
|
||||
return jobIDs
|
||||
}
|
||||
|
||||
func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error {
|
||||
runIDs := jobs.GetRunIDs()
|
||||
runs := make(map[int64]*ActionRun, len(runIDs))
|
||||
|
|
@ -63,6 +55,8 @@ type FindRunJobOptions struct {
|
|||
CommitSHA string
|
||||
Statuses []Status
|
||||
UpdatedBefore timeutil.TimeStamp
|
||||
Events []string // []webhook_module.HookEventType
|
||||
RunNumber int64
|
||||
RunNeedsApproval optional.Option[bool]
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +68,7 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
|
|||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
if opts.OwnerID != 0 {
|
||||
if opts.OwnerID > 0 {
|
||||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
}
|
||||
if opts.CommitSHA != "" {
|
||||
|
|
@ -86,10 +80,16 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
|
|||
if opts.UpdatedBefore > 0 {
|
||||
cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
|
||||
}
|
||||
if has, value := opts.RunNeedsApproval.Get(); has {
|
||||
if len(opts.Events) > 0 {
|
||||
cond = cond.And(builder.In("event", opts.Events))
|
||||
}
|
||||
if opts.RunNumber > 0 {
|
||||
cond = cond.And(builder.Eq{"`index`": opts.RunNumber})
|
||||
}
|
||||
if opts.RunNeedsApproval.Has() {
|
||||
cond = cond.And(builder.Exists(builder.Select("id").From("action_run", "outer_run").
|
||||
Where(builder.Eq{
|
||||
"outer_run.need_approval": value,
|
||||
"outer_run.need_approval": opts.RunNeedsApproval.Value(),
|
||||
"outer_run.id": builder.Expr("run_id"),
|
||||
})))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"forgejo.org/modules/container"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestActionJobList_GetJobIDs(t *testing.T) {
|
||||
jobs := ActionJobList{
|
||||
&ActionRunJob{JobID: "job 1"},
|
||||
&ActionRunJob{JobID: "job 2"},
|
||||
}
|
||||
|
||||
assert.Equal(t, container.SetOf("job 2", "job 1"), jobs.GetJobIDs())
|
||||
}
|
||||
|
|
@ -8,8 +8,6 @@ import (
|
|||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/container"
|
||||
"forgejo.org/modules/timeutil"
|
||||
|
||||
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -74,7 +72,7 @@ func TestActionRunJob_HTMLURL(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestActionRunJob_HasIncompleteMatrix(t *testing.T) {
|
||||
func TestActionRunJob_IsIncompleteMatrix(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
|
|
@ -102,7 +100,7 @@ func TestActionRunJob_HasIncompleteMatrix(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isIncomplete, needs, err := tt.job.HasIncompleteMatrix()
|
||||
isIncomplete, needs, err := tt.job.IsIncompleteMatrix()
|
||||
if tt.errContains != "" {
|
||||
assert.ErrorContains(t, err, tt.errContains)
|
||||
} else {
|
||||
|
|
@ -114,7 +112,7 @@ func TestActionRunJob_HasIncompleteMatrix(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestActionRunJob_HasIncompleteRunsOn(t *testing.T) {
|
||||
func TestActionRunJob_IsIncompleteRunsOn(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
|
|
@ -149,129 +147,7 @@ func TestActionRunJob_HasIncompleteRunsOn(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isIncomplete, needs, matrix, err := tt.job.HasIncompleteRunsOn()
|
||||
if tt.errContains != "" {
|
||||
assert.ErrorContains(t, err, tt.errContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.isIncomplete, isIncomplete)
|
||||
assert.Equal(t, tt.needs, needs)
|
||||
assert.Equal(t, tt.matrix, matrix)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRunJob_IsWorkflowCallOuterJob(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
isWorkflowCallOuterJob bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "normal workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: workflow")},
|
||||
isWorkflowCallOuterJob: false,
|
||||
},
|
||||
{
|
||||
name: "workflow_call outer job",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: test\njobs:\n job:\n if: false\n__metadata:\n workflow_call_id: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed\n")},
|
||||
isWorkflowCallOuterJob: true,
|
||||
},
|
||||
{
|
||||
name: "unparseable workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: []\nincomplete_runs_on: true")},
|
||||
errContains: "failure unmarshaling WorkflowPayload to SingleWorkflow: yaml: unmarshal errors",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isWorkflowCallOuterJob, err := tt.job.IsWorkflowCallOuterJob()
|
||||
if tt.errContains != "" {
|
||||
assert.ErrorContains(t, err, tt.errContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.isWorkflowCallOuterJob, isWorkflowCallOuterJob)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRunJob_IsWorkflowCallInnerJob(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
isWorkflowCallInnerJob bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "normal workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("on: [workflow_dispatch]\nname: workflow")},
|
||||
isWorkflowCallInnerJob: false,
|
||||
},
|
||||
{
|
||||
name: "inner job",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("on:\n workflow_call:\nname: workflow\n__metadata:\n workflow_call_parent: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed\n")},
|
||||
isWorkflowCallInnerJob: true,
|
||||
},
|
||||
{
|
||||
name: "unparseable workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: []\nincomplete_runs_on: true")},
|
||||
errContains: "failure unmarshaling WorkflowPayload to SingleWorkflow: yaml: unmarshal errors",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isWorkflowCallInnerJob, err := tt.job.IsWorkflowCallInnerJob()
|
||||
if tt.errContains != "" {
|
||||
assert.ErrorContains(t, err, tt.errContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.isWorkflowCallInnerJob, isWorkflowCallInnerJob)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRunJob_HasIncompleteWith(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
isIncomplete bool
|
||||
needs *jobparser.IncompleteNeeds
|
||||
matrix *jobparser.IncompleteMatrix
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "normal workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: workflow")},
|
||||
isIncomplete: false,
|
||||
},
|
||||
{
|
||||
name: "incomplete_with workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: workflow\nincomplete_with: true\nincomplete_with_needs: { job: abc }")},
|
||||
needs: &jobparser.IncompleteNeeds{Job: "abc"},
|
||||
isIncomplete: true,
|
||||
},
|
||||
{
|
||||
name: "incomplete_with workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: workflow\nincomplete_with: true\nincomplete_with_matrix: { dimension: abc }")},
|
||||
matrix: &jobparser.IncompleteMatrix{Dimension: "abc"},
|
||||
isIncomplete: true,
|
||||
},
|
||||
{
|
||||
name: "unparseable workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: []\nincomplete_with: true")},
|
||||
errContains: "failure unmarshaling WorkflowPayload to SingleWorkflow: yaml: unmarshal errors",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isIncomplete, needs, matrix, err := tt.job.HasIncompleteWith()
|
||||
isIncomplete, needs, matrix, err := tt.job.IsIncompleteRunsOn()
|
||||
if tt.errContains != "" {
|
||||
assert.ErrorContains(t, err, tt.errContains)
|
||||
} else {
|
||||
|
|
@ -303,202 +179,3 @@ func TestRunHasOtherJobs(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
||||
func TestActionRunJobPrepareNextAttempt(t *testing.T) {
|
||||
lastHandle := "original-handle"
|
||||
job := ActionRunJob{ID: 46, Handle: lastHandle}
|
||||
|
||||
err := job.PrepareNextAttempt(StatusWaiting)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotEqual(t, lastHandle, job.Handle)
|
||||
assert.NotEmpty(t, job.Handle)
|
||||
assert.Equal(t, int64(1), job.Attempt)
|
||||
assert.Zero(t, job.Started)
|
||||
assert.Zero(t, job.Stopped)
|
||||
assert.Zero(t, job.TaskID)
|
||||
assert.Equal(t, StatusWaiting, job.Status)
|
||||
|
||||
lastHandle = job.Handle
|
||||
job.Started = timeutil.TimeStampNow()
|
||||
job.Stopped = timeutil.TimeStampNow()
|
||||
job.TaskID = int64(59)
|
||||
job.Status = StatusFailure
|
||||
|
||||
err = job.PrepareNextAttempt(StatusBlocked)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotEqual(t, lastHandle, job.Handle)
|
||||
assert.NotEmpty(t, job.Handle)
|
||||
assert.Equal(t, int64(2), job.Attempt)
|
||||
assert.Zero(t, job.Started)
|
||||
assert.Zero(t, job.Stopped)
|
||||
assert.Zero(t, job.TaskID)
|
||||
assert.Equal(t, StatusBlocked, job.Status)
|
||||
|
||||
lastHandle = job.Handle
|
||||
|
||||
// The job hasn't finished yet. Preparing a next attempt should not be possible. It should be left untouched.
|
||||
err = job.PrepareNextAttempt(StatusWaiting)
|
||||
require.ErrorContains(t, err, "cannot prepare next attempt because job 46 is active: blocked")
|
||||
|
||||
assert.Equal(t, lastHandle, job.Handle)
|
||||
assert.Equal(t, int64(2), job.Attempt)
|
||||
assert.Zero(t, job.Started)
|
||||
assert.Zero(t, job.Stopped)
|
||||
assert.Zero(t, job.TaskID)
|
||||
assert.Equal(t, StatusBlocked, job.Status)
|
||||
}
|
||||
|
||||
func TestIsRequestedByRunner(t *testing.T) {
|
||||
sameHandle := "4a1ca0be-4470-486d-8504-89b4a5ac00cf"
|
||||
differentHandle := "88423da3-67af-4f2d-9a92-a0db822697e9"
|
||||
emptyHandle := ""
|
||||
|
||||
job := &ActionRunJob{ID: 422, Attempt: 5, Handle: sameHandle}
|
||||
|
||||
assert.True(t, job.IsRequestedByRunner(nil))
|
||||
assert.True(t, job.IsRequestedByRunner(&sameHandle))
|
||||
assert.False(t, job.IsRequestedByRunner(&differentHandle))
|
||||
assert.False(t, job.IsRequestedByRunner(&emptyHandle))
|
||||
|
||||
// Old jobs that were created before the introduction of Handle do not have one.
|
||||
emptyHandleJob := &ActionRunJob{ID: 422, Attempt: 5, Handle: ""}
|
||||
|
||||
assert.True(t, emptyHandleJob.IsRequestedByRunner(nil))
|
||||
assert.True(t, emptyHandleJob.IsRequestedByRunner(&emptyHandle))
|
||||
|
||||
assert.False(t, emptyHandleJob.IsRequestedByRunner(&differentHandle))
|
||||
}
|
||||
|
||||
func TestAllNeedsExist(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
existingJobIDs container.Set[string]
|
||||
expectedUnknownIDs []string
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
name: "no needs",
|
||||
job: ActionRunJob{Needs: nil},
|
||||
existingJobIDs: container.Set[string]{},
|
||||
expectedUnknownIDs: []string{},
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "empty needs",
|
||||
job: ActionRunJob{Needs: []string{}},
|
||||
existingJobIDs: container.Set[string]{},
|
||||
expectedUnknownIDs: []string{},
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "satisfied needs",
|
||||
job: ActionRunJob{Needs: []string{"job1", "job2"}},
|
||||
existingJobIDs: container.SetOf("job2", "job1"),
|
||||
expectedUnknownIDs: []string{},
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "unsatisfied needs",
|
||||
job: ActionRunJob{Needs: []string{"unknown", "job2"}},
|
||||
existingJobIDs: container.SetOf("job2", "job1"),
|
||||
expectedUnknownIDs: []string{"unknown"},
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
name: "comparison is case-sensitive",
|
||||
job: ActionRunJob{Needs: []string{"Job1", "job2"}},
|
||||
existingJobIDs: container.SetOf("job2", "job1"),
|
||||
expectedUnknownIDs: []string{"Job1"},
|
||||
ok: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
unknownIDs, ok := testCase.job.AllNeedsExist(testCase.existingJobIDs)
|
||||
|
||||
assert.Equal(t, testCase.ok, ok)
|
||||
assert.Equal(t, testCase.expectedUnknownIDs, unknownIDs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRunJob_CanBeRerun(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
canBeRerun bool
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "job with unknown status",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusUnknown},
|
||||
canBeRerun: false,
|
||||
},
|
||||
{
|
||||
name: "successful job",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusSuccess},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "failed job",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusFailure},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "cancelled job",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusCancelled},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "skipped job",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusSkipped},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "waiting job",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusWaiting},
|
||||
canBeRerun: false,
|
||||
},
|
||||
{
|
||||
name: "blocked job",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusSuccess}, Status: StatusBlocked},
|
||||
canBeRerun: false,
|
||||
},
|
||||
{
|
||||
name: "ActionRun is nil",
|
||||
job: ActionRunJob{ID: 12, Run: nil, Status: StatusSuccess},
|
||||
expectedError: "cannot load run 0 of job 12",
|
||||
},
|
||||
{
|
||||
name: "with busy run but completed job",
|
||||
job: ActionRunJob{Run: &ActionRun{Status: StatusRunning}, Status: StatusSuccess},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "with run that cannot be run",
|
||||
job: ActionRunJob{
|
||||
Run: &ActionRun{Status: StatusRunning, PreExecutionErrorCode: ErrorCodeEventDetectionError},
|
||||
Status: StatusSuccess,
|
||||
},
|
||||
canBeRerun: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
result, err := testCase.job.CanBeRerun(t.Context())
|
||||
|
||||
if testCase.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.ErrorContains(t, err, testCase.expectedError)
|
||||
}
|
||||
|
||||
assert.Equal(t, testCase.canBeRerun, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package actions
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
|
|
@ -14,8 +13,6 @@ import (
|
|||
"forgejo.org/modules/translation"
|
||||
webhook_module "forgejo.org/modules/webhook"
|
||||
|
||||
"golang.org/x/text/collate"
|
||||
"golang.org/x/text/language"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
|
|
@ -75,9 +72,6 @@ type FindRunOptions struct {
|
|||
TriggerEvent webhook_module.HookEventType
|
||||
Approved bool // not util.OptionalBool, it works only when it's true
|
||||
Status []Status
|
||||
Events []string // []webhook_module.HookEventType
|
||||
RunNumber int64
|
||||
CommitSHA string
|
||||
}
|
||||
|
||||
func (opts FindRunOptions) ToConds() builder.Cond {
|
||||
|
|
@ -85,7 +79,7 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
|||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
if opts.OwnerID != 0 {
|
||||
if opts.OwnerID > 0 {
|
||||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
}
|
||||
if opts.WorkflowID != "" {
|
||||
|
|
@ -106,15 +100,6 @@ func (opts FindRunOptions) ToConds() builder.Cond {
|
|||
if opts.TriggerEvent != "" {
|
||||
cond = cond.And(builder.Eq{"trigger_event": opts.TriggerEvent})
|
||||
}
|
||||
if len(opts.Events) > 0 {
|
||||
cond = cond.And(builder.In("event", opts.Events))
|
||||
}
|
||||
if opts.RunNumber > 0 {
|
||||
cond = cond.And(builder.Eq{"`index`": opts.RunNumber})
|
||||
}
|
||||
if opts.CommitSHA != "" {
|
||||
cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
|
|
@ -130,18 +115,14 @@ type StatusInfo struct {
|
|||
// GetStatusInfoList returns a slice of StatusInfo
|
||||
func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo {
|
||||
// same as those in aggregateJobStatus
|
||||
allStatus := []Status{StatusBlocked, StatusCancelled, StatusFailure, StatusRunning, StatusSkipped, StatusSuccess, StatusWaiting}
|
||||
statusInfoList := make([]StatusInfo, 0, 7)
|
||||
allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning}
|
||||
statusInfoList := make([]StatusInfo, 0, 4)
|
||||
for _, s := range allStatus {
|
||||
statusInfoList = append(statusInfoList, StatusInfo{
|
||||
Status: int(s),
|
||||
DisplayedStatus: s.LocaleString(lang),
|
||||
})
|
||||
}
|
||||
collator := collate.New(language.Und, collate.IgnoreCase)
|
||||
slices.SortFunc(statusInfoList, func(a, b StatusInfo) int {
|
||||
return collator.CompareString(a.DisplayedStatus, b.DisplayedStatus)
|
||||
})
|
||||
return statusInfoList
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/translation"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestActionStatusList(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
translation.InitLocales(t.Context())
|
||||
|
||||
statusInfoList := GetStatusInfoList(t.Context(), translation.NewLocale("en-US"))
|
||||
assert.Len(t, statusInfoList, 7)
|
||||
statuses := []string{"Blocked", "Canceled", "Failure", "Running", "Skipped", "Success", "Waiting"}
|
||||
statusInts := []int{7, 3, 2, 6, 4, 1, 5}
|
||||
for i, statusString := range statuses {
|
||||
assert.Equal(t, statusInfoList[i].Status, statusInts[i])
|
||||
assert.Equal(t, statusInfoList[i].DisplayedStatus, statusString)
|
||||
}
|
||||
|
||||
statusInfoList = GetStatusInfoList(t.Context(), translation.NewLocale("de-DE"))
|
||||
assert.Len(t, statusInfoList, 7)
|
||||
statuses = []string{"Abgebrochen", "Blockiert", "Erfolg", "Fehler", "Laufend", "Übersprungen", "Wartend"}
|
||||
statusInts = []int{3, 7, 1, 2, 6, 4, 5}
|
||||
for i, statusString := range statuses {
|
||||
assert.Equal(t, statusInfoList[i].Status, statusInts[i])
|
||||
assert.Equal(t, statusInfoList[i].DisplayedStatus, statusString)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,14 +11,15 @@ import (
|
|||
repo_model "forgejo.org/models/repo"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/cache"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/test"
|
||||
|
||||
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetRunBefore(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetConcurrencyGroup(t *testing.T) {
|
||||
run := ActionRun{}
|
||||
run.SetConcurrencyGroup("abc123")
|
||||
|
|
@ -44,135 +45,6 @@ func TestSetDefaultConcurrencyGroup(t *testing.T) {
|
|||
assert.Equal(t, "refs/heads/main_testing_pull_request__auto", run.ConcurrencyGroup)
|
||||
}
|
||||
|
||||
func TestGetWorkflowPath(t *testing.T) {
|
||||
run := ActionRun{
|
||||
WorkflowID: "ci.yml",
|
||||
WorkflowDirectory: ".some/path/to/workflows",
|
||||
}
|
||||
assert.Equal(t, ".some/path/to/workflows/ci.yml", run.WorkflowPath())
|
||||
}
|
||||
|
||||
func TestGetCommitLink(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
run := ActionRun{
|
||||
Repo: repo,
|
||||
CommitSHA: "a356d1f1f82945a039cd16d4ce0137bd55284e77",
|
||||
}
|
||||
assert.Equal(t, "/sub/user2/repo1/commit/a356d1f1f82945a039cd16d4ce0137bd55284e77", run.CommitLink())
|
||||
}
|
||||
|
||||
func TestIsScheduledRun(t *testing.T) {
|
||||
scheduledRun := ActionRun{
|
||||
CommitSHA: "a356d1f1f82945a039cd16d4ce0137bd55284e77",
|
||||
TriggerEvent: "schedule",
|
||||
}
|
||||
pushRun := ActionRun{
|
||||
CommitSHA: "8f9b5c6ab342eb11d7422deecef7195b18058b90",
|
||||
TriggerEvent: "push",
|
||||
}
|
||||
|
||||
assert.True(t, scheduledRun.IsScheduledRun())
|
||||
assert.False(t, pushRun.IsScheduledRun())
|
||||
}
|
||||
|
||||
func TestIsManualRun(t *testing.T) {
|
||||
manualRunRun := ActionRun{
|
||||
CommitSHA: "a356d1f1f82945a039cd16d4ce0137bd55284e77",
|
||||
TriggerEvent: "workflow_dispatch",
|
||||
}
|
||||
pushRun := ActionRun{
|
||||
CommitSHA: "8f9b5c6ab342eb11d7422deecef7195b18058b90",
|
||||
TriggerEvent: "push",
|
||||
}
|
||||
|
||||
assert.True(t, manualRunRun.IsDispatchedRun())
|
||||
assert.False(t, pushRun.IsDispatchedRun())
|
||||
}
|
||||
|
||||
func TestActionRun_IsValid(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
run ActionRun
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid run",
|
||||
run: ActionRun{},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
name: "with pre-execution error",
|
||||
run: ActionRun{PreExecutionErrorCode: ErrorCodeIncompleteRunsOnMissingOutput},
|
||||
isValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
assert.Equal(t, testCase.isValid, testCase.run.IsValid())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRun_CanBeRerun(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
run ActionRun
|
||||
canBeRerun bool
|
||||
}{
|
||||
{
|
||||
name: "run with unknown status",
|
||||
run: ActionRun{Status: StatusUnknown},
|
||||
canBeRerun: false,
|
||||
},
|
||||
{
|
||||
name: "successful run",
|
||||
run: ActionRun{Status: StatusSuccess},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "failed run",
|
||||
run: ActionRun{Status: StatusFailure},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "cancelled run",
|
||||
run: ActionRun{Status: StatusCancelled},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "skipped run",
|
||||
run: ActionRun{Status: StatusSkipped},
|
||||
canBeRerun: true,
|
||||
},
|
||||
{
|
||||
name: "waiting run",
|
||||
run: ActionRun{Status: StatusWaiting},
|
||||
canBeRerun: false,
|
||||
},
|
||||
{
|
||||
name: "blocked run",
|
||||
run: ActionRun{Status: StatusBlocked},
|
||||
canBeRerun: false,
|
||||
},
|
||||
{
|
||||
name: "with pre-execution error",
|
||||
run: ActionRun{PreExecutionErrorCode: ErrorCodeIncompleteRunsOnMissingOutput, Status: StatusSuccess},
|
||||
canBeRerun: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
assert.Equal(t, testCase.canBeRerun, testCase.run.CanBeRerun())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoNumOpenActions(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
err := cache.Init()
|
||||
|
|
@ -401,109 +273,6 @@ jobs:
|
|||
assert.Equal(t, StatusBlocked, job.Status)
|
||||
}
|
||||
|
||||
func TestActionRun_FindOuterWorkflowCall(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
pullRequestPosterID := int64(4)
|
||||
repoID := int64(10)
|
||||
pullRequestID := int64(2)
|
||||
run := &ActionRun{
|
||||
RepoID: repoID,
|
||||
PullRequestID: pullRequestID,
|
||||
PullRequestPosterID: pullRequestPosterID,
|
||||
}
|
||||
|
||||
workflowRaw := []byte(`
|
||||
jobs:
|
||||
outer-job:
|
||||
uses: ./.forgejo/workflows/reusable.yml
|
||||
`)
|
||||
workflows, err := jobparser.Parse(workflowRaw, false,
|
||||
jobparser.WithJobOutputs(map[string]map[string]string{}),
|
||||
jobparser.ExpandLocalReusableWorkflows(func(job *jobparser.Job, path string) ([]byte, error) {
|
||||
return []byte(`
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
inner-job-1:
|
||||
runs-on: debian
|
||||
steps: []
|
||||
inner-job-2:
|
||||
runs-on: debian
|
||||
steps: []
|
||||
`), nil
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, InsertRun(t.Context(), run, workflows))
|
||||
|
||||
jobs, err := db.Find[ActionRunJob](t.Context(), FindRunJobOptions{RunID: run.ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, jobs, 3)
|
||||
|
||||
for _, j := range jobs {
|
||||
t.Run(j.Name, func(t *testing.T) {
|
||||
_, err := j.DecodeWorkflowPayload()
|
||||
require.NoError(t, err)
|
||||
outer, err := run.FindOuterWorkflowCall(t.Context(), j)
|
||||
if j.Name == "outer-job" {
|
||||
require.ErrorContains(t, err, "invalid state for FindOuterWorkflowCall")
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, outer)
|
||||
assert.Equal(t, "outer-job", outer.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRun_IncompleteWith(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
pullRequestPosterID := int64(4)
|
||||
repoID := int64(10)
|
||||
pullRequestID := int64(2)
|
||||
runDoesNotNeedApproval := &ActionRun{
|
||||
RepoID: repoID,
|
||||
PullRequestID: pullRequestID,
|
||||
PullRequestPosterID: pullRequestPosterID,
|
||||
}
|
||||
|
||||
workflowRaw := []byte(`
|
||||
jobs:
|
||||
outer-job:
|
||||
with:
|
||||
some_input: ${{ needs.other-job.outputs.some-output }}
|
||||
uses: ./.forgejo/workflows/reusable.yml
|
||||
`)
|
||||
workflows, err := jobparser.Parse(workflowRaw, false,
|
||||
jobparser.WithJobOutputs(map[string]map[string]string{}),
|
||||
jobparser.ExpandLocalReusableWorkflows(func(job *jobparser.Job, path string) ([]byte, error) {
|
||||
return []byte(`
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
some_input:
|
||||
type: string
|
||||
jobs:
|
||||
inner-job:
|
||||
runs-on: debian
|
||||
steps: []
|
||||
`), nil
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
require.True(t, workflows[0].IncompleteWith) // must be set for this test scenario to be valid
|
||||
|
||||
require.NoError(t, InsertRun(t.Context(), runDoesNotNeedApproval, workflows))
|
||||
|
||||
jobs, err := db.Find[ActionRunJob](t.Context(), FindRunJobOptions{RunID: runDoesNotNeedApproval.ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, jobs, 1)
|
||||
job := jobs[0]
|
||||
|
||||
// Expect job with an incomplete with to be StatusBlocked:
|
||||
assert.Equal(t, StatusBlocked, job.Status)
|
||||
}
|
||||
|
||||
func TestComputeRunStatus(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
|
|
@ -622,73 +391,3 @@ func TestComputeRunStatus(t *testing.T) {
|
|||
assert.Contains(t, columns, "stopped")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInsertRunJobs(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
pullRequestPosterID := int64(4)
|
||||
repoID := int64(10)
|
||||
pullRequestID := int64(2)
|
||||
actionRun := &ActionRun{
|
||||
RepoID: repoID,
|
||||
PullRequestID: pullRequestID,
|
||||
PullRequestPosterID: pullRequestPosterID,
|
||||
CommitSHA: "1421f75bc5474c69fdb1dc176bcb96d381f935dd",
|
||||
}
|
||||
|
||||
workflowRaw := []byte(`
|
||||
jobs:
|
||||
build:
|
||||
runs-on: fedora
|
||||
test:
|
||||
runs-on: debian
|
||||
steps: []
|
||||
`)
|
||||
jobs, err := jobparser.Parse(workflowRaw, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, InsertRun(t.Context(), actionRun, jobs))
|
||||
|
||||
insertedJobs, err := db.Find[ActionRunJob](t.Context(), FindRunJobOptions{RunID: actionRun.ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, insertedJobs, 2)
|
||||
|
||||
assert.Equal(t, actionRun.ID, insertedJobs[0].RunID)
|
||||
assert.Equal(t, actionRun.RepoID, insertedJobs[0].RepoID)
|
||||
assert.Equal(t, actionRun.OwnerID, insertedJobs[0].OwnerID)
|
||||
assert.Equal(t, actionRun.CommitSHA, insertedJobs[0].CommitSHA)
|
||||
assert.Equal(t, actionRun.IsForkPullRequest, insertedJobs[0].IsForkPullRequest)
|
||||
assert.Equal(t, "build", insertedJobs[0].Name)
|
||||
assert.Equal(t, "build", insertedJobs[0].JobID)
|
||||
assert.Empty(t, insertedJobs[0].Needs)
|
||||
assert.Equal(t, []string{"fedora"}, insertedJobs[0].RunsOn)
|
||||
assert.Equal(t, int64(1), insertedJobs[0].Attempt)
|
||||
assert.Zero(t, insertedJobs[0].Started)
|
||||
assert.Zero(t, insertedJobs[0].Stopped)
|
||||
assert.Zero(t, insertedJobs[0].TaskID)
|
||||
assert.Equal(t, StatusWaiting, insertedJobs[0].Status)
|
||||
|
||||
assert.Equal(t, actionRun.ID, insertedJobs[1].RunID)
|
||||
assert.Equal(t, actionRun.RepoID, insertedJobs[1].RepoID)
|
||||
assert.Equal(t, actionRun.OwnerID, insertedJobs[1].OwnerID)
|
||||
assert.Equal(t, actionRun.CommitSHA, insertedJobs[1].CommitSHA)
|
||||
assert.Equal(t, actionRun.IsForkPullRequest, insertedJobs[1].IsForkPullRequest)
|
||||
assert.Equal(t, "test", insertedJobs[1].Name)
|
||||
assert.Equal(t, "test", insertedJobs[1].JobID)
|
||||
assert.Empty(t, insertedJobs[1].Needs)
|
||||
assert.Equal(t, []string{"debian"}, insertedJobs[1].RunsOn)
|
||||
assert.Equal(t, int64(1), insertedJobs[1].Attempt)
|
||||
assert.Zero(t, insertedJobs[1].Started)
|
||||
assert.Zero(t, insertedJobs[1].Stopped)
|
||||
assert.Zero(t, insertedJobs[1].TaskID)
|
||||
assert.Equal(t, StatusWaiting, insertedJobs[1].Status)
|
||||
}
|
||||
|
||||
func TestActionRunLoadAttributes(t *testing.T) {
|
||||
run := &ActionRun{
|
||||
RepoID: 10,
|
||||
TriggerUserID: 1000,
|
||||
}
|
||||
require.NoError(t, run.LoadAttributes(t.Context()))
|
||||
assert.Equal(t, "ghost", run.TriggerUser.LowerName)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,8 +61,6 @@ type ActionRunner struct {
|
|||
|
||||
// Store labels defined in state file (default: .runner file) of `act_runner`
|
||||
AgentLabels []string `xorm:"TEXT"`
|
||||
// Store if this is a runner that only ever get one single job assigned
|
||||
Ephemeral bool `xorm:"ephemeral NOT NULL DEFAULT false"`
|
||||
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
|
|
@ -127,18 +125,6 @@ func (r *ActionRunner) IsOnline() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (r *ActionRunner) IsActive() bool {
|
||||
return r.Status() == runnerv1.RunnerStatus_RUNNER_STATUS_ACTIVE
|
||||
}
|
||||
|
||||
func (r *ActionRunner) IsIdle() bool {
|
||||
return r.Status() == runnerv1.RunnerStatus_RUNNER_STATUS_IDLE
|
||||
}
|
||||
|
||||
func (r *ActionRunner) IsOffline() bool {
|
||||
return r.Status() == runnerv1.RunnerStatus_RUNNER_STATUS_OFFLINE
|
||||
}
|
||||
|
||||
// Editable checks if the runner is editable by the user
|
||||
func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
|
||||
if ownerID == 0 && repoID == 0 {
|
||||
|
|
@ -152,7 +138,6 @@ func (r *ActionRunner) Editable(ownerID, repoID int64) bool {
|
|||
|
||||
// LoadAttributes loads the attributes of the runner
|
||||
func (r *ActionRunner) LoadAttributes(ctx context.Context) error {
|
||||
// nosemgrep: forgejo-logic-suspicious-OwnerID-check (system users are not stored in the database)
|
||||
if r.OwnerID > 0 {
|
||||
var user user_model.User
|
||||
has, err := db.GetEngine(ctx).ID(r.OwnerID).Get(&user)
|
||||
|
|
@ -197,12 +182,12 @@ func init() {
|
|||
|
||||
type FindRunnerOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
OwnerID int64 // it will be ignored if RepoID is set
|
||||
Sort string
|
||||
Filter string
|
||||
IsOnline optional.Option[bool]
|
||||
WithVisible bool // include all runners that are visible to the repository, owner, or instance
|
||||
RepoID int64
|
||||
OwnerID int64 // it will be ignored if RepoID is set
|
||||
Sort string
|
||||
Filter string
|
||||
IsOnline optional.Option[bool]
|
||||
WithAvailable bool // not only runners belong to, but also runners can be used
|
||||
}
|
||||
|
||||
func (opts FindRunnerOptions) ToConds() builder.Cond {
|
||||
|
|
@ -210,27 +195,25 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
|
|||
|
||||
if opts.RepoID > 0 {
|
||||
c := builder.NewCond().And(builder.Eq{"repo_id": opts.RepoID})
|
||||
if opts.WithVisible {
|
||||
if opts.WithAvailable {
|
||||
c = c.Or(builder.Eq{"owner_id": builder.Select("owner_id").From("repository").Where(builder.Eq{"id": opts.RepoID})})
|
||||
c = c.Or(builder.Eq{"repo_id": 0, "owner_id": 0})
|
||||
}
|
||||
cond = cond.And(c)
|
||||
} else if opts.OwnerID != 0 { // OwnerID is ignored if RepoID is set
|
||||
} else if opts.OwnerID > 0 { // OwnerID is ignored if RepoID is set
|
||||
c := builder.NewCond().And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
if opts.WithVisible {
|
||||
if opts.WithAvailable {
|
||||
c = c.Or(builder.Eq{"repo_id": 0, "owner_id": 0})
|
||||
}
|
||||
cond = cond.And(c)
|
||||
} else if !opts.WithVisible {
|
||||
cond = cond.And(builder.Eq{"repo_id": 0, "owner_id": 0})
|
||||
}
|
||||
|
||||
if opts.Filter != "" {
|
||||
cond = cond.And(builder.Like{"name", opts.Filter}).Or(builder.Like{"uuid", opts.Filter})
|
||||
cond = cond.And(builder.Like{"name", opts.Filter})
|
||||
}
|
||||
|
||||
if has, value := opts.IsOnline.Get(); has {
|
||||
if value {
|
||||
if opts.IsOnline.Has() {
|
||||
if opts.IsOnline.Value() {
|
||||
cond = cond.And(builder.Gt{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
|
||||
} else {
|
||||
cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
|
||||
|
|
@ -281,31 +264,6 @@ func GetRunnerByID(ctx context.Context, id int64) (*ActionRunner, error) {
|
|||
return &runner, nil
|
||||
}
|
||||
|
||||
// GetVisibleRunnerByID is like GetRunnerByID, but it only finds the runner if it is visible to the given owner or
|
||||
// repository. If it is not, util.ErrNotExist will be returned even if the runner exists.
|
||||
func GetVisibleRunnerByID(ctx context.Context, id, ownerID, repoID int64) (*ActionRunner, error) {
|
||||
query := db.GetEngine(ctx).Where("id=?", id)
|
||||
|
||||
if repoID > 0 {
|
||||
cond := builder.NewCond().And(builder.Eq{"repo_id": repoID})
|
||||
cond = cond.Or(builder.Eq{"owner_id": builder.Select("owner_id").From("repository").Where(builder.Eq{"id": repoID})})
|
||||
cond = cond.Or(builder.Eq{"repo_id": 0, "owner_id": 0})
|
||||
query = query.And(cond)
|
||||
} else if ownerID > 0 { // ownerID is ignored if repoID is set
|
||||
cond := builder.NewCond().And(builder.Eq{"owner_id": ownerID}).Or(builder.Eq{"repo_id": 0, "owner_id": 0})
|
||||
query = query.And(cond)
|
||||
}
|
||||
|
||||
var runner ActionRunner
|
||||
has, err := query.Get(&runner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, fmt.Errorf("runner with ID %d: %w", id, util.ErrNotExist)
|
||||
}
|
||||
return &runner, nil
|
||||
}
|
||||
|
||||
// UpdateRunner updates runner's information.
|
||||
func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
|
||||
e := db.GetEngine(ctx)
|
||||
|
|
@ -396,13 +354,6 @@ func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
|
|||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
// DeleteEphemeralRunner removes the ephemeral runner with the given ID. If the runner with the given ID is not an
|
||||
// ephemeral runner, nothing happens.
|
||||
func DeleteEphemeralRunner(ctx context.Context, id int64) error {
|
||||
_, err := db.GetEngine(ctx).Where(builder.Eq{"id": id, "ephemeral": true}).Delete(&ActionRunner{})
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteOfflineRunners(ctx context.Context, olderThan timeutil.TimeStamp, globalOnly bool) error {
|
||||
log.Info("Doing: DeleteOfflineRunners")
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ func (runners RunnerList) LoadOwners(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
for _, runner := range runners {
|
||||
// nosemgrep: forgejo-logic-suspicious-OwnerID-check (system users are not stored in the database)
|
||||
if runner.OwnerID > 0 && runner.Owner == nil {
|
||||
runner.Owner = users[runner.OwnerID]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/repo"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/timeutil"
|
||||
|
||||
|
|
@ -141,400 +140,3 @@ func TestDeleteOfflineRunnersErrorOnInvalidOlderThanValue(t *testing.T) {
|
|||
defer timeutil.MockUnset()
|
||||
require.Error(t, DeleteOfflineRunners(db.DefaultContext, timeutil.TimeStampNow(), false))
|
||||
}
|
||||
|
||||
func TestRunnerEditable(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
runner *ActionRunner
|
||||
ownerID int64
|
||||
repoID int64
|
||||
editable bool
|
||||
}{
|
||||
{
|
||||
name: "admin-can-edit-global-runner",
|
||||
runner: &ActionRunner{Name: "global-runner", OwnerID: 0, RepoID: 0},
|
||||
ownerID: 0,
|
||||
repoID: 0,
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "admin-can-edit-user-runner",
|
||||
runner: &ActionRunner{Name: "user-runner", OwnerID: 36, RepoID: 0},
|
||||
ownerID: 0,
|
||||
repoID: 0,
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "admin-can-edit-repository-runner",
|
||||
runner: &ActionRunner{Name: "user-runner", OwnerID: 0, RepoID: 110},
|
||||
ownerID: 0,
|
||||
repoID: 0,
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "user-can-edit-its-runner",
|
||||
runner: &ActionRunner{Name: "user-runner", OwnerID: 469, RepoID: 0},
|
||||
ownerID: 469,
|
||||
repoID: 0,
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "user-cannot-edit-global-runner",
|
||||
runner: &ActionRunner{Name: "global-runner", OwnerID: 0, RepoID: 0},
|
||||
ownerID: 469,
|
||||
repoID: 0,
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
name: "user-cannot-edit-other-users-runner",
|
||||
runner: &ActionRunner{Name: "user-runner", OwnerID: 892, RepoID: 0},
|
||||
ownerID: 469,
|
||||
repoID: 0,
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
name: "user-cannot-edit-repo-runner",
|
||||
runner: &ActionRunner{Name: "repo-runner", OwnerID: 0, RepoID: 151},
|
||||
ownerID: 469,
|
||||
repoID: 0,
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
name: "repo-can-edit-its-runner",
|
||||
runner: &ActionRunner{Name: "repo-runner", OwnerID: 0, RepoID: 693},
|
||||
ownerID: 0,
|
||||
repoID: 693,
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
name: "repo-cannot-edit-other-repo-runner",
|
||||
runner: &ActionRunner{Name: "repo-runner", OwnerID: 0, RepoID: 519},
|
||||
ownerID: 0,
|
||||
repoID: 693,
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
name: "repo-cannot-edit-global-runner",
|
||||
runner: &ActionRunner{Name: "global-runner", OwnerID: 0, RepoID: 0},
|
||||
ownerID: 0,
|
||||
repoID: 693,
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
name: "repo-cannot-edit-user-runner",
|
||||
runner: &ActionRunner{Name: "user-runner", OwnerID: 6, RepoID: 0},
|
||||
ownerID: 0,
|
||||
repoID: 693,
|
||||
editable: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
result := testCase.runner.Editable(testCase.ownerID, testCase.repoID)
|
||||
assert.Equal(t, testCase.editable, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunner_GetVisibleRunnerByID(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("models/actions/TestRunner_GetVisibleRunnerByID")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repository32 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 32, OwnerID: 3})
|
||||
repository1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1, OwnerID: 2})
|
||||
|
||||
runner1 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719931, OwnerID: 3, RepoID: 0}) // Owned by org3
|
||||
runner2 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719932, OwnerID: 2, RepoID: 0}) // Owned by user2
|
||||
runner3 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719933, OwnerID: 0, RepoID: 0})
|
||||
runner4 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719934, OwnerID: 0, RepoID: repository32.ID})
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
runner *ActionRunner
|
||||
ownerID int64
|
||||
repoID int64
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Organization runner",
|
||||
runner: runner1,
|
||||
ownerID: 3,
|
||||
repoID: 0,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Organization runner visible to admins",
|
||||
runner: runner1,
|
||||
ownerID: 0,
|
||||
repoID: 0,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Organization runner invisible to different owner",
|
||||
runner: runner1,
|
||||
ownerID: 2,
|
||||
repoID: 0,
|
||||
expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner1.ID),
|
||||
},
|
||||
{
|
||||
name: "Organization runner visible to its repositories",
|
||||
runner: runner1,
|
||||
ownerID: 0,
|
||||
repoID: repository32.ID,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Organization runner invisible to repositories owned by somebody else",
|
||||
runner: runner1,
|
||||
ownerID: 0,
|
||||
repoID: repository1.ID,
|
||||
expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner1.ID),
|
||||
},
|
||||
{
|
||||
name: "User runner",
|
||||
runner: runner2,
|
||||
ownerID: 2,
|
||||
repoID: 0,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "User runner invisible to different user",
|
||||
runner: runner2,
|
||||
ownerID: 1,
|
||||
repoID: 0,
|
||||
expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner2.ID),
|
||||
},
|
||||
{
|
||||
name: "User runner visible to repository owned by user",
|
||||
runner: runner2,
|
||||
ownerID: 0,
|
||||
repoID: repository1.ID,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "User runner invisible to repository owned by different user",
|
||||
runner: runner2,
|
||||
ownerID: 0,
|
||||
repoID: repository32.ID,
|
||||
expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner2.ID),
|
||||
},
|
||||
{
|
||||
name: "Global runner",
|
||||
runner: runner3,
|
||||
ownerID: 0,
|
||||
repoID: 0,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Global runner is visible to any user",
|
||||
runner: runner3,
|
||||
ownerID: 2,
|
||||
repoID: 0,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Global runner is visible to any repository",
|
||||
runner: runner3,
|
||||
ownerID: 0,
|
||||
repoID: repository32.ID,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Repository runner",
|
||||
runner: runner4,
|
||||
ownerID: 0,
|
||||
repoID: repository32.ID,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Repository runner is visible to admins",
|
||||
runner: runner4,
|
||||
ownerID: 0,
|
||||
repoID: 0,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Repository runner is invisible to repository owner",
|
||||
runner: runner4,
|
||||
ownerID: repository32.OwnerID,
|
||||
repoID: 0,
|
||||
expectedError: fmt.Sprintf("runner with ID %d: resource does not exist", runner4.ID),
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
_, err := GetVisibleRunnerByID(t.Context(), testCase.runner.ID, testCase.ownerID, testCase.repoID)
|
||||
if testCase.expectedError == "" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
assert.ErrorContains(t, err, testCase.expectedError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunner_FindRunnerOptionsToConds(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("models/actions/TestRunner_FindRunnerOptionsToConds")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
runner1 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719931, OwnerID: 3, RepoID: 0}) // Owned by org3
|
||||
runner2 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719932, OwnerID: 2, RepoID: 0}) // Owned by user2
|
||||
runner3 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719933, OwnerID: 0, RepoID: 0})
|
||||
runner4 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719934, OwnerID: 0, RepoID: 32})
|
||||
runner5 := unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: 719935, OwnerID: 0, RepoID: 36})
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
opts FindRunnerOptions
|
||||
expectedRunners RunnerList
|
||||
unexpectedRunners RunnerList
|
||||
}{
|
||||
{
|
||||
name: "Only runners owned by instance",
|
||||
opts: FindRunnerOptions{OwnerID: 0, RepoID: 0, WithVisible: false},
|
||||
expectedRunners: RunnerList{runner3},
|
||||
unexpectedRunners: RunnerList{runner1, runner2, runner4, runner5},
|
||||
},
|
||||
{
|
||||
name: "All runners on instance",
|
||||
opts: FindRunnerOptions{OwnerID: 0, RepoID: 0, WithVisible: true},
|
||||
expectedRunners: RunnerList{runner1, runner2, runner3, runner4, runner5},
|
||||
unexpectedRunners: RunnerList{},
|
||||
},
|
||||
{
|
||||
name: "Only runners owned by organization",
|
||||
opts: FindRunnerOptions{OwnerID: 3, RepoID: 0, WithVisible: false},
|
||||
expectedRunners: RunnerList{runner1},
|
||||
unexpectedRunners: RunnerList{runner2, runner3, runner4, runner5},
|
||||
},
|
||||
{
|
||||
name: "Runners available to organization",
|
||||
opts: FindRunnerOptions{OwnerID: 3, RepoID: 0, WithVisible: true},
|
||||
expectedRunners: RunnerList{runner1, runner3},
|
||||
unexpectedRunners: RunnerList{runner2, runner4, runner5},
|
||||
},
|
||||
{
|
||||
name: "Only runners owned by user",
|
||||
opts: FindRunnerOptions{OwnerID: 2, RepoID: 0, WithVisible: false},
|
||||
expectedRunners: RunnerList{runner2},
|
||||
unexpectedRunners: RunnerList{runner1, runner3, runner4, runner5},
|
||||
},
|
||||
{
|
||||
name: "Runners available to user",
|
||||
opts: FindRunnerOptions{OwnerID: 2, RepoID: 0, WithVisible: true},
|
||||
expectedRunners: RunnerList{runner2, runner3},
|
||||
unexpectedRunners: RunnerList{runner1, runner4, runner5},
|
||||
},
|
||||
{
|
||||
name: "Only runners owned by organization repository",
|
||||
opts: FindRunnerOptions{OwnerID: 0, RepoID: 32, WithVisible: false},
|
||||
expectedRunners: RunnerList{runner4},
|
||||
unexpectedRunners: RunnerList{runner1, runner2, runner3, runner5},
|
||||
},
|
||||
{
|
||||
name: "Runners available to organization repository",
|
||||
opts: FindRunnerOptions{OwnerID: 0, RepoID: 32, WithVisible: true},
|
||||
expectedRunners: RunnerList{runner1, runner3, runner4},
|
||||
unexpectedRunners: RunnerList{runner2, runner5},
|
||||
},
|
||||
{
|
||||
name: "Only runners owned by user repository",
|
||||
opts: FindRunnerOptions{OwnerID: 0, RepoID: 36, WithVisible: false},
|
||||
expectedRunners: RunnerList{runner5},
|
||||
unexpectedRunners: RunnerList{runner1, runner2, runner3, runner4},
|
||||
},
|
||||
{
|
||||
name: "Runners available to user repository",
|
||||
opts: FindRunnerOptions{OwnerID: 0, RepoID: 36, WithVisible: true},
|
||||
expectedRunners: RunnerList{runner2, runner3, runner5},
|
||||
unexpectedRunners: RunnerList{runner1, runner4},
|
||||
},
|
||||
{
|
||||
name: "Runners with partially matching name",
|
||||
opts: FindRunnerOptions{Filter: "er-3"},
|
||||
expectedRunners: RunnerList{runner3},
|
||||
unexpectedRunners: RunnerList{runner1, runner2, runner4, runner5},
|
||||
},
|
||||
{
|
||||
name: "Runners with partially matching UUID",
|
||||
opts: FindRunnerOptions{Filter: "21f75233798b"},
|
||||
expectedRunners: RunnerList{runner4},
|
||||
unexpectedRunners: RunnerList{runner1, runner2, runner3, runner5},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
runners, err := db.Find[ActionRunner](t.Context(), testCase.opts)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, expectedRunner := range testCase.expectedRunners {
|
||||
assert.Contains(t, runners, expectedRunner)
|
||||
}
|
||||
for _, unexpectedRunner := range testCase.unexpectedRunners {
|
||||
assert.NotContains(t, runners, unexpectedRunner)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteEphemeralRunner(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
persistentRunnerOne := &ActionRunner{
|
||||
ID: 606526,
|
||||
UUID: "d53a1222-ae7a-4430-97f8-8fcb6efd04c9",
|
||||
Name: "persistent-runner-one",
|
||||
OwnerID: 2,
|
||||
RepoID: 0,
|
||||
Ephemeral: false,
|
||||
TokenHash: "J9YDsQL",
|
||||
}
|
||||
persistentRunnerTwo := &ActionRunner{
|
||||
ID: 606527,
|
||||
UUID: "3dc23067-b2fd-4daf-b428-dddad80d7f37",
|
||||
Name: "persistent-runner-two",
|
||||
OwnerID: 2,
|
||||
RepoID: 0,
|
||||
Ephemeral: false,
|
||||
TokenHash: "jvIylZtHsS",
|
||||
}
|
||||
ephemeralRunnerOne := &ActionRunner{
|
||||
ID: 606528,
|
||||
UUID: "2d9bc0a1-7019-4ed3-ba67-6415415ac2a9",
|
||||
Name: "ephemeral-runner-one",
|
||||
OwnerID: 2,
|
||||
RepoID: 0,
|
||||
Ephemeral: true,
|
||||
TokenHash: "t9C8L0kM3W",
|
||||
}
|
||||
ephemeralRunnerTwo := &ActionRunner{
|
||||
ID: 606529,
|
||||
UUID: "da7a03f8-ab39-4c54-9ec9-2bd312fe3be1",
|
||||
Name: "ephemeral-runner-two",
|
||||
OwnerID: 2,
|
||||
RepoID: 0,
|
||||
Ephemeral: true,
|
||||
TokenHash: "g9oTOFM",
|
||||
}
|
||||
|
||||
require.NoError(t, CreateRunner(t.Context(), persistentRunnerOne))
|
||||
require.NoError(t, CreateRunner(t.Context(), persistentRunnerTwo))
|
||||
require.NoError(t, CreateRunner(t.Context(), ephemeralRunnerOne))
|
||||
require.NoError(t, CreateRunner(t.Context(), ephemeralRunnerTwo))
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerOne.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerTwo.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: ephemeralRunnerOne.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: ephemeralRunnerTwo.ID})
|
||||
|
||||
require.NoError(t, DeleteEphemeralRunner(t.Context(), persistentRunnerOne.ID))
|
||||
require.NoError(t, DeleteEphemeralRunner(t.Context(), ephemeralRunnerOne.ID))
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerOne.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: persistentRunnerTwo.ID})
|
||||
unittest.AssertNotExistsBean(t, &ActionRunner{ID: ephemeralRunnerOne.ID})
|
||||
unittest.AssertExistsAndLoadBean(t, &ActionRunner{ID: ephemeralRunnerTwo.ID})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,8 @@ import (
|
|||
"forgejo.org/models/db"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/optional"
|
||||
"forgejo.org/modules/timeutil"
|
||||
"forgejo.org/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ActionRunnerToken represents runner tokens
|
||||
|
|
@ -32,14 +29,15 @@ import (
|
|||
type ActionRunnerToken struct {
|
||||
ID int64
|
||||
Token string `xorm:"UNIQUE"`
|
||||
OwnerID optional.Option[int64] `xorm:"index REFERENCES(user, id)"`
|
||||
OwnerID int64 `xorm:"index"`
|
||||
Owner *user_model.User `xorm:"-"`
|
||||
RepoID optional.Option[int64] `xorm:"index REFERENCES(repository, id)"`
|
||||
RepoID int64 `xorm:"index"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
IsActive bool // true means it can be used
|
||||
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
Deleted timeutil.TimeStamp `xorm:"deleted"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -72,11 +70,11 @@ func UpdateRunnerToken(ctx context.Context, r *ActionRunnerToken, cols ...string
|
|||
|
||||
// NewRunnerToken creates a new active runner token and invalidate all old tokens
|
||||
// ownerID will be ignored and treated as 0 if repoID is non-zero.
|
||||
func NewRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64]) (*ActionRunnerToken, error) {
|
||||
if ownerID.Has() && repoID.Has() {
|
||||
func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
// It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally.
|
||||
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
||||
ownerID = optional.None[int64]()
|
||||
ownerID = 0
|
||||
}
|
||||
|
||||
token := util.CryptoRandomString(util.RandomStringHigh)
|
||||
|
|
@ -88,7 +86,7 @@ func NewRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64])
|
|||
}
|
||||
|
||||
return runnerToken, db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if _, err := db.GetEngine(ctx).Where(runnerTokenCond(ownerID, repoID)).Cols("is_active").Update(&ActionRunnerToken{
|
||||
if _, err := db.GetEngine(ctx).Where("owner_id =? AND repo_id = ?", ownerID, repoID).Cols("is_active").Update(&ActionRunnerToken{
|
||||
IsActive: false,
|
||||
}); err != nil {
|
||||
return err
|
||||
|
|
@ -99,32 +97,16 @@ func NewRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64])
|
|||
})
|
||||
}
|
||||
|
||||
func runnerTokenCond(ownerID, repoID optional.Option[int64]) builder.Cond {
|
||||
var condOwnerID builder.Cond
|
||||
if has, value := ownerID.Get(); !has {
|
||||
condOwnerID = builder.IsNull{"owner_id"}
|
||||
} else {
|
||||
condOwnerID = builder.Eq{"owner_id": value}
|
||||
}
|
||||
var condRepoID builder.Cond
|
||||
if has, value := repoID.Get(); !has {
|
||||
condRepoID = builder.IsNull{"repo_id"}
|
||||
} else {
|
||||
condRepoID = builder.Eq{"repo_id": value}
|
||||
}
|
||||
return builder.And(condOwnerID, condRepoID)
|
||||
}
|
||||
|
||||
// GetLatestRunnerToken returns the latest runner token
|
||||
func GetLatestRunnerToken(ctx context.Context, ownerID, repoID optional.Option[int64]) (*ActionRunnerToken, error) {
|
||||
if ownerID.Has() && repoID.Has() {
|
||||
// It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally.
|
||||
func GetLatestRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
// It's trying to get a runner token that belongs to a repository, but OwnerID has been set accidentally.
|
||||
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
||||
ownerID = optional.None[int64]()
|
||||
ownerID = 0
|
||||
}
|
||||
|
||||
var runnerToken ActionRunnerToken
|
||||
has, err := db.GetEngine(ctx).Where(runnerTokenCond(ownerID, repoID)).
|
||||
has, err := db.GetEngine(ctx).Where("owner_id=? AND repo_id=?", ownerID, repoID).
|
||||
OrderBy("id DESC").Get(&runnerToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/modules/optional"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -17,16 +16,16 @@ import (
|
|||
func TestGetLatestRunnerToken(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3})
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]())
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedToken, token)
|
||||
}
|
||||
|
||||
func TestNewRunnerToken(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
token, err := NewRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]())
|
||||
token, err := NewRunnerToken(db.DefaultContext, 1, 0)
|
||||
require.NoError(t, err)
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]())
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedToken, token)
|
||||
}
|
||||
|
|
@ -35,8 +34,8 @@ func TestUpdateRunnerToken(t *testing.T) {
|
|||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3})
|
||||
token.IsActive = true
|
||||
require.NoError(t, UpdateRunnerToken(db.DefaultContext, token, "is_active"))
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, optional.Some[int64](1), optional.None[int64]())
|
||||
require.NoError(t, UpdateRunnerToken(db.DefaultContext, token))
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedToken, token)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package actions
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
|
|
@ -20,7 +21,7 @@ import (
|
|||
type ActionSchedule struct {
|
||||
ID int64
|
||||
Title string
|
||||
Specs []*ActionScheduleSpec `xorm:"-"`
|
||||
Specs []string
|
||||
RepoID int64 `xorm:"index"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
OwnerID int64 `xorm:"index"`
|
||||
|
|
@ -72,12 +73,25 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Loop through each schedule spec and create a new spec row
|
||||
now := time.Now()
|
||||
|
||||
for _, spec := range row.Specs {
|
||||
spec.ScheduleID = row.ID
|
||||
spec.RepoID = row.RepoID
|
||||
specRow := &ActionScheduleSpec{
|
||||
RepoID: row.RepoID,
|
||||
ScheduleID: row.ID,
|
||||
Spec: spec,
|
||||
}
|
||||
// Parse the spec and check for errors
|
||||
schedule, err := specRow.Parse()
|
||||
if err != nil {
|
||||
continue // skip to the next spec if there's an error
|
||||
}
|
||||
|
||||
specRow.Next = timeutil.TimeStamp(schedule.Next(now).Unix())
|
||||
|
||||
// Insert the new schedule spec row
|
||||
if err = db.Insert(ctx, spec); err != nil {
|
||||
if err = db.Insert(ctx, specRow); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -116,7 +130,7 @@ func (opts FindScheduleOptions) ToConds() builder.Cond {
|
|||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
}
|
||||
if opts.OwnerID != 0 {
|
||||
if opts.OwnerID > 0 {
|
||||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,10 +10,9 @@ import (
|
|||
|
||||
"forgejo.org/models/db"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
"forgejo.org/modules/optional"
|
||||
"forgejo.org/modules/timeutil"
|
||||
|
||||
"github.com/gdgvda/cron"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
// ActionScheduleSpec represents a schedule spec of a workflow file
|
||||
|
|
@ -28,58 +27,36 @@ type ActionScheduleSpec struct {
|
|||
// started or this entry's schedule is unsatisfiable
|
||||
Next timeutil.TimeStamp `xorm:"index"`
|
||||
// Prev is the last time this job was run, or the zero time if never.
|
||||
Prev timeutil.TimeStamp
|
||||
Spec string
|
||||
TimeZone optional.Option[string]
|
||||
Prev timeutil.TimeStamp
|
||||
Spec string
|
||||
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
func NewActionScheduleSpec(cron string, tz optional.Option[string], referenceTime time.Time) (*ActionScheduleSpec, error) {
|
||||
spec := &ActionScheduleSpec{
|
||||
Spec: cron,
|
||||
TimeZone: tz,
|
||||
}
|
||||
cronSchedule, err := spec.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec.Next = timeutil.TimeStamp(cronSchedule.Next(referenceTime).Unix())
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// Parse parses the spec and returns a cron.Schedule
|
||||
// Unlike the default cron parser, Parse uses UTC timezone as the default if none is specified.
|
||||
func (s *ActionScheduleSpec) Parse() (cron.Schedule, error) {
|
||||
parser, err := cron.NewDefaultParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
|
||||
schedule, err := parser.Parse(s.Spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If `timezone` is not defined in the workflow, but the spec includes a timezone, use it.
|
||||
if !s.TimeZone.Has() && (strings.HasPrefix(s.Spec, "TZ=") || strings.HasPrefix(s.Spec, "CRON_TZ=")) {
|
||||
// If the spec has specified a timezone, use it
|
||||
if strings.HasPrefix(s.Spec, "TZ=") || strings.HasPrefix(s.Spec, "CRON_TZ=") {
|
||||
return schedule, nil
|
||||
}
|
||||
|
||||
var location *time.Location
|
||||
if present, tz := s.TimeZone.Get(); present {
|
||||
location, err = time.LoadLocation(tz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// UTC is the default time zone.
|
||||
location = time.UTC
|
||||
specSchedule, ok := schedule.(*cron.SpecSchedule)
|
||||
// If it's not a spec schedule, like "@every 5m", timezone is not relevant
|
||||
if !ok {
|
||||
return schedule, nil
|
||||
}
|
||||
|
||||
return schedule.WithLocation(location), nil
|
||||
// Set the timezone to UTC
|
||||
specSchedule.Location = time.UTC
|
||||
return specSchedule, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
|||
|
|
@ -7,50 +7,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"forgejo.org/modules/optional"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestActionScheduleSpec_NewActionScheduleSpec(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
refTime time.Time
|
||||
cronPattern string
|
||||
timeZone string
|
||||
want string
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "without timezone",
|
||||
refTime: time.Date(2026, 4, 6, 11, 56, 0, 0, time.UTC),
|
||||
cronPattern: "58 14 * * *",
|
||||
want: "2026-04-06T14:58:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "with separate timezone",
|
||||
refTime: time.Date(2026, 4, 6, 11, 56, 0, 0, time.UTC),
|
||||
cronPattern: "58 14 * * *",
|
||||
timeZone: "Europe/Tallinn", // +03 (EEST)
|
||||
want: "2026-04-06T11:58:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
s, err := NewActionScheduleSpec(test.cronPattern, optional.FromNonDefault(test.timeZone), test.refTime)
|
||||
test.wantErr(t, err)
|
||||
|
||||
if err == nil {
|
||||
assert.Equal(t, test.want, s.Next.AsTime().UTC().Format(time.RFC3339))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionScheduleSpec_Parse(t *testing.T) {
|
||||
// Mock the local timezone is not UTC
|
||||
local := time.Local
|
||||
|
|
@ -61,105 +21,50 @@ func TestActionScheduleSpec_Parse(t *testing.T) {
|
|||
}()
|
||||
time.Local = tz
|
||||
|
||||
now, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
refTime time.Time
|
||||
spec string
|
||||
timeZone string
|
||||
want string
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
name string
|
||||
spec string
|
||||
want string
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "regular",
|
||||
refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local),
|
||||
spec: "0 10 * * *",
|
||||
want: "2024-07-31T10:00:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local),
|
||||
spec: "0 10 * *",
|
||||
want: "",
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "with TZ in cron schedule",
|
||||
refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local),
|
||||
name: "with timezone",
|
||||
spec: "TZ=America/New_York 0 10 * * *",
|
||||
want: "2024-07-31T14:00:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "with CRON_TZ in cron schedule",
|
||||
refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local),
|
||||
spec: "CRON_TZ=America/New_York 0 10 * * *",
|
||||
want: "2024-07-31T14:00:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "with separate time zone",
|
||||
refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local),
|
||||
spec: "0 10 * * *",
|
||||
timeZone: "America/New_York",
|
||||
want: "2024-07-31T14:00:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "separate time zone takes precedence over inlined time zone",
|
||||
refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local),
|
||||
spec: "CRON_TZ=Europe/Berlin 0 10 * * *",
|
||||
timeZone: "America/New_York",
|
||||
want: "2024-07-31T14:00:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "time zone irrelevant",
|
||||
refTime: time.Date(2024, 7, 31, 15, 47, 55, 0, time.Local),
|
||||
name: "timezone irrelevant",
|
||||
spec: "@every 5m",
|
||||
want: "2024-07-31T07:52:55Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
// The various cron implementations handle the DST jump forwards differently. The most popular approaches
|
||||
// are (a) scheduling all jobs at 3 o'clock that were supposed to run between 2 and 3 o'clock, or (b)
|
||||
// skipping the execution on that day because any time between 2 and 3 o'clock never happened. Forgejo uses
|
||||
// option B because the code it inherited already did that and was exposed to users.
|
||||
name: "skips execution during DST jump forwards",
|
||||
refTime: time.Date(2025, 3, 30, 0, 55, 0, 0, time.UTC), // 01:55 local time
|
||||
spec: "10 2 * * *", // The clock jumps at 2 o'clock to 3 o'clock.
|
||||
timeZone: "Europe/Berlin",
|
||||
want: "2025-03-31T00:10:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "executes a first time before DST jump backwards",
|
||||
refTime: time.Date(2025, 10, 26, 0, 5, 0, 0, time.UTC), // 02:05 local time
|
||||
spec: "10 2 * * *", // The clock jumps at 3 o'clock to 2 o'clock.
|
||||
timeZone: "Europe/Berlin",
|
||||
want: "2025-10-26T00:10:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "executes a second time after DST jump backwards",
|
||||
refTime: time.Date(2025, 10, 26, 1, 5, 0, 0, time.UTC), // 02:05 local time
|
||||
spec: "10 2 * * *", // The clock jumps at 3 o'clock to 2 o'clock.
|
||||
timeZone: "Europe/Berlin",
|
||||
want: "2025-10-26T01:10:00Z",
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &ActionScheduleSpec{
|
||||
Spec: tt.spec,
|
||||
TimeZone: optional.FromNonDefault(tt.timeZone),
|
||||
Spec: tt.spec,
|
||||
}
|
||||
got, err := s.Parse()
|
||||
tt.wantErr(t, err)
|
||||
|
||||
if err == nil {
|
||||
assert.Equal(t, tt.want, got.Next(tt.refTime).UTC().Format(time.RFC3339))
|
||||
assert.Equal(t, tt.want, got.Next(now).UTC().Format(time.RFC3339))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
// Copyright 2026 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/repo"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/models/user"
|
||||
"forgejo.org/modules/optional"
|
||||
"forgejo.org/modules/timeutil"
|
||||
"forgejo.org/modules/webhook"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestScheduleCreateScheduleTask(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 2})
|
||||
repo62 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 62, Name: "test_workflows", OwnerID: user2.ID})
|
||||
|
||||
content := `
|
||||
on:
|
||||
push:
|
||||
schedule:
|
||||
- cron: "2 13 * * *"
|
||||
- cron: "03 13 * * *"
|
||||
timezone: Europe/Paris
|
||||
jobs:
|
||||
test:
|
||||
runs-on: debian
|
||||
steps:
|
||||
- run: |
|
||||
echo "OK"
|
||||
`
|
||||
|
||||
referenceTime := time.Date(2026, 3, 27, 17, 41, 21, 0, time.UTC)
|
||||
|
||||
specWithoutTZ, err := NewActionScheduleSpec("2 13 * * *", optional.None[string](), referenceTime)
|
||||
require.NoError(t, err)
|
||||
|
||||
specWithTZ, err := NewActionScheduleSpec("3 13 * * *", optional.Some("Europe/Paris"), referenceTime)
|
||||
require.NoError(t, err)
|
||||
|
||||
schedule := &ActionSchedule{
|
||||
Title: ".forgejo/workflows/test.yaml",
|
||||
Specs: []*ActionScheduleSpec{specWithoutTZ, specWithTZ},
|
||||
RepoID: repo62.ID,
|
||||
OwnerID: user2.ID,
|
||||
WorkflowID: "test.yaml",
|
||||
WorkflowDirectory: ".forgejo/workflows",
|
||||
TriggerUserID: -2,
|
||||
Ref: "main",
|
||||
CommitSHA: "6af834a5bc97c1a337eb3a21d26903c5cdceca0c",
|
||||
Event: webhook.HookEventPush,
|
||||
EventPayload: "{\"action\":\"schedule\"}",
|
||||
Content: []byte(content),
|
||||
}
|
||||
|
||||
err = CreateScheduleTask(t.Context(), []*ActionSchedule{schedule})
|
||||
require.NoError(t, err)
|
||||
|
||||
schedules, err := db.Find[ActionSchedule](t.Context(), FindScheduleOptions{OwnerID: user2.ID, RepoID: repo62.ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, schedules, 1)
|
||||
|
||||
assert.NotZero(t, schedules[0].ID)
|
||||
assert.Equal(t, ".forgejo/workflows/test.yaml", schedules[0].Title)
|
||||
assert.Equal(t, "test.yaml", schedules[0].WorkflowID)
|
||||
assert.Equal(t, ".forgejo/workflows", schedules[0].WorkflowDirectory)
|
||||
assert.Equal(t, int64(-2), schedules[0].TriggerUserID)
|
||||
assert.Equal(t, "main", schedules[0].Ref)
|
||||
assert.Equal(t, "6af834a5bc97c1a337eb3a21d26903c5cdceca0c", schedules[0].CommitSHA)
|
||||
assert.Equal(t, webhook.HookEventPush, schedules[0].Event)
|
||||
assert.JSONEq(t, "{\"action\":\"schedule\"}", schedules[0].EventPayload)
|
||||
assert.Equal(t, []byte(content), schedules[0].Content)
|
||||
|
||||
specs, total, err := FindSpecs(t.Context(), FindSpecOptions{RepoID: repo62.ID})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int64(2), total)
|
||||
|
||||
assert.NotZero(t, specs[0].ID)
|
||||
assert.Equal(t, schedules[0].ID, specs[0].ScheduleID)
|
||||
assert.Equal(t, timeutil.TimeStamp(1774699380), specs[0].Next)
|
||||
assert.Equal(t, "3 13 * * *", specs[0].Spec)
|
||||
assert.Equal(t, optional.Some("Europe/Paris"), specs[0].TimeZone)
|
||||
assert.Zero(t, specs[0].Prev)
|
||||
|
||||
assert.NotZero(t, specs[1].ID)
|
||||
assert.Equal(t, schedules[0].ID, specs[1].ScheduleID)
|
||||
assert.Equal(t, timeutil.TimeStamp(1774702920), specs[1].Next)
|
||||
assert.Equal(t, "2 13 * * *", specs[1].Spec)
|
||||
assert.Equal(t, optional.None[string](), specs[1].TimeZone)
|
||||
assert.Zero(t, specs[1].Prev)
|
||||
}
|
||||
|
|
@ -4,8 +4,6 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"forgejo.org/modules/translation"
|
||||
|
||||
runnerv1 "code.forgejo.org/forgejo/actions-proto/runner/v1"
|
||||
|
|
@ -109,7 +107,12 @@ func (s Status) IsBlocked() bool {
|
|||
|
||||
// In returns whether s is one of the given statuses
|
||||
func (s Status) In(statuses ...Status) bool {
|
||||
return slices.Contains(statuses, s)
|
||||
for _, v := range statuses {
|
||||
if s == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s Status) AsResult() runnerv1.Result {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue