Compare commits

..

77 commits

Author SHA1 Message Date
Andreas Ahlenstorf
2b1813fb87 feat: filter action runs by Git reference (#11013)
Make it possible to filter action runs returned by `/api/v1/repos/andreas/test/actions/runs` by Git reference.

Resolves https://codeberg.org/forgejo/forgejo/issues/10874.

## Checklist

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

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] 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.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11013
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
Co-committed-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
(cherry picked from commit d996dfb476)
2026-01-24 21:12:29 +00:00
Mathieu Fenniak
3aaef3b3dd [v14.0/forgejo] fix: don't clobber authorized_keys file during installation (#10948) (#11009)
**Backport**: https://codeberg.org/forgejo/forgejo/pulls/10948

(cherry picked from commit c52ecd2258)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10948
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [ ] I did not document these changes and I do not expect someone else to do it.

### 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.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11009
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
2026-01-23 21:29:47 +01:00
0ko
097680a64d merge commit: [v14.0/forgejo] i18n: backport of translations from Codeberg Translate (#10988)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10988
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2026-01-23 14:50:26 +01:00
Renovate Bot
bdc7c65173 Update module code.forgejo.org/forgejo/runner/v12 to v12.6.0 (v14.0/forgejo) (#11001)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [code.forgejo.org/forgejo/runner/v12](https://code.forgejo.org/forgejo/runner) | `v12.2.0` -> `v12.6.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/code.forgejo.org%2fforgejo%2frunner%2fv12/v12.6.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/code.forgejo.org%2fforgejo%2frunner%2fv12/v12.2.0/v12.6.0?slim=true) |

---

### Release Notes

<details>
<summary>forgejo/runner (code.forgejo.org/forgejo/runner/v12)</summary>

### [`v12.6.0`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.6.0)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.5.3...v12.6.0)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- features
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1236): <!--number 1236 --><!--line 0 --><!--description ZmVhdDogYWRkICd1c2luZzogZG9ja2VyJyBhY3Rpb24gcHJlIGFuZCBwb3N0LWVudHJ5cG9pbnQgc3VwcG9ydA==-->feat: add 'using: docker' action pre and post-entrypoint support<!--description-->
- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1287): <!--number 1287 --><!--line 0 --><!--description Zml4OiBlbGltaW5hdGUgbG9nIGRhdGEgbG9zcyBjYXVzZWQgYnkgTFhDICYgUFRZIGJ1ZmZlcg==-->fix: eliminate log data loss caused by LXC & PTY buffer<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1307): <!--number 1307 --><!--line 0 --><!--description Zml4KGpvYnBhcnNlcik6IGV2YWx1YXRpbmcgbWF0cml4IHZhbHVlcyB3aXRoaW4gcnVucy1vbiByZWZlcmVuY2VzIGZpcnN0IG1hdHJpeCB2YWx1ZSBvbmx5-->fix(jobparser): evaluating matrix values within runs-on references first matrix value only<!--description-->
- other
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1312): <!--number 1312 --><!--line 0 --><!--description dGVzdDogdmVyaWZ5IHRoYXQgY29uZmlndXJhdGlvbiBpcyBsb2FkZWQgY29ycmVjdGx5-->test: verify that configuration is loaded correctly<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1315): <!--number 1315 --><!--line 0 --><!--description VXBkYXRlIGh0dHBzOi8vZGF0YS5mb3JnZWpvLm9yZy9mb3JnZWpvL2Zvcmdlam8tYnVpbGQtcHVibGlzaCBhY3Rpb24gdG8gdjUuNS4x-->Update <https://data.forgejo.org/forgejo/forgejo-build-publish> action to v5.5.1<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1314): <!--number 1314 --><!--line 0 --><!--description VXBkYXRlIGh0dHBzOi8vZGF0YS5mb3JnZWpvLm9yZy9mb3JnZWpvL2Zvcmdlam8tYnVpbGQtcHVibGlzaCBhY3Rpb24gdG8gdjUuNS4w-->Update <https://data.forgejo.org/forgejo/forgejo-build-publish> action to v5.5.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1290): <!--number 1290 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21vYnkvZ28tYXJjaGl2ZSB0byB2MC4yLjA=-->Update module github.com/moby/go-archive to v0.2.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1308): <!--number 1308 --><!--line 0 --><!--description VXBkYXRlIGh0dHBzOi8vZGF0YS5mb3JnZWpvLm9yZy9hY3Rpb25zL3NldHVwLWZvcmdlam8gYWN0aW9uIHRvIHYzLjEuMQ==-->Update <https://data.forgejo.org/actions/setup-forgejo> action to v3.1.1<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1306): <!--number 1306 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL3NpcnVwc2VuL2xvZ3J1cyB0byB2MS45LjQ=-->Update module github.com/sirupsen/logrus to v1.9.4<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1305): <!--number 1305 --><!--line 0 --><!--description VXBkYXRlIGZvcmdlam8tcnVubmVyIHRvIHYxMi41LjM=-->Update forgejo-runner to v12.5.3<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1304): <!--number 1304 --><!--line 0 --><!--description VXBkYXRlIGNvZGUuZm9yZ2Vqby5vcmcvZm9yZ2Vqby9mb3JnZWpvIERvY2tlciB0YWcgdG8gdjExLjAuMTA=-->Update code.forgejo.org/forgejo/forgejo Docker tag to v11.0.10<!--description-->

<!--end release-notes-assistant-->

### [`v12.5.3`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.5.3)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.5.2...v12.5.3)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1303): <!--number 1303 --><!--line 0 --><!--description Zml4OiBzdXBwb3J0IEZvcmdlam8gcmV0dXJuaW5nIEFkZGl0aW9uYWxUYXNrcyBidXQgbm90IFRhc2s=-->fix: support Forgejo returning AdditionalTasks but not Task<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1293): <!--number 1293 --><!--line 0 --><!--description Zml4OiBvbmx5IHNraXAgZmV0Y2hpbmcgd2hlbiBhIGNvbW1pdCBleGlzdHMgbG9jYWxseQ==-->fix: only skip fetching when a commit exists locally<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1294): <!--number 1294 --><!--line 0 --><!--description Zml4OiB1bmJyZWFrIHRoZSBidWlsZCBmb3IgcGxhdGZvcm1zIHcvbyBkb2NrZXIgc3VwcG9ydA==-->fix: unbreak the build for platforms w/o docker support<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1277): <!--number 1277 --><!--line 0 --><!--description Zml4OiB0cnkgdG8gZW1wdHkgY2xvbmUgZGlyZWN0b3J5IHRoYXQgaXMgbm90IGVtcHR5-->fix: try to empty clone directory that is not empty<!--description-->
- other
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1301): <!--number 1301 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZm9yZ2Vqby9yZWxlYXNlLW5vdGVzLWFzc2lzdGFudCB0byB2MS41LjE=-->Update dependency forgejo/release-notes-assistant to v1.5.1<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1299): <!--number 1299 --><!--line 0 --><!--description Y2hvcmU6IGltcHJvdmUgaW50ZXJuYWwgcmVwb3J0ZXIuZ28gbG9nZ2luZw==-->chore: improve internal reporter.go logging<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1298): <!--number 1298 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZ28gdG8gdjEuMjUuNg==-->Update dependency go to v1.25.6<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1297): <!--number 1297 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvdGVybSB0byB2MC4zOS4w-->Update module golang.org/x/term to v0.39.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1295): <!--number 1295 --><!--line 0 --><!--description Y2hvcmU6IGJ1aWxkIHVuc3VwcG9ydGVkIHBsYXRmb3Jtcw==-->chore: build unsupported platforms<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1296): <!--number 1296 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvc3lzIHRvIHYwLjQwLjA=-->Update module golang.org/x/sys to v0.40.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1288): <!--number 1288 --><!--line 0 --><!--description VXBkYXRlIGRhdGEuZm9yZ2Vqby5vcmcvb2NpL2FscGluZSBEb2NrZXIgdGFnIHRvIHYzLjIz-->Update data.forgejo.org/oci/alpine Docker tag to v3.23<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1286): <!--number 1286 --><!--line 0 --><!--description dGVzdDogYWRkIGFuIExYQyBlbnZpcm9ubWVudCB0ZXN0-->test: add an LXC environment test<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1261): <!--number 1261 --><!--line 0 --><!--description Y2hvcmU6IGVuc3VyZSBhbGwgdGVzdHMgYXJlIHJ1bg==-->chore: ensure all tests are run<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1279): <!--number 1279 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL3NwZjEzL2NvYnJhIHRvIHYxLjEwLjI=-->Update module github.com/spf13/cobra to v1.10.2<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1281): <!--number 1281 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvbGFuZ2NpL2dvbGFuZ2NpLWxpbnQvdjIvY21kL2dvbGFuZ2NpLWxpbnQgdG8gdjIuOC4w-->Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.8.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1280): <!--number 1280 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZm9yZ2Vqby1ydW5uZXItc2VydmljZS15cSB0byB2NC41MC4x-->Update dependency forgejo-runner-service-yq to v4.50.1<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1284): <!--number 1284 --><!--line 0 --><!--description dGVzdDogcGluZyBob3N0bmFtZSBpbnN0ZWFkIG9mIGxvY2FsaG9zdA==-->test: ping hostname instead of localhost<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1285): <!--number 1285 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgbHhjLW5vZGUgdG8gdjI0-->Update dependency lxc-node to v24<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1283): <!--number 1283 --><!--line 0 --><!--description VXBkYXRlIE5vZGUuanMgdG8gdjI0-->Update Node.js to v24<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1278): <!--number 1278 --><!--line 0 --><!--description VXBkYXRlIGZvcmdlam8tcnVubmVyIHRvIHYxMi41LjI=-->Update forgejo-runner to v12.5.2<!--description-->

<!--end release-notes-assistant-->

### [`v12.5.2`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.5.2)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.5.1...v12.5.2)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

In combination with a runner with capacity > 1, and using Forgejo v14.0.0 or v14.0.1, this release has a known defect [#&#8203;1302](https://github.com/forgejo/runner/issues/1302) that may cause jobs to appear to be stalled in Forgejo.  Please upgrade the runner to v12.5.3 to avoid this issue.

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- other
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1267): <!--number 1267 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2RvY2tlci9kb2NrZXIgdG8gdjI4LjUuMitpbmNvbXBhdGlibGU=-->Update module github.com/docker/docker to v28.5.2+incompatible<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1275): <!--number 1275 --><!--line 0 --><!--description VXBkYXRlIGZvcmdlam8tcnVubmVyIHRvIHYxMi41LjE=-->Update forgejo-runner to v12.5.1<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1276): <!--number 1276 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZm9yZ2Vqby9yZWxlYXNlLW5vdGVzLWFzc2lzdGFudCB0byB2MS41LjA=-->Update dependency forgejo/release-notes-assistant to v1.5.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1272): <!--number 1272 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL3JoeXNkL2FjdGlvbmxpbnQgdG8gdjEuNy4xMA==-->Update module github.com/rhysd/actionlint to v1.7.10<!--description-->

<!--end release-notes-assistant-->

### [`v12.5.1`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.5.1)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.5.0...v12.5.1)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

In combination with a runner with capacity > 1, and using Forgejo v14.0.0 or v14.0.1, this release has a known defect [#&#8203;1302](https://github.com/forgejo/runner/issues/1302) that may cause jobs to appear to be stalled in Forgejo.  Please upgrade the runner to v12.5.3 to avoid this issue.

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1268): <!--number 1268 --><!--line 0 --><!--description Zml4OiBwcml2YXRlIHJlcG9zaXRvcnkgY2xvbmluZyB3aXRoIGF1dGhlbnRpY2F0aW9uIHRva2Vu-->fix: private repository cloning with authentication token<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1270): <!--number 1270 --><!--line 0 --><!--description Zml4OiBtaW5pbWl6ZSBsb2cgZGF0YSBsb3NzIGNhdXNlZCBieSBMWEMgJiBQVFkgYnVmZmVy-->fix: minimize log data loss caused by LXC & PTY buffer<!--description-->
- other
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1269): <!--number 1269 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvLWdpdC9nby1naXQvdjUgdG8gdjUuMTYuNA==-->Update module github.com/go-git/go-git/v5 to v5.16.4<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1266): <!--number 1266 --><!--line 0 --><!--description VXBkYXRlIGNvZGUuZm9yZ2Vqby5vcmcvZm9yZ2Vqby9mb3JnZWpvIERvY2tlciB0YWcgdG8gdjExLjAuOQ==-->Update code.forgejo.org/forgejo/forgejo Docker tag to v11.0.9<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1263): <!--number 1263 --><!--line 0 --><!--description VXBkYXRlIGZvcmdlam8tcnVubmVyIHRvIHYxMi41LjA=-->Update forgejo-runner to v12.5.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1264): <!--number 1264 --><!--line 0 --><!--description VXBkYXRlIGh0dHBzOi8vZGF0YS5mb3JnZWpvLm9yZy9hY3Rpb25zL3NldHVwLWZvcmdlam8gYWN0aW9uIHRvIHYzLjEuMA==-->Update <https://data.forgejo.org/actions/setup-forgejo> action to v3.1.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1262): <!--number 1262 --><!--line 0 --><!--description UmVwbGFjZSBOb2RlLmpzIHdpdGggZGF0YS5mb3JnZWpvLm9yZy9vY2kvbm9kZQ==-->Replace Node.js with data.forgejo.org/oci/node<!--description-->

<!--end release-notes-assistant-->

### [`v12.5.0`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.5.0)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.4.0...v12.5.0)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

In combination with a runner with capacity > 1, and using Forgejo v14.0.0 or v14.0.1, this release has a known defect [#&#8203;1302](https://github.com/forgejo/runner/issues/1302) that may cause jobs to appear to be stalled in Forgejo.  Please upgrade the runner to v12.5.3 to avoid this issue.

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- features
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1259): <!--number 1259 --><!--line 0 --><!--description ZmVhdDogaW50cm9kdWNlIGV4cGVyaW1lbnRhbCBjb21tYW5kIGNvbm5lY3Qgd2l0aCB0b2tlbiBvcHRpb24=-->feat: introduce experimental command connect with token option<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1232): <!--number 1232 --><!--line 0 --><!--description ZmVhdDogYWRkIE9JREMgd29ya2xvYWQgaWRlbnRpdHkgZmVkZXJhdGlvbiBzdXBwb3J0-->feat: add OIDC workload identity federation support<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1249): <!--number 1249 --><!--line 0 --><!--description ZmVhdDogaW5jbHVkZSBhdXRoIHRva2VuIHdoZW4gY2xvbmluZyBhY3Rpb25zIGZyb20gb3duIGluc3RhbmNl-->feat: include auth token when cloning actions from own instance<!--description-->
- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1255): <!--number 1255 --><!--line 0 --><!--description Zml4OiBjbG9uZSBhY3Rpb25zIHdpdGggR2l0IGluc3RlYWQgb2YgZ28tZ2l0-->fix: clone actions with Git instead of go-git<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1257): <!--number 1257 --><!--line 0 --><!--description Zml4OiBob3N0LWJhc2VkIGV4ZWN1dG9yLCBjbGFyaWZ5IGVycm9yIG1lc3NhZ2UgZnJvbSBtaXNzaW5nIHdvcmtpbmctZGlyZWN0b3J5-->fix: host-based executor, clarify error message from missing working-directory<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1250): <!--number 1250 --><!--line 0 --><!--description Zml4OiBzdG9wIGNyZWF0aW9uIG9mIGJyYW5jaCBjYWxsZWQgSEVBRCBkdXJpbmcgZmV0Y2g=-->fix: stop creation of branch called HEAD during fetch<!--description-->
- other
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1252): <!--number 1252 --><!--line 0 --><!--description ZG9jczogcmVtb3ZlICdyZXF1aXJlZCcgZmxhZyBmcm9tIHJldXNhYmxlIHdvcmtmbG93ICdydW5zLW9uJyBmaWVsZA==-->docs: remove 'required' flag from reusable workflow 'runs-on' field<!--description-->

<!--end release-notes-assistant-->

### [`v12.4.0`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.4.0)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.3.1...v12.4.0)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

In combination with a runner with capacity > 1, and using Forgejo v14.0.0 or v14.0.1, this release has a known defect [#&#8203;1302](https://github.com/forgejo/runner/issues/1302) that may cause jobs to appear to be stalled in Forgejo.  Please upgrade the runner to v12.5.3 to avoid this issue.

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- features
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1247): <!--number 1247 --><!--line 0 --><!--description ZmVhdDogaW5jbHVkZSBhbGwgaW50ZWdyYXRpb24gdGVzdHMgaW4gYG1ha2UgaW50ZWdyYXRpb24tdGVzdGA=-->feat: include all integration tests in `make integration-test`<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1238): <!--number 1238 --><!--line 0 --><!--description ZmVhdChkb2NrZXJfcnVuKTogYWRkIHN1cHBvcnQgZm9yIHVzZXIgYW5kIGdyb3VwLWFkZCBqb2Igb3B0aW9ucw==-->feat(docker\_run): add support for user and group-add job options<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1245): <!--number 1245 --><!--line 0 --><!--description ZmVhdDogcmVxdWVzdCB1cCB0byBgY2FwYWNpdHlgIGpvYnMgZnJvbSBGb3JnZWpvIGluIG9uZSBBUEkgY2FsbA==-->feat: request up to `capacity` jobs from Forgejo in one API call<!--description-->
- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1240): <!--number 1240 --><!--line 0 --><!--description Zml4OiByZW1vdmUgYGNtZGAgYW5kIGBwb3J0c2AgZnJvbSBjb250YWluZXIgc2NoZW1h-->fix: remove `cmd` and `ports` from container schema<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1244): <!--number 1244 --><!--line 0 --><!--description Zml4OiBldmFsdWF0ZSBqb2JzLjxqb2JfaWQ+LmNvbnRhaW5lci52b2x1bWVz-->fix: evaluate jobs.\<job\_id>.container.volumes<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1251): <!--number 1251 --><!--line 0 --><!--description Zml4OiByZS1wYXJzaW5nIGluY29tcGxldGUgam9icyBtdXN0IHVzZSBvbi53b3JrZmxvd19jYWxsLmlucHV0cywgbm90IGdsb2JhbCBpbnB1dHM=-->fix: re-parsing incomplete jobs must use on.workflow\_call.inputs, not global inputs<!--description-->
- other
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1246): <!--number 1246 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBjb2RlLmZvcmdlam8ub3JnL2Zvcmdlam8vYWN0aW9ucy1wcm90byB0byB2MC42LjA=-->Update module code.forgejo.org/forgejo/actions-proto to v0.6.0<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1242): <!--number 1242 --><!--line 0 --><!--description VXBkYXRlIGZvcmdlam8tcnVubmVyIHRvIHYxMi4zLjE=-->Update forgejo-runner to v12.3.1<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1241): <!--number 1241 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZ28gdG8gdjEuMjU=-->Update dependency go to v1.25<!--description-->

<!--end release-notes-assistant-->

### [`v12.3.1`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.3.1)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.3.0...v12.3.1)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- features
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1239): <!--number 1239 --><!--line 0 --><!--description ZmVhdChqb2JwYXJzZXIpOiBzZXBhcmF0ZSB0aGUgY29uY2VwdCBvZiBhbiBpbnN0YW5jZSAmIGV4dGVybmFsIHJldXNhYmxlIHdvcmtmbG93-->feat(jobparser): separate the concept of an instance & external reusable workflow<!--description-->
- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1237): <!--number 1237 --><!--line 0 --><!--description Zml4OiBzZWNvbmQtbGF5ZXIgcmV1c2FibGUgd29ya2Zsb3dzIGxvc2luZyB0aGVpciBpbnB1dHMgd2hlbiBleHBhbmRlZA==-->fix: second-layer reusable workflows losing their inputs when expanded<!--description-->

<!--end release-notes-assistant-->

### [`v12.3.0`](https://code.forgejo.org/forgejo/runner/releases/tag/v12.3.0)

[Compare Source](https://code.forgejo.org/forgejo/runner/compare/v12.2.0...v12.3.0)

- [User guide](https://forgejo.org/docs/next/user/actions/overview/)
- [Administrator guide](https://forgejo.org/docs/next/admin/actions/)
- [Container images](https://code.forgejo.org/forgejo/-/packages/container/runner/versions)

Release Notes

***

<!--start release-notes-assistant-->

<!--URL:https://code.forgejo.org/forgejo/runner-->

- features
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1234): <!--number 1234 --><!--line 0 --><!--description ZmVhdChqb2JwYXJzZXIpOiBleHBvc2UgSm9iIHRvIHJldXNhYmxlIHdvcmtmbG93IGZldGNoZXJz-->feat(jobparser): expose Job to reusable workflow fetchers<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1227): <!--number 1227 --><!--line 0 --><!--description ZmVhdChqb2JwYXJzZXIpOiBhZGQgdHJhY2tpbmcgSURzIGZvciBvdXRlci9pbm5lciBqb2JzIGluIHJldXNhYmxlIHdvcmtmbG93cw==-->feat(jobparser): add tracking IDs for outer/inner jobs in reusable workflows<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1228): <!--number 1228 --><!--line 0 --><!--description ZmVhdChqb2JwYXJzZXIpOiBpZ25vcmUgYF9fbWV0YWRhdGFgIGluIHdvcmtmbG93IHNjaGVtYSB2YWxpZGF0aW9u-->feat(jobparser): ignore `__metadata` in workflow schema validation<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1229): <!--number 1229 --><!--line 0 --><!--description ZmVhdChqb2JwYXJzZXIpOiBleHBvc2UgQVBJIGZvciBgRXZhbHVhdGVXb3JrZmxvd0NhbGxTZWNyZXRzYA==-->feat(jobparser): expose API for `EvaluateWorkflowCallSecrets`<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1210): <!--number 1210 --><!--line 0 --><!--description ZmVhdChydW5uZXIpOiBza2lwIHNlcnZpY2UgY29udGFpbmVycyB3aXRoIGVtcHR5IGltYWdlIGFmdGVyIGludGVycG9sYXRpb24=-->feat(runner): skip service containers with empty image after interpolation<!--description-->
- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1235): <!--number 1235 --><!--line 0 --><!--description Zml4KGpvYnBhcnNlcik6IHByZXNlcnZlIHdvcmtmbG93X3BhcmVudF9pZCBvbiByZXBhcnNpbmcgaW5jb21wbGV0ZSB3b3JrZmxvd3M=-->fix(jobparser): preserve workflow\_parent\_id on reparsing incomplete workflows<!--description-->
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/1230): <!--number 1230 --><!--line 0 --><!--description Zml4OiBhY2NlcHQgZW52IHJlZmVyZW5jZXMgaW4gc2VydmljZSBkZWZpbml0aW9ucw==-->fix: accept env references in service definitions<!--description-->

<!--end release-notes-assistant-->

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi44NC4yIiwidXBkYXRlZEluVmVyIjoiNDIuODQuMiIsInRhcmdldEJyYW5jaCI6InYxNC4wL2Zvcmdlam8iLCJsYWJlbHMiOlsiZGVwZW5kZW5jeS11cGdyYWRlIiwidGVzdC9ub3QtbmVlZGVkIl19-->

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11001
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-23 04:51:52 +01:00
0ko
f9e2ccb108 [v14.0/forgejo] i18n: backport of translations from Codeberg Translate
Translation updates that were relevant to v14 branch were picked from this commit: 5c67663706

Changes to strings that are only present in the v15 branch were not picked.

Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v15.

Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Atalanttore <atalanttore@noreply.codeberg.org>
Co-authored-by: BKehayov <bkehayov@noreply.codeberg.org>
Co-authored-by: Benedikt Straub <benedikt-straub@web.de>
Co-authored-by: Bullbagaren <bullbagaren@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Edgarsons <edgarsons@noreply.codeberg.org>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: KWik <kwik@noreply.codeberg.org>
Co-authored-by: Klenje <klenje@noreply.codeberg.org>
Co-authored-by: Lzebulon <lzebulon@noreply.codeberg.org>
Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: SomeTr <sometr@noreply.codeberg.org>
Co-authored-by: Vyxie <kitakita@disroot.org>
Co-authored-by: Wuzzy <wuzzy@disroot.org>
Co-authored-by: aindriu80 <aindriu80@noreply.codeberg.org>
Co-authored-by: artnay <artnay@noreply.codeberg.org>
Co-authored-by: atarwn <atarwn@noreply.codeberg.org>
Co-authored-by: bespinas <bespinas@noreply.codeberg.org>
Co-authored-by: hanklank <hanklank@noreply.codeberg.org>
Co-authored-by: hugoalh <hugoalh@noreply.codeberg.org>
Co-authored-by: jimkats <jimkats@noreply.codeberg.org>
Co-authored-by: justbispo <justbispo@noreply.codeberg.org>
Co-authored-by: lanticy <lanticy@noreply.codeberg.org>
Co-authored-by: mahlzahn <mahlzahn@posteo.de>
Co-authored-by: ospalh <ospalh@noreply.codeberg.org>
Co-authored-by: retarded-beast <retarded-beast@noreply.codeberg.org>
Co-authored-by: smlxdesign <smlxdesign@noreply.codeberg.org>
Co-authored-by: tibfulv <tibfulv@noreply.codeberg.org>
Co-authored-by: vmtj <vmtj@noreply.codeberg.org>
Co-authored-by: xtex <xtexchooser@duck.com>
Co-authored-by: yookoala <yookoala@noreply.codeberg.org>
2026-01-22 17:34:15 +05:00
Renovate Bot
39a5f838c7 Update https://data.forgejo.org/forgejo/forgejo-build-publish action to v5.5.1 (v14.0/forgejo) (#10983)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10983
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-22 11:36:45 +01:00
Renovate Bot
da0df4b5d9 Update https://data.forgejo.org/infrastructure/issue-action action to v1.5.0 (v14.0/forgejo) (#10984)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10984
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-22 11:23:52 +01:00
Shiny Nematoda
c3fe2a5e34 [v14.0/forgejo] fix(ui): add missing translation for code search when keyword is empty string (#10970)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10964

- `CodeSearchMode` was is now set when keyword is empty
- The default value for search mode should be exact, use fuzzy ONLY when fuzziness is enabled in settings

(cherry picked from commit da7ce17533)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10970
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
Co-committed-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
2026-01-22 06:05:16 +01:00
forgejo-backport-action
191b309486 [v14.0/forgejo] fix(api): default new release 'title' field to label name, if not provided (#10949)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10925

## Checklist

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

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### 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.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/10949): <!--number 10949 --><!--line 0 --><!--description Zml4KGFwaSk6IGRlZmF1bHQgbmV3IHJlbGVhc2UgJ3RpdGxlJyBmaWVsZCB0byBsYWJlbCBuYW1lLCBpZiBub3QgcHJvdmlkZWQ=-->fix(api): default new release 'title' field to label name, if not provided<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: emilycares <emilydoescare@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10949
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-20 21:13:53 +01:00
forgejo-backport-action
4d7dfe38bf [v14.0/forgejo] fix(ui): improve force-push layout alignment (#10950)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10939

Followup to https://codeberg.org/forgejo/forgejo/pulls/7746
Replaces https://codeberg.org/forgejo/forgejo/pulls/10938

grid layout (desktop): give the Compare button a wrapper with same height as the first text line so it stays aligned to it (and to timeline badge)

inline layout (mobile): give the Compare button left margin so there's a gap between it and the text line

Initial fix for grid was proposed by luisadame.

Co-authored-by: Luis <luis@adame.dev>
Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10950
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-20 20:15:15 +01:00
forgejo-backport-action
186664b881 [v14.0/forgejo] fix: remove infinite loop in UpdateRunJobWithoutNotification when run in transaction (#10947)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10945

#10893 introduced a retry loop to manage concurrent updates when updating the state of `action_run` in the function `UpdateRunJobWithoutNotification`.  However, when `UpdateRunJobWithoutNotification` is called from within a transaction, the retry loop continues to read the same data from the DB (due to repeatable read isolation) and loops infinitely.

As #10893 was later identified to not be required to fix the target problem (https://code.forgejo.org/forgejo/runner/issues/1302), this PR reverts the change.  The only retained change is that the error `ErrActionRunOutOfDate` is a constant rather than `errors.New("run has changed")`.

## Checklist

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

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
      - [x] Reverted the test added for 10893 after confirming that it is the cause of the problem.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10947
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-20 18:31:11 +01:00
forgejo-backport-action
1d1a62187a [v14.0/forgejo] feat: strip newlines on og image rendering (#10928)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10914

Replace newlines from the repo description with spaces to match in-app
rendering.

Related issue: #10823

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] 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.

Co-authored-by: Enrique Sanchez Cardoso <enriqueesanchz@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10928
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-19 19:23:25 +01:00
forgejo-backport-action
ec28d5885d [v14.0/forgejo] fix: don't return AdditionalTasks from FetchTask if there is no Task (#10909)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10899

Possible bug that could cause https://code.forgejo.org/forgejo/runner/issues/1302: by picking more tasks after the first `PickTask` didn't find anything, they'll be returned in `AdditionalTasks`.  But the runner doesn't act upon additional tasks if there is no "first" task.

I can't see a practical way to cover this with an automated test other than mutating the production code to provide a synchronization point between the two operations.

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10909
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-18 02:35:02 +01:00
forgejo-backport-action
f33e2d1efd [v14.0/forgejo] fix: retry ActionRun updates when optimistic-concurrency-control indicates record changed (#10906)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10893

When concurrent updates occur to the `action_run` table, fetching a task via `FetchTask` can result in an error:
```
time="2026-01-16T16:02:30Z" level=error msg="failed to fetch task" error="internal: pick task: CreateTaskForRunner: update run 2358339: run has changed"
```

This is an error with no known harm.  However, this error is correlated with the forgejo/forgejo repo encountering zombie tasks, where they appear as if they are recorded as dispatched to a runner but a runner doesn't pick them up.  I think it would be worthwhile to prevent this error and see if it fixes the zombie tasks, or eliminate it as a potential cause.

See https://code.forgejo.org/forgejo/runner/issues/1302#issuecomment-73859 for a more detailed technical analysis.

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [ ] I did not document these changes and I do not expect someone else to do it.

### 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10906
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-18 02:00:05 +01:00
Renovate Bot
964288a4a8 Update module github.com/klauspost/compress to v1.18.3 (v14.0/forgejo) (#10908)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10908
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-18 00:45:41 +01:00
forgejo-backport-action
f3028a7768 [v14.0/forgejo] ci: tie go cache to go version and add Makefile to key hash (#10886)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10883

- tie go cache to go version, so it will cleanup from time to time [see](https://codeberg.org/forgejo/discussions/issues/436)
- Add `Makefile` to cache key hash, because it also has go deps

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10886
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-17 22:34:59 +01:00
forgejo-backport-action
16f98ebaec [v14.0/forgejo] fix: use ALTER TABLE in SQLite DropTableColumns(), allowing unexpected database sources to work better in migrations (#10903)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10888

The existing implementation of `DropTableColumns()` came from before SQLite had the ability to `ALTER TABLE ... DROP COLUMN ...`.  It works by parsing the table definition and rewriting it without the columns that are to be dropped, but it will fail to do this correctly if the table definition is not in the exact expected format.  In #10887, a database that had probably come through some migration tool was not exactly formatted the way Forgejo expected, resulting in a migration failure.

This replaces `DropTableColumns()`'s hacky SQLite implementation with a more straightforward implementation.  Affected indexes touching the target fields are dropped, then the field is dropped.

DROP COLUMN is supported on SQLite since [3.35.0, 2021-03-12](https://sqlite.org/releaselog/3_35_0.html).

## Checklist

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

- Existing `test-sqlite-migration` coverage is relied upon for this change.  During development it was proven to exercise the affected code -- in other words, multiple iterations of changes were required due to it failing as I worked on it.
- No coverage is added for "database with unexpected schema definition format" as the trigger issue for this change though, a point that can be raised if someone believes it is worthwhile.
- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10903
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-17 22:34:10 +01:00
Renovate Bot
ca46a3f68b Update dependency @citation-js/plugin-software-formats to v0.6.2 (v14.0/forgejo) (#10876)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10876
Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-16 16:57:25 +01:00
forgejo-backport-action
16ee36b023 [v14.0/forgejo] fix: make concurrency group job cancellation effect runs that are failed (#10871)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10863

When an action's job fails, it marks the entire run as failed.  Concurrency group cancellation was only looking for runs that are in a pending state, and therefore after a single job failed in the run, none of the other jobs in the run could be cancelled by a matching cancel-in-progress job.

Raised in https://codeberg.org/Codeberg/Community/issues/2315.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10871
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-16 15:36:58 +01:00
forgejo-backport-action
e147d8d805 [v14.0/forgejo] fix: recreate-tables doesn't work on PostgreSQL with multiple Forgejo schemas (#10870)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10854

Discovered while trying to reproduce #10848 -- when using `forgejo doctor recreate-tables` against a PostgreSQL database with multiple Forgejo schemas in it, it fails.  The reason is that when querying for index and sequence information, it begins to get information from the other schemas.

```
2026/01/15 15:19:15 ...3.6.1/command_run.go:288:run() [I] PING DATABASE postgresschema
2026/01/15 15:19:15 ...igrations/base/db.go:51:func2() [I] Creating temp table: tmp_recreate__external_login_user for Bean: ExternalLoginUser
2026/01/15 15:19:15 ...igrations/base/db.go:108:func2() [I] Copying table external_login_user to temp table tmp_recreate__external_login_user
2026/01/15 15:19:15 ...igrations/base/db.go:120:func2() [I] Dropping existing table external_login_user, and renaming temp table tmp_recreate__external_login_user in its place
2026/01/15 15:19:15 cmd/doctor.go:216:func1() [E] [Error SQL Query] ALTER INDEX "external_login_user_pkey" RENAME TO "external_login_user_pkey" [] - ERROR: relation "external_login_user_pkey" does not exist (SQLSTATE 42P01)
2026/01/15 15:19:15 ...igrations/base/db.go:404:renameTable() [E] Unable to rename external_login_user_pkey to external_login_user_pkey. Error: ERROR: relation "external_login_user_pkey" does not exist (SQLSTATE 42P01)
Command error: migrate: ERROR: relation "external_login_user_pkey" does not exist (SQLSTATE 42P01)
```

This is a very niche use-case that is likely to only affect a developer using PostgreSQL and popping back to older releases often enough to keep them around in different DB schemas.  I don't think it's worth an automated test, which would require creating a secondary DB schema in a specific migration test.  Manually tested on my dev environment.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10870
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-16 11:41:57 +01:00
forgejo-backport-action
cd00d61b91 [v14.0/forgejo] fix: use strict-origin as referrer policy (#10858)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10851

- Resolves forgejo/forgejo#10849
- Yes, the referrer policy is causing cross-origin protection to fail.
Why? Because someone really cared about privacy, the referrer policy was
set to no-referrer. So no `Referrer` HTTP header and `Origin` is either
omited or set to `null`, because hey the browser isn't allowed to leak
it via that header either. The new cross-origin protection relies on
Sec-Fetch metadata to determine if the request is same-origin or not.
This metadata is only sent to trustworthy origins, and thus not when
you visit Forgejo on your intranet. But the new protection has a
fallback to compare the Origin to the Host header... but the Origin
header was conviently set to `null` to protect the user's privacy.
- We now set the referrer policy to strict-origin, which means only for
same-origin requests a Origin header is set. For cross-origin the
behavior is unchanged and the user's privacy is preserved.

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10858
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-16 02:40:53 +01:00
Renovate Bot
0f20b2e51a Update dependency go to v1.25.6 (v14.0/forgejo) (#10853)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [go](https://go.dev/) ([source](https://github.com/golang/go)) | toolchain | patch | `1.25.5` -> `1.25.6` |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi43OC4yIiwidXBkYXRlZEluVmVyIjoiNDIuNzguMiIsInRhcmdldEJyYW5jaCI6InYxNC4wL2Zvcmdlam8iLCJsYWJlbHMiOlsiZGVwZW5kZW5jeS11cGdyYWRlIiwidGVzdC9ub3QtbmVlZGVkIl19-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10853
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-16 02:12:22 +01:00
forgejo-backport-action
a3a52251ad [v14.0/forgejo] chore: correct spelling error in cleanup-commit-status CLI docs (#10838)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10780

Fixes a typo that prevents the forgejo docs builds from being updated.

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [ ] I did not document these changes and I do not expect someone else to do it.

### 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.
- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10838
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-14 20:11:57 +01:00
0ko
46d0192f5c [v14.0/forgejo]: chore(release): delete previously announced release notes (#10835)
It was already announced in release notes of v13 and v11 point releases and shouldn't be in release notes of v14, which this file is causing.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10835
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
2026-01-14 17:06:35 +01:00
forgejo-backport-action
745134a89f [v14.0/forgejo] fix: proper styling for global time tracker popup (#10831)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10827

- Resolves forgejo/forgejo#10819
- CSS from tippy is overriding the style we are providing, so override
it via `!important` (tippy also uses that hence we lose the priority
race).

## Test
1. Start timer on any issue.
2. See that the styling is okay in the global time tracker popup in the navbar.

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10831
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-14 12:27:11 +01:00
forgejo-backport-action
04132b338d [v14.0/forgejo] fix(ui): show switch default branch button in branch list only for repo admins (#10826)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10814

The default braunch is configured in the repo settings. Only users with
administrator privileges for the repository can access the repo
settings. When the feature was implemented (72e956b79a),
the button in the branch list was only guarded with a check for repo
write permissions. This means the button is shown to too many users.
If no an user with write, but not admin permissions clicks on the button,
they see just a 404 page. Which is bad UX.

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10826
Reviewed-by: Beowulf <beowulf@beocode.eu>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-14 08:53:35 +01:00
forgejo-backport-action
527c1a4fda [v14.0/forgejo] fix: make lastcommit available for non-signed-in users (#10817)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10815

- Regression of forgejo/forgejo!9830
- `reqSignIn` means it requires sign-in, but it does not require sign-in (can be hit by visiting large repository) so `ignSignIn` is the better option.
- Resulted in behavior of being redirected to `/user/login` when visiting a repository such as comaps or forgejo when not being logged in.

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10817
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-13 23:41:35 +01:00
forgejo-backport-action
48bb631f20 [v14.0/forgejo] fix: drop sqlite shared cache (#10816)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10812

Use of sqlite shared cache is discouraged and obsolete. wal mode should be used instead.

- https://sqlite.org/sharedcache.html#use_of_shared_cache_is_discouraged

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10816
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-13 21:58:23 +01:00
0ko
92ea9b7055 merge commit: [v14.0/forgejo] i18n: backport of translations from Codeberg Translate (#10786)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10786
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
2026-01-12 15:18:52 +01:00
forgejo-backport-action
d912a9b21f [v14.0/forgejo] fix: add forgejo doctor cleanup-commit-status command to recover from #10671 (#10781)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10686

```
NAME:
   forgejo doctor cleanup-commit-status - Cleanup extra records in commit_status table

USAGE:
   forgejo doctor cleanup-commit-status

DESCRIPTION:
   Forgejo suffered from a bug which caused the creation of more entries in the
   "commit_status" table than necessary. This operation removes the redundant
   data caused by the bug. Removing this data is almost always safe.
   These reundant records can be accessed by users through the API, making it
   possible, but unlikely, that removing it could have an impact to
   integrating services (API: /repos/{owner}/{repo}/commits/{ref}/statuses).

   It is safe to run while Forgejo is online.

   On very large Forgejo instances, the performance of operation will improve
   if the buffer-size option is used with large values. Approximately 130 MB of
   memory is required for every 100,000 records in the buffer.

   Bug reference: https://codeberg.org/forgejo/forgejo/issues/10671

OPTIONS:
   --help, -h                       show help
   --custom-path string, -C string  Set custom path (defaults to '{WorkPath}/custom')
   --config string, -c string       Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')
   --work-path string, -w string    Set Forgejo's working path (defaults to the directory of the Forgejo binary)
   --verbose, -V                    Show process details
   --dry-run                        Report statistics from the operation but do not modify the database
   --buffer-size int                Record count per query while iterating records; larger values are typically faster but use more memory (default: 100000)
   --delete-chunk-size int          Number of records to delete per DELETE query (default: 1000)
```

The cleanup effectively performs `SELECT * FROM commit_status ORDER BY repo_id, sha, context, index, id`, and iterates through the records.  Whenever `index, id` changes without the other fields changing, then it's a useless record that can be deleted.  The major complication is doing that at scale without bringing the entire database table into memory, which is performed through a new iteration method `IterateByKeyset`.

Manually tested against a 455,303 record table in PostgreSQL, MySQL, and SQLite, which was reduced to 10,781 records, dropping 97.5% of the records.

## Checklist

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

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
    - Documentation should be via release notes and automatic CLI documentation updates.
- [ ] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10781
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-12 13:11:04 +01:00
forgejo-backport-action
b042992694 [v14.0/forgejo] fix: correctly compute required commit status (#10788)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10747

We need to take all matching required status into account to get the desired status because there can be some pending.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/10788): <!--number 10788 --><!--line 0 --><!--description Zml4OiBjb3JyZWN0bHkgY29tcHV0ZSByZXF1aXJlZCBjb21taXQgc3RhdHVz-->fix: correctly compute required commit status<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10788
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-12 13:05:58 +01:00
0ko
f962b32177 [v14.0/forgejo] i18n: backport of translations from Codeberg Translate
Translation updates that were relevant to v14 branch were picked from this commit:
f6399a0d25

Changes to strings that are only present in the v15 branch were not picked.

Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v15.

Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Arthur Zamarin <arthurzam@gentoo.org>
Co-authored-by: Benedikt Straub <benedikt-straub@web.de>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-authored-by: SomeTr <sometr@noreply.codeberg.org>
Co-authored-by: killawabbit <killawabbit@noreply.codeberg.org>
Co-authored-by: m-casanova <m-casanova@noreply.codeberg.org>
Co-authored-by: nykula <nykula@noreply.codeberg.org>
Co-authored-by: xtex <xtexchooser@duck.com>
2026-01-12 13:00:58 +05:00
0ko
5c9a2e91f4 [v14.0/forgejo] i18n: backport of translations from Codeberg Translate
Translation updates that were relevant to v14 branch were picked from this commit:
cc11e56483

Changes to strings that are only present in the v15 branch were not picked.

Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v15.

Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: AYT04 <ayt04@noreply.codeberg.org>
Co-authored-by: Arthur Zamarin <arthurzam@gentoo.org>
Co-authored-by: Astrak <astrak@noreply.codeberg.org>
Co-authored-by: Baempaieo <baempaieo@noreply.codeberg.org>
Co-authored-by: Benedikt Straub <benedikt-straub@web.de>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Edgarsons <edgarsons@noreply.codeberg.org>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-authored-by: Kenneth Bruen <kenny@kbruen.ro>
Co-authored-by: Kyush <kyush@noreply.codeberg.org>
Co-authored-by: Lzebulon <lzebulon@noreply.codeberg.org>
Co-authored-by: Nimplex <nimplex@noreply.codeberg.org>
Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: Schmerling <schmerling@noreply.codeberg.org>
Co-authored-by: SomeTr <sometr@noreply.codeberg.org>
Co-authored-by: Vyxie <kitakita@disroot.org>
Co-authored-by: WithLithum <withlithum@noreply.codeberg.org>
Co-authored-by: Wuzzy <wuzzy@disroot.org>
Co-authored-by: Zughy <zughy@noreply.codeberg.org>
Co-authored-by: adam <me@adamperkowski.dev>
Co-authored-by: adriand <adriand@noreply.codeberg.org>
Co-authored-by: artnay <artnay@noreply.codeberg.org>
Co-authored-by: butterflyoffire <butterflyoffire@noreply.codeberg.org>
Co-authored-by: jimkats <jimkats@noreply.codeberg.org>
Co-authored-by: justbispo <justbispo@noreply.codeberg.org>
Co-authored-by: mahlzahn <mahlzahn@posteo.de>
Co-authored-by: pixelcode <pixelcode@noreply.codeberg.org>
Co-authored-by: vmtj <vmtj@noreply.codeberg.org>
Co-authored-by: xtex <xtexchooser@duck.com>
2026-01-12 12:59:40 +05:00
Renovate Bot
915c436d95 Update CodeMirror (v14.0/forgejo) (#10776)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10776
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-11 11:43:36 +01:00
forgejo-backport-action
686f780673 [v14.0/forgejo] chore(ui): cleanup PR checks area (#10772)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10768

Mostly a UI followup to https://codeberg.org/forgejo/forgejo/pulls/9397 with a few fixes.

Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10772
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-11 09:33:45 +01:00
oliverpool
659f1fc0c6 [v14.0/forgejo] fix: internal server error on a large .gitmodules (#10767)
Backport: #10744

---

Fix #10714 (introduced in #8438) by silently ignoring large .gitmodules files.

Additionally:
- the limit was bumped from 10KB to 64KB (https://github.com/boostorg/boost/blob/master/.gitmodules has 20KB)
- a warning is shown on the .gitmodules view page if this limit is exceeded

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10744
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: oliverpool <git@olivier.pfad.fr>
Co-committed-by: oliverpool <git@olivier.pfad.fr>
(cherry picked from commit 970b0da24d)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10767
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
2026-01-11 09:32:09 +01:00
forgejo-backport-action
f87ec19130 [v14.0/forgejo] feat: provide multiple tasks to Runner in one FetchTask when requested (#10751)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10602

Permits the Forgejo to return multiple tasks to the Runner in one API call, if requested.  Fixes #8917.

Related runner PR: https://code.forgejo.org/forgejo/runner/pulls/1245

## Checklist

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

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] 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.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/10602): <!--number 10602 --><!--line 0 --><!--description cHJvdmlkZSBtdWx0aXBsZSB0YXNrcyB0byBSdW5uZXIgaW4gb25lIEZldGNoVGFzayB3aGVuIHJlcXVlc3RlZA==-->provide multiple tasks to Runner in one FetchTask when requested<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10751
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-09 21:26:27 +01:00
Renovate Bot
47b9fdc590 Update module code.forgejo.org/forgejo/actions-proto to v0.6.0 (v14.0/forgejo) (#10754)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [code.forgejo.org/forgejo/actions-proto](https://code.forgejo.org/forgejo/actions-proto) | `v0.5.3` -> `v0.6.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/code.forgejo.org%2fforgejo%2factions-proto/v0.6.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/code.forgejo.org%2fforgejo%2factions-proto/v0.5.3/v0.6.0?slim=true) |

---

### Release Notes

<details>
<summary>forgejo/actions-proto (code.forgejo.org/forgejo/actions-proto)</summary>

### [`v0.6.0`](https://code.forgejo.org/forgejo/actions-proto/compare/v0.5.3...v0.6.0)

[Compare Source](https://code.forgejo.org/forgejo/actions-proto/compare/v0.5.3...v0.6.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi43MS4wIiwidXBkYXRlZEluVmVyIjoiNDIuNzEuMCIsInRhcmdldEJyYW5jaCI6InYxNC4wL2Zvcmdlam8iLCJsYWJlbHMiOlsiZGVwZW5kZW5jeS11cGdyYWRlIiwidGVzdC9ub3QtbmVlZGVkIl19-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10754
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2026-01-09 20:29:59 +01:00
Michael Kriese
90ab3b1940 [v14.0/forgejo] January 8th security patches (#10720)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10720
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
2026-01-08 10:37:46 +01:00
forgejo-backport-action
0b7e6ff363 [v14.0/forgejo] fix(ui): actions list layout breakage with long content (#10723)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10648

Fixes issue #4580

## Context

Fixes the layout of the actions list specifically when an action name is too long to be displayed within the column's constrained width.

I took the opportunity to add some ancillary improvements:
- Center elements vertically
- Space elements consistently: the error badge didn't have the same margin on the left like the disabled badge.

Co-authored-by: Luis <luis@adame.dev>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10723
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-07 15:17:15 +01:00
forgejo-backport-action
440f38913e [v14.0/forgejo] fix(ui): improve rendering of commit links (#10613)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10530

This commit changes the commit link rendering (link to a single commit,
a diff, a PR, etc):

1. If it is a link to something on the local instance:
  1.1. If it is to the same org and repo, the link is just e.g. the
  commit hash
  1.2. If it is to another repo, the link is the org/repo/commit hash
2. If the link is to another instance:
  The link is the domain/optional sub path/org/repo/commit hash

This change is made to keep the link as short as possible, while not
hiding that the link may go to an external instance.

Followup to !9146

Closes #10241

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10613
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-07 13:51:34 +01:00
Mathieu Fenniak
3556875c51
doc: add release notes for Jan 8 security release 2026-01-06 10:34:13 -07:00
Gusted
f5603d2210 fix: load reviewer for pull review dismiss action notifier
This was implicitly loaded during the mail notifications notifier. If
you disable mail notifications on Forgejo then this will result in the
reviewer not being loaded and NPE.
2026-01-06 10:33:22 -07:00
Gusted
ca3166ddba chore: add integration test
Add a integration test that verifies that only the verified key is shown
in `{user}.gpg`.
2026-01-06 10:33:22 -07:00
Gusted
1ca9cbb7c2 fix: use correct GPG key for export
`GPGKeyToEntity` incorrectly assumed that within a keyring with multiple
keys that the first key is verified and should be exported. Look at all
keys and find the one that matches the verified key ID.
2026-01-06 10:33:22 -07:00
Mathieu Fenniak
590795f592
fix: don't use attachment size as max memory for ParseMultipart 2026-01-06 10:33:22 -07:00
Mathieu Fenniak
168dfbb70b
fix: incorrect whitespace handling on pre&post receive hooks 2026-01-06 10:33:22 -07:00
Mathieu Fenniak
bade14ee69
fix: hide user profile anonymous options on public repo APIs 2026-01-06 10:33:22 -07:00
forgejo-backport-action
8e083c9f3e [v14.0/forgejo] fix: prevent intermittent test failures caused by uncancellable tasks (#10717)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10713

Attempt to fix intermittent test failure noted in #10633, detailed technical notes in https://codeberg.org/forgejo/forgejo/issues/10633#issuecomment-9571199.

- Failure to cancel the previous processes is now a test error that aborts immediately, preventing 2hr long test runs that won't succeed.
- When the process cancellation fails, the stack trace of all goroutines is printed to help diagnose the cause of any failure to cancel tasks.
- `context.Background()` referenced in the actions notifier is corrected when opening git repos, which seems to be a cause of failure to cancel the tasks -- git subprocesses are spawned from the repo context, which is the background context, and that prevents the context registered in the process manager from cancelling them.

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10717
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-06 17:06:22 +01:00
forgejo-backport-action
342a54a142 [v14.0/forgejo] fix: retain Forgejo Action's commit_status entries with distinct descriptions (#10704)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10696

In #10678, I fixed an incorrect codepath that was intended to prevent duplicate redundant entries in `commit_status`.  However, the codepath that was repaired didn't take into account changes in the `description` field -- eg. going from `Waiting to run` to `Has started running` both have the `pending` commit status state but are distinct and should be retained.  This PR fixes the fix so that changes in description do still cause new entries into the `commit_status` table.

This issue was raised due to an end-to-end test failure in the `push-cancel` actions test.  I've manually tested that this fixes the end-to-end test.

Will require manual backport to v11 and v13 top of #10680 and #10681; automated backport to v14.

## Checklist

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

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10704
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-05 18:20:14 +01:00
Mathieu Fenniak
a53aa04f5d [v14.0/forgejo] fix: in-progress job icon doesn't rotate on repo's action list (#10656) (#10663)
**Backport:** #10656

(cherry picked from commit 0af52cdca2)

In-progress jobs don't have a rotating status icon on `/org/repo/actions`.  Likely a regression from #9444 as the rotation style was in `RepoActionView` which won't be loaded on the action list page.

Manually tested and confirmed that the styling is effective on both `/org/repo/actions` and in the action log page (`.../runs/#/jobs/#/attempt/#`).

Reported-by: limiting-factor
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10656
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10663
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2026-01-05 15:02:08 +01:00
forgejo-backport-action
40e4f6f354 [v14.0/forgejo] fix(ui): pull request merge menu item clipping the auto merge tip (#10685)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10652

Fixes the auto merge tip not being properly displayed due to an `overflow: hidden` set at the `.item` level. The fix resets that overflow rule if the auto merge button is being hovered and goes back to `hidden` when no longer being hovered.

Fixed also a small misalignment between the tip and the button.

Resolves #4822

### Test

1. Go to a pull request that merges into a protected branch that requires status checks to pass.
2. Go to the merge area of that pull request.
3. Click on the "Create merge commit" button.
4. Hover over the clock icon.
5. Observe that some text appears to the right of that clock icon.

Co-authored-by: Luis <luis@adame.dev>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10685
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-05 12:14:35 +01:00
forgejo-backport-action
abab629d90 [v14.0/forgejo] chore(cleanup): move all test blank imports in a single package (#10672)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10662

- create the modules/testimport/import.go to centralize blank import needed for tests (in order to run the init() function)  to simplify maintenance
- remove the imports that are not needed

Co-authored-by: limiting-factor <limiting-factor@posteo.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10672
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-05 12:14:11 +01:00
Beowulf
626929eaa3 feat(ui): replace Monaco with CodeMirror (#10559) (#10697)
- Replace the [Monaco Editor](https://microsoft.github.io/monaco-editor/)
with [CodeMirror 6](https://codemirror.net/). This editor is used to
facilitate the 'Add file' and 'Edit file' functionality.
- Rationale:
  - Monaco editor is a great and powerful editor, however for Forgejo's
  purpose it acts more like a small IDE than a code editor and is doing
  too much. In my limited user research the usage of editing files via
  the web UI is largely for small changes that does not need the
  features that Monaco editor provides.
  - Monaco editor has no mobile support, Codemirror is very usable on mobile.
  - Monaco editor pulls in large dependencies (for language support) and
  by replacing it with Codemirror the amount of time that webpack needs
  to build the frontend is reduced by 50% (~30s -> ~15s).
  - The binary of Forgejo (build with `bindata` tag) is reduced by 2MiB.
  - Codemirror is much more lightweight and should be more usable on
  less powerful hardware, most notably the lazy loading is much faster
  as codemirror uses less javascript.
  - Because Codemirror is modular it is much easier to change the
  behavior of the code editor if we wish to.
- Drawbacks:
  - Codemirror is quite modular and as seen in `package.json` and in
  `codeeditor.ts` we have to supply a lot more of its features to have
  feature parity with Monaco editor.
  - Monaco editor has great integrated language support (features that
  an lsp would provide), Codemirror only has such language support to an
  extend.
  - Monaco editor has its famous command palette (known by many as its
  also available in VSCode), this is not available in code mirror.
- Good to note:
  - All features that was added on top of the monaco editor (such as
  dynamically changing language  support depending on the filename)
  still works and the theme is based on the VSCode colors which largely
  resembles the monaco editor.
  - The code editor is still lazy-loaded (this is painfully clear by
  reading how imports are passed around in `codeeditor.ts`).
  - This change was privately tested by a few people, a few bugs were
  found (and fixed) but no major drawbacks were noted for their usage of
  the web editor.
  - There's a "search" button in the top bar, so that search can be used
  on mobile. It is otherwise only accessible via
  <kbd>Ctrl</kbd>+<kbd>f</kbd>.

Co-authored-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Beowulf <beowulf@beocode.eu>

(cherry picked from commit 28e0af23fa)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10697
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2026-01-05 06:26:42 +01:00
forgejo-backport-action
a604f85c60 [v14.0/forgejo] chore: download git-man over TLS (#10694)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10692

- No need to use http when https is available.

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10694
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-04 05:38:18 +01:00
forgejo-backport-action
ee42a69b3a [v14.0/forgejo] fix: don't duplicate commit status records on workflows with empty name (#10679)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10678

Fixes #10671.

Cleanup for the inflated number of records in this table will come in a near future change.

## Checklist

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

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10679
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2026-01-02 22:10:33 +01:00
forgejo-backport-action
766104acae [v14.0/forgejo] feat: add Forgejo server version to runner context (#10643)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10642

Currently, there's no way for actions runners to know what version of
Forgejo is running on the server side. This makes it
difficult/impossible to know which features are available and can make
maintaining compatibility tricky.

Let's add the Forgejo server version to the context.

See associated PR in the runner repo: https://code.forgejo.org/forgejo/runner/pulls/1249

Co-authored-by: John Moon <john.moon@vts-i.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10643
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-31 00:32:51 +01:00
forgejo-backport-action
180ebee6de [v14.0/forgejo] fix: build-release workflow stops its own end-to-end checks when run concurrently (#10635)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10632

`build-release.yml` attempts to run an end-to-end check with a cascading PR, but it doesn't target the currently building branch.  When two releases build simultaneously (eg. `forgejo/v14.0` and `forgejo`), whichever one starts the end-to-end test first is then "cancelled" by the second one as it pushes an update to the same branch.

This will be a bit of an experimental change due to the difficulty in setting up a test environment.  After merge, I intend to watch a v14 and forgejo build and verify that they are independent, and, that both are actually tested with the correct target build.

This introduces a need to backport any changes to `.forgejo/cascading-release-end-to-end` in the future to maintain cascading functionality in all active releases.

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10635
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-30 04:28:18 +01:00
forgejo-backport-action
400c17e290 [v14.0/forgejo] fix(ui): process dynamically added content via htmx (#10630)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10572

When new content is added via JS and htmx is not used for this change,
htmx need to be informed that DOM changes happened and that it needs to
reprocess the DOM (or at least the changed parts).

When a diff is really large, it is hidden by default. The user can press
a button to load the diff, which then will be added via JS.
The diff contains buttons to expand it, which are using htmx behind the
scenes. Therefore a reprocessing via htmx needs to be triggered after
adding the large diff.

Fixes #10570

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10630
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-30 02:26:44 +01:00
forgejo-backport-action
15f891abd7 [v14.0/forgejo] fix(ui): don't stretch activity top author image (#10628)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10556

This is a followup to !10524.

In addition I changed that the tooltip triggers for the whole height, instead for only the bar height, because otherwise it is esp for small bars nearly impossible to get the tooltip to open.

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10628
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-29 22:13:14 +01:00
forgejo-backport-action
8514af643d [v14.0/forgejo] fix: display orphan branches separately in commit graph (#10622)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10484

Fixes #2705.
Fixes #7635.

This PR fixes the commit graph showing false connections for orphan/root commits. Connection lines are now shown only when a parent/child relationship exists. Visible relationships are determined using `git log`'s `%P` output by the new `ComputeGlyphConnectivity` function. The SVG template is adapted to render vertical lines conditionally.

Unit tests for `ComputeGlyphConnectivity` cover regular linear commit history, orphan commits, merge commits, and non-commit glyphs (`|`, `/`, `\`). Unit tests also cover the changes to the `git log` parsing. The SVG template was verified manually.

Co-authored-by: Bram Hagens <bram@bramh.me>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10622
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-29 20:56:20 +01:00
forgejo-backport-action
763547f43f [v14.0/forgejo] migration: update existing foreign key migrations to automatically fix inconsistencies (#10621)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10568

Changes foreign key database inconsistency handling so that inconsistent records are automatically deleted with an administrator warning during migration.  As noted in discussion: https://codeberg.org/forgejo/discussions/issues/385#issuecomment-9175566

Because these migrations are now deleting data, rather than allowing the administrator to do it, all migrations have been covered with an integration test that verifies expected data is deleted.  This is particularly interesting with nullable fields.

## Checklist

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

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10621
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-29 03:49:03 +01:00
forgejo-backport-action
a89978a207 [v14.0/forgejo] fix: allow Actions trust management on conflicted PRs (#10600)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10594

Fixes #10589.

![20251226_134200](/attachments/0aac3594-62d7-467f-82f2-2d9063fb5ffb)

## Checklist

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

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10600
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-27 17:24:50 +01:00
forgejo-backport-action
c6c51dcde6 [v14.0/forgejo] test: fix intermittent PostgreSQL failure in TestAdminViewRepos (#10592)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10587

Intermittent test failure ([example](https://codeberg.org/forgejo/forgejo/actions/runs/125874/jobs/9/attempt/1)):
```
=== TestAdminViewRepos (tests/test_utils.go:327)
--- FAIL: TestAdminViewRepos (0.39s)
    testlogger.go:411: 2025/12/26 15:21:27 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspace/forgejo/forgejo/tests/gitea-lfs-meta
    testlogger.go:411: 2025/12/26 15:21:27 ...eb/routing/logger.go:102:func1() [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 4.7ms @ auth/auth.go:178(auth.SignInPost)
    testlogger.go:411: 2025/12/26 15:21:27 ...eb/routing/logger.go:102:func1() [I] router: completed GET /admin/repos for test-mock:12345, 200 OK in 75.1ms @ admin/repos.go:29(admin.Repos)
    admin_repo_test.go:29:
        	Error Trace:	/workspace/forgejo/forgejo/tests/integration/admin_repo_test.go:29
        	Error:      	Not equal:
        	            	expected: 1
        	            	actual  : 0
        	Test:       	TestAdminViewRepos
    admin_repo_test.go:30:
        	Error Trace:	/workspace/forgejo/forgejo/tests/integration/admin_repo_test.go:30
        	Error:      	Not equal:
        	            	expected: "repo49"
        	            	actual  : ""

        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-repo49
        	            	+
        	Test:       	TestAdminViewRepos
```

Cause: the page is displaying 50 out of 65 repos in the fixture with a default sort of "recently updated"; on PostgreSQL that is occasionally causing the target link not to appear on the first page.  As a fix, I've switched the test to load with reverse alphabetical order which should cause it to consistently appear on the first page.

## Checklist

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

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10592
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-27 02:23:25 +01:00
forgejo-backport-action
462fe3819b [v14.0/forgejo] fix: ListTrackedTimes API has no defined record ordering (#10593)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10588

API call `GET /repos/{owner}/{repo}/issues/{index}/times` has no defined ordering implemented in it, causing PostgreSQL to have intermittent test failures on `TestAPIGetTrackedTimes` which expected records to be returned in ID order.  ID order is reasonable enough, so this PR adds that ordering.

Fixes #10577.

## Checklist

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

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] 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.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10593
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-26 23:01:50 +01:00
forgejo-backport-action
8dff8ba7c2 [v14.0/forgejo] port(gitea): Fix password leak in log messages (go-gitea/gitea!35584) (#10555)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10550

Link to original PR: https://github.com/go-gitea/gitea/pull/35584
Original Author: https://github.com/shashank-netapp

Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10555
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-23 00:52:58 +01:00
forgejo-backport-action
5a131275c1 [v14.0/forgejo] fix(ui): avatar for dismissed review is stretched if not square (#10540)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10524

The timeline event for a dismissed review didn't used the `AvatarUtils` until now. The `AvatarUtils` also adds classes to the img tag, which makes sure the avatar is correctly styled and not stretched.
This PR replaces the img tag with the expected call to the `AvatarUtils`.

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10540
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-22 23:07:10 +01:00
forgejo-backport-action
6907601529 [v14.0/forgejo] fix(ui): add dynamic aria-label to monospace button in markdown editor (#10543)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/8244

The aria-label now changes dynamically depending on whether
the monospace font is enabled or disabled.

Greetings from GPN :)

Fixes #7669.

Co-authored-by: JohnnyJayJay <johnny@leftfold.tech>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10543
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-22 14:49:27 +01:00
forgejo-backport-action
fcb22b1a47 [v14.0/forgejo] fix: always search for issue posters by user and full name (#10542)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10394

Previously searching for posters would use full name or username depending on the `[ui].DEFAULT_SHOW_FULL_NAME` setting, now it searches for both of them regardless of the setting.

This also a fixes a bug when `[ui].DEFAULT_SHOW_FULL_NAME=true` that users without a full name where not able to searched for.

Co-authored-by: BtbN <btbn@btbn.de>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10542
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-22 13:45:28 +01:00
forgejo-backport-action
650252f851 [v14.0/forgejo] Add to html button in markdown type="button" (#10527)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10520

This is for preventing that a markdown button is recognized as button for submission in a html form.

Buttons can't be stripped from the markdown due to: https://codeberg.org/forgejo/forgejo/pulls/7670#issuecomment-4086608

There is no issue with buttons if they always have `type="button"`, so this should be fine.

This is a "follow-up" to !7670.

Fixes #7656

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10527
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-22 00:29:08 +01:00
forgejo-backport-action
cd0afc4f90 [v14.0/forgejo] fix(ui): add missing space before 'Commit' back (#10526)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10521

Regression from 8039240c26

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10526
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-22 00:28:33 +01:00
forgejo-backport-action
dd75d0957d [v14.0/forgejo] feat(ui): show cancel button until all jobs are finished (#10531)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9261

Change that the Cancel button is shown until all jobs are finished and do not hide it, when the first job failed.
Additionally the wrapping of the header was changed.

Fixes #8922

Co-authored-by: Beowulf <beowulf@beocode.eu>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10531
Reviewed-by: Beowulf <beowulf@beocode.eu>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-21 19:18:37 +01:00
forgejo-backport-action
83da3ae68c [v14.0/forgejo] feat(ui): show update time when sorting by recently updated (#10500)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10488

Fixes #4712
Fixes #7783

When filtering issues or PRs by "Recently updated" or "Least recently updated", the last updated time is shown:
![image](/attachments/f8e52a05-6055-42f9-9370-78196a173108)
![image](/attachments/50b63323-fe73-4ca5-8283-79fd6952d318)

Co-authored-by: Bram Hagens <bram@bramh.me>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10500
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-20 16:22:36 +01:00
forgejo-backport-action
44102c47d4 [v14.0/forgejo] fix: ignore private .profile repo on user profile page (#10495)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10486

Fixes #4202

Co-authored-by: Bram Hagens <bram@bramh.me>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10495
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-20 07:06:26 +01:00
forgejo-backport-action
fed7d64861 [v14.0/forgejo] fix(ui): align due date icon in issue list (#10494)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10489

Flattened nested `<span>` tags for the calender icon in the issue list, to fix the vertical alignment

Before:
![image](/attachments/f5049acb-41dc-438e-9256-ef30542e168d)

After:
![image](/attachments/c4d8bc64-0474-4a3e-9061-9e2bca6abff9)
![image](/attachments/0b2c4d9c-7d34-4627-be55-2099ed32dd19)

Co-authored-by: Bram Hagens <bram@bramh.me>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10494
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-20 06:05:54 +01:00
Andreas Ahlenstorf
6df7514417 Revert "feat: add support for ephemeral runners compatible with autoscaling tools (#9409)" (#10463)
Remove the unreleased HTTP API for managing runners that was introduced in https://codeberg.org/forgejo/forgejo/pulls/9409. It needs more time to mature. See also https://codeberg.org/forgejo/forgejo/pulls/10450.

## Checklist

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

- 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 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)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [ ] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] 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.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10463
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
Co-committed-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
2025-12-19 17:24:03 +01:00
forgejo-backport-action
0861a01192 [v14.0/forgejo] feat: allow to add pam source from command line (#10485)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10388

The forgejo admin command line allows to deal with all the propose auth mecanism but pam, this PR adds full support for adding and updating pam auth mecanism via the command line without limitation.

Co-authored-by: Baptiste Daroussin <bapt@FreeBSD.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10485
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
2025-12-19 17:00:47 +01:00
1955 changed files with 37819 additions and 96228 deletions

View file

@ -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
@ -50,6 +50,9 @@ forgejo.org/models/organization
forgejo.org/models/perm/access
GetRepoWriters
forgejo.org/models/repo
WatchRepoMode
forgejo.org/models/user
IsErrUserWrongType
IsErrExternalLoginUserAlreadyExist
@ -58,6 +61,7 @@ forgejo.org/models/user
IsErrUserSettingIsNotExist
GetUserAllSettings
DeleteUserSetting
GetFederatedUser
forgejo.org/modules/activitypub
NewContext
@ -87,6 +91,7 @@ forgejo.org/modules/eventsource
Event.String
forgejo.org/modules/forgefed
NewForgeFollow
NewForgeUndoLike
ForgeUndoLike.UnmarshalJSON
ForgeUndoLike.Validate
@ -132,9 +137,6 @@ forgejo.org/modules/json
StdJSON.Indent
forgejo.org/modules/log
eventWriterBuffer.Close
eventWriterBuffer.Write
eventWriterBuffer.GetString
NewEventWriterBuffer
forgejo.org/modules/markup
@ -225,6 +227,9 @@ forgejo.org/routers/web/org
forgejo.org/services/context
GetPrivateContext
forgejo.org/services/federation
FollowRemoteActor
forgejo.org/services/notify
UnregisterNotifier

View file

@ -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": {

View file

@ -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

View file

@ -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]

View file

@ -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

View file

@ -1,4 +1,3 @@
blank_issues_enabled: false
contact_links:
- name: 🔓 Security Reports
url: mailto:security@forgejo.org

View file

@ -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.

View file

@ -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

View file

@ -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: |

View file

@ -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

View file

@ -1,9 +1,6 @@
name: Integration tests for the release process
enable-email-notifications: true
env:
FORGEJO_VERSION: 11.0.13 # 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.0.6
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

View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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 }}

View file

@ -6,7 +6,7 @@ on:
env:
RNA_WORKDIR: /srv/rna
RNA_VERSION: v1.6.1 # 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 }}

View file

@ -8,16 +8,16 @@ on:
- labeled
env:
RNA_VERSION: v1.6.1 # 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 }}

View 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 }}

View file

@ -31,10 +31,10 @@ jobs:
if: vars.ROLE == 'forgejo-integration'
runs-on: docker
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
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
@ -50,10 +50,10 @@ jobs:
if: vars.ROLE == 'forgejo-integration'
runs-on: docker
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
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
@ -75,7 +75,7 @@ jobs:
matrix:
version: ['10.6', '11.8']
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
options: --tmpfs /tmp:exec,noatime
services:
mysql:
@ -85,12 +85,12 @@ 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
env:
DEBIAN_FRONTEND: noninteractive
- name: install dependencies & git >= 2.42
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git git-lfs
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-mysql-migration test-mysql'

View file

@ -14,7 +14,7 @@ jobs:
if: vars.ROLE == 'forgejo-coding' || vars.ROLE == 'forgejo-testing'
runs-on: docker
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
options: --tmpfs /tmp:exec,noatime
steps:
- name: event info
@ -22,31 +22,23 @@ 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'
runs-on: docker
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
options: --tmpfs /tmp:exec,noatime
steps:
- uses: https://data.forgejo.org/actions/checkout@v6
- uses: https://data.forgejo.org/actions/setup-node@v6
with:
node-version-file: .node-version
- uses: https://data.forgejo.org/actions/checkout@v5
- 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 +50,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 }}
@ -68,7 +60,7 @@ jobs:
runs-on: docker
needs: [backend-checks, frontend-checks]
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
options: --tmpfs /tmp:exec,noatime
services:
elasticsearch:
@ -76,7 +68,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,8 +78,12 @@ 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: install git >= 2.42
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git
- name: test release-notes-assistant.sh
run: |
apt-get -q install -qq -y jq
@ -109,17 +105,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 +140,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/
@ -154,7 +150,7 @@ jobs:
runs-on: docker
needs: [backend-checks, frontend-checks, test-unit]
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
options: --tmpfs /tmp:exec,noatime
name: ${{ format('test-remote-cacher ({0})', matrix.cacher.name) }}
strategy:
@ -177,10 +173,14 @@ 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
- name: install git >= 2.42
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-remote-cacher test-check'
@ -194,7 +194,7 @@ jobs:
runs-on: docker
needs: [backend-checks, frontend-checks]
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
options: --tmpfs /tmp:exec,noatime
services:
mysql:
@ -208,12 +208,12 @@ 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
env:
DEBIAN_FRONTEND: noninteractive
- name: install dependencies & git >= 2.42
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git git-lfs
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-mysql-migration test-mysql'
@ -225,7 +225,7 @@ jobs:
runs-on: docker
needs: [backend-checks, frontend-checks]
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
options: --tmpfs /tmp:exec,noatime
services:
minio:
@ -246,12 +246,12 @@ 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
env:
DEBIAN_FRONTEND: noninteractive
- name: install dependencies & git >= 2.42
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git git-lfs
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-pgsql-migration test-pgsql'
@ -265,15 +265,15 @@ jobs:
runs-on: docker
needs: [backend-checks, frontend-checks]
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
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
env:
DEBIAN_FRONTEND: noninteractive
- name: install dependencies & git >= 2.42
uses: ./.forgejo/workflows-composite/apt-install-from
with:
packages: git git-lfs
- uses: ./.forgejo/workflows-composite/build-backend
- run: |
su forgejo -c 'make test-sqlite-migration test-sqlite'
@ -293,23 +293,10 @@ jobs:
- test-remote-cacher
- test-unit
container:
image: 'data.forgejo.org/oci/node:24-trixie'
image: 'data.forgejo.org/oci/node:24-bookworm'
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
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1 +1 @@
24.15.0
24.12.0

View file

@ -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: |

View file

@ -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"

View file

@ -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".

View file

@ -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`.

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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}

View file

@ -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"]

View file

@ -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.160.6 # 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

103
assets/go-licenses.json generated

File diff suppressed because one or more lines are too long

View file

@ -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.

View file

@ -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
}

View file

@ -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))

View file

@ -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)
}
}
}
}

View file

@ -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:
@ -77,6 +73,12 @@ func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.N
funcname = nodeVar.Ident[2]
}
if funcname == "IterWithTr" {
for i := 2; i < len(nodeCommand.Args); i += 2 {
handler.handleTemplateMsgid(fset, nodeCommand.Args[i])
}
}
var gotUnexpectedInvoke *int
ltf, ok := handler.LocaleTrFunctions[funcname]
if !ok {
@ -150,12 +152,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)

View file

@ -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

View file

@ -47,6 +47,7 @@ func subcmdRegenerate() *cli.Command {
Name: "regenerate",
Usage: "Regenerate specific files",
Commands: []*cli.Command{
microcmdRegenHooks,
microcmdRegenKeys,
},
}

View file

@ -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 {

View file

@ -17,7 +17,6 @@ func subcmdUser() *cli.Command {
microcmdUserChangePassword(),
microcmdUserDelete(),
microcmdUserGenerateAccessToken(),
microcmdUserCreateAuthorizedIntegration(),
microcmdUserMustChangePassword(),
microcmdUserResetMFA(),
},

View file

@ -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
}

View file

@ -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

View file

@ -1,225 +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.`,
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-glob",
Value: map[string]string{},
Usage: `Zero-or-more claim glob checks, formatted as claim=value, example: "sub=repo:forgejo/*:pull_request"`,
},
// 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"),
}
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-glob") {
rules = append(rules, auth_model.ClaimRule{
Claim: claim,
Comparison: auth_model.ClaimGlob,
Value: value,
})
}
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"`
}
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.ClaimGlob:
description = fmt.Sprintf("%q matches %q", cr.Claim, cr.Value)
}
output.ClaimRules = append(output.ClaimRules, ClaimRuleDescription{
Description: description,
Claim: cr.Claim,
Comparison: cr.Comparison,
Value: cr.Value,
})
}
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
}

View file

@ -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 {

View file

@ -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) {

View file

@ -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")
}
})
}
}

View file

@ -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)
}
})
}()
}
}

View file

@ -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

View file

@ -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)
}

View file

@ -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)

View file

@ -126,9 +126,7 @@ func installSignals(ctx context.Context) (context.Context, context.CancelFunc) {
)
select {
case <-signalChannel:
break
case <-ctx.Done():
break
}
cancel()
signal.Reset()

View file

@ -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

View file

@ -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")
}

View file

@ -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)

View file

@ -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 {

View file

@ -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)
}

View file

@ -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" {

View file

@ -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 =
;;
@ -365,7 +362,7 @@ RUN_USER = ; git
DB_TYPE = sqlite3
;PATH= ; defaults to data/forgejo.db
;SQLITE_TIMEOUT = ; Query timeout defaults to: 500
;SQLITE_JOURNAL_MODE = WAL; defaults to sqlite database default (often DELETE), can be used to enable WAL mode. https://www.sqlite.org/pragma.html#pragma_journal_mode
;SQLITE_JOURNAL_MODE = ; defaults to sqlite database default (often DELETE), can be used to enable WAL mode. https://www.sqlite.org/pragma.html#pragma_journal_mode
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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],

200
go.mod
View file

@ -1,37 +1,37 @@
module forgejo.org
go 1.26.0
go 1.25.0
toolchain go1.26.3
toolchain go1.25.6
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.0
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.0
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.0
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.3
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/go-github/v74 v74.0.0
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.32
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.8.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.39.0
golang.org/x/net v0.53.0
golang.org/x/oauth2 v0.36.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/crypto v0.46.0
golang.org/x/image v0.33.0
golang.org/x/net v0.47.0
golang.org/x/oauth2 v0.34.0
golang.org/x/sync v0.19.0
golang.org/x/sys v0.40.0
golang.org/x/text v0.32.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.0 // 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/cloudflare/circl v1.6.1 // 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.6.2 // indirect
github.com/go-git/go-git/v5 v5.16.4 // 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.10.9 // 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.31.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.39.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.4

418
go.sum
View file

@ -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.0 h1:qs2mvrZeSqsR2sHQHO0kRTUErGrNMHHjm1qRy6EfLZY=
code.forgejo.org/forgejo/runner/v12 v12.6.0/go.mod h1:G75xcAhTaFE++/57qcBu+Zk7B7R3kLiUz8y2IkIwUKY=
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.4 h1:kyJHREXNEIuzpMwQoouTbUldPP6s/UlL3ZAcNlO4C5s=
code.forgejo.org/xorm/xorm v1.3.9-forgejo.4/go.mod h1:5ouTxqMcalQUvlBpQynRpzu/44GwaMpkA1nU+encsDE=
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.0 h1:Hof0MCcsa+1fS17gf86fTTZ8AQnMY9h9kzcc+2C6mVg=
code.superseriousbusiness.org/exif-terminator v0.11.0/go.mod h1:9sutT1axa/kSdlPLlRFjCNKmyo/KNx8eX3XZvWBlAEY=
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.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/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=
@ -185,8 +182,8 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -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.0 h1:Xx/5Ydg9CeBDX/wi4VJqStNtohYjitZhhlHt4h3St1M=
github.com/fsnotify/fsnotify v1.10.0/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.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
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.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y=
github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
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=
@ -382,14 +384,12 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-github/v81 v81.0.0 h1:hTLugQRxSLD1Yei18fk4A5eYjOGLUBKAl/VCqOfFkZc=
github.com/google/go-github/v81 v81.0.0/go.mod h1:upyjaybucIbBIuxgJS7YLOZGziyvvJ92WX6WEBNE3sM=
github.com/google/go-github/v74 v74.0.0 h1:yZcddTUn8DPbj11GxnMrNiAnXH14gNs559AsUpNpPgM=
github.com/google/go-github/v74 v74.0.0/go.mod h1:ubn/YdyftV80VPSI26nSJvaEsTOnsjrxG3o9kJhcyak=
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.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
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.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/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=
@ -553,8 +553,8 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG
github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
@ -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.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI=
github.com/urfave/cli/v3 v3.8.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.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
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-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
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.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
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.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
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.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
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=
@ -814,8 +813,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.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.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
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.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
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.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
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.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
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.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4=
modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
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=

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,85 +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
}
// 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
}

View file

@ -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")

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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) {

View file

@ -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 {
@ -581,35 +500,4 @@ func UpdateRunWithoutNotification(ctx context.Context, run *ActionRun, cols ...s
return nil
}
// Compute the Status, Started, and Stopped fields of an ActionRun based upon the current job state within the run.
// Returned is the [ActionRun] with modifications if necessary, a slice of column names that have been updated, or an
// error if the calculation failed. The caller is responsible for then invoking [actions_service.UpdateRun] for an
// update with notifications, or [actions_model.UpdateRunWithoutNotification] if notifications are already handled.
func ComputeRunStatus(ctx context.Context, runID int64) (run *ActionRun, columns []string, err error) {
run, err = GetRunByID(ctx, runID)
if err != nil {
return nil, nil, err
}
jobs, err := GetRunJobsByRunID(ctx, runID)
if err != nil {
return nil, nil, err
}
newStatus := AggregateJobStatus(jobs)
if run.Status != newStatus {
run.Status = newStatus
columns = append(columns, "status")
}
if run.Started.IsZero() && run.Status.IsRunning() {
run.Started = timeutil.TimeStampNow()
columns = append(columns, "started")
}
if run.Stopped.IsZero() && run.Status.IsDone() {
run.Stopped = timeutil.TimeStampNow()
columns = append(columns, "stopped")
}
return run, columns, nil
}
type ActionRunIndex db.ResourceIndex

View file

@ -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)
@ -173,22 +137,6 @@ func GetRunJobsByRunID(ctx context.Context, runID int64) ([]*ActionRunJob, error
return jobs, nil
}
// Check if the ActionRun has any jobs other than those included in the jobs parameter.
func RunHasOtherJobs(ctx context.Context, runID int64, jobs []*ActionRunJob) (bool, error) {
jobIDs := make([]int64, len(jobs))
for i, job := range jobs {
jobIDs[i] = job.ID
}
otherJobs, err := db.GetEngine(ctx).
Where("run_id = ?", runID).
Where(builder.NotIn("id", jobIDs)).
Count(&ActionRunJob{})
if err != nil {
return false, err
}
return otherJobs > 0, nil
}
// All calls to UpdateRunJobWithoutNotification that change run.Status for any run from a not done status to a done status must call the ActionRunNowDone notification channel.
// Use the wrapper function UpdateRunJob instead.
func UpdateRunJobWithoutNotification(ctx context.Context, job *ActionRunJob, cond builder.Cond, cols ...string) (int64, error) {
@ -226,12 +174,38 @@ func UpdateRunJobWithoutNotification(ctx context.Context, job *ActionRunJob, con
}
}
run, columns, err := ComputeRunStatus(ctx, job.RunID)
if err != nil {
return 0, fmt.Errorf("compute run status: %w", err)
}
if err := UpdateRunWithoutNotification(ctx, run, columns...); err != nil {
return 0, fmt.Errorf("update run %d: %w", run.ID, err)
{
// Other goroutines may aggregate the status of the run and update it too.
// So we need load the run and its jobs before updating the run.
run, err := GetRunByID(ctx, job.RunID)
if err != nil {
return 0, err
}
jobs, err := GetRunJobsByRunID(ctx, job.RunID)
if err != nil {
return 0, err
}
updateRequired := false
newStatus := AggregateJobStatus(jobs)
if run.Status != newStatus {
run.Status = newStatus
updateRequired = true
}
if run.Started.IsZero() && run.Status.IsRunning() {
run.Started = timeutil.TimeStampNow()
updateRequired = true
}
if run.Stopped.IsZero() && run.Status.IsDone() {
run.Stopped = timeutil.TimeStampNow()
updateRequired = true
}
if updateRequired {
// As the caller has to ensure the ActionRunNowDone notification is sent we can ignore doing so here.
if err := UpdateRunWithoutNotification(ctx, run, "status", "started", "stopped"); err != nil {
return 0, fmt.Errorf("update run %d: %w", run.ID, err)
}
}
}
return affected, nil
@ -270,9 +244,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 +268,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,62 +278,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
}

View file

@ -8,7 +8,6 @@ import (
"forgejo.org/models/db"
"forgejo.org/modules/container"
"forgejo.org/modules/optional"
"forgejo.org/modules/timeutil"
"xorm.io/builder"
@ -22,14 +21,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))
@ -57,13 +48,12 @@ func (jobs ActionJobList) LoadAttributes(ctx context.Context, withRepo bool) err
type FindRunJobOptions struct {
db.ListOptions
RunID int64
RepoID int64
OwnerID int64
CommitSHA string
Statuses []Status
UpdatedBefore timeutil.TimeStamp
RunNeedsApproval optional.Option[bool]
RunID int64
RepoID int64
OwnerID int64
CommitSHA string
Statuses []Status
UpdatedBefore timeutil.TimeStamp
}
func (opts FindRunJobOptions) ToConds() builder.Cond {
@ -74,7 +64,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,12 +76,5 @@ 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 {
cond = cond.And(builder.Exists(builder.Select("id").From("action_run", "outer_run").
Where(builder.Eq{
"outer_run.need_approval": value,
"outer_run.id": builder.Expr("run_id"),
})))
}
return cond
}

View file

@ -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())
}

View file

@ -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,7 +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()
isIncomplete, needs, matrix, err := tt.job.IsIncompleteRunsOn()
if tt.errContains != "" {
assert.ErrorContains(t, err, tt.errContains)
} else {
@ -161,344 +159,3 @@ func TestActionRunJob_HasIncompleteRunsOn(t *testing.T) {
})
}
}
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()
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 TestRunHasOtherJobs(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
jobs, err := GetRunJobsByRunID(t.Context(), 791)
require.NoError(t, err)
assert.Len(t, jobs, 1)
has, err := RunHasOtherJobs(t.Context(), 791, nil)
require.NoError(t, err)
assert.True(t, has)
has, err = RunHasOtherJobs(t.Context(), 791, []*ActionRunJob{})
require.NoError(t, err)
assert.True(t, has)
has, err = RunHasOtherJobs(t.Context(), 791, jobs)
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)
})
}
}

View file

@ -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"
)
@ -85,7 +82,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 != "" {
@ -130,18 +127,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
}

View file

@ -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)
}
}

View file

@ -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()
@ -400,295 +272,3 @@ jobs:
// Expect job with an incomplete runs-on to be StatusBlocked:
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())
t.Run("no changes", func(t *testing.T) {
run, columns, err := ComputeRunStatus(t.Context(), 791)
require.NoError(t, err)
assert.Equal(t, StatusSuccess, run.Status)
assert.NotContains(t, columns, "status")
assert.EqualValues(t, 1683636528, run.Started)
assert.NotContains(t, columns, "started")
assert.EqualValues(t, 1683636626, run.Stopped)
assert.NotContains(t, columns, "stopped")
})
t.Run("change status", func(t *testing.T) {
job := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: 192})
job.Status = StatusFailure
affected, err := db.GetEngine(t.Context()).Cols("status").ID(job.ID).Update(job)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
run, columns, err := ComputeRunStatus(t.Context(), 791)
require.NoError(t, err)
assert.Equal(t, StatusFailure, run.Status)
assert.Contains(t, columns, "status")
assert.NotContains(t, columns, "started")
assert.NotContains(t, columns, "stopped")
})
t.Run("won't change started if not running", func(t *testing.T) {
job := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: 192})
job.Status = StatusBlocked
affected, err := db.GetEngine(t.Context()).Cols("status").ID(job.ID).Update(job)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
preRun := unittest.AssertExistsAndLoadBean(t, &ActionRun{ID: 791})
preRun.Started = 0
affected, err = db.GetEngine(t.Context()).Cols("started").ID(preRun.ID).Update(preRun)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
run, columns, err := ComputeRunStatus(t.Context(), 791)
require.NoError(t, err)
assert.Equal(t, StatusBlocked, run.Status)
assert.EqualValues(t, 0, run.Started)
assert.Contains(t, columns, "status")
assert.NotContains(t, columns, "started")
assert.NotContains(t, columns, "stopped")
})
t.Run("change started", func(t *testing.T) {
// Need the job to be "Running" for started to appear to change
job := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: 192})
job.Status = StatusRunning
affected, err := db.GetEngine(t.Context()).Cols("status").ID(job.ID).Update(job)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
preRun := unittest.AssertExistsAndLoadBean(t, &ActionRun{ID: 791})
preRun.Started = 0
affected, err = db.GetEngine(t.Context()).Cols("started").ID(preRun.ID).Update(preRun)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
run, columns, err := ComputeRunStatus(t.Context(), 791)
require.NoError(t, err)
assert.Equal(t, StatusRunning, run.Status)
assert.NotEqualValues(t, 0, run.Started)
assert.Contains(t, columns, "status")
assert.Contains(t, columns, "started")
assert.NotContains(t, columns, "stopped")
})
t.Run("won't change stopped if not done", func(t *testing.T) {
job := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: 192})
job.Status = StatusRunning
affected, err := db.GetEngine(t.Context()).Cols("status").ID(job.ID).Update(job)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
preRun := unittest.AssertExistsAndLoadBean(t, &ActionRun{ID: 791})
preRun.Stopped = 0
affected, err = db.GetEngine(t.Context()).Cols("stopped").ID(preRun.ID).Update(preRun)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
run, columns, err := ComputeRunStatus(t.Context(), 791)
require.NoError(t, err)
assert.Equal(t, StatusRunning, run.Status)
assert.EqualValues(t, 0, run.Stopped)
assert.Contains(t, columns, "status")
assert.NotContains(t, columns, "stopped")
})
t.Run("change stopped", func(t *testing.T) {
// Need the job to be some version of Done for stopped to appear to change
job := unittest.AssertExistsAndLoadBean(t, &ActionRunJob{ID: 192})
job.Status = StatusSuccess
affected, err := db.GetEngine(t.Context()).Cols("status").ID(job.ID).Update(job)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
preRun := unittest.AssertExistsAndLoadBean(t, &ActionRun{ID: 791})
preRun.Stopped = 0
affected, err = db.GetEngine(t.Context()).Cols("stopped").ID(preRun.ID).Update(preRun)
require.NoError(t, err)
require.EqualValues(t, 1, affected)
run, columns, err := ComputeRunStatus(t.Context(), 791)
require.NoError(t, err)
assert.Equal(t, StatusSuccess, run.Status)
assert.NotEqualValues(t, 0, run.Stopped)
assert.NotContains(t, columns, "status")
assert.NotContains(t, columns, "started")
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)
}

View file

@ -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)

View file

@ -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]
}

View file

@ -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,341 +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)
}
})
}
}

View file

@ -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

View file

@ -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)
}

View file

@ -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})
}

View file

@ -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() {

View file

@ -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))
}
})
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -29,7 +29,7 @@ type ActionTask struct {
Job *ActionRunJob `xorm:"-"`
Steps []*ActionTaskStep `xorm:"-"`
Attempt int64
RunnerID int64 `xorm:"index index(request_key)"`
RunnerID int64 `xorm:"index"`
Status Status `xorm:"index"`
Started timeutil.TimeStamp `xorm:"index"`
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
@ -51,15 +51,6 @@ type ActionTask struct {
LogIndexes LogIndexes `xorm:"LONGBLOB"` // line number to offset
LogExpired bool `xorm:"index(stopped_log_expired)"` // files that are too old will be deleted
// When the FetchTask() API is invoked to create a task, unpreventable environmental errors may occur; for example,
// network disconnects and timeouts. If that API call has a unique identifier associated with it, it is stored in
// RunnerRequestKey. This allows the API call to be implemented idempotently using this state: if one API call
// assigns a task to a runner and a second API call is received from the same runner with the same request key, the
// existing assigned tasks can be returned.
//
// Indexed for an efficient search on runner_id=? AND runner_request_key=?.
RunnerRequestKey string `xorm:"index(request_key)"`
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated index"`
}
@ -156,11 +147,6 @@ func (task *ActionTask) GenerateToken() {
task.Token, task.TokenSalt, task.TokenHash, task.TokenLastEight = generateSaltedToken()
}
// After using GenerateToken, UpdateToken can be used to update the database record affecting the same columns.
func (task *ActionTask) UpdateToken(ctx context.Context) error {
return UpdateTask(ctx, task, "token_hash", "token_salt", "token_last_eight")
}
// Retrieve all the attempts from the same job as the target `ActionTask`. Limited fields are queried to avoid loading
// the LogIndexes blob when not needed.
func (task *ActionTask) GetAllAttempts(ctx context.Context) ([]*ActionTask, error) {
@ -188,10 +174,6 @@ func GetTaskByID(ctx context.Context, id int64) (*ActionTask, error) {
return &task, nil
}
func HasTaskForRunner(ctx context.Context, runnerID int64) (bool, error) {
return db.GetEngine(ctx).Where("runner_id = ?", runnerID).Exist(&ActionTask{})
}
func GetTaskByJobAttempt(ctx context.Context, jobID, attempt int64) (*ActionTask, error) {
var task ActionTask
has, err := db.GetEngine(ctx).Where("job_id=?", jobID).Where("attempt=?", attempt).Get(&task)
@ -256,15 +238,6 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
return nil, errNotExist
}
func GetTasksByRunnerRequestKey(ctx context.Context, runner *ActionRunner, requestKey string) ([]*ActionTask, error) {
var tasks []*ActionTask
err := db.GetEngine(ctx).Where("runner_id = ? AND runner_request_key = ?", runner.ID, requestKey).Find(&tasks)
if err != nil {
return nil, err
}
return tasks, nil
}
func getConcurrencyCondition() builder.Cond {
concurrencyCond := builder.NewCond()
@ -347,12 +320,12 @@ func GetAvailableJobsForRunner(e db.Engine, runner *ActionRunner) ([]*ActionRunJ
return jobs, nil
}
func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey, handle *string) (*ActionTask, bool, error) {
ctx, committer, err := db.TxContext(ctx)
func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) {
ctx, commiter, err := db.TxContext(ctx)
if err != nil {
return nil, false, err
}
defer committer.Close()
defer commiter.Close()
e := db.GetEngine(ctx)
@ -364,9 +337,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey,
// TODO: a more efficient way to filter labels
var job *ActionRunJob
log.Trace("runner labels: %v", runner.AgentLabels)
for _, j := range jobs {
if j.IsRequestedByRunner(handle) && j.ItRunsOn(runner.AgentLabels) {
job = j
for _, v := range jobs {
if v.ItRunsOn(runner.AgentLabels) {
job = v
break
}
}
@ -378,6 +351,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey,
}
now := timeutil.TimeStampNow()
job.Attempt++
job.Started = now
job.Status = StatusRunning
@ -392,9 +366,6 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey,
CommitSHA: job.CommitSHA,
IsForkPullRequest: job.IsForkPullRequest,
}
if requestKey != nil {
task.RunnerRequestKey = *requestKey
}
task.GenerateToken()
var workflowJob *jobparser.Job
@ -443,51 +414,13 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner, requestKey,
task.Job = job
if err := committer.Commit(); err != nil {
if err := commiter.Commit(); err != nil {
return nil, false, err
}
return task, true, nil
}
// Placeholder tasks are created when the status/content of an [ActionRunJob] is resolved by Forgejo without dispatch to
// a runner, specifically in the case of a workflow call's outer job.
func CreatePlaceholderTask(ctx context.Context, job *ActionRunJob, outputs map[string]string) (*ActionTask, error) {
actionTask := &ActionTask{
JobID: job.ID,
Attempt: job.Attempt,
Started: timeutil.TimeStampNow(),
Stopped: timeutil.TimeStampNow(),
Status: job.Status,
RepoID: job.RepoID,
OwnerID: job.OwnerID,
CommitSHA: job.CommitSHA,
IsForkPullRequest: job.IsForkPullRequest,
}
// token isn't used on a placeholder task, but generation is needed due to the unique constraint on field TokenHash
actionTask.GenerateToken()
err := db.WithTx(ctx, func(ctx context.Context) error {
_, err := db.GetEngine(ctx).Insert(actionTask)
if err != nil {
return fmt.Errorf("failure inserting action_task: %w", err)
}
for key, value := range outputs {
err := InsertTaskOutputIfNotExist(ctx, actionTask.ID, key, value)
if err != nil {
return fmt.Errorf("failure inserting action_task_output %q: %w", key, err)
}
}
return nil
})
if err != nil {
return nil, err
}
return actionTask, nil
}
func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
sess := db.GetEngine(ctx).ID(task.ID)
if len(cols) > 0 {

View file

@ -1,5 +1,4 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2026 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
@ -65,13 +64,13 @@ func (opts FindTaskOptions) 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 != "" {
cond = cond.And(builder.Eq{"commit_sha": opts.CommitSHA})
}
if len(opts.Status) > 0 {
if opts.Status != nil {
cond = cond.And(builder.In("status", opts.Status))
}
if opts.UpdatedBefore > 0 {
@ -83,11 +82,11 @@ func (opts FindTaskOptions) ToConds() builder.Cond {
if opts.RunnerID > 0 {
cond = cond.And(builder.Eq{"runner_id": opts.RunnerID})
}
if has, value := opts.LogExpired.Get(); has {
cond = cond.And(builder.Eq{"log_expired": value})
if opts.LogExpired.Has() {
cond = cond.And(builder.Eq{"log_expired": opts.LogExpired.Value()})
}
if has, value := opts.LogInStorage.Get(); has {
cond = cond.And(builder.Eq{"log_in_storage": value})
if opts.LogInStorage.Has() {
cond = cond.And(builder.Eq{"log_in_storage": opts.LogInStorage.Value()})
}
return cond
}

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