Compare commits

...
Sign in to create a new pull request.

49 commits

Author SHA1 Message Date
Alexander Bokovoy
621106123a fix: add required headers to pagure migration (#9973)
See https://pagure.io/fedora-infrastructure/issue/12886 for details.

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

## Test
1. Go to https://dev.gusted.xyz/repo/migrate?service_type=10
2. Fill in https://pagure.io/slapi-nis
3. Migrate.
4. Verify the migration succeeded.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9973
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Alexander Bokovoy <ab@samba.org>
Co-committed-by: Alexander Bokovoy <ab@samba.org>
(cherry picked from commit e7ef2eb370)
2025-11-06 12:58:04 +00:00
forgejo-backport-action
f1a497d3c1 [v13.0/forgejo] fix(alt): handle package names with dots in ALT repository (#9938)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9763

Followup to https://codeberg.org/forgejo/forgejo/pulls/6351

Previously, ALT RPM repository did not match packages with dots in their
names, causing 404 errors. Updated the regexp to correctly parse these paths.

Co-authored-by: Alex619829 <alex619829@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9938
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-11-03 03:32:00 +01:00
forgejo-backport-action
8ac5410a62 [v13.0/forgejo] fix: pull request review comment position (#9920)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9914

## Checklist

This PR contains both #9889 and #9912, since it depends on the one, and the other provides a test for it.
The exact reasoning behind its logic is described here: https://codeberg.org/forgejo/forgejo/issues/9473#issuecomment-7976186

This PR should return the behaviour back to how it was before a PR to Gitea changed it.
Only the resulting Database-Entry will reference the line blamed commit, now also with the correct adjusted line.
While the context diff view is pulled from the commit the commenter actually commented on.

Resolves forgejo/forgejo#9473

### 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.
  - [x] 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: BtbN <btbn@btbn.de>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9920
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-10-31 17:18:48 +01:00
forgejo-backport-action
cb0845cd3e [v13.0/forgejo] fix: don't show ConEmu OSC escape sequences (#9919)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9875

- Remove all [ConEMU OSC commands](https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC) from the output of Forgejo action logs when rendering.
- The regex is constructed as followed: Match the prefix `ESC ] 9 ;`. Then matches any number of digits, then match everything up to and including `ST` (this is either `ESC\` or `BELL`).
- Resolves forgejo/forgejo#9244

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9919
Reviewed-by: Earl Warren <earl-warren@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-10-31 16:15:29 +01:00
forgejo-backport-action
a50968d0de [v13.0/forgejo] fix: set tag message on tag addition (#9918)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9913

- When `SyncReleasesWithTags` is called, the code that synchronizes new tags to the database did not set the Note of the release to the tag's message. This was particularly noticeable when using the migration feature that tags have a empty description.
- Resolves forgejo/forgejo#7647

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9918
Reviewed-by: Earl Warren <earl-warren@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-10-31 08:28:08 +01:00
forgejo-backport-action
3bc1ae21ac [v13.0/forgejo] fix: construct project links in timeline better (#9901)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9872

- When a issue sees a modification regarding to which project they are assigned, then a timeline event is created for this. The link to the project that is constructed for this timeline event incorrectly assumes the project is a repository project.
- Use the `Link` function to construct the link to the project, this will correctly take into account if the project is a org, user or repo project.
- Resolves forgejo/forgejo#9817

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9901
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-10-30 08:57:44 +01:00
Mathieu Fenniak
b8448e7cde [v13.0/forgejo] fix: 2025-10-26 Security Patches (#9849)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9849
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2025-10-26 04:23:20 +01:00
Mathieu Fenniak
fc14793f7d
[v13.0/forgejo] fix: add release notes 9849.md 2025-10-25 09:17:46 -06:00
Earl Warren
fa1a2ba669 [v13.0/forgejo] fix: return on error if an LFS token cannot be parsed
Extracted from https://github.com/go-gitea/gitea/pull/35708
2025-10-25 09:13:47 -06:00
Mathieu Fenniak
afbf1efe02
[v13.0/forgejo] fix: prevent .forgejo/template from being out-of-repo content 2025-10-24 22:11:26 -06:00
Mathieu Fenniak
449b5bf10e
[v13.0/forgejo] fix: prevent writing to out-of-repo symlink destinations while evaluating template repos 2025-10-24 22:11:16 -06:00
Mathieu Fenniak
8885844e72
[v13.0/forgejo] fix: prevent commit API from leaking user's hidden email address on valid GPG signed commits 2025-10-24 22:11:08 -06:00
Earl Warren
a2068a47ce [v13.0/forgejo] i18n: update of translations from Codeberg Translate (#9825)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9825
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2025-10-24 08:11:42 +02:00
forgejo-backport-action
2c26525a9a [v13.0/forgejo] chore: update go target language version to v1.25.0 (#9827)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9822

Requirement for upcoming work to use APIs added to the standard library in Go 1.25.  I've broken this out into a separate PR to ensure there are no related test failures.

## 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/9827
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-10-23 21:50:33 +02:00
Renovate Bot
6e26d31473 Update data.forgejo.org/oci/golang Docker tag to v1.25 (v13.0/forgejo) (#9824)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [data.forgejo.org/oci/golang](https://hub.docker.com/_/golang) ([source](https://github.com/docker-library/golang)) | stage | minor | `1.24-alpine3.22` -> `1.25-alpine3.22` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTIuOSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Mi45IiwidGFyZ2V0QnJhbmNoIjoidjEzLjAvZm9yZ2VqbyIsImxhYmVscyI6WyJkZXBlbmRlbmN5LXVwZ3JhZGUiLCJ0ZXN0L25vdC1uZWVkZWQiXX0=-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9824
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
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>
2025-10-23 19:30:31 +02:00
0ko
6e60d538bd [v13.0/forgejo] i18n: update of translations from Codeberg Translate
Translation updates that were relevant to v13 branch were picked from this commit:
aad2efdbd6

Changes to strings that are only present in the v14 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 v14.

Co-authored-by: 0ko <0ko@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: Gusted <postmaster@gusted.xyz>
Co-authored-by: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Outbreak2096 <outbreak2096@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: X1SystemError0X <x1systemerror0x@noreply.codeberg.org>
Co-authored-by: bespinas <bespinas@noreply.codeberg.org>
Co-authored-by: butterflyoffire <butterflyoffire@noreply.codeberg.org>
Co-authored-by: justbispo <justbispo@noreply.codeberg.org>
Co-authored-by: nykula <nykula@noreply.codeberg.org>
Co-authored-by: stanek <stanek@noreply.codeberg.org>
Co-authored-by: victordargallo <victordargallo@noreply.codeberg.org>
Co-authored-by: xtex <xtexchooser@duck.com>
2025-10-23 21:18:52 +05:00
0ko
2022feee7d [v13.0/forgejo] i18n: update of translations from Codeberg Translate
Translation updates that were relevant to v13 branch were picked from this commit:
5494d8b3cd

Changes to strings that are only present in the v14 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 v14.

Co-authored-by: 0ko <0ko@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: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: SomeTr <sometr@noreply.codeberg.org>
Co-authored-by: Wuzzy <wuzzy@disroot.org>
Co-authored-by: anorprogrammer <anorprogrammer@noreply.codeberg.org>
Co-authored-by: artnay <artnay@noreply.codeberg.org>
Co-authored-by: bespinas <bespinas@noreply.codeberg.org>
Co-authored-by: butterflyoffire <butterflyoffire@noreply.codeberg.org>
Co-authored-by: emansije <emansije@noreply.codeberg.org>
Co-authored-by: nykula <nykula@noreply.codeberg.org>
Co-authored-by: pgmtx <pgmtx@noreply.codeberg.org>
Co-authored-by: victordargallo <victordargallo@noreply.codeberg.org>
Co-authored-by: xtex <xtexchooser@duck.com>
Co-authored-by: yeager <yeager@noreply.codeberg.org>
2025-10-23 21:18:02 +05:00
Renovate Bot
494d5625e2 Update dependency go to v1.25 (v13.0/forgejo) (#9816)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [go](https://go.dev/) ([source](https://github.com/golang/go)) | toolchain | minor | `1.24.7` -> `1.25.3` |

---

### 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:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNTIuOSIsInVwZGF0ZWRJblZlciI6IjQxLjE1Mi45IiwidGFyZ2V0QnJhbmNoIjoidjEzLjAvZm9yZ2VqbyIsImxhYmVscyI6WyJkZXBlbmRlbmN5LXVwZ3JhZGUiLCJ0ZXN0L25vdC1uZWVkZWQiXX0=-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9816
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>
2025-10-23 01:37:46 +02:00
Mathieu Fenniak
c7f2d7394b [v13.0/forgejo] fix(perf): add missing index on action_task table (#9795)
**Backport:** #9789

Fixes #9755, performance regression from #9017.

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

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

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9795): <!--number 9795 --><!--line 0 --><!--description Zml4KHBlcmYpOiBhZGQgbWlzc2luZyBpbmRleCBvbiBhY3Rpb25fdGFzayB0YWJsZQ==-->fix(perf): add missing index on action_task table<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9795
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-10-21 18:40:28 +02:00
forgejo-backport-action
76afa21433 [v13.0/forgejo] fix: GLOBAL_TWO_FACTOR_REQUIREMENT all prevents actions/checkout from cloning repositories (#9772)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9764

Resolves: #9738
I hope the test is ok, when not please say what kind of test I should add

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

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

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9764): <!--number 9764 --><!--line 0 --><!--description R0xPQkFMX1RXT19GQUNUT1JfUkVRVUlSRU1FTlQgYWxsIHByZXZlbnRzIGFjdGlvbnMvY2hlY2tvdXQgZnJvbSBjbG9uaW5nIHJlcG9zaXRvcmllcw==-->GLOBAL_TWO_FACTOR_REQUIREMENT all prevents actions/checkout from cloning repositories<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: zokki <zokki.softwareschmiede@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9772
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-10-20 22:00:02 +02:00
Mathieu Fenniak
d7e08cfb8c [v13.0/forgejo] fix: strict error handling on corrupted DB migration tracking tables (#9776)
**Backport:** #9773

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

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

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9776): <!--number 9776 --><!--line 0 --><!--description Zml4OiBzdHJpY3QgZXJyb3IgaGFuZGxpbmcgb24gY29ycnVwdGVkIERCIG1pZ3JhdGlvbiB0cmFja2luZyB0YWJsZXM=-->fix: strict error handling on corrupted DB migration tracking tables<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9776
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-10-20 19:13:50 +02:00
forgejo-backport-action
714b88f8b2 [v13.0/forgejo] fix: db.Iterate can miss records, can return records twice (#9723)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9657

Fixes #9644.

Rewrites `db.Iterate` so that it performs DB queries in this format:
- First: `SELECT ...columns... FROM table ORDER BY id LIMIT ...buffer-size...`
- Subsequent buffer fills: adding a `WHERE id > ...last-id-from-previous...`

This approach:
- Prevents records from being missed or returned twice
- Returns records in a predictable order
- Should be faster, by virtue of using database indexes on the primary key to perform the query
- Doesn't rely on any unpredictable database behaviour when using `LIMIT` and `OFFSET` without an `ORDER BY`
- (Downside: does require reflection to read field values off Go structures for the primary key value)

Expands the automated tests to include the predicted failure case identified in #9644, which verified the previous broken behaviour, as well as verifying that the `cond` parameter is applied which was previously not covered by test automation.

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9723
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
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-10-17 06:45:24 +02:00
forgejo-backport-action
d59c49ec52 [v13.0/forgejo] fix: release email links (#9714)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9690

Use correct links (instance.com/owner/repo/archive/tag.extension) links in release emails, instead of the (/owner/repo) incomplete links.

I have manually tested this. Below is a screenshot of an email sent with the new version having correct hyperlinks.

Fixes #9482

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

#### Manual test screenshots

![image](/attachments/f00fb1f9-17f2-4df8-bc0d-3e8f215020cb)

### 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: VewDev <vewdev@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9714
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
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-10-16 18:58:48 +02:00
forgejo-backport-action
48914b9465 [v13.0/forgejo] fix: use scrollHeight for rendered iframe if offsetHeight is unavailable (#9713)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9508

Fixes #9421.

Co-authored-by: Bojidar Marinov <bojidar.marinov.bg@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9713
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-10-16 16:53:54 +02:00
forgejo-backport-action
f7603e7356 [v13.0/forgejo] feat: strip EXIF information from uploaded avatars (#9689)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9638

Strips EXIF information from uploaded avatars (excluding the orientation tag), affecting both user & repo avatars.  Adds a new subcommand `forgejo doctor avatar-strip-exif` to perform a retroactive update of avatar files.

Fixes #9608.

## 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.
- [ ] I want the title to show in the release notes with a link to this pull request.
- [x] 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/9689): <!--number 9689 --><!--line 0 --><!--description VXBsb2FkZWQgYXZhdGFyIGltYWdlcyBjYW4gc29tZXRpbWVzIGNvbnRhaW4gdW5leHBlY3RlZCBtZXRhZGF0YSBzdWNoIGFzIHRoZSBsb2NhdGlvbiB3aGVyZSB0aGUgaW1hZ2Ugd2FzIGNyZWF0ZWQsIG9yIHRoZSBkZXZpY2UgdGhlIGltYWdlIHdhcyBjcmVhdGVkIHdpdGgsIHN0b3JlZCBpbiBhIGZvcm1hdCBjYWxsZWQgRVhJRi4gRm9yZ2VqbyBub3cgcmVtb3ZlcyBFWElGIGRhdGEgd2hlbiBjdXN0b20gdXNlciBhbmQgcmVwb3NpdG9yeSBpbWFnZXMgYXJlIHVwbG9hZGVkIGluIG9yZGVyIHRvIHJlZHVjZSB0aGUgcmlzayBvZiBwZXJzb25hbGx5IGlkZW50aWZpYWJsZSBpbmZvcm1hdGlvbiBiZWluZyBsZWFrZWQgdW5leHBlY3RlZGx5LiBBIG5ldyBDTEkgc3ViY29tbWFuZCBgZm9yZ2VqbyBkb2N0b3IgYXZhdGFyLXN0cmlwLWV4aWZgIGNhbiBiZSB1c2VkIHRvIHN0cmlwIEVYSUYgaW5mb3JtYXRpb24gZnJvbSBhbGwgZXhpc3RpbmcgYXZhdGFyczsgd2UgcmVjb21tZW5kIHRoYXQgYWRtaW5pc3RyYXRvcnMgcnVuIHRoaXMgY29tbWFuZCBvbmNlIGFmdGVyIHVwZ3JhZGUgaW4gb3JkZXIgdG8gbWluaW1pemUgdGhpcyByaXNrIGZvciBleGlzdGluZyBzdG9yZWQgZmlsZXMu-->Uploaded avatar images can sometimes contain unexpected metadata such as the location where the image was created, or the device the image was created with, stored in a format called EXIF. Forgejo now removes EXIF data when custom user and repository images are uploaded in order to reduce the risk of personally identifiable information being leaked unexpectedly. A new CLI subcommand `forgejo doctor avatar-strip-exif` can be used to strip EXIF information from all existing avatars; we recommend that administrators run this command once after upgrade in order to minimize this risk for existing stored files.<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9689
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-10-15 08:53:51 +02:00
Beowulf
0fda75e08e fix: re-add accidentally removed closing tag of div (#9687)
Regression from b1b99d5c70

# Testing

- Go to a repo
- Try to create an issue
- Check that the page looks correct and the forms are in place

After the fix it is correct again:

![grafik](/attachments/cce2906f-02d2-4f15-a307-8ad0376ec8db)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9687
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Beowulf <beowulf@beocode.eu>
Co-committed-by: Beowulf <beowulf@beocode.eu>
2025-10-14 12:13:02 +02:00
Gusted
08f37b5771 reverts "[v13.0/forgejo] fix: temporarily pin release builds to Go 1.24.7 (#9658)" (#9680)
Go 1.24.9 has been released which fixes the regression from Go 1.24.8

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9680
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-10-14 10:24:35 +02:00
Gusted
b1b99d5c70 [v13.0/forgejo] fix: avoid jumping to begin of page on edit comment action (#9645) (#9674)
**Backport: forgejo/forgejo#9645**

When you edit a comment and the comment already has a markdown editor,
then the code will click on the 'Write' tab, in case you canceled
editting the comment when you were at the 'Preview' tab. In
forgejo/forgejo#2681 I added `href="#"` to the tab items, this causes
that when the 'Write' tab is being clicked by the code the page is
jumped the beginning of the page.

Instead of being clever and trying to make this item interactive via
another way or via javascript avoid this jumping, we do better and make
this element a button. This item is not a link, it's a button that will
perform a action. This entirely avoids the issue of jumping and it's
still interactive.

Resolves forgejo/forgejo#9542

---

Conflict resolution: trivial
(cherry picked from commit d0a6f93f9e)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9674
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-10-13 23:05:03 +02:00
forgejo-backport-action
5a622f7640 [v13.0/forgejo] chore(e2e): test flakiness in webauthn.test.e2e.ts (#9672)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9662

Test failure:
```
  1) [chromium] › tests/e2e/webauthn.test.e2e.ts:14:1 › WebAuthn register & login flow ─────────────
    Error: page.goto: Navigation to "http://localhost:3003/user/login" is interrupted by another navigation to "http://localhost:3003/"
    Call log:
      - navigating to "http://localhost:3003/user/login", waiting until "load"
      46 |
      47 |   // Login.
    > 48 |   response = await page.goto('/user/login');
         |                         ^
      49 |   expect(response?.status()).toBe(200);
      50 |
      51 |   await page.getByLabel('Username or email address').fill(username);
        at /workspace/forgejo/forgejo/tests/e2e/webauthn.test.e2e.ts:48:25
```

I have not been able to reproduce this locally.

What seems to be happening is that the current code is clicking the "Sign out" menu option, and then while the browser is busy (navigating to `/logout`, redirecting to `/`), the test attempts to navigate directly to `/user/login`.  The two navigations are racey, depending on how fast they work they may result in this error.  The proposed fix is to wait for the sign-out operation to complete by waiting for the URL to land on `/`, before then proceeding with the rest of the test with the second login.

Normally this would be *just* a `waitForURL` call.  But because of the redirect on logout, I've encountered the below error if the code is just invoking `waitForURL`.  So I put the `waitForURL` invocation into an `expect(...).toPass()`.  This isn't technically the correct usage of `toPass` which is intended for *assertions* which will eventually become successful, whereas this is attempting to retry a wait... but... a wait shouldn't need a retry.  (I'd argue this is a Playwright bug.)
```
Error: page.waitForURL: net::ERR_ABORTED; maybe frame was detached?
```

Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9672
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-10-13 19:56:15 +02:00
forgejo-backport-action
4dbd9c7261 [v13.0/forgejo] fix(ui): add markup class to project descriptions (#9667)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9634

Fixes #9630

### Project page
Before
![project-page-before](/attachments/1a6b15e6-6959-4ec2-978b-241a31958f1c)
After
![project-page-after](/attachments/50fe81a7-6c29-49e5-8336-a8261171c77c)

### Projects list
Before
![project-list-before](/attachments/ec92a7f5-de76-4c36-90b4-4f8951ad14b7)
After
![project-list-after](/attachments/f1d82688-3030-45d6-8ca5-6d601f5cbc8f)

---

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

I'm unsure if this change is substantial enough to warrant that?

### 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-->
- User Interface bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9634): <!--number 9634 --><!--line 0 --><!--description Zml4KHVpKTogYWRkIGBtYXJrdXBgIGNsYXNzIHRvIHByb2plY3QgZGVzY3JpcHRpb25z-->fix(ui): add `markup` class to project descriptions<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Cyborus <cyborus@cyborus.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9667
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-10-13 05:14:47 +02:00
Earl Warren
9ff712ee6a [v13.0/forgejo] fix: temporarily pin release builds to Go 1.24.7 (#9658)
Refs: https://codeberg.org/forgejo/forgejo/issues/9656

## Testing

```
$ docker build --progress=plain --tag local:v13-test --load . |& tee /tmp/build
...
#8 [build-env  1/16] FROM data.forgejo.org/oci/golang:1.24.7-alpine3.22@sha256:fc2cff6625f3c1c92e6c85938ac5bd09034ad0d4bc2dfb08278020b68540dbb5
#8 ...

#11 [stage-2 3/9] RUN addgroup     -S -g 1000     git &&   adduser     -S -H -D     -h /data/git     -s /bin/bash     -u 1000     -G git     git &&   echo "git:*" | chpasswd -e
...
#23 59.25 CGO_CFLAGS="-O2 -g -DSQLITE_MAX_VARIABLE_NUMBER=32766" /usr/local/go/bin/go build -trimpath  -tags 'netgo osusergo bindata timetzdata sqlite sqlite_unlock_notify' -ldflags '-linkmode external -extldflags "-static" -buildid= -s -w -X "main.ReleaseVersion=" -X "main.MakeVersion=GNU Make 4.4.1" -X "main.Version=13.0.0-dev-544-f1b30a71b0+gitea-1.22.0" -X "main.Tags=bindata timetzdata sqlite sqlite_unlock_notify" -X "main.ForgejoVersion=13.0.0-dev-544-f1b30a71b0+gitea-1.22.0"' -o gitea
#23 DONE 116.3s

#24 [build-env 14/16] COPY docker/root /tmp/local
#24 DONE 0.0s

#25 [build-env 15/16] RUN chmod 755 /tmp/local/usr/bin/entrypoint               /tmp/local/usr/local/bin/gitea               /tmp/local/etc/s6/gitea/*               /tmp/local/etc/s6/openssh/*               /tmp/local/etc/s6/.s6-svscan/*               /go/src/forgejo.org/gitea               /go/src/forgejo.org/environment-to-ini
#25 DONE 0.3s

#26 [build-env 16/16] RUN chmod 644 /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete
#26 DONE 0.3s

#27 [stage-2 4/9] COPY --from=build-env /tmp/local /
#27 DONE 0.1s

#28 [stage-2 5/9] RUN cd /usr/local/bin ; ln -s gitea forgejo
#28 DONE 0.2s

#29 [stage-2 6/9] COPY --from=build-env /go/src/forgejo.org/gitea /app/gitea/gitea
#29 DONE 0.2s

#30 [stage-2 7/9] RUN ln -s /app/gitea/gitea /app/gitea/forgejo-cli
#30 DONE 0.2s

#31 [stage-2 8/9] COPY --from=build-env /go/src/forgejo.org/environment-to-ini /usr/local/bin/environment-to-ini
#31 DONE 0.0s

#32 [stage-2 9/9] COPY --from=build-env /go/src/forgejo.org/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
#32 DONE 0.1s

#33 exporting to image
#33 exporting layers
#33 exporting layers 0.2s done
#33 writing image sha256:02eab5877a38b53094d76a138ccfe5f3d8b2032e5e9161329350c7e663acd308 done
#33 naming to docker.io/library/local:v13-test done
#33 DONE 0.2s
```

and then:

```
$ docker run --rm local:v13-test /usr/local/bin/forgejo --version
forgejo version 13.0.0-dev-544-f1b30a71b0+gitea-1.22.0 built with GNU Make 4.4.1, go1.24.7 : bindata, timetzdata, sqlite, sqlite_unlock_notify
```

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9658
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Earl Warren <contact@earl-warren.org>
Co-committed-by: Earl Warren <contact@earl-warren.org>
2025-10-12 18:46:56 +02:00
forgejo-backport-action
01f0dbde9e [v13.0/forgejo] fix(ui/releases): strech elements apart when no search bar (#9637)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9626

Fix minor visual defect, which is a regression of https://codeberg.org/forgejo/forgejo/pulls/8399.

When viewing individual releases, there's no search bar.

I would have modified `.list-header` directly but blast radius is too large. So just added a helper. It does not affect the display in any other situations.

## Preview

![1](/attachments/5621e544-6d18-4f58-b4f5-f15bf2c10e3b)

![2](/attachments/f6ebc7bb-9168-4a3f-86ba-0b526c2ede89)

Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9637
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-10-11 11:18:20 +02:00
forgejo-backport-action
83e7ff3ba2 [v13.0/forgejo] chore(ci): limit LDAP service container memory usage to 500M (#9620)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9611

This is a noop and will be silently ignored until Forgejo runner v11.2.0 is servicing this repository with https://code.forgejo.org/forgejo/runner/pulls/1079

---

Resolves forgejo/forgejo#9406

Co-authored-by: Earl Warren <contact@earl-warren.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9620
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-10-10 23:03:41 +02:00
Earl Warren
72a38d19ac [v13.0/forgejo] chore: TestParseGitURLs must use a valid IPv6 address (#9622)
**Backport: https://codeberg.org/forgejo/forgejo/pulls/8908**

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8908
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>
(cherry picked from commit 94c068e91e)

```
Conflicts:
	go.mod
  only the IPv6 fix part is needed
```

Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9622
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2025-10-10 20:01:31 +02:00
0ko
72faa337d0 merge commit: [v13.0/forgejo] i18n: update of translations from Codeberg Translate (#9600)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9600
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2025-10-10 12:31:13 +02:00
0ko
65189bdb3e [v13.0/forgejo] i18n: update of translations from Codeberg Translate
Translation updates that were relevant to v13 branch were picked from this commit:
d5742c31fb

Changes to strings that are only present in the v14 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 v14.

Co-authored-by: 0ko <0ko@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: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Lzebulon <lzebulon@noreply.codeberg.org>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: Ricky-Tigg <ricky-tigg@noreply.codeberg.org>
Co-authored-by: SomeTr <sometr@noreply.codeberg.org>
Co-authored-by: Vaibhav Sunder <vaibhavswire@gmail.com>
Co-authored-by: Vyxie <kitakita@disroot.org>
Co-authored-by: Wuzzy <wuzzy@disroot.org>
Co-authored-by: X1SystemError0X <x1systemerror0x@noreply.codeberg.org>
Co-authored-by: arifpedia <arifpedia@noreply.codeberg.org>
Co-authored-by: artnay <artnay@noreply.codeberg.org>
Co-authored-by: bespinas <bespinas@noreply.codeberg.org>
Co-authored-by: butterflyoffire <butterflyoffire@noreply.codeberg.org>
Co-authored-by: dyniec <dyniec@noreply.codeberg.org>
Co-authored-by: erral <erral@noreply.codeberg.org>
Co-authored-by: justbispo <justbispo@noreply.codeberg.org>
Co-authored-by: m13o <m13o@noreply.codeberg.org>
Co-authored-by: smlxdesign <smlxdesign@noreply.codeberg.org>
Co-authored-by: talu <talu@noreply.codeberg.org>
Co-authored-by: xtex <xtexchooser@duck.com>
2025-10-09 23:46:41 +05:00
0ko
d6d4033a19 [v13.0/forgejo] i18n: update of translations from Codeberg Translate
Translation updates that were relevant to v13 branch were picked from this commit:
cd7a8f4546

Changes to strings that are only present in the v14 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 v14.

Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: 11xx <11xx@noreply.codeberg.org>
Co-authored-by: Baturax <baturax@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: Gusted <postmaster@gusted.xyz>
Co-authored-by: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: Languages add-on <noreply-addon-languages@weblate.org>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: Salif Mehmed <mail@salif.eu>
Co-authored-by: SomeTr <sometr@noreply.codeberg.org>
Co-authored-by: Wuzzy <wuzzy@disroot.org>
Co-authored-by: Xinayder <xinayder@noreply.codeberg.org>
Co-authored-by: matheusgomesms <matheusgomesms@noreply.codeberg.org>
Co-authored-by: smlxdesign <smlxdesign@noreply.codeberg.org>
Co-authored-by: thecoolcats <thecoolcats@noreply.codeberg.org>
2025-10-09 23:45:15 +05:00
Earl Warren
80fcbf165c [v13.0/forgejo] fix: failure to parse on block results in unconditional workflow execution (#9536)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9536
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2025-10-06 06:50:52 +02:00
forgejo-backport-action
2d3d5048d6 [v13.0/forgejo] fix(ui): reworked file preview placement towards better HTML validity, take 2 (#9540)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9534

Closes: forgejo/forgejo#9472.
Followup of: forgejo/forgejo#9181
Examples with render panic on code.forgejo.org:
- https://code.forgejo.org/forgejo/runner/issues/485#issue-6254
- https://code.forgejo.org/forgejo-helm/forgejo-helm/issues/705#issuecomment-37753

### 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: Robert Wolff <mahlzahn@posteo.de>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9540
Reviewed-by: Earl Warren <earl-warren@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-10-06 06:34:09 +02:00
Mathieu Fenniak
029a493779 display pre-execution errors in action view UI
(cherry picked from commit 02ea77c6a0)
2025-10-05 21:21:30 -06:00
Mathieu Fenniak
877726a2bf don't execute workflows when event parsing fails; create a pre-execution error instead
(cherry picked from commit 25ba696b6a)
2025-10-05 21:21:30 -06:00
Mathieu Fenniak
6755ff1631 save pre-execution errors to the DB when workflow execution is prevented by user data
(cherry picked from commit 152b98da90)
2025-10-05 21:21:30 -06:00
Mathieu Fenniak
72abb7079b test: fix 'Missing required prop' warning during RepoActionView frontend tests (#9454)
Fixes warnings in the `RepoActionView.test.js`, introduced in #9444 but caused by the frontend tests not providing all required properties to the component.

```
stderr | web_src/js/components/RepoActionView.test.js > processes ##[group] and ##[endgroup]
[Vue warn]: Missing required prop: "runIndex"
  at <RepoActionView jobIndex="1" attemptNumber="1" initialJobData= {
  state: {
    run: { status: 'success', commit: [Object] },
    currentJob: { steps: [Array] }
  },
  logs: { stepsLog: [] }
}  ... >
  at <VTUROOT>
```

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

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9454
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
(cherry picked from commit 0b923a03b4)
2025-10-05 21:21:07 -06:00
forgejo-backport-action
7a3fcbabf2 [v13.0/forgejo] fix: allow unactivated users to send recovery mails (#9515)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9504

With forgejo/forgejo#9075 the `GetUserByEmail` now actually only used activated emails. This however broke sending recovery mails to unactivated users, as their email are not yet activated.

Use the newly introduced function `GetUserByEmailSimple` to not care about this activated email requirement and also avoid the no-reply address being a valid email address for this functionality.

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9515
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-10-03 08:16:44 +02:00
forgejo-backport-action
a674198198 [v13.0/forgejo] fix(ui): improve Pagure migrator private issues clarity (#9506)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9502

Followup to https://codeberg.org/forgejo/forgejo/pulls/8513
More at https://codeberg.org/fedora/forgejo-deployment/issues/186
Related: https://codeberg.org/forgejo/forgejo/pulls/8513#issuecomment-6075304, https://codeberg.org/forgejo/forgejo/pulls/8513/files#issuecomment-6075322

Previously, users were getting confused about the token/private issues
behavior in the Pagure migrator, as the UI didn't clearly explain the
two-repository workflow or the security implications of using an API token.
This commit updates the Pagure migration template to make the behavior
clearer by:

- Adding a collapsible details section for private issues functionality to
  reduce UI clutter since this feature is not expected to be used frequently
- Replacing inline help text with proper Fomantic UI message boxes (blue
  info for description, red warning for security notice)
- Providing clear explanations of the two-step migration process (public
  content first, then private issues archive)
- Adding prominent security warnings about repository visibility when
  importing private content

The private issues section is now hidden by default in a collapsible
element, making the standard migration flow cleaner while still providing
access to advanced functionality when needed.

Co-authored-by: Ryan Lerch <rlerch@redhat.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9506
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-10-02 17:06:48 +02:00
forgejo-backport-action
d0b820039d [v13.0/forgejo] feat(ui): improve rendering commit links for PR commits, external repos and diffs (#9493)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9146

- fix links to PR commits, e.g. `!7979 (commit 4d968c08e0)`
- show the repo slug for links other than the current repository, e.g. `forgejo/docs@498bd80133`
- truncate long `diff-...` fragments, e.g. `600be26638 (diff-953bb4f01)`
- show `files` query values for compare links, e.g. `8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini)`

Closes: #7980
Closes: #9130
Closes: #8023
Ref: #5901

## 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] 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-->
- User Interface features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9146): <!--number 9146 --><!--line 0 --><!--description ZmVhdCh1aSk6IGltcHJvdmUgcmVuZGVyaW5nIGNvbW1pdCBsaW5rcyBmb3IgUFIgY29tbWl0cywgZXh0ZXJuYWwgcmVwb3MgYW5kIGRpZmZz-->feat(ui): improve rendering commit links for PR commits, external repos and diffs<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Lucas Schwiderski <lucas@lschwiderski.de>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9493
Reviewed-by: Earl Warren <earl-warren@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-10-01 05:44:52 +02:00
forgejo-backport-action
d452207e50 [v13.0/forgejo] fix: markup rendering panic must not abort the process (#9479)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9478

It must return on error instead, and log a stack trace for forensic analysis.

Refs https://codeberg.org/forgejo/forgejo/issues/9472

## 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: Earl Warren <contact@earl-warren.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9479
Reviewed-by: Earl Warren <earl-warren@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-09-30 10:26:11 +02:00
forgejo-backport-action
010366d641 [v13.0/forgejo] fix: package cleaned rule fails if the keep count is too high (#9471)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9468

If the keep count of a cleanup rule is greater than the number of available packages, it fails with:

```
panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceB})
.../packages/packages.go:175
.../routers/web/org/setting_packages.go:108
```

Regression of https://codeberg.org/forgejo/forgejo/pulls/9219/files

Refs https://codeberg.org/forgejo/forgejo/issues/9461

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

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9468): <!--number 9468 --><!--line 0 --><!--description cGFja2FnZSBjbGVhbmVkIHJ1bGUgZmFpbHMgaWYgdGhlIGtlZXAgY291bnQgaXMgdG9vIGhpZ2g=-->package cleaned rule fails if the keep count is too high<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Earl Warren <contact@earl-warren.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9471
Reviewed-by: Earl Warren <earl-warren@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-09-29 18:01:24 +02:00
forgejo-backport-action
391f9a2f2c [v13.0/forgejo] fix: replace "All pull requests" in repo issue filter (#9445)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/9401

When on the issue page, show "All issues" instead of "All pull requests" inside the "Type" filter.

Co-authored-by: Max Günther <code-mg@mailbox.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9445
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
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-09-27 17:48:41 +02:00
152 changed files with 8444 additions and 644 deletions

View file

@ -233,6 +233,7 @@ jobs:
options: --tmpfs /bitnami/minio/data
ldap:
image: data.forgejo.org/oci/forgejo-test-openldap:1
options: --memory 500M
pgsql:
image: data.forgejo.org/oci/bitnami/postgresql:16
env:

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.24-alpine3.22 AS build-env
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.25-alpine3.22 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.24-alpine3.22 AS build-env
FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.25-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-https://proxy.golang.org,direct}

View file

@ -41,7 +41,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.3.1 # 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.31.0 # 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@v1.6.0 # renovate: datasource=go
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
@ -514,7 +514,7 @@ lint-disposable-emails-fix:
.PHONY: security-check
security-check:
go run $(GOVULNCHECK_PACKAGE) -show color ./...
$(GO) run $(GOVULNCHECK_PACKAGE) -show color ./...
###
# Development and testing targets

File diff suppressed because one or more lines are too long

View file

@ -6,6 +6,8 @@ package cmd
import (
"context"
"fmt"
"image"
"io"
golog "log"
"os"
"path/filepath"
@ -15,11 +17,15 @@ import (
"forgejo.org/models/db"
"forgejo.org/models/migrations"
migrate_base "forgejo.org/models/migrations/base"
repo_model "forgejo.org/models/repo"
user_model "forgejo.org/models/user"
"forgejo.org/modules/container"
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
"forgejo.org/modules/storage"
"forgejo.org/services/doctor"
exif_terminator "code.superseriousbusiness.org/exif-terminator"
"github.com/urfave/cli/v3"
)
@ -34,6 +40,7 @@ func cmdDoctor() *cli.Command {
cmdDoctorCheck(),
cmdRecreateTable(),
cmdDoctorConvert(),
cmdAvatarStripExif(),
},
}
}
@ -98,6 +105,14 @@ You should back-up your database before doing this and ensure that your database
}
}
func cmdAvatarStripExif() *cli.Command {
return &cli.Command{
Name: "avatar-strip-exif",
Usage: "Strip EXIF metadata from all images in the avatar storage",
Action: runAvatarStripExif,
}
}
func runRecreateTable(stdCtx context.Context, ctx *cli.Command) error {
stdCtx, cancel := installSignals(stdCtx)
defer cancel()
@ -230,3 +245,78 @@ func runDoctorCheck(stdCtx context.Context, ctx *cli.Command) error {
}
return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
}
func runAvatarStripExif(ctx context.Context, c *cli.Command) error {
ctx, cancel := installSignals(ctx)
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
if err := storage.Init(); err != nil {
return err
}
type HasCustomAvatarRelativePath interface {
CustomAvatarRelativePath() string
}
doExifStrip := func(obj HasCustomAvatarRelativePath, name string, target_storage storage.ObjectStorage) error {
if obj.CustomAvatarRelativePath() == "" {
return nil
}
log.Info("Stripping avatar for %s...", name)
avatarFile, err := target_storage.Open(obj.CustomAvatarRelativePath())
if err != nil {
return fmt.Errorf("storage.Avatars.Open: %w", err)
}
_, imgType, err := image.DecodeConfig(avatarFile)
if err != nil {
return fmt.Errorf("image.DecodeConfig: %w", err)
}
// reset io.Reader for exif termination scan
_, err = avatarFile.Seek(0, io.SeekStart)
if err != nil {
return fmt.Errorf("avatarFile.Seek: %w", err)
}
cleanedData, err := exif_terminator.Terminate(avatarFile, imgType)
if err != nil && strings.Contains(err.Error(), "cannot be processed") {
// expected error for an image type that isn't supported by exif_terminator
log.Info("... image type %s is not supported by exif_terminator, skipping.", imgType)
return nil
} else if err != nil {
return fmt.Errorf("error cleaning exif data: %w", err)
}
if err := storage.SaveFrom(target_storage, obj.CustomAvatarRelativePath(), func(w io.Writer) error {
_, err := io.Copy(w, cleanedData)
return err
}); err != nil {
return fmt.Errorf("Failed to create dir %s: %w", obj.CustomAvatarRelativePath(), err)
}
log.Info("... completed %s.", name)
return nil
}
err := db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
return doExifStrip(user, fmt.Sprintf("user %s", user.Name), storage.Avatars)
})
if err != nil {
return err
}
err = db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
return doExifStrip(repo, fmt.Sprintf("repo %s", repo.Name), storage.RepoAvatars)
})
if err != nil {
return err
}
return nil
}

16
go.mod
View file

@ -1,8 +1,8 @@
module forgejo.org
go 1.24.0
go 1.25.0
toolchain go1.24.7
toolchain go1.25.3
require (
code.forgejo.org/f3/gof3/v3 v3.11.0
@ -17,6 +17,8 @@ require (
code.forgejo.org/go-chi/session v1.0.2
code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/sdk/gitea v0.21.0
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.18.1
github.com/42wim/httpsig v1.2.3
@ -34,6 +36,7 @@ require (
github.com/djherbis/buffer v1.2.0
github.com/djherbis/nio/v3 v3.0.1
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b
github.com/dustin/go-humanize v1.0.1
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3
github.com/emersion/go-imap v1.2.1
@ -116,6 +119,7 @@ require (
require (
cloud.google.com/go/compute/metadata v0.6.0 // indirect
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
@ -160,6 +164,10 @@ require (
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
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c // indirect
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.18.0 // indirect
@ -167,6 +175,7 @@ require (
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-errors/errors v1.1.1 // 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
@ -176,8 +185,10 @@ require (
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // 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-20200319012246-673a6f80352d // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
@ -249,6 +260,7 @@ require (
golang.org/x/tools v0.36.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
)

38
go.sum
View file

@ -46,6 +46,12 @@ code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zC
code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
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.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
@ -209,6 +215,22 @@ github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cn
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
github.com/dsoprea/go-exif/v3 v3.0.0-20210428042052-dca55bf8ca15/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b h1:NgNuLvW/gAFKU30ULWW0gtkCt56JfB7FrZ2zyo0wT8I=
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb h1:gwjJjUr6FY7zAWVEueFPrcRHhd9+IK81TcItbqw2du4=
github.com/dsoprea/go-iptc v0.0.0-20200609062250-162ae6b44feb/go.mod h1:kYIdx9N9NaOyD7U6D+YtExN7QhRm+5kq7//yOsRXQtM=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c h1:7j5aWACOzROpr+dvMtu8GnI97g9ShLWD72XIELMgn+c=
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200609050348-3db9b63b202c/go.mod h1:pqKB+ijp27cEcrHxhXVgUUMlSDRuGJJp1E+20Lj5H0E=
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e h1:IxIbA7VbCNrwumIYjDoMOdf4KOSkMC6NJE4s8oRbE7E=
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs=
@ -258,6 +280,10 @@ github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6n
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=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
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=
@ -291,6 +317,8 @@ github.com/go-webauthn/webauthn v0.14.0 h1:ZLNPUgPcDlAeoxe+5umWG/tEeCoQIDr7gE2Zx
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=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
@ -308,6 +336,9 @@ github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9v
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=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -404,6 +435,7 @@ github.com/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+
github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE=
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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8=
github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@ -722,7 +754,11 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -921,8 +957,10 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
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=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=

View file

@ -56,6 +56,8 @@ type ActionRun struct {
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated"`
NotifyEmail bool
PreExecutionError string `xorm:"LONGTEXT"` // used to report errors that blocked execution of a workflow
}
func init() {

View file

@ -25,7 +25,7 @@ import (
// ActionTask represents a distribution of job
type ActionTask struct {
ID int64
JobID int64
JobID int64 `xorm:"index"`
Job *ActionRunJob `xorm:"-"`
Steps []*ActionTaskStep `xorm:"-"`
Attempt int64

View file

@ -5,39 +5,82 @@ package db
import (
"context"
"fmt"
"reflect"
"forgejo.org/modules/setting"
"xorm.io/builder"
)
// Iterate iterate all the Bean object
// Iterate iterate all the Bean object. The table being iterated must have a single-column primary key.
func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
var start int
var dummy Bean
batchSize := setting.Database.IterateBufferSize
sess := GetEngine(ctx)
table, err := TableInfo(&dummy)
if err != nil {
return fmt.Errorf("unable to fetch table info for bean %v: %w", dummy, err)
}
if len(table.PrimaryKeys) != 1 {
return fmt.Errorf("iterate only supported on a table with 1 primary key field, but table %s had %d", table.Name, len(table.PrimaryKeys))
}
pkDbName := table.PrimaryKeys[0]
var pkStructFieldName string
for _, c := range table.Columns() {
if c.Name == pkDbName {
pkStructFieldName = c.FieldName
break
}
}
if pkStructFieldName == "" {
return fmt.Errorf("iterate unable to identify struct field for primary key %s", pkDbName)
}
var lastPK any
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
beans := make([]*Bean, 0, batchSize)
sess := GetEngine(ctx)
sess = sess.OrderBy(pkDbName)
if cond != nil {
sess = sess.Where(cond)
}
if err := sess.Limit(batchSize, start).Find(&beans); err != nil {
if lastPK != nil {
sess = sess.Where(builder.Gt{pkDbName: lastPK})
}
if err := sess.Limit(batchSize).Find(&beans); err != nil {
return err
}
if len(beans) == 0 {
return nil
}
start += len(beans)
for _, bean := range beans {
if err := f(ctx, bean); err != nil {
return err
}
}
lastBean := beans[len(beans)-1]
lastPK = extractFieldValue(lastBean, pkStructFieldName)
}
}
}
func extractFieldValue(bean any, fieldName string) any {
v := reflect.ValueOf(bean)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
field := v.FieldByName(fieldName)
return field.Interface()
}

View file

@ -5,42 +5,113 @@ package db_test
import (
"context"
"fmt"
"slices"
"testing"
"forgejo.org/models/db"
repo_model "forgejo.org/models/repo"
"forgejo.org/models/unittest"
"forgejo.org/modules/setting"
"forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/builder"
)
func TestIterate(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
xe, err := unittest.GetXORMEngine()
require.NoError(t, err)
require.NoError(t, xe.Sync(&repo_model.RepoUnit{}))
db.SetLogSQL(t.Context(), true)
defer test.MockVariableValue(&setting.Database.IterateBufferSize, 50)()
cnt, err := db.GetEngine(db.DefaultContext).Count(&repo_model.RepoUnit{})
require.NoError(t, err)
t.Run("No Modifications", func(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
xe, err := unittest.GetXORMEngine()
require.NoError(t, err)
require.NoError(t, xe.Sync(&repo_model.RepoUnit{}))
var repoUnitCnt int
err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repo *repo_model.RepoUnit) error {
repoUnitCnt++
return nil
// Fetch all the repo unit IDs...
var remainingRepoIDs []int64
db.GetEngine(t.Context()).Table(&repo_model.RepoUnit{}).Cols("id").Find(&remainingRepoIDs)
// Ensure that every repo unit ID is found when doing iterate:
err = db.Iterate(t.Context(), nil, func(ctx context.Context, repo *repo_model.RepoUnit) error {
remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool {
return repo.ID == n
})
return nil
})
require.NoError(t, err)
assert.Empty(t, remainingRepoIDs)
})
require.NoError(t, err)
assert.EqualValues(t, cnt, repoUnitCnt)
err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error {
has, err := db.ExistByID[repo_model.RepoUnit](ctx, repoUnit.ID)
if err != nil {
return err
}
if !has {
return db.ErrNotExist{Resource: "repo_unit", ID: repoUnit.ID}
}
return nil
t.Run("Concurrent Delete", func(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
xe, err := unittest.GetXORMEngine()
require.NoError(t, err)
require.NoError(t, xe.Sync(&repo_model.RepoUnit{}))
// Fetch all the repo unit IDs...
var remainingRepoIDs []int64
db.GetEngine(t.Context()).Table(&repo_model.RepoUnit{}).Cols("id").Find(&remainingRepoIDs)
// Ensure that every repo unit ID is found, even if someone else performs a DELETE on the table while we're
// iterating. In real-world usage the deleted record may or may not be returned, but the important
// subject-under-test is that no *other* record is skipped.
didDelete := false
err = db.Iterate(t.Context(), nil, func(ctx context.Context, repo *repo_model.RepoUnit) error {
// While on page 2 (assuming ID ordering, 50 record buffer size)...
if repo.ID == 51 {
// Delete a record that would have been on page 1.
affected, err := db.GetEngine(t.Context()).ID(25).Delete(&repo_model.RepoUnit{})
if err != nil {
return err
} else if affected != 1 {
return fmt.Errorf("expected to delete 1 record, but affected %d records", affected)
}
didDelete = true
}
remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool {
return repo.ID == n
})
return nil
})
require.NoError(t, err)
assert.True(t, didDelete, "didDelete")
assert.Empty(t, remainingRepoIDs)
})
t.Run("Verify cond applied", func(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
xe, err := unittest.GetXORMEngine()
require.NoError(t, err)
require.NoError(t, xe.Sync(&repo_model.RepoUnit{}))
// Fetch all the repo unit IDs...
var remainingRepoIDs []int64
db.GetEngine(t.Context()).Table(&repo_model.RepoUnit{}).Cols("id").Find(&remainingRepoIDs)
// Remove those that we're not expecting to find based upon `Iterate`'s condition. We'll trim the front few
// records and last few records, which will confirm that cond is applied on all pages.
remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool {
return n <= 15 || n > 1000
})
err = db.Iterate(t.Context(), builder.Gt{"id": 15}.And(builder.Lt{"id": 1000}), func(ctx context.Context, repo *repo_model.RepoUnit) error {
removedRecord := false
// Remove the record from remainingRepoIDs, but track to make sure we did actually remove a record
remainingRepoIDs = slices.DeleteFunc(remainingRepoIDs, func(n int64) bool {
if repo.ID == n {
removedRecord = true
return true
}
return false
})
if !removedRecord {
return fmt.Errorf("unable to find record in remainingRepoIDs for repo %d, indicating a cond application failure", repo.ID)
}
return nil
})
require.NoError(t, err)
assert.Empty(t, remainingRepoIDs)
})
require.NoError(t, err)
}

View file

@ -0,0 +1,24 @@
-
id: 7
owner_id: 2
key_id: C21381CD63F609EE
primary_key_id:
content: xsBNBGjmzbQBCADPn1Cl1LibN3K7+9A+If/RjDdTfFVbGk7A8pEDpkgzkmbIcMsi+WvPypbnoTJWyc4E5rsj7MYU6NKK3HADFX4rI6h/732xLmK/R+ekeT8wv+XKO1ncTtm/YITC5qRl84xeYfg4DuGGikhCc8xj8ZKWrx5r1ekISidyvNDJrrcHFToqlqGX74w9mRSSQJm1RayX+5nDph7qOpV8ZCpZT2LS00BYkYO62OHl2Ecm+qWiLyyA4iDR4DVSR9qyLzlXdEI2qQXf28zLV4YPaHckNWdRnaqlr/Mlbd+rbZt/49IBDgXPc+EGo6OXYmZls7pyPSCyirz/ObIyM/9t0JIh8rFrABEBAAE=
verified: true
can_sign: true
can_encrypt_comms: true
can_encrypt_storage: true
can_certify: true
emails: "[{\"ID\":8,\"UID\":1,\"Email\":\"user2+signingkey@example.com\",\"LowerEmail\":\"user2+signingkey@example.com\",\"IsActivated\":true,\"IsPrimary\":false}]"
-
id: 8
owner_id: 2
key_id: 9836974DF1195913
primary_key_id: C21381CD63F609EE
content: zsBNBGjmzbQBCADUa4mDg7owJwoBGi85flwoNw/QOvZew2LSZh7++dt+ClKcxryDpkAF4jmPsmKZEjhYfe4QE53E6jlYXRhprISONuR7gYPvvgCrvmEQKoh6o/tOlv0I4Ngix3SkE44u/CpFEVOrmFHBUvjgRHreOhwL3eyUao3PVte4kyAmvzZuGCmFFr7e0D48EGVbSLTGGj/GtHArnWPbs96S6qjXU/4qSvHsmNbGousgbovxXF/AxqIzyjvXheNEEQzUPjainDlOi3Z9BPJXr7T3ytt3slp48teKEXAC/uGVvFICwGN3WRCG8XsVwIQLUE23xclVdVqdzkO4Jr1D+Gtg95fYC2PvABEBAAE=
verified: false
can_sign: true
can_encrypt_comms: true
can_encrypt_storage: true
can_certify: true

View file

@ -119,6 +119,9 @@ var migrations = []*Migration{
NewMigration("Migrate `data` column of `secret` table to store keying material", MigrateActionSecretsToKeying),
// v39 -> v40
NewMigration("Add index for release sha1", AddIndexForReleaseSha1),
// NOTE: v42 -> v43 -- effectively backported into Forgejo v13 as part of backporting
// https://codeberg.org/forgejo/forgejo/pulls/9530, but the migration was omitted to avoid future upgrade conflicts.
// The migration will effectively occur automatically via table `Sync` on DB engine initialization.
}
// GetCurrentDBVersion returns the current Forgejo database version.
@ -171,19 +174,29 @@ func Migrate(x *xorm.Engine) error {
return fmt.Errorf("sync: %w", err)
}
currentVersion := &ForgejoVersion{ID: 1}
has, err := x.Get(currentVersion)
if err != nil {
return fmt.Errorf("get: %w", err)
} else if !has {
// If the version record does not exist we think
// it is a fresh installation and we can skip all migrations.
currentVersion.ID = 0
currentVersion.Version = ExpectedVersion()
if _, err = x.InsertOne(currentVersion); err != nil {
var versionRecords []*ForgejoVersion
if err := x.Find(&versionRecords); err != nil {
return fmt.Errorf("find: %w", err)
}
if len(versionRecords) == 0 {
// If the version record does not exist we think it is a fresh installation and we can skip all migrations;
// engine init calls `SyncAllTables` which will create the fresh database.
upToDate := &ForgejoVersion{ID: 1, Version: ExpectedVersion()}
if _, err := x.InsertOne(upToDate); err != nil {
return fmt.Errorf("insert: %w", err)
}
// continue with the migration routine, but nothing will be applied; this allows transition into the newer
// forgejo_migration library and for it to be configured and populated.
versionRecords = []*ForgejoVersion{upToDate}
} else if len(versionRecords) > 1 {
return fmt.Errorf(
"corrupt migrations: Forgejo database has unexpected records in the table `forgejo_version`; a single record is expected w/ ID=1, but %d records were found",
len(versionRecords))
}
currentVersion := versionRecords[0]
if currentVersion.ID != 1 {
return fmt.Errorf(
"corrupt migrations: Forgejo database has corrupted records in the table `forgejo_version`; a single record with ID=1 is expected, but a record with ID=%d was found instead", currentVersion.ID)
}
v := currentVersion.Version
@ -202,7 +215,7 @@ func Migrate(x *xorm.Engine) error {
// Some migration tasks depend on the git command
if git.DefaultContext == nil {
if err = git.InitSimple(context.Background()); err != nil {
if err := git.InitSimple(context.Background()); err != nil {
return err
}
}
@ -212,11 +225,11 @@ func Migrate(x *xorm.Engine) error {
log.Info("Migration[%d]: %s", v+int64(i), m.description)
// Reset the mapper between each migration - migrations are not supposed to depend on each other
x.SetMapper(names.GonicMapper{})
if err = m.migrate(x); err != nil {
if err := m.migrate(x); err != nil {
return fmt.Errorf("migration[%d]: %s failed: %w", v+int64(i), m.description, err)
}
currentVersion.Version = v + int64(i) + 1
if _, err = x.ID(1).Update(currentVersion); err != nil {
if _, err := x.ID(1).Update(currentVersion); err != nil {
return err
}
}

View file

@ -8,6 +8,7 @@ import (
migration_tests "forgejo.org/models/migrations/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -37,3 +38,38 @@ func TestEnsureUpToDate(t *testing.T) {
err = EnsureUpToDate(x)
require.Error(t, err)
}
func TestMigrateFreshDB(t *testing.T) {
x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoVersion))
defer deferable()
require.NotNil(t, x)
err := Migrate(x)
require.NoError(t, err)
var versionRecords []*ForgejoVersion
err = x.Find(&versionRecords)
require.NoError(t, err)
require.Len(t, versionRecords, 1)
v := versionRecords[0]
assert.EqualValues(t, 1, v.ID)
assert.EqualValues(t, 40, v.Version)
}
func TestMigrateFailWithCorruption(t *testing.T) {
x, deferable := migration_tests.PrepareTestEnv(t, 0, new(ForgejoVersion))
defer deferable()
require.NotNil(t, x)
// ID != 1
_, err := x.InsertOne(&ForgejoVersion{ID: 100, Version: 100})
require.NoError(t, err)
err = Migrate(x)
require.ErrorContains(t, err, "corrupted records in the table `forgejo_version`")
// Two versions...
_, err = x.InsertOne(&ForgejoVersion{ID: 1, Version: 1000})
require.NoError(t, err)
err = Migrate(x)
require.ErrorContains(t, err, "unexpected records in the table `forgejo_version`")
}

View file

@ -0,0 +1,14 @@
// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package migrations
import (
"testing"
migration_tests "forgejo.org/models/migrations/test"
)
func TestMain(m *testing.M) {
migration_tests.MainTest(m)
}

View file

@ -449,27 +449,38 @@ func Migrate(x *xorm.Engine) error {
}
var previousVersion int64
currentVersion := &Version{ID: 1}
has, err := x.Get(currentVersion)
if err != nil {
return fmt.Errorf("get: %w", err)
} else if !has {
// If the version record does not exist, it is a fresh installation, and we can skip all migrations.
// XORM model framework will create all tables when initializing.
currentVersion.ID = 0
currentVersion.Version = maxDBVer
if _, err = x.InsertOne(currentVersion); err != nil {
var versionRecords []*Version
if err := x.Find(&versionRecords); err != nil {
return fmt.Errorf("find: %w", err)
}
if len(versionRecords) == 0 {
// If the version record does not exist we think it is a fresh installation and we can skip all migrations;
// engine init calls `SyncAllTables` which will create the fresh database.
upToDate := &Version{ID: 1, Version: maxDBVer}
if _, err := x.InsertOne(upToDate); err != nil {
return fmt.Errorf("insert: %w", err)
}
// continue with the migration routine, but nothing will be applied; this allows transition into the newer
// forgejo library and for it to be configured and populated.
versionRecords = []*Version{upToDate}
} else if len(versionRecords) > 1 {
return fmt.Errorf(
"corrupt migrations: Forgejo database has unexpected records in the table `version`; a single record is expected w/ ID=1, but %d records were found",
len(versionRecords))
} else {
previousVersion = currentVersion.Version
previousVersion = versionRecords[0].Version
}
currentVersion := versionRecords[0]
if currentVersion.ID != 1 {
return fmt.Errorf(
"corrupt migrations: Forgejo database has corrupted records in the table `version`; a single record with ID=1 is expected, but a record with ID=%d was found instead", currentVersion.ID)
}
curDBVer := currentVersion.Version
// Outdated Forgejo database version is not supported
if curDBVer < minDBVersion {
log.Fatal(`Forgejo no longer supports auto-migration from your previously installed version.
Please try upgrading to a lower version first (suggested v1.6.4), then upgrade to this version.`)
Please try upgrading to a lower version first (suggested v1.6.4), then upgrade to this version.`)
return nil
}
@ -486,7 +497,7 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
// Some migration tasks depend on the git command
if git.DefaultContext == nil {
if err = git.InitSimple(context.Background()); err != nil {
if err := git.InitSimple(context.Background()); err != nil {
return err
}
}
@ -500,11 +511,11 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
log.Info("Migration[%d]: %s", m.idNumber, m.description)
// Reset the mapper between each migration - migrations are not supposed to depend on each other
x.SetMapper(names.GonicMapper{})
if err = m.Migrate(x); err != nil {
if err := m.Migrate(x); err != nil {
return fmt.Errorf("migration[%d]: %s failed: %w", m.idNumber, m.description, err)
}
currentVersion.Version = migrationIDNumberToDBVersion(m.idNumber)
if _, err = x.ID(1).Update(currentVersion); err != nil {
if _, err := x.ID(1).Update(currentVersion); err != nil {
return err
}
}

View file

@ -6,9 +6,11 @@ package migrations
import (
"testing"
migration_tests "forgejo.org/models/migrations/test"
"forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMigrations(t *testing.T) {
@ -25,3 +27,38 @@ func TestMigrations(t *testing.T) {
assert.Equal(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations))
assert.Equal(t, []*migration{}, getPendingMigrations(72, preparedMigrations))
}
func TestMigrateFreshDB(t *testing.T) {
x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Version))
defer deferable()
require.NotNil(t, x)
err := Migrate(x)
require.NoError(t, err)
var versionRecords []*Version
err = x.Find(&versionRecords)
require.NoError(t, err)
require.Len(t, versionRecords, 1)
v := versionRecords[0]
assert.EqualValues(t, 1, v.ID)
assert.EqualValues(t, 305, v.Version)
}
func TestMigrateFailWithCorruption(t *testing.T) {
x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Version))
defer deferable()
require.NotNil(t, x)
// ID != 1
_, err := x.InsertOne(&Version{ID: 100, Version: 100})
require.NoError(t, err)
err = Migrate(x)
require.ErrorContains(t, err, "corrupted records in the table `version`")
// Two versions...
_, err = x.InsertOne(&Version{ID: 1, Version: 1000})
require.NoError(t, err)
err = Migrate(x)
require.ErrorContains(t, err, "unexpected records in the table `version`")
}

View file

@ -236,7 +236,7 @@ func GetAllAdmins(ctx context.Context) ([]*User, error) {
// MustHaveTwoFactor returns true if the user is a individual and requires 2fa
func (u *User) MustHaveTwoFactor() bool {
if !u.IsIndividual() || setting.GlobalTwoFactorRequirement.IsNone() {
if u.IsActions() || !u.IsIndividual() || setting.GlobalTwoFactorRequirement.IsNone() {
return false
}
@ -1194,7 +1194,9 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) []
return newCommits
}
// GetUserByEmail returns the user object by given e-mail if exists.
// GetUserByEmail returns the user associated with the email, if it exists
// and is activated. If the email is a no-reply address, then the user
// associated with that no-reply address is returned.
func GetUserByEmail(ctx context.Context, email string) (*User, error) {
if len(email) == 0 {
return nil, ErrUserNotExist{Name: email}
@ -1227,6 +1229,26 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) {
return nil, ErrUserNotExist{Name: email}
}
// GetUserByEmailSimple returns the user associated with the email, if it exists.
//
// NOTE: You likely should use `GetUserByEmail`, which handles the no-reply
// address and only uses activated emails to get the user.
func GetUserByEmailSimple(ctx context.Context, email string) (*User, error) {
if len(email) == 0 {
return nil, ErrUserNotExist{Name: email}
}
emailAddress := &EmailAddress{}
has, err := db.GetEngine(ctx).Where("lower_email = ?", strings.ToLower(email)).Get(emailAddress)
if err != nil {
return nil, err
} else if !has {
return nil, ErrUserNotExist{Name: email}
}
return GetUserByID(ctx, emailAddress.UID)
}
// GetUser checks if a user already exists
func GetUser(ctx context.Context, user *User) (bool, error) {
return db.GetEngine(ctx).Get(user)

View file

@ -645,6 +645,7 @@ func TestMustHaveTwoFactor(t *testing.T) {
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17})
restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
ghostUser := user_model.NewGhostUser()
actionsUser := user_model.NewActionsUser()
t.Run("NoneTwoFactorRequirement", func(t *testing.T) {
// this should be the default, so don't have to set the variable
@ -653,6 +654,7 @@ func TestMustHaveTwoFactor(t *testing.T) {
assert.False(t, restrictedUser.MustHaveTwoFactor())
assert.False(t, org.MustHaveTwoFactor())
assert.False(t, ghostUser.MustHaveTwoFactor())
assert.False(t, actionsUser.MustHaveTwoFactor())
})
t.Run("AllTwoFactorRequirement", func(t *testing.T) {
@ -663,6 +665,7 @@ func TestMustHaveTwoFactor(t *testing.T) {
assert.True(t, restrictedUser.MustHaveTwoFactor())
assert.False(t, org.MustHaveTwoFactor())
assert.True(t, ghostUser.MustHaveTwoFactor())
assert.False(t, actionsUser.MustHaveTwoFactor())
})
t.Run("AdminTwoFactorRequirement", func(t *testing.T) {
@ -673,6 +676,7 @@ func TestMustHaveTwoFactor(t *testing.T) {
assert.False(t, restrictedUser.MustHaveTwoFactor())
assert.False(t, org.MustHaveTwoFactor())
assert.False(t, ghostUser.MustHaveTwoFactor())
assert.False(t, actionsUser.MustHaveTwoFactor())
})
}
@ -696,6 +700,7 @@ func TestIsAccessAllowed(t *testing.T) {
restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
prohibitLoginUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 37})
ghostUser := user_model.NewGhostUser()
actionsUser := user_model.NewActionsUser()
// users with enabled WebAuthn
normalWebAuthnUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 32})
@ -711,6 +716,7 @@ func TestIsAccessAllowed(t *testing.T) {
runTest(t, restrictedUser, false, true)
runTest(t, prohibitLoginUser, false, false)
runTest(t, ghostUser, false, false)
runTest(t, actionsUser, false, true)
})
t.Run("enabled 2fa", func(t *testing.T) {
@ -736,6 +742,7 @@ func TestIsAccessAllowed(t *testing.T) {
runTest(t, restrictedUser, false, false)
runTest(t, prohibitLoginUser, false, false)
runTest(t, ghostUser, false, false)
runTest(t, actionsUser, false, true)
})
t.Run("enabled 2fa", func(t *testing.T) {
@ -761,6 +768,7 @@ func TestIsAccessAllowed(t *testing.T) {
runTest(t, restrictedUser, false, true)
runTest(t, prohibitLoginUser, false, false)
runTest(t, ghostUser, false, false)
runTest(t, actionsUser, false, true)
})
t.Run("enabled 2fa", func(t *testing.T) {
@ -999,6 +1007,7 @@ func TestPronounsPrivacy(t *testing.T) {
func TestGetUserByEmail(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
defer test.MockVariableValue(&setting.Service.NoReplyAddress, "noreply.example.org")()
t.Run("Normal", func(t *testing.T) {
u, err := user_model.GetUserByEmail(t.Context(), "user2@example.com")
@ -1017,4 +1026,33 @@ func TestGetUserByEmail(t *testing.T) {
require.NoError(t, err)
assert.EqualValues(t, 1, u.ID)
})
t.Run("No-reply", func(t *testing.T) {
u, err := user_model.GetUserByEmail(t.Context(), "user1@noreply.example.org")
require.NoError(t, err)
assert.EqualValues(t, 1, u.ID)
})
}
func TestGetUserByEmailSimple(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
defer test.MockVariableValue(&setting.Service.NoReplyAddress, "noreply.example.org")()
t.Run("Normal", func(t *testing.T) {
u, err := user_model.GetUserByEmailSimple(t.Context(), "user2@example.com")
require.NoError(t, err)
assert.EqualValues(t, 2, u.ID)
})
t.Run("Not activated", func(t *testing.T) {
u, err := user_model.GetUserByEmailSimple(t.Context(), "user11@example.com")
require.NoError(t, err)
assert.EqualValues(t, 11, u.ID)
})
t.Run("No-reply", func(t *testing.T) {
u, err := user_model.GetUserByEmailSimple(t.Context(), "user1@noreply.example.org")
require.ErrorIs(t, err, user_model.ErrUserNotExist{Name: "user1@noreply.example.org"})
assert.Nil(t, u)
})
}

View file

@ -21,9 +21,10 @@ import (
)
type DetectedWorkflow struct {
EntryName string
TriggerEvent *jobparser.Event
Content []byte
EntryName string
TriggerEvent *jobparser.Event
Content []byte
EventDetectionError error
}
func init() {
@ -127,7 +128,8 @@ func DetectWorkflows(
TriggerEvent: &jobparser.Event{
Name: triggedEvent.Event(),
},
Content: content,
Content: content,
EventDetectionError: err,
}
workflows = append(workflows, dwf)
continue

View file

@ -10,6 +10,7 @@ import (
"image"
"image/color"
"image/png"
"io"
_ "image/gif" // for processing gif images
_ "image/jpeg" // for processing jpeg images
@ -17,6 +18,7 @@ import (
"forgejo.org/modules/avatar/identicon"
"forgejo.org/modules/setting"
exif_terminator "code.superseriousbusiness.org/exif-terminator"
"golang.org/x/image/draw"
_ "golang.org/x/image/webp" // for processing webp images
@ -66,15 +68,29 @@ func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) {
return nil, fmt.Errorf("image height is too large: %d > %d", imgCfg.Height, setting.Avatar.MaxHeight)
}
var cleanedBytes []byte
if imgType != "gif" { // "gif" is the only imgType supported above, but not supported by exif_terminator
cleanedData, err := exif_terminator.Terminate(bytes.NewReader(data), imgType)
if err != nil {
return nil, fmt.Errorf("error cleaning exif data: %w", err)
}
cleanedBytes, err = io.ReadAll(cleanedData)
if err != nil {
return nil, fmt.Errorf("error reading cleaned data: %w", err)
}
} else { // gif
cleanedBytes = data
}
// If the origin is small enough, just use it, then APNG could be supported,
// otherwise, if the image is processed later, APNG loses animation.
// And one more thing, webp is not fully supported, for animated webp, image.DecodeConfig works but Decode fails.
// So for animated webp, if the uploaded file is smaller than maxOriginSize, it will be used, if it's larger, there will be an error.
if len(data) < int(maxOriginSize) {
return data, nil
return cleanedBytes, nil
}
img, _, err := image.Decode(bytes.NewReader(data))
img, _, err := image.Decode(bytes.NewReader(cleanedBytes))
if err != nil {
return nil, fmt.Errorf("image.Decode: %w", err)
}
@ -94,7 +110,7 @@ func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) {
// usually the png compression is not good enough, use the original image (no cropping/resizing) if the origin is smaller
if len(data) <= len(resized) {
return data, nil
return cleanedBytes, nil
}
return resized, nil

View file

@ -11,7 +11,10 @@ import (
"testing"
"forgejo.org/modules/setting"
"forgejo.org/modules/test"
jpegstructure "code.superseriousbusiness.org/go-jpeg-image-structure/v2"
"github.com/dsoprea/go-exif/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -30,8 +33,8 @@ func Test_RandomImage(t *testing.T) {
}
func Test_ProcessAvatarPNG(t *testing.T) {
setting.Avatar.MaxWidth = 4096
setting.Avatar.MaxHeight = 4096
defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)()
defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)()
data, err := os.ReadFile("testdata/avatar.png")
require.NoError(t, err)
@ -41,8 +44,8 @@ func Test_ProcessAvatarPNG(t *testing.T) {
}
func Test_ProcessAvatarJPEG(t *testing.T) {
setting.Avatar.MaxWidth = 4096
setting.Avatar.MaxHeight = 4096
defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)()
defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)()
data, err := os.ReadFile("testdata/avatar.jpeg")
require.NoError(t, err)
@ -51,17 +54,28 @@ func Test_ProcessAvatarJPEG(t *testing.T) {
require.NoError(t, err)
}
func Test_ProcessAvatarGIF(t *testing.T) {
defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)()
defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)()
data, err := os.ReadFile("testdata/avatar.gif")
require.NoError(t, err)
_, err = processAvatarImage(data, 262144)
require.NoError(t, err)
}
func Test_ProcessAvatarInvalidData(t *testing.T) {
setting.Avatar.MaxWidth = 5
setting.Avatar.MaxHeight = 5
defer test.MockVariableValue(&setting.Avatar.MaxWidth, 5)()
defer test.MockVariableValue(&setting.Avatar.MaxHeight, 5)()
_, err := processAvatarImage([]byte{}, 12800)
assert.EqualError(t, err, "image.DecodeConfig: image: unknown format")
}
func Test_ProcessAvatarInvalidImageSize(t *testing.T) {
setting.Avatar.MaxWidth = 5
setting.Avatar.MaxHeight = 5
defer test.MockVariableValue(&setting.Avatar.MaxWidth, 5)()
defer test.MockVariableValue(&setting.Avatar.MaxHeight, 5)()
data, err := os.ReadFile("testdata/avatar.png")
require.NoError(t, err)
@ -71,8 +85,8 @@ func Test_ProcessAvatarInvalidImageSize(t *testing.T) {
}
func Test_ProcessAvatarImage(t *testing.T) {
setting.Avatar.MaxWidth = 4096
setting.Avatar.MaxHeight = 4096
defer test.MockVariableValue(&setting.Avatar.MaxWidth, 4096)()
defer test.MockVariableValue(&setting.Avatar.MaxHeight, 4096)()
scaledSize := DefaultAvatarSize * setting.Avatar.RenderedSizeFactor
newImgData := func(size int, optHeight ...int) []byte {
@ -135,3 +149,40 @@ func Test_ProcessAvatarImage(t *testing.T) {
_, err = processAvatarImage(origin, 262144)
require.ErrorContains(t, err, "image width is too large: 10 > 5")
}
func safeExifJpeg(t *testing.T, jpeg []byte) {
t.Helper()
parser := jpegstructure.NewJpegMediaParser()
mediaContext, err := parser.ParseBytes(jpeg)
require.NoError(t, err)
sl := mediaContext.(*jpegstructure.SegmentList)
rootIfd, _, err := sl.Exif()
require.NoError(t, err)
err = rootIfd.EnumerateTagsRecursively(func(ifd *exif.Ifd, ite *exif.IfdTagEntry) error {
assert.Equal(t, "Orientation", ite.TagName(), "only Orientation EXIF tag expected")
return nil
})
require.NoError(t, err)
}
func Test_ProcessAvatarExif(t *testing.T) {
t.Run("greater than max origin size", func(t *testing.T) {
data, err := os.ReadFile("testdata/exif.jpg")
require.NoError(t, err)
processedData, err := processAvatarImage(data, 12800)
require.NoError(t, err)
safeExifJpeg(t, processedData)
})
t.Run("smaller than max origin size", func(t *testing.T) {
data, err := os.ReadFile("testdata/exif.jpg")
require.NoError(t, err)
processedData, err := processAvatarImage(data, 128000)
require.NoError(t, err)
safeExifJpeg(t, processedData)
})
}

BIN
modules/avatar/testdata/avatar.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

BIN
modules/avatar/testdata/exif.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -7,6 +7,8 @@ import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
var (
@ -17,7 +19,7 @@ var (
)
// LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, file string, line uint64) (*Commit, error) {
func (repo *Repository) LineBlame(revision, file string, line uint64) (*Commit, uint64, error) {
res, _, gitErr := NewCommand(repo.Ctx, "blame").
AddOptionFormat("-L %d,%d", line, line).
AddOptionValues("-p", revision).
@ -26,24 +28,40 @@ func (repo *Repository) LineBlame(revision, file string, line uint64) (*Commit,
stdErr := gitErr.Stderr()
if stdErr == fmt.Sprintf("fatal: no such path %s in %s\n", file, revision) {
return nil, ErrBlameFileDoesNotExist
return nil, 0, ErrBlameFileDoesNotExist
}
if notEnoughLinesRe.MatchString(stdErr) {
return nil, ErrBlameFileNotEnoughLines
return nil, 0, ErrBlameFileNotEnoughLines
}
return nil, gitErr
return nil, 0, gitErr
}
objectFormat, err := repo.GetObjectFormat()
if err != nil {
return nil, err
return nil, 0, err
}
objectIDLen := objectFormat.FullLength()
if len(res) < objectIDLen {
return nil, fmt.Errorf("output of blame is invalid, cannot contain commit ID: %s", res)
if len(res) < objectIDLen+1 {
return nil, 0, fmt.Errorf("output of blame is invalid, cannot contain commit ID: %s", res)
}
return repo.GetCommit(res[:objectIDLen])
commit, err := repo.GetCommit(res[:objectIDLen])
if err != nil {
return nil, 0, fmt.Errorf("GetCommit: %w", err)
}
endIdxOriginalLineNo := strings.IndexRune(res[objectIDLen+1:], ' ')
if endIdxOriginalLineNo == -1 {
return nil, 0, fmt.Errorf("output of blame is invalid, cannot contain original line number: %s", res)
}
originalLineNo, err := strconv.ParseUint(res[objectIDLen+1:objectIDLen+1+endIdxOriginalLineNo], 10, 64)
if err != nil {
return nil, 0, fmt.Errorf("strconv.ParseUint: %w", err)
}
return commit, originalLineNo, nil
}

View file

@ -4,6 +4,9 @@
package git
import (
"bytes"
"os"
"path"
"path/filepath"
"testing"
@ -17,17 +20,20 @@ func TestLineBlame(t *testing.T) {
require.NoError(t, err)
defer repo.Close()
commit, err := repo.LineBlame("HEAD", "foo/link_short", 1)
commit, lineno, err := repo.LineBlame("HEAD", "foo/link_short", 1)
require.NoError(t, err)
assert.Equal(t, "37991dec2c8e592043f47155ce4808d4580f9123", commit.ID.String())
assert.EqualValues(t, 1, lineno)
commit, err = repo.LineBlame("HEAD", "foo/link_short", 512)
commit, lineno, err = repo.LineBlame("HEAD", "foo/link_short", 512)
require.ErrorIs(t, err, ErrBlameFileNotEnoughLines)
assert.Nil(t, commit)
assert.Zero(t, lineno)
commit, err = repo.LineBlame("HEAD", "non-existent/path", 512)
commit, lineno, err = repo.LineBlame("HEAD", "non-existent/path", 512)
require.ErrorIs(t, err, ErrBlameFileDoesNotExist)
assert.Nil(t, commit)
assert.Zero(t, lineno)
})
t.Run("SHA256", func(t *testing.T) {
@ -37,16 +43,68 @@ func TestLineBlame(t *testing.T) {
require.NoError(t, err)
defer repo.Close()
commit, err := repo.LineBlame("HEAD", "foo/link_short", 1)
commit, lineno, err := repo.LineBlame("HEAD", "foo/link_short", 1)
require.NoError(t, err)
assert.Equal(t, "6aae864a3d1d0d6a5be0cc64028c1e7021e2632b031fd8eb82afc5a283d1c3d1", commit.ID.String())
assert.EqualValues(t, 1, lineno)
commit, err = repo.LineBlame("HEAD", "foo/link_short", 512)
commit, lineno, err = repo.LineBlame("HEAD", "foo/link_short", 512)
require.ErrorIs(t, err, ErrBlameFileNotEnoughLines)
assert.Nil(t, commit)
assert.Zero(t, lineno)
commit, err = repo.LineBlame("HEAD", "non-existent/path", 512)
commit, lineno, err = repo.LineBlame("HEAD", "non-existent/path", 512)
require.ErrorIs(t, err, ErrBlameFileDoesNotExist)
assert.Nil(t, commit)
assert.Zero(t, lineno)
})
t.Run("Moved line", func(t *testing.T) {
test := func(t *testing.T, objectFormatName string) {
t.Helper()
tmpDir := t.TempDir()
require.NoError(t, InitRepository(t.Context(), tmpDir, false, objectFormatName))
gitRepo, err := OpenRepository(t.Context(), tmpDir)
require.NoError(t, err)
defer gitRepo.Close()
require.NoError(t, os.WriteFile(path.Join(tmpDir, "ANSWER"), []byte("abba\n"), 0o666))
require.NoError(t, AddChanges(tmpDir, true))
require.NoError(t, CommitChanges(tmpDir, CommitChangesOptions{Message: "Favourite singer of everyone who follows a automata course"}))
firstCommit, err := gitRepo.GetRefCommitID("HEAD")
require.NoError(t, err)
require.NoError(t, os.WriteFile(path.Join(tmpDir, "ANSWER"), append(bytes.Repeat([]byte("baba\n"), 9), []byte("abba\n")...), 0o666))
require.NoError(t, AddChanges(tmpDir, true))
require.NoError(t, CommitChanges(tmpDir, CommitChangesOptions{Message: "Now there's several of them"}))
secondCommit, err := gitRepo.GetRefCommitID("HEAD")
require.NoError(t, err)
commit, lineno, err := gitRepo.LineBlame("HEAD", "ANSWER", 10)
require.NoError(t, err)
assert.Equal(t, firstCommit, commit.ID.String())
assert.EqualValues(t, 1, lineno)
for i := range uint64(9) {
commit, lineno, err = gitRepo.LineBlame("HEAD", "ANSWER", i+1)
require.NoError(t, err)
assert.Equal(t, secondCommit, commit.ID.String())
assert.Equal(t, i+1, lineno)
}
}
t.Run("SHA1", func(t *testing.T) {
test(t, Sha1ObjectFormat.Name())
})
t.Run("SHA256", func(t *testing.T) {
skipIfSHA256NotSupported(t)
test(t, Sha256ObjectFormat.Name())
})
})
}

View file

@ -29,12 +29,12 @@ func TestParseGitURLs(t *testing.T) {
},
},
{
kase: "git@[fe80:14fc:cec5:c174:d88%2510]:go-gitea/gitea.git",
kase: "git@[fe80::14fc:cec5:c174:d88%2510]:go-gitea/gitea.git",
expected: &GitURL{
URL: &url.URL{
Scheme: "ssh",
User: url.User("git"),
Host: "[fe80:14fc:cec5:c174:d88%10]",
Host: "[fe80::14fc:cec5:c174:d88%10]",
Path: "go-gitea/gitea.git",
},
extraMark: 1,
@ -132,11 +132,11 @@ func TestParseGitURLs(t *testing.T) {
},
},
{
kase: "https://[fe80:14fc:cec5:c174:d88%2510]:20/go-gitea/gitea.git",
kase: "https://[fe80::14fc:cec5:c174:d88%2510]:20/go-gitea/gitea.git",
expected: &GitURL{
URL: &url.URL{
Scheme: "https",
Host: "[fe80:14fc:cec5:c174:d88%10]:20",
Host: "[fe80::14fc:cec5:c174:d88%10]:20",
Path: "/go-gitea/gitea.git",
},
extraMark: 0,

View file

@ -55,10 +55,13 @@ var (
shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`)
// anyHashPattern splits url containing SHA into parts
anyHashPattern = regexp.MustCompile(`https?://(?:(?:\S+/){3,4}(?:commit|tree|blob)/)([0-9a-f]{7,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`)
anyHashPattern = regexp.MustCompile(`https?://[^\s/]+/(\S+/(?:commit|tree|blob))/([0-9a-f]{7,64})(/[-+~_%.a-zA-Z0-9/]+)?(\?[-+~_%\.a-zA-Z0-9=&]+)?(#[-+~_%.a-zA-Z0-9]+)?`)
// comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash"
comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`)
comparePattern = regexp.MustCompile(`https?://[^\s/]+/(?:\S+/)?([^\s/]+/[^\s/]+)/compare/([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(\?[-+~_%\.a-zA-Z0-9=&/]+)?(#[-+~_%.a-zA-Z0-9]+)?`)
// pullReviewCommitPattern matches "https://domain.tld/<subpath...>/<owner>/<repo>/pulls/<id>/commits/<sha>"
pullReviewCommitPattern = regexp.MustCompile(`https?://[^\s/]+/(?:\S+/)?([^\s/]+/[^\s/]+)/pulls/(\d+)/commits/([0-9a-f]{7,64})(#[-+~_%.a-zA-Z0-9]+)?`)
validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
@ -147,6 +150,7 @@ func (p *postProcessError) Error() string {
type processor func(ctx *RenderContext, node *html.Node)
var defaultProcessors = []processor{
pullReviewCommitPatternProcessor,
fullIssuePatternProcessor,
comparePatternProcessor,
filePreviewPatternProcessor,
@ -177,6 +181,7 @@ func PostProcess(
}
var commitMessageProcessors = []processor{
pullReviewCommitPatternProcessor,
fullIssuePatternProcessor,
comparePatternProcessor,
fullHashPatternProcessor,
@ -209,6 +214,7 @@ func RenderCommitMessage(
}
var commitMessageSubjectProcessors = []processor{
pullReviewCommitPatternProcessor,
fullIssuePatternProcessor,
comparePatternProcessor,
fullHashPatternProcessor,
@ -796,6 +802,64 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
}
}
// pullReviewCommitPatternProcessor creates links to pull review commits.
func pullReviewCommitPatternProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := pullReviewCommitPattern.FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
urlFull := node.Data[m[0]:m[1]]
repoSlug := node.Data[m[2]:m[3]]
id := node.Data[m[4]:m[5]]
sha := base.ShortSha(node.Data[m[6]:m[7]])
// Create an `<a>` node with a text of
// `!123 (commit <code>abcdef1234</code>)`
aNode := &html.Node{
Type: html.ElementNode,
Data: atom.A.String(),
Attr: []html.Attribute{{Key: "href", Val: urlFull}, {Key: "class", Val: "commit"}},
}
text := "!" + id + " (commit "
baseURLEnd := strings.Index(urlFull, repoSlug) + len(repoSlug)
if len(ctx.Links.Base) > 0 && !strings.HasPrefix(ctx.Links.Base, urlFull[:baseURLEnd]) {
text = repoSlug + "@" + text
}
aNode.AppendChild(&html.Node{
Type: html.TextNode,
Data: text,
})
textNode := &html.Node{
Type: html.TextNode,
Data: sha,
}
codeNode := &html.Node{
Type: html.ElementNode,
Data: atom.Code.String(),
Attr: []html.Attribute{{Key: "class", Val: "nohighlight"}},
}
codeNode.AppendChild(textNode)
aNode.AppendChild(codeNode)
aNode.AppendChild(&html.Node{
Type: html.TextNode,
Data: ")",
})
replaceContent(node, m[0], m[1], aNode)
node = node.NextSibling.NextSibling
}
}
func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
@ -952,7 +1016,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
}
}
// fullHashPatternProcessor renders SHA containing URLs
// fullHashPatternProcessor renders URLs that contain a SHA
func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
@ -966,37 +1030,103 @@ func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
}
urlFull := node.Data[m[0]:m[1]]
text := base.ShortSha(node.Data[m[2]:m[3]])
// 3rd capture group matches a optional path
subpath := ""
if m[5] > 0 {
subpath = node.Data[m[4]:m[5]]
// In most cases, the URL will look like this:
// `https://domain.tld/<owner>/<repo>/<path>/<sha>`.
// The amount of components in `<path>` is variable, but that alone is doable with regexp.
//
// However, Forgejo also allows being hosted on a sub path, i.e.
// `https://domain.tld/<sub>/<owner>/<repo>/<path>/<sha>`.
// And this sub path can also have any amount of components. But fishing out a section
// between two variable length matches is not something regular grammars are capable of.
//
// Instead, the regexp extracts the entire path section before the SHA
// (i.e. `<sub>/<owner>/<repo>/<path>`), and we find the components we need by counting.
// `<sub>` is unknown, but the possible values for `<path>` are defined by us
// (see `router/web/web.go`). So we count from the back.
subPath := node.Data[m[2]:m[3]]
components := strings.Split(subPath, "/")
componentCount := len(components)
// In most cases, the `<owner>` component is right at the start of the path.
ownerIndex := 0
// But if there are more than three components, this could be `<sub>` or an app route
// with two components. Or both.
if componentCount > 3 {
// As mentioned, we count from the back. We decrement for the `<repo>` component, and the one
// component from the app route that's guaranteed to be there.
// We also adjust this to be an array index, so we subtract one more.
ownerIndex = componentCount - 3
// We then check for known app routes that use two components.
// Currently, this checks for:
// - `src/commit`
// - `commits/commit`
//
// This does have one scenario where we cannot figure things out reliably:
// If there is a sub path, and the repository is named like one of the known app routes
// (e.g. `src`), we cannot distinguish between the repo and the app route.
// We assume that naming a repository like that is uncommon, and prioritize the case where its
// part of the app route.
if components[componentCount-1] == "commit" &&
(components[componentCount-2] == "src" || components[componentCount-2] == "commits") {
ownerIndex--
}
}
repoSlug := components[ownerIndex] + "/" + components[ownerIndex+1]
text := base.ShortSha(node.Data[m[4]:m[5]])
// We need to figure out the base of the provided URL, which is up to and including the
// `<owner>/<repo>` slug.
// With that we can determine if it matches the current repo, or if the slug should be shown.
baseURLEnd := strings.Index(urlFull, repoSlug) + len(repoSlug)
if len(ctx.Links.Base) > 0 && !strings.HasPrefix(ctx.Links.Base, urlFull[:baseURLEnd]) {
text = repoSlug + "@" + text
}
// 3rd capture group matches an optional file path after the SHA
filePath := ""
if m[7] > 0 {
filePath = node.Data[m[6]:m[7]]
}
// 5th capture group matches a optional url hash
hash := ""
if m[9] > 0 {
hash = node.Data[m[8]:m[9]][1:]
if m[11] > 0 {
hash = node.Data[m[10]:m[11]][1:]
// Truncate long diff IDs
if len(hash) > 15 && strings.HasPrefix(hash, "diff-") {
hash = hash[:15]
}
}
start := m[0]
end := m[1]
// If url ends in '.', it's very likely that it is not part of the
// actual url but used to finish a sentence.
// If the URL ends in '.', it's very likely that it is not part of the
// actual URL but used to finish a sentence.
if strings.HasSuffix(urlFull, ".") {
end--
urlFull = urlFull[:len(urlFull)-1]
if hash != "" {
hash = hash[:len(hash)-1]
} else if subpath != "" {
subpath = subpath[:len(subpath)-1]
} else if filePath != "" {
filePath = filePath[:len(filePath)-1]
}
}
if subpath != "" {
text += subpath
if filePath != "" {
decoded, err := url.QueryUnescape(filePath)
if err != nil {
text += decoded
} else {
text += filePath
}
}
if hash != "" {
@ -1019,41 +1149,71 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
return
}
// Ensure that every group (m[0]...m[7]) has a match
for i := 0; i < 8; i++ {
// Ensure that every group (m[0]...m[9]) has a match
for i := 0; i < 10; i++ {
if m[i] == -1 {
return
}
}
urlFull := node.Data[m[0]:m[1]]
text1 := base.ShortSha(node.Data[m[2]:m[3]])
textDots := base.ShortSha(node.Data[m[4]:m[5]])
text2 := base.ShortSha(node.Data[m[6]:m[7]])
repoSlug := node.Data[m[2]:m[3]]
text1 := base.ShortSha(node.Data[m[4]:m[5]])
textDots := base.ShortSha(node.Data[m[6]:m[7]])
text2 := base.ShortSha(node.Data[m[8]:m[9]])
query := ""
if m[11] > 0 {
query = node.Data[m[10]:m[11]][1:]
}
hash := ""
if m[9] > 0 {
hash = node.Data[m[8]:m[9]][1:]
if m[13] > 0 {
hash = node.Data[m[12]:m[13]][1:]
}
start := m[0]
end := m[1]
// If url ends in '.', it's very likely that it is not part of the
// actual url but used to finish a sentence.
// If the URL ends in '.', it's very likely that it is not part of the
// actual URL but used to finish a sentence.
if strings.HasSuffix(urlFull, ".") {
end--
urlFull = urlFull[:len(urlFull)-1]
if hash != "" {
hash = hash[:len(hash)-1]
} else if query != "" {
query = query[:len(query)-1]
} else if text2 != "" {
text2 = text2[:len(text2)-1]
}
}
text := text1 + textDots + text2
baseURLEnd := strings.Index(urlFull, repoSlug) + len(repoSlug)
if len(ctx.Links.Base) > 0 && !strings.HasPrefix(ctx.Links.Base, urlFull[:baseURLEnd]) {
text = repoSlug + "@" + text
}
extra := ""
if query != "" {
query, err := url.ParseQuery(query)
if err == nil && query.Has("files") {
extra = query.Get("files")
}
}
if hash != "" {
text += " (" + hash + ")"
if extra != "" {
extra += "#"
}
extra += hash
}
if extra != "" {
text += " (" + extra + ")"
}
replaceContent(node, start, end, createCodeLink(urlFull, text, "compare"))
node = node.NextSibling.NextSibling
@ -1094,7 +1254,7 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
// Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div
before := node.Data[:(preview.start - offset)]
after := node.Data[(preview.end - offset):]
afterNode := &html.Node{
afterTextNode := &html.Node{
Type: html.TextNode,
Data: after,
}
@ -1103,22 +1263,20 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
case "div", "li", "td", "th", "details":
nextSibling := node.NextSibling
node.Parent.InsertBefore(previewNode, nextSibling)
node.Parent.InsertBefore(afterNode, nextSibling)
node.Parent.InsertBefore(afterTextNode, nextSibling)
case "p", "span", "em", "strong":
nextSibling := node.Parent.NextSibling
node.Parent.Parent.InsertBefore(previewNode, nextSibling)
afterPNode := &html.Node{
nextParentSibling := node.Parent.NextSibling
node.Parent.Parent.InsertBefore(previewNode, nextParentSibling)
afterNode := &html.Node{
Type: html.ElementNode,
Data: node.Parent.Data,
Attr: node.Parent.Attr,
}
afterPNode.AppendChild(afterNode)
node.Parent.Parent.InsertBefore(afterPNode, nextSibling)
siblingNode := node.NextSibling
if siblingNode != nil {
node.NextSibling = nil
siblingNode.PrevSibling = nil
afterPNode.AppendChild(siblingNode)
afterNode.AppendChild(afterTextNode)
node.Parent.Parent.InsertBefore(afterNode, nextParentSibling)
for sibling := node.NextSibling; sibling != nil; sibling = node.NextSibling {
sibling.Parent.RemoveChild(sibling)
afterNode.AppendChild(sibling)
}
default:
matched = false
@ -1126,7 +1284,7 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
if matched {
offset = preview.end
node.Data = before
node = afterNode
node = afterTextNode
}
}
node = node.NextSibling

View file

@ -303,12 +303,12 @@ func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *Rend
func TestRender_AutoLink(t *testing.T) {
setting.AppURL = TestAppURL
test := func(input, expected string) {
test := func(input, expected, base string) {
var buffer strings.Builder
err := PostProcess(&RenderContext{
Ctx: git.DefaultContext,
Links: Links{
Base: TestRepoURL,
Base: base,
},
Metas: localMetas,
}, strings.NewReader(input), &buffer)
@ -319,7 +319,7 @@ func TestRender_AutoLink(t *testing.T) {
err = PostProcess(&RenderContext{
Ctx: git.DefaultContext,
Links: Links{
Base: TestRepoURL,
Base: base,
},
Metas: localMetas,
IsWiki: true,
@ -328,19 +328,87 @@ func TestRender_AutoLink(t *testing.T) {
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
}
// render valid issue URLs
test(util.URLJoin(TestRepoURL, "issues", "3333"),
numericIssueLink(util.URLJoin(TestRepoURL, "issues"), "ref-issue", 3333, "#"))
t.Run("Issue", func(t *testing.T) {
// render valid issue URLs
test(util.URLJoin(TestRepoURL, "issues", "3333"),
numericIssueLink(util.URLJoin(TestRepoURL, "issues"), "ref-issue", 3333, "#"),
TestRepoURL)
})
// render valid commit URLs
tmp := util.URLJoin(TestRepoURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24</code></a>")
tmp += "#diff-2"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
t.Run("Commit", func(t *testing.T) {
// render valid commit URLs
tmp := util.URLJoin(TestRepoURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24</code></a>", TestRepoURL)
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">"+TestOrgRepo+"@d8a994ef24</code></a>", "https://localhost/forgejo/forgejo")
test(
tmp+"#diff-2",
"<a href=\""+tmp+"#diff-2\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>",
TestRepoURL,
)
test(
tmp+"#diff-953bb4f01b7c77fa18f0cd54211255051e647dbc",
"<a href=\""+tmp+"#diff-953bb4f01b7c77fa18f0cd54211255051e647dbc\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-953bb4f01b)</code></a>",
TestRepoURL,
)
// render other commit URLs
tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>")
// render other commit URLs
tmp = "https://external-link.gitea.io/go-gitea/gitea/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">d8a994ef24 (diff-2)</code></a>", "https://external-link.gitea.io/go-gitea/gitea")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">go-gitea/gitea@d8a994ef24 (diff-2)</code></a>", TestRepoURL)
tmp = "http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">190d949293</code></a>", "http://localhost:3000/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">gogits/gogs@190d949293</code></a>", "https://external-link.gitea.io/go-gitea/gitea")
tmp = "http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">190d949293</code></a>", "http://localhost:3000/sub/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">gogits/gogs@190d949293</code></a>", "http://localhost:3000/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">gogits/gogs@190d949293</code></a>", "https://external-link.gitea.io/go-gitea/gitea")
tmp = "http://localhost:3000/sub1/sub2/sub3/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">190d949293</code></a>", "http://localhost:3000/sub1/sub2/sub3/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">gogits/gogs@190d949293</code></a>", "http://localhost:3000/sub1/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">gogits/gogs@190d949293</code></a>", "https://external-link.gitea.io/go-gitea/gitea")
// if the repository happens to be named like one of the known app routes (e.g. `src`),
// we can parse the URL correctly, if there is no sub path
tmp = "http://localhost:3000/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">gogits/src@190d949293</code></a>", TestRepoURL)
tmp = "http://localhost:3000/gogits/src/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">gogits/src@190d949293</code></a>", TestRepoURL)
// but if there is a sub path, we cannot reliably distinguish the repo name from the app route
tmp = "http://localhost:3000/sub/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20"
test(tmp, "<a href=\""+tmp+"\" class=\"commit\"><code class=\"nohighlight\">sub/gogits@190d949293</code></a>", TestRepoURL)
})
t.Run("Compare", func(t *testing.T) {
tmp := util.URLJoin(TestRepoURL, "compare", "d8a994ef243349f321568f9e36d5c3f444b99cae..190d9492934af498c3f669d6a2431dc5459e5b20")
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">d8a994ef24..190d949293</code></a>", TestRepoURL)
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">"+TestOrgRepo+"@d8a994ef24..190d949293</code></a>", "https://localhost/forgejo/forgejo")
tmp = "http://localhost:3000/sub/gogits/gogs/compare/190d9492934af498c3f669d6a2431dc5459e5b20..d8a994ef243349f321568f9e36d5c3f444b99cae"
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">190d949293..d8a994ef24</code></a>", "http://localhost:3000/sub/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">gogits/gogs@190d949293..d8a994ef24</code></a>", "http://localhost:3000/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">gogits/gogs@190d949293..d8a994ef24</code></a>", "https://external-link.gitea.io/go-gitea/gitea")
tmp = "http://localhost:3000/sub1/sub2/sub3/gogits/gogs/compare/190d9492934af498c3f669d6a2431dc5459e5b20..d8a994ef243349f321568f9e36d5c3f444b99cae"
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">190d949293..d8a994ef24</code></a>", "http://localhost:3000/sub1/sub2/sub3/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">gogits/gogs@190d949293..d8a994ef24</code></a>", "http://localhost:3000/sub1/gogits/gogs")
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">gogits/gogs@190d949293..d8a994ef24</code></a>", "https://external-link.gitea.io/go-gitea/gitea")
tmp = "https://codeberg.org/forgejo/forgejo/compare/8bbac4c679bea930c74849c355a60ed3c52f8eb5...e2278e5a38187a1dc84dc41d583ec8b44e7257c1?files=options/locale/locale_fi-FI.ini"
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini)</code></a>", "https://codeberg.org/forgejo/forgejo")
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">forgejo/forgejo@8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini)</code></a>", TestRepoURL)
test(tmp+".", "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">forgejo/forgejo@8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini)</code></a>.", TestRepoURL)
tmp = "https://codeberg.org/forgejo/forgejo/compare/8bbac4c679bea930c74849c355a60ed3c52f8eb5...e2278e5a38187a1dc84dc41d583ec8b44e7257c1?files=options/locale/locale_fi-FI.ini#L2"
test(tmp, "<a href=\""+tmp+"\" class=\"compare\"><code class=\"nohighlight\">8bbac4c679...e2278e5a38 (options/locale/locale_fi-FI.ini#L2)</code></a>", "https://codeberg.org/forgejo/forgejo")
})
t.Run("Invalid URLs", func(t *testing.T) {
tmp := "https://local host/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20"
test(tmp, "<a href=\"https://local\" class=\"link\">https://local</a> host/gogits/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20", TestRepoURL)
})
}
func TestRender_IssueIndexPatternRef(t *testing.T) {
@ -429,45 +497,79 @@ func TestRegExp_hashCurrentPattern(t *testing.T) {
func TestRegExp_anySHA1Pattern(t *testing.T) {
testCases := map[string][]string{
"https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": {
"jquery/jquery/blob",
"a644101ed04d0beacea864ce805e0c4f86ba1cd1",
"/test/unit/event.js",
"",
"#L2703",
},
"https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": {
"jquery/jquery/blob",
"a644101ed04d0beacea864ce805e0c4f86ba1cd1",
"/test/unit/event.js",
"",
"",
},
"https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": {
"jquery/jquery/commit",
"0705be475092aede1eddae01319ec931fb9c65fc",
"",
"",
"",
},
"https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": {
"jquery/jquery/tree",
"0705be475092aede1eddae01319ec931fb9c65fc",
"/src",
"",
"",
},
"https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": {
"gogs/gogs/commit",
"d8a994ef243349f321568f9e36d5c3f444b99cae",
"",
"",
"#diff-2",
},
"https://codeberg.org/forgejo/forgejo/src/commit/949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0/RELEASE-NOTES.md?display=source&w=1#L7-L9": {
"forgejo/forgejo/src/commit",
"949ab9a5c4cac742f84ae5a9fa186f8d6eb2cdc0",
"/RELEASE-NOTES.md",
"?display=source&w=1",
"#L7-L9",
},
"http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": {
"gogits/gogs/src/commit",
"190d9492934af498c3f669d6a2431dc5459e5b20",
"/path/to/file.go",
"",
"#L2-L3",
},
"http://localhost:3000/sub/gogits/gogs/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": {
"sub/gogits/gogs/commit",
"190d9492934af498c3f669d6a2431dc5459e5b20",
"/path/to/file.go",
"",
"#L2-L3",
},
"http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": {
"sub/gogits/gogs/src/commit",
"190d9492934af498c3f669d6a2431dc5459e5b20",
"/path/to/file.go",
"",
"#L2-L3",
},
"http://localhost:3000/sub1/sub2/sub3/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3": {
"sub1/sub2/sub3/gogits/gogs/src/commit",
"190d9492934af498c3f669d6a2431dc5459e5b20",
"/path/to/file.go",
"",
"#L2-L3",
},
}
for k, v := range testCases {
assert.Equal(t, anyHashPattern.FindStringSubmatch(k)[1:], v)
assert.Equal(t, v, anyHashPattern.FindStringSubmatch(k)[1:])
}
for _, v := range []string{"https://codeberg.org/forgejo/forgejo/attachments/774421a1-b0ae-4501-8fba-983874b76811"} {

View file

@ -241,6 +241,45 @@ func TestRender_links(t *testing.T) {
markup.CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
}
func TestRender_PullReviewCommitLink(t *testing.T) {
setting.AppURL = markup.TestAppURL
sha := "190d9492934af498c3f669d6a2431dc5459e5b20"
prCommitLink := util.URLJoin(markup.TestRepoURL, "pulls", "1", "commits", sha)
test := func(input, expected, base string) {
buffer, err := markup.RenderString(&markup.RenderContext{
Ctx: git.DefaultContext,
RelativePath: ".md",
Links: markup.Links{
AbsolutePrefix: true,
Base: base,
},
Metas: localMetas,
}, input)
require.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test(prCommitLink, `<p><a href="`+prCommitLink+`" rel="nofollow">!1 (commit <code>`+sha[0:10]+`</code>)</a></p>`, markup.TestRepoURL)
prCommitLink = util.URLJoin(markup.TestAppURL, "sub1", "sub2", markup.TestOrgRepo, "pulls", "1", "commits", sha)
test(
prCommitLink,
`<p><a href="`+prCommitLink+`" rel="nofollow">!1 (commit <code>`+sha[0:10]+`</code>)</a></p>`,
util.URLJoin(markup.TestAppURL, "sub1", "sub2", markup.TestOrgRepo),
)
test(
prCommitLink,
`<p><a href="`+prCommitLink+`" rel="nofollow">`+markup.TestOrgRepo+`@!1 (commit <code>`+sha[0:10]+`</code>)</a></p>`,
markup.TestRepoURL,
)
prCommitLink = "https://codeberg.org/forgejo/forgejo/pulls/7979/commits/4d968c08e0a8d24bd2f3fb2a3a48b37e6d84a327#diff-7649acfa98a9ee3faf0d28b488bbff428317fc72"
test(prCommitLink, `<p><a href="`+prCommitLink+`" rel="nofollow">!7979 (commit <code>4d968c08e0</code>)</a></p>`, "https://codeberg.org/forgejo/forgejo")
test(prCommitLink, `<p><a href="`+prCommitLink+`" rel="nofollow">forgejo/forgejo@!7979 (commit <code>4d968c08e0</code>)</a></p>`, markup.TestRepoURL)
}
func TestRender_email(t *testing.T) {
setting.AppURL = markup.TestAppURL
@ -686,6 +725,9 @@ func TestIssue18471(t *testing.T) {
err := markup.PostProcess(&markup.RenderContext{
Ctx: git.DefaultContext,
Metas: localMetas,
Links: markup.Links{
Base: "http://domain/org/repo",
},
}, strings.NewReader(data), &res)
require.NoError(t, err)
@ -723,6 +765,9 @@ func TestRender_FilePreview(t *testing.T) {
Ctx: git.DefaultContext,
RelativePath: ".md",
Metas: metas,
Links: markup.Links{
Base: markup.TestRepoURL,
},
}, input)
require.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
@ -835,7 +880,7 @@ func TestRender_FilePreview(t *testing.T) {
testRender(
urlWithSub,
`<p><a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" rel="nofollow"><code>190d949293/path/to/file.go (L2-L3)</code></a></p>`,
`<p><a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" rel="nofollow"><code>gogits/gogs@190d949293/path/to/file.go (L2-L3)</code></a></p>`,
localMetas,
)
@ -1253,4 +1298,20 @@ func TestRender_FilePreview(t *testing.T) {
localMetas,
)
})
t.Run("file previews followed by new line", func(t *testing.T) {
testRender(
commitFileURLFirstLine+"\nand\n"+commitFileURLFirstLine,
"<p></p>"+filePreviewBox+"<p><br/>\nand<br/>\n</p>"+filePreviewBox+"<p></p>",
localMetas,
)
})
t.Run("file previews followed by new line in div", func(t *testing.T) {
testRender(
"<div>"+commitFileURLFirstLine+"\nand\n"+commitFileURLFirstLine+"</div>",
"<div>"+filePreviewBox+"\nand\n"+filePreviewBox+"</div>",
localMetas,
)
})
}

View file

@ -17,6 +17,7 @@ import (
"forgejo.org/modules/git"
"forgejo.org/modules/setting"
"forgejo.org/modules/util"
"forgejo.org/modules/util/donotpanic"
"github.com/yuin/goldmark/ast"
)
@ -267,6 +268,15 @@ sandbox="allow-scripts"
return err
}
func postProcessOrCopy(ctx *RenderContext, renderer Renderer, reader io.Reader, writer io.Writer) (err error) {
if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() {
err = PostProcess(ctx, reader, writer)
} else {
_, err = io.Copy(writer, reader)
}
return err
}
func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
var wg sync.WaitGroup
var err error
@ -293,7 +303,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
wg.Add(1)
go func() {
err = SanitizeReader(pr2, renderer.Name(), output)
err = donotpanic.SafeFuncWithError(func() error { return SanitizeReader(pr2, renderer.Name(), output) })
_ = pr2.Close()
wg.Done()
}()
@ -303,11 +313,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
wg.Add(1)
go func() {
if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() {
err = PostProcess(ctx, pr, pw2)
} else {
_, err = io.Copy(pw2, pr)
}
err = donotpanic.SafeFuncWithError(func() error { return postProcessOrCopy(ctx, renderer, pr, pw2) })
_ = pr.Close()
_ = pw2.Close()
wg.Done()
@ -320,7 +326,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() {
// Append a short script to the iframe's contents, which will communicate the scroll height of the embedded document via postMessage, either once loaded (in case the containing page loads first) in response to a postMessage from external.js, in case the iframe loads first
// We use '*' as a target origin for postMessage, because can be certain we are embedded on the same domain, due to X-Frame-Options configured elsewhere. (Plus, the offsetHeight of an embedded document is likely not sensitive data anyway.)
_, _ = pw.Write([]byte("<script>{let postHeight = () => {window.parent.postMessage({frameHeight: document.documentElement.offsetHeight}, '*')}; window.addEventListener('load', postHeight); window.addEventListener('message', (event) => {if (event.source === window.parent && event.data.requestOffsetHeight) postHeight()});}</script>"))
_, _ = pw.Write([]byte("<script>{let postHeight = () => {window.parent.postMessage({frameHeight: document.documentElement.offsetHeight || document.documentElement.scrollHeight}, '*')}; window.addEventListener('load', postHeight); window.addEventListener('message', (event) => {if (event.source === window.parent && event.data.requestOffsetHeight) postHeight()});}</script>"))
}
_ = pw.Close()

View file

@ -1,4 +1,49 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package markup_test
package markup
import (
"bytes"
"errors"
"strings"
"testing"
"forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type failReader struct{}
func (*failReader) Read(p []byte) (n int, err error) {
return 0, errors.New("FAIL")
}
func TestRender_postProcessOrCopy(t *testing.T) {
renderContext := &RenderContext{Ctx: t.Context()}
t.Run("CopyOK", func(t *testing.T) {
input := "SOMETHING"
output := &bytes.Buffer{}
require.NoError(t, postProcessOrCopy(renderContext, nil, strings.NewReader(input), output))
assert.Equal(t, input, output.String())
})
renderer := GetRendererByType("markdown")
t.Run("PostProcessOK", func(t *testing.T) {
input := "SOMETHING"
output := &bytes.Buffer{}
defer test.MockVariableValue(&defaultProcessors, []processor{})()
require.NoError(t, postProcessOrCopy(renderContext, renderer, strings.NewReader(input), output))
assert.Equal(t, input, output.String())
})
t.Run("PostProcessError", func(t *testing.T) {
input := &failReader{}
defer test.MockVariableValue(&defaultProcessors, []processor{})()
assert.ErrorContains(t, postProcessOrCopy(renderContext, renderer, input, &bytes.Buffer{}), "FAIL")
})
}

View file

@ -164,6 +164,7 @@ func PushUpdateAddTag(ctx context.Context, repo *repo_model.Repository, gitRepo
NumCommits: commitsCount,
CreatedUnix: timeutil.TimeStamp(createdAt.Unix()),
IsTag: true,
Note: tag.Message,
}
if author != nil {
rel.PublisherID = author.ID

View file

@ -1,14 +1,20 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
import (
"os"
"path"
"testing"
repo_model "forgejo.org/models/repo"
"forgejo.org/models/unittest"
"forgejo.org/modules/git"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_calcSync(t *testing.T) {
@ -74,3 +80,56 @@ func Test_calcSync(t *testing.T) {
assert.Equal(t, *gitTags[1], *updates[0], "updates equal")
}
}
func TestSyncReleasesWithTags(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
// Can be any repository that doesn't have the git tag releases.
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
t.Run("SHA1", func(t *testing.T) {
tmpDir := t.TempDir()
require.NoError(t, git.InitRepository(t.Context(), tmpDir, false, git.Sha1ObjectFormat.Name()))
gitRepo, err := git.OpenRepository(t.Context(), tmpDir)
require.NoError(t, err)
defer gitRepo.Close()
require.NoError(t, os.WriteFile(path.Join(tmpDir, "README.md"), []byte("testing the testing"), 0o666))
require.NoError(t, git.AddChanges(tmpDir, true))
require.NoError(t, git.CommitChanges(tmpDir, git.CommitChangesOptions{Message: "Add README"}))
require.NoError(t, gitRepo.CreateAnnotatedTag("v1.0.0", "First release \\o/", "HEAD"))
require.NoError(t, SyncReleasesWithTags(t.Context(), repo, gitRepo))
release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{RepoID: repo.ID, TagName: "v1.0.0"})
assert.Equal(t, "First release \\o/\n", release.Note)
assert.True(t, release.IsTag)
assert.EqualValues(t, 1, release.NumCommits)
})
t.Run("SHA256", func(t *testing.T) {
if !git.SupportHashSha256 {
t.Skip("skipping because installed Git version doesn't support SHA256")
}
tmpDir := t.TempDir()
require.NoError(t, git.InitRepository(t.Context(), tmpDir, false, git.Sha256ObjectFormat.Name()))
gitRepo, err := git.OpenRepository(t.Context(), tmpDir)
require.NoError(t, err)
defer gitRepo.Close()
require.NoError(t, os.WriteFile(path.Join(tmpDir, "README.md"), []byte("testing the testing"), 0o666))
require.NoError(t, git.AddChanges(tmpDir, true))
require.NoError(t, git.CommitChanges(tmpDir, git.CommitChangesOptions{Message: "Add README"}))
require.NoError(t, gitRepo.CreateAnnotatedTag("v2.0.0", "Second release \\o/", "HEAD"))
require.NoError(t, SyncReleasesWithTags(t.Context(), repo, gitRepo))
release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{RepoID: repo.ID, TagName: "v2.0.0"})
assert.Equal(t, "Second release \\o/\n", release.Note)
assert.True(t, release.IsTag)
assert.EqualValues(t, 1, release.NumCommits)
})
}

View file

@ -0,0 +1,28 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package donotpanic
import (
"fmt"
"forgejo.org/modules/log"
)
type FuncWithError func() error
func SafeFuncWithError(fun FuncWithError) (err error) {
defer func() {
if r := recover(); r != nil {
log.Error("PANIC recovered: %v\nStacktrace: %s", r, log.Stack(2))
rErr, ok := r.(error)
if ok {
err = fmt.Errorf("PANIC recover with error: %w", rErr)
} else {
err = fmt.Errorf("PANIC recover: %v", r)
}
}
}()
return fun()
}

View file

@ -0,0 +1,28 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package donotpanic
import (
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDoNotPanic_SafeFuncWithError(t *testing.T) {
t.Run("OK", func(t *testing.T) {
assert.NoError(t, SafeFuncWithError(func() error { return nil }))
})
t.Run("PanickString", func(t *testing.T) {
errorMessage := "ERROR MESSAGE"
assert.ErrorContains(t, SafeFuncWithError(func() error { panic(errorMessage) }), fmt.Sprintf("recover: %s", errorMessage))
})
t.Run("PanickError", func(t *testing.T) {
errorMessage := "ERROR MESSAGE"
assert.ErrorContains(t, SafeFuncWithError(func() error { panic(errors.New(errorMessage)) }), fmt.Sprintf("recover with error: %s", errorMessage))
})
}

View file

@ -1887,7 +1887,7 @@ org_full_name_holder = Пълно име на организацията
teams = Екипи
lower_members = участници
lower_repositories = хранилища
settings.repoadminchangeteam = Админ. на хранилището да може да добавя и премахва достъп за екипи
settings.repoadminchangeteam = Админ. на хранилище да може да добавя и премахва достъп за екипи
settings.email = Ел. поща за връзка
settings.delete_account = Изтриване на тази организация
settings.delete_org_title = Изтриване на организацията
@ -1954,6 +1954,9 @@ settings.change_orgname_redirect_prompt.with_cooldown.one = Старото им
teams.add_nonexistent_repo = Хранилището, което се опитвате да добавите, не съществува, моля, първо го създайте.
teams.invite.by = Поканен от %s
teams.can_create_org_repo_helper = Участниците могат да създават нови хранилища в организацията. Създателят ще получи администраторски достъп до новото хранилище.
teams.owners_permission_desc = Притежателите имат пълен достъп до <strong>всички хранилища</strong> и имат <strong>администраторски достъп</strong> до организацията.
[install]
admin_password = Парола
user = Потребителско име
@ -2068,7 +2071,7 @@ projects = Проекти
code = Код
overview = Обзор
watched = Наблюдавани хранилища
unfollow = Прекратяване на следването
unfollow = Отследване
block = Блокиране
settings = Потребителски настройки
starred = Отбелязани хранилища

View file

@ -484,13 +484,13 @@ team_invite.subject = %[1]s us convida a unir-vos a l'organització %[2]s
release.title = Títol: %s
release.downloads = Baixades:
release.download.zip = Codi font (ZIP)
repo.transfer.subject_to_you = %s us vol transferir el repositori "%s"
repo.transfer.subject_to_you = %s us vol transferir el repositori «%s»
repo.collaborator.added.subject = %s us ha afegit a %s com a col·laborador
repo.collaborator.added.text = Us han afegit com a col·laborador al repositori:
issue_assigned.issue = @%[1]s us ha assignat l'incidència %[2]s del repositori %[3]s.
issue.x_mentioned_you = <b>@%s</b> us ha mencionat:
issue.action.new = <b>@%[1]s</b> ha creat #%[2]d.
repo.transfer.subject_to = %s vol transferir el repositori "%s" a %s
repo.transfer.subject_to = %s vol transferir el repositori «%s» a %s
repo.transfer.to_you = tu
register_notify.text_3 = Si algú altre us ha fet aquest compte, necessitareu <a href="%s">configurar la vostra contrasenya</a> abans.
password_change.subject = S'ha canviat la vostra contrasenya
@ -508,6 +508,28 @@ issue.action.approve = <b>@%[1]s</b> ha aprovat aquesta sol·licitud d'extracci
issue.action.reject = <b>@%[1]s</b> ha sol·licitat canvis en aquesta sol·licitud d'extracció.
register_notify.text_2 = Podeu iniciar sessió al vostre compte fent servir el vostre nom d'usuari: %s
register_notify.text_1 = aquest és el vostre correu electrònic de confirmació pel registre de %s!
password_change.text_1 = S'acaba de canviar la contrasenya pel vostre compte.
issue.action.review = <b>@%[1]s</b> ha deixat un comentari a la vostra sol·licitud d'extracció.
issue.action.review_dismissed = <b>@%[1]s</b> ha rebutjat l'última revisió de %[2]s per aquesta sol·licitud d'extracció.
reset_password = Recupereu el vostre compte
reset_password.text = Si heu sigut vós, cliqueu el següent enllaç per recuperar el vostre compte abans de <b>%s</b>:
primary_mail_change.text_1 = S'ha canviat la vostra adreça de correu electrònic principal a %[1]s. Això vol dir que aquesta adreça de correu electrònic no rebrà més notificacions de correu pel vostre compte.
totp_disabled.text_1 = S'han desactivat les contrasenyes d'un sol ús basades en el temps (TOTP) pel vostre compte.
totp_disabled.no_2fa = Ja no hi ha altres mètodes d'autenticació de doble factor configurats, per la qual cosa ja no és necessari iniciar sessió al vostre compte mitjançat autenticació de doble factor.
removed_security_key.no_2fa = Ja no hi ha altres mètodes d'autenticació de doble factor configurats, per la qual cosa ja no és necessari iniciar sessió al vostre compte mitjançat autenticació de doble factor.
totp_enrolled.text_1.has_webauthn = Heu habilitat el TOTP per al vostre compte. Això vol dir que, per a totes les futures connexions al vostre compte, podreu utilitzar el TOTP com a mètode d'autenticació en dos passos (2FA) o bé utilitzar qualsevol de les vostres claus de seguretat.
issue.action.force_push = <b>%[1]s</b> ha realitzat un «force push» de <b>%[2]s</b> des de %[3]s fins a %[4]s.
issue.action.push_1 = <b>@%[1]s</b> ha pujat $[3]d commit a %[2]s
issue.action.push_n = <b>@%[1]s</b> ha pujat $[3]d commits a %[2]s
issue.action.ready_for_review = <b>@%[1]s</b> ha marcat aquesta «pull request» com a preparada per a la revisió.
issue.in_tree_path = A %s:
release.new.text = <b>@%[1]s</b> ha publicat %[2]s a %[3]s
release.note = Nota:
release.new.subject = %s a %s llançat
[modal]
yes =
no = No
@ -580,6 +602,26 @@ min_size_error = ` ha de contenir %s caràcters com a mínim.`
max_size_error = ` ha de contenir %s caràcters com a màxim.`
email_domain_is_not_allowed = El domini de l'adreça de correu electrònic <b>%s</b> de l'usuari entra en conflicte amb EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Assegureu-vos d'haver introduït l'adreça de correu electrònic correctament.
Website = Lloc web
Location = Ubicació
AdminEmail = Correu electrònic de l'administrador
AccessToken = Testimoni d'accés
CommitSummary = Resum del commit
CommitMessage = Missatge del commit
CommitChoice = Selecció de commit
TreeName = Camí del fitxer
alpha_dash_error = ` hauria de contenir només caràcters alfanumèrics, guions ("-") i barres baixes ("_").`
alpha_dash_dot_error = ` hauria de contenir només caràcters alfanumèrics, guions ("-"), barres baixes ("_") i punts (".").`
regex_pattern_error = ` el patró d'expressió regular no és vàlid: %s.`
required_prefix = L'entrada ha de començar amb "%s"
PayloadUrl = URL de càrrega útil
git_ref_name_error = ` ha de ser un nom correcte de referència de Git.`
size_error = ` ha de tenir una mida de %s.`
include_error = ` ha de contenir la subcadena «%s».`
glob_pattern_error = ` el patró glob no és vàlid: %s.`
[settings]
pronouns = Pronoms
change_username_prompt = Nota: canviar el vostre nom d'usuari també canvia l'URL del vostre compte.
@ -739,6 +781,108 @@ generate_token_name_duplicate = Ja s'ha usat <strong>%s</strong> com a nom d'apl
delete_token = Eliminar
access_token_deletion =
biography_placeholder = Explica una mica sobre tu als altres! (Compatible amb Markdown)
delete_email = Eliminar
add_new_email = Afegir una adreça de correu electrònic
add_new_openid = Afegir una nova URI d'OpenID
add_email = Afegir una adreça de correu electrònic
add_openid = Afegir una URI d'OpenID
keep_email_private = Oculta l'adreça de correu electrònic
delete_key = Eliminar
added_on = Afegit el %s
last_used = Usat per últim cop el
can_read_info = Llegir
can_write_info = Escriure
show_openid = Mostrar al perfil
hide_openid = Ocultar del perfil
ssh_disabled = SSH està deshabilitat
access_token_deletion_desc = Eliminar un testimoni revocarà l'accés al vostre compte de les aplicacions que l'estiguin fent servir. Aquesta acció no es pot desfer. Voleu continuar?
delete_token_success = S'ha eliminat el testimoni. Les aplicacions que l'estiguin fent servir ja no tenen accés al vostre compte.
regenerate_token = Regenerar
access_token_regeneration = Regenerar un testimoni d'accés
access_token_regeneration_desc = Regenerar un testimoni revocarà l'accés al vostre compte de les aplicacions que l'estiguin fent servir. Aquesta acció no es pot desfer. Voleu continuar?
regenerate_token_success = S'ha regenerat el testimoni. Les aplicacions que el fan servir ja no tenen accés al vostre compte i s'han actualitzar al nou testimoni.
permissions_public_only = Només públic
permissions_access_all = Tot (públic, privat i limitat)
select_permissions = Selecció de permisos
permission_no_access = Sense accés
permission_read = Lectura
permission_write = Lectura i escriptura
at_least_one_permission = Heu de seleccionar un permís com a mínim per crear un testimoni
permissions_list = Permisos:
manage_oauth2_applications = Gestionar aplicacions OAuth2
edit_oauth2_application = Modificar aplicació OAuth2
remove_oauth2_application = Eliminar aplicació OAuth2
remove_oauth2_application_success = S'ha eliminat l'aplicació.
create_oauth2_application = Crear una nova aplicació OAuth2
create_oauth2_application_button = Crear aplicació
create_oauth2_application_success = Heu creat amb èxit una nova aplicació OAuth2.
update_oauth2_application_success = Heu actualitzat amb èxit l'aplicació OAuth2.
oauth2_application_name = Nom de l'aplicació
oauth2_redirect_uris = URIs de redirecció. Si us plau, poseu cada URI en una línia nova.
save_application = Guardar
oauth2_client_id = ID del client
oauth2_client_secret = Secret del client
oauth2_regenerate_secret = Regenerar secret
oauth2_regenerate_secret_hint = Heu perdut el vostre secret?
oauth2_application_edit = Modificar
authorized_oauth2_applications = Aplicacions OAuth2 autoritzades
authorized_oauth2_applications_description = Heu permès accés al vostre compte personal de Forgejo a aquestes aplicacions de tercers. Si us plau, revoqueu l'accés de les aplicacions que ja no feu servir.
revoke_key = Revocar
revoke_oauth2_grant = Revocar l'accés
revoke_oauth2_grant_description = Revocar l'accés d'aquesta aplicació de tercers impedirà que accedeixi a la vostra informació. N'esteu segurs?
revoke_oauth2_grant_success = L'accés s'ha revocat amb èxit.
twofa_desc = Per protegir el vostre compte del robatori de contrasenyes, podeu fer servir un telèfon intel·ligent o un altre dispositiu per rebre contrasenyes d'un sol ús basades en el temps ("TOTP").
twofa_recovery_tip = Si perdeu el vostre dispositiu, podreu fer servir una clau de recuperació d'un sol ús per recuperar l'accés al vostre compte.
twofa_disable = Desactivar l'autenticació de doble factor
twofa_scratch_token_regenerate = Regenerar la clau de recuperació d'un sol ús
twofa_scratch_token_regenerated = La vostra clau de recuperació d'un sol ús ara és %s. Emmagatzemeu-la en un lloc segur, ja que no es tornarà a mostrar.
twofa_disable_note = L'autenticació de doble factor es pot desactivar si és necessari.
twofa_disable_desc = Desactivar l'autenticació de doble factor farà que el vostre compte sigui menys segur. Voleu continuar?
regenerate_scratch_token_desc = Si heu perdur la vostra clau de recuperació o ja l'heu fet servir per iniciar sessió, la podeu reiniciar aquí.
twofa_disabled = S'ha desactivat l'autenticació de doble factor.
scan_this_image = Escanegeu aquesta imatge amb la vostra aplicació d'autenticació:
or_enter_secret = O introduïu el secret: %s
then_enter_passcode = I introduïu el codi d'accés que es mostra a l'aplicació:
passcode_invalid = El codi d'accés és incorrecte. Intenteu-ho de nou.
twofa_failed_get_secret = No s'ha pogut obtenir el secret.
webauthn_desc = Les claus de seguretat són dispositius que contenen claus criptogràfiques. Es poden fer servir per l'autenticació de doble factor. Les claus de seguretat han de ser compatibles amb l'estàndard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
webauthn_register_key = Afegir una clau de seguretat
webauthn_nickname = Sobrenom
webauthn_delete_key_desc = Si elimineu una clau de seguretat no la podreu fer servir per iniciar sessió. Voleu continuar?
webauthn_key_loss_warning = Si perdeu les vostres claus de seguretat, perdreu també l'accés al vostre compte.
manage_account_links = Comptes vinculats
manage_account_links_desc = Aquests comptes externs estan vinculats al vostre compte de Forgejo.
link_account = Vincular un compte
remove_account_link_desc = Eliminar un compte vinculat revocarà el seu accés al vostre compte de Forgejo. Voleu continuar?
remove_account_link_success = S'ha eliminat el compte vinculat.
orgs_none = No sou membres de cap organització.
repos_none = No sou propietaris de cap repositori.
blocked_users_none = No hi ha cap usuari bloquejat.
delete_account = Eliminar el vostre compte
delete_prompt = Aquest acció eliminarà permanentment el vostre compte. Aquesta acció <strong>no</strong> es pot desfer.
confirm_delete_account = Confirmar l'eliminació
delete_account_title = Eliminar el compte d'usuari
delete_account_desc = Esteu segurs de voler eliminar permanentment aquest compte d'usuari?
email_notifications.enable = Activar les notificacions per correu electrònic
email_notifications.disable = Desactivar les notificacions per correu electrònic
visibility.public_tooltip = Visible per a tothom
visibility.limited_tooltip = Visible només pels usuaris que hagin iniciat sessió
visibility.private_tooltip = Visible només pels membres de les organitzacions a les quals us hàgiu unit
user_unblock_success = S'ha desbloquejat l'usuari amb èxit.
user_block_success = S'ha bloquejat l'usuari amb èxit.
quota.sizes.repos.all = Repositoris
quota.sizes.repos.public = Repositoris públics
quota.sizes.repos.private = Repositoris privats
quota.sizes.git.all = Contingut de Git
quota.sizes.git.lfs = Git LFS
quota.sizes.assets.attachments.all = Fitxers adjunts
quota.sizes.assets.attachments.issues = Fitxers adjunts a incidències
quota.sizes.assets.artifacts = Artefactes
quota.sizes.assets.packages.all = Paquets
enable_custom_avatar = Usa un avatar personalitzat
[repo]
settings.basic_settings = Configuració bàsica
settings.event_issues = Modificació
@ -952,3 +1096,5 @@ public_activity.visibility_hint.admin_public = Aquesta activitat és visible per
public_activity.visibility_hint.self_private = La vostra activitat és visible només per vós i pels administradors de la instància. <a href="%s">Configurar</a>.
public_activity.visibility_hint.admin_private = Aquesta activitat és visible per vós perquè sou un administrador, però l'usuari vol que romangui privada.
public_activity.visibility_hint.self_private_profile = La vostra activitat és visible només per vós i pels administradors de la instància perquè el vostre perfil és privat. <a href="%s">Configurar</a>.
change_avatar = Canvieu el vostre avatar…
joined_on = S'ha unit el %s

View file

@ -3683,7 +3683,7 @@ owner.settings.cleanuprules.keep.count=Zachovat nejnovější
owner.settings.cleanuprules.keep.count.1=1 verze na balíček
owner.settings.cleanuprules.keep.count.n=%d verzí na balíček
owner.settings.cleanuprules.keep.pattern=Ponechat odpovídající verze
owner.settings.cleanuprules.keep.pattern.container=U balíčků Container je vždy zachována <code>nejnovější</code> verze.
owner.settings.cleanuprules.keep.pattern.container=U balíčků Container je vždy zachována <code>nejnovější</code> verze.
owner.settings.cleanuprules.remove.title=Verze, které odpovídají těmto pravidlům, jsou odstraněny, pokud výše uvedené pravidlo neukládá jejich zachování.
owner.settings.cleanuprules.remove.days=Odstranit verze starší než
owner.settings.cleanuprules.remove.pattern=Odstranit odpovídající verze
@ -3691,7 +3691,7 @@ owner.settings.cleanuprules.success.update=Pravidlo pro čištění bylo aktuali
owner.settings.cleanuprules.success.delete=Pravidlo pro čištění bylo odstraněno.
owner.settings.chef.title=Registr Chef
owner.settings.chef.keypair=Generovat pár klíčů
owner.settings.chef.keypair.description=Žádosti odeslané do registru Chef musí být kryptograficky podepsané jako způsob ověření. Při generování páru klíčů je ve službě Forgejo uložen pouze veřejný klíč. Soukromý klíč je poskytnut vám, abyste jej mohli použít s programem knife. Vygenerováním nového páru klíčů přepíšete ten předchozí.
owner.settings.chef.keypair.description=Žádosti odeslané do registru Chef musí být kryptograficky podepsané jako způsob ověření. Při generování páru klíčů je ve službě Forgejo uložen pouze veřejný klíč. Soukromý klíč je poskytnut vám, abyste jej mohli použít s programem knife. Vygenerováním nového páru klíčů přepíšete ten předchozí.
owner.settings.cargo.rebuild.description = Opětovné sestavení může být užitečné, pokud není index synchronizován s uloženými balíčky Cargo.
owner.settings.cargo.rebuild.no_index = Opětovné vytvoření selhalo, nebyl inicializován žádný index.
npm.dependencies.bundle = Přidružené závislosti
@ -3726,7 +3726,7 @@ secrets=Tajné klíče
description=Tejné klíče budou předány určitým akcím a nelze je přečíst jinak.
none=Zatím zde nejsou žádné tajné klíče.
creation=Přidat tajný klíč
creation.name_placeholder=nerozlišovat velká a malá písmena, pouze alfanumerické znaky nebo podtržítka, nemohou začínat na GITEA_ nebo GITHUB_
creation.name_placeholder=nerozlišovat velká a malá písmena, pouze alfanumerické znaky nebo podtržítka, nemohou začínat na GITEA_ nebo GITHUB_
creation.value_placeholder=Vložte jakýkoliv obsah. Mezery na začátku a konci budou vynechány.
creation.success=Tajný klíč „%s“ byl přidán.
creation.failed=Nepodařilo se přidat tajný klíč.

View file

@ -776,7 +776,7 @@ activate_email=Aktivierung senden
activations_pending=Aktivierung ausstehend
can_not_add_email_activations_pending=Es gibt eine ausstehende Aktivierung, versuche es in ein paar Minuten erneut, wenn du eine neue E-Mail hinzufügen möchtest.
delete_email=Löschen
email_deletion=E-Mail-Adresse löschen
email_deletion=E-Mail-Adresse entfernen
email_deletion_desc=Diese E-Mail-Adresse und die damit verbundenen Informationen werden von deinem Konto entfernt. Git-Commits von dieser E-Mail-Addresse bleiben unverändert. Fortfahren?
email_deletion_success=Die E-Mail-Adresse wurde entfernt.
theme_update_success=Deine Theme-Auswahl wurde gespeichert.
@ -1653,8 +1653,8 @@ issues.add_time=Zeit manuell hinzufügen
issues.del_time=Diese Zeiterfassung löschen
issues.add_time_short=Zeit hinzufügen
issues.add_time_cancel=Abbrechen
issues.add_time_history=`hat %s den Zeitaufwand hinzugefügt`
issues.del_time_history=`hat %s den Zeitaufwand gelöscht`
issues.add_time_history=`hat den Zeitaufwand von %s hinzugefügt`
issues.del_time_history=`hat den Zeitaufwand von %s gelöscht`
issues.add_time_hours=Stunden
issues.add_time_minutes=Minuten
issues.add_time_sum_to_small=Es wurde keine Zeit eingegeben.
@ -3523,7 +3523,7 @@ mark_as_read=Als gelesen markieren
mark_as_unread=Als ungelesen markieren
mark_all_as_read=Alle als gelesen markieren
subscriptions=Abonnements
watching=Gefolgt
watching=Beobachtet
no_subscriptions=Keine Abonnements
[gpg]

View file

@ -1579,6 +1579,7 @@ issues.filter_poster = Author
issues.filter_poster_no_select = All authors
issues.filter_type = Type
issues.filter_type.all_pull_requests = All pull requests
issues.filter_type.all_issues = All issues
issues.filter_type.assigned_to_you = Assigned to you
issues.filter_type.created_by_you = Created by you
issues.filter_type.mentioning_you = Mentioning you

View file

@ -98,7 +98,7 @@ ok = Bone
download_logs = Elsuti protokolojn
unknown = Nekonata
issues = Eraroj
error404 = Aŭ tiu ĉi paĝo <strong>ne ekzistas</strong>, <strong>estis forigita</strong> aŭ <strong>vi ne rajtas</strong> vidi ĝin.
error404 = Aŭ tiu ĉi paĝo <strong>ne ekzistas</strong>, <strong>estas forigita</strong> aŭ <strong>vi ne rajtas</strong> vidi ĝin.
retry = Reprovi
activities = Aktivecoj
confirm_delete_selected = Konfirmi forigon de ĉiu elektito?
@ -142,9 +142,11 @@ new_org.link = Novan organizaĵon
error413 = Vi plenkonsumis vian kvoton.
twofa_scratch = Sukuranta kodo por duobla aŭtentikigo
copy_path = Kopii dosiervojon
[editor]
buttons.list.ordered.tooltip = Aldoni nombran liston
buttons.bold.tooltip = Aldoni grasan tekston
buttons.bold.tooltip = Aldoni grasan tekston (Ctrl+B / ⌘B)
buttons.quote.tooltip = Citi tekston
buttons.code.tooltip = Aldoni kodtekston
buttons.list.unordered.tooltip = Aldoni punktan liston
@ -154,7 +156,7 @@ buttons.ref.tooltip = Citi eraron aŭ tirpeton
buttons.list.task.tooltip = Aldoni liston de taskoj
buttons.enable_monospace_font = Ŝalti egallarĝan signoformaron
buttons.mention.tooltip = Mencii uzanton aŭ grupon
buttons.italic.tooltip = Aldoni oblikvan tekston
buttons.italic.tooltip = Aldoni oblikvan tekston (Ctrl+I / ⌘I)
buttons.link.tooltip = Aldoni ligilon
buttons.disable_monospace_font = Malsalti egallarĝan signoformaron
buttons.indent.tooltip = Krommarĝeni erojn je unu nivelo
@ -167,6 +169,10 @@ table_modal.label.rows = Horizontaloj
table_modal.label.columns = Vertikaloj
link_modal.description = Priskribo
link_modal.header = Aldoni ligilon
link_modal.url = Url
link_modal.paste_reminder = Aludo: kun URL en via tondujo, vi povas alglui senpere al la redaktilo por krei ligilon.
[aria]
navbar = Esplora breto
footer.software = Pri ĉi tiu programaro
@ -203,6 +209,8 @@ lightweight_desc = Forgejo ne penigos vian servilon, kaj eĉ ruleblas je Raspber
platform = Plursistema
license_desc = Ek, prenu <a target="_blank" rel="noopener noreferrer" href="%[1]s">Forgejon</a>! Aliĝu kaj <a target="_blank" rel="noopener noreferrer" href="%[2]s">helpu</a> nin plibonigi la projekton. Ne timu kontribui!
platform_desc = Forgejo estas konfirmita ruli sur liberaj operaciumoj kiel Linukso kaj FreeBSD, kaj malsamaj arkitekturprocesoroj. Elektu tion, kion vi preferas.
[install]
title = Komenca agordado
install = Instalado
@ -311,7 +319,7 @@ enable_update_checker = Aktivigi novversian kontrolanton
password_algorithm = Pasvorthaketiga algoritmo
env_config_keys = Mediagordoj
invalid_password_algorithm = Malvalida pasvorthakeita algoritmo
password_algorithm_helper = Agordas la pasvorthaketigan algoritmon. Algoritmoj havas malsamajn postulojn kaj efikecojn. La algoritmo argon2 sufiĉe sekuras, sed postulas multan memoron kaj eble ne taŭgas por nepotencaj serviloj.
password_algorithm_helper = Agordu la pasvorthaketigan algoritmon. Algoritmoj havas malsamajn postulojn kaj efikecojn. La algoritmo argon2 sufiĉe sekuras, sed postulas multan memoron kaj eble ne taŭgas por nepotencaj serviloj.
internal_token_failed = Malsukcesis krei internan ĵetonon: %v
smtp_from_invalid = La «Sendu retleterojn kiel» adreso malvalidas
allow_only_external_registration = Permesi registriĝon nur per fremdaj servoj
@ -398,7 +406,7 @@ openid_register_title = Krei novan konton
email_domain_blacklisted = Vi ne povas registriĝi per via retpoŝtadreso.
verify = Konfirmi
oauth_signup_submit = Finfari konton
prohibit_login_desc = Via konto estas suspendita kaj ne povas interagi kun la instanco. Bonvolu kontakti vian retejestron por regajni aliron.
prohibit_login_desc = Via konto estas suspendita kaj ne povas interagi kun la instanco. Bonvolu kontakti la retejestron por regajni aliron.
openid_connect_desc = La elektita OpenID URI estas nekonata. Ligu ĝin al nova konto ĉi tie.
oauth.signin.error = Eraris traktante aprobpeton. Se plu eraros, bonvolu kunparoli la retejestron.
invalid_code = Via konfirmkodo malvalidas aŭ eksdatiĝis.
@ -428,7 +436,7 @@ hint_login = Ĉu vi jam havas konton? <a href="%s">Salutu nun!</a>
hint_register = Ĉu vi bezonas konton? <a href="%s">Reĝistriĝi nun.</a>
sign_up_button = Reĝistriĝi nun.
sign_in_openid = Daŭrigi kun OpenID
back_to_sign_in = Reen en la saluton
back_to_sign_in = Reen en la ensaluton
use_onetime_code = Uzi unufojan kodon
[mail]
@ -484,7 +492,7 @@ password_change.text_1 = La pasvorto de via konto ĵus ŝanĝiĝis.
primary_mail_change.subject = Via ĉefa retpoŝtadreso ŝanĝiĝis
totp_disabled.text_1 = La tempobazita unufoja pasvorto (TOTP) en via konto ĵus malaktiviĝis.
admin.new_user.text = Bonvolu <a href="%s">klaki ĉi tie</a> por konduki ĉi tiun uzanton el la administranta agordilo.
removed_security_key.subject = Sekureca ŝlosilo estas forigita
removed_security_key.subject = Sekurŝlosilo estas forigita
removed_security_key.text_1 = La sekureca ŝlosilo "%[1]s" ĵus estas forigita de via konton.
totp_enrolled.text_1.has_webauthn = Vi ĵus aktivigis TOTP-n por via konto. Tio volas diri ke por ĉiuj venontaj salutoj al via konto, vi povus uzi TOTP-n kiel 2FA metodo aŭ ajnan sekurecan ŝlosilon.
totp_enrolled.text_1.no_webauthn = Vi ĵus aktivigis TOTP-n por via konto. Tio volas diri ke por ĉiuj venontaj salutoj al via konto, vi devos uzi TOTP-n kiel 2FA metodo.
@ -492,6 +500,13 @@ removed_security_key.no_2fa = Ne estas aliaj 2FA agorditaj metodoj, tio estas ke
totp_disabled.no_2fa = Ne estas plu aliaj 2FA agorditaj metodoj, tio estas ke ne plus necesas uzi 2FA-n por saluti.
account_security_caution.text_1 = Se tio estis vi, vi povas sekure ignori ĉi tiun retmesaĝon.
account_security_caution.text_2 = Se ne estis vi, via konto estas kompromitata. Bonvolu kontakti la administrantojn de la retpaĝaro.
totp_enrolled.subject = Vi aktivigis TOTP-n kiel 2FA metodo
issue_assigned.pull = @%[1]s asignis al vi la tirpeton %[2]s en la deponejo %[3]s.
issue_assigned.issue = @%[1]s asignis al vi ĉi tiun eraron %[2]s en la deponejo %[3]s.
issue.action.review_dismissed = <b>@%[1]s</b> maldungis la lastan revizion de %[2]s por ĉi tiu tirpeto.
repo.transfer.subject_to_you = %s volas reposedigi la deponejon "%s" al vi
[form]
TeamName = Gruponomo
RepoName = Deponejonomo
@ -560,6 +575,16 @@ last_org_owner = Vi ne povas forigi la lastan uzanton de la «posendantoj» grup
still_has_org = "Via konto anas de almenaŭ unu organizaĵoj, forlasu ilin unue."
invalid_ssh_key = Ne povis konfirmi vian SSH-ŝlosilon: %s
FullName = Plena nomo
Description = Priskribo
Pronouns = Pronomoj
Biography = Biografio
Website = Retpaĝaro
Location = Kieo
To = Branĉonomo
AccessToken = Atingoĵetono
required_prefix = La enigaĵo devas komenciĝi per "%s"
[modal]
confirm = Konfirmi
no = Ne
@ -728,6 +753,91 @@ permission_write = Lega kaj Skriba
key_content = Enhavo
key_signature_gpg_placeholder = Komenciĝas per «-----BEGIN PGP SIGNATURE-----»
storage_overview = Stokada superrigardo
quota = Kvoto
pronouns = Pronomoj
pronouns_unspecified = Nespecifitaj
change_username_redirect_prompt.with_cooldown.one = La malnova uzantnomo disponeblos al ĉiuj post atendoperiodo de %[1]d tago. Vi povas ankoraŭ reakiri la malnovan uzantnomon dum ĉi tiu periodo.
change_username_redirect_prompt.with_cooldown.few = La malnova uzantnomo disponeblos al ĉiuj post atendoperiodo de %[1]d tagoj. Vi povas ankoraŭ reakiri la malnovan uzantnomon dum ĉi tiu periodo.
language.title = Defaŭlta lingvo
language.description = Ĉi tiu lingvo estos konservota en via konto kaj estos uzota kiel defaŭlta post kiam vi ensalutos.
language.localization_project = Helpu nin traduki Forgejo-n en via lingvo! <a href="%s">Lerni plu</a>.
hints = Sugestoj
update_hints = Ĝisdatigi la sugestojn
update_hints_success = La sugestoj ĝisdatiĝis.
hidden_comment_types.ref_tooltip = Komentoj, kie ĉi tiu eraro estas referencita de alia eraro/enmeto/…
comment_type_group_reference = Referenco
comment_type_group_milestone = Celo
comment_type_group_assignee = Asignito
comment_type_group_lock = Rigli staton
comment_type_group_review_request = Revizia peto
comment_type_group_issue_ref = Referenco de la eraro
keep_activity_private = Kaŝi la aktivecon de la profilpaĝo
keep_activity_private.description = Via <a href="%s">publika aktiveco</a> nur videblos al vi kaj la instancaj administrantoj.
enable_custom_avatar = Uzi propran profilbildon
change_password = Ŝanĝi pasvorton
keep_pronouns_private = Montri pronomojn nur al la aŭtentikigitaj uzantoj
keep_pronouns_private.description = Tio maskos viajn pronomojn kontraŭ neaŭtentikigitaj vizitantoj.
add_new_principal =
gpg_token_required = Vi devas disponigi signaturon por la malsupran ĵetono
gpg_token = Ĵetono
gpg_token_help = Vi povas generi signaturon uzante:
ssh_token_required = Vi devas disponigi signaturon for la malsupran ĵetono
ssh_token = Ĵetono
ssh_token_help = Vi povas generi signaturon uzante:
no_activity = Neniu ĵusa aktiveco
token_state_desc = Ĉi tiu ĵetono estis uzata dum la 7 lastaj tagoj
manage_access_token = Atingoĵetonoj
generate_new_token = Generi novan ĵetonon
token_name = Ĵetono-nomo
generate_token = Generi ĵetonon
generate_token_success = Via nova ĵetono estas generita. Kopiu ĝin nun, ĉar ĝi ne estos montrata ree.
access_token_deletion = Forigi atingoĵetonon
delete_token_success = La ĵetono estas forigita. Aplikaĵoj uzantaj ĝin ne atingos plu vian konton.
regenerate_token = Regeneri
access_token_regeneration = Regeneri atingoĵetonon
at_least_one_permission = Vi devas selekti almenaŭ unu permeson por krei ĵetonon
remove_oauth2_application = Forigi OAuth2-aplikaĵon
remove_oauth2_application_success = La aplikaĵo estas forigita.
create_oauth2_application = Krei novan OAuth2-aplikaĵon
create_oauth2_application_button = Krei aplikaĵon
create_oauth2_application_success = Vi sukcese kreis novan OAuth2 aplikaĵon.
update_oauth2_application_success = Vi sukcese ĝisdatigis la OAuth2 aplikaĵon.
oauth2_application_name = Aplikaĵonomo
save_application = Konservi
oauth2_client_id = Klienta ID
oauth2_client_secret = Klienta sekreto
oauth2_regenerate_secret = Regeneri sekreton
oauth2_regenerate_secret_hint = Ĉu vi perdis vian sekreton?
oauth2_application_edit = Redakti
authorized_oauth2_applications = Permesitaj OAuth2-aplikaĵoj
visibility = Uzanta videbleco
visibility.public = Publika
visibility.public_tooltip = Videbla al ĉiuj
visibility.limited = Limigata
visibility.limited_tooltip = Videbla nur al ensalutitaj uzantoj
visibility.private = Privata
visibility.private_tooltip = Videbla nur al membroj de organizaĵoj, kiujn vi aliĝis
blocked_since = Blokata ekde %s
user_unblock_success = La uzanto sukcese estas malblokita.
user_block_success = La uzanto sukcese estas blokita.
user_block_yourself = Vi ne povas bloki vin mem.
quota.applies_to_user = La sekvantaj kvotaj reguloj aplikiĝas al via konto
quota.applies_to_org = La sekvantaj kvotaj reguloj aplikiĝas al ĉi tiu organizaĵo
quota.rule.exceeded = Superita
quota.sizes.all = Ĉio
quota.sizes.repos.all = Deponejoj
quota.sizes.repos.public = Publikaj deponejoj
quota.sizes.repos.private = Privataj deponejoj
quota.sizes.git.all = Git-enhavo
quota.sizes.git.lfs = Git LFS
quota.sizes.assets.attachments.all = Kunsendaĵoj
quota.sizes.assets.attachments.issues = Kunsendaĵoj de eraro
quota.sizes.assets.attachments.releases = Kunsendaĵoj de eldono
quota.sizes.assets.artifacts = Artfaritaĵoj
quota.sizes.assets.packages.all = Pakaĵoj
quota.sizes.wiki = Vikio
[user]
form.name_reserved = La uzantonomo «%s» estas protektita.
joined_on = Aliĝis je %s
@ -756,6 +866,22 @@ block_user = Bloki uzanton
change_avatar = Ŝanĝi vian profilbildon…
activity = Publika aktiveco
followers.title.one = Sekvanto
followers.title.few = Sekvantoj
following.title.one = Sekvata
following.title.few = Sekvataj
followers_one = %d sekvanto
following_one = %d sekvataj
overview = Superrigardo
disabled_public_activity = Ĉi tiu uzanto malaktivigis la publikan videblecon de sia aktiveco.
public_activity.visibility_hint.self_public = Via aktiveco videblas al ĉiuj, escepte de interagoj en privataj spacoj. <a href="%s">Agordi</a>.
public_activity.visibility_hint.admin_public = Ĉi tiu aktiveco videblas al ĉiuj, sed kiel administranto vi povas ankaŭ vidi interagojn en privataj spacoj.
public_activity.visibility_hint.self_private = Via aktiveco nur videblas al vi kaj la instancaj administrantoj. <a href="%s">Agordi</a>.
public_activity.visibility_hint.admin_private = Ĉi tiu aktiveco videblas al vi ĉar vi estas administranto, sed la uzanto volas ke ĝi restu privata.
public_activity.visibility_hint.self_private_profile = Via aktiveco nur videblas al vi kaj la instancaj administrantoj, ĉar via profilo estas privata. <a href="%s">Agordi</a>.
form.name_pattern_not_allowed = La ŝablono "%s" ne estas permesata en uzantnomo.
[repo]
editor.add_file = Aldoni dosieron
settings.tags = Etikedoj
@ -803,6 +929,8 @@ settings.archive.text = Arĥivigi la deponejon igus ĝin sole legebla. Ĝi kaŝi
migrate_items_releases = Eldonoj
commits.commits = Enmetoj
rss.must_be_on_branch = Vi devas esti en branĉo por havi RSS-fluon.
[org]
code = Fontkodo
settings = Agordoj
@ -838,3 +966,26 @@ fuzzy = Svaga
branch_kind = Serĉi disbranĉigojn…
runner_kind = Serĉi rulantojn…
pull_kind = Serĉi tirpetojn…
[actions]
variables.creation.success = La variablo "%s" estas aldonita.
variables.update.failed = Ne eblas redakti la variablon.
variables.update.success = La variablo estas redaktita.
[projects]
deleted.display_name = Forigita projekto
type-1.display_name = Persona projekto
type-2.display_name = Deponejoprojekto
[git.filemode]
changed_filemode = %[1]s → %[2]s
directory = Dosierujo
normal_file = Normala dosiero
executable_file = Rulebla dosiero
symbolic_link = Simbola ligilo
submodule = Submodulo
[markup]
filepreview.line = Linio %[1]d en %[2]s
filepreview.lines = Linioj %[1]d ĝis %[2]d en %[3]s
filepreview.truncated = La superrigardo estas mallongigita

View file

@ -1 +1,2 @@
[common]
home = Etxea

View file

@ -40,7 +40,7 @@ webauthn_use_twofa=Käytä kaksivaihesta todennusta puhelimestasi
webauthn_error=Turva-avainta ei voitu lukea.
webauthn_unsupported_browser=Selaimesi ei tällä hetkellä tue WebAuthnia.
webauthn_error_unknown=Tuntematon virhe. Yritä uudelleen.
webauthn_error_insecure=WebAuthn tukee vain suojattuja yhteyksiä. Testatessa HTTP-yhteydellä voit käyttää osoitetta "localhost" tai "127.0.0.1"
webauthn_error_insecure=WebAuthn tukee vain turvallisia yhteyksiä. Testaamista varten HTTP-välityksellä voit käyttää alkuperää "localhost" tai "127.0.0.1"
webauthn_error_unable_to_process=Palvelin ei pystynyt käsittelemään pyyntöä.
webauthn_error_duplicated=Turva-avainta ei ole sallittu tässä pyynnössä. Varmista, ettei avainta ole jo rekisteröity.
webauthn_error_empty=Sinun täytyy asettaa nimi tälle avaimelle.
@ -848,7 +848,7 @@ twofa_enroll=Ota kaksivaiheinen todennus käyttöön
twofa_disabled=Kaksivaiheinen todennus on otettu pois käytöstä.
scan_this_image=Skannaa tämä kuva todennussovelluksellasi:
or_enter_secret=Tai kirjoita salainen avain: %s
twofa_enrolled=Tiliisi on otettu käyttöön kaksivaiheinen todennus. Ota kertakäyttöinen palautusavain (%s) talteen turvalliseen paikkaan, sillä se näytetään vain kerran!
twofa_enrolled=Tilisi on otettu mukaan onnistuneesti. Säilytä kertakäyttöistä palautusavainta (%s) turvallisessa paikassa, sillä sitä ei enää näytetä.
webauthn_nickname=Nimimerkki
@ -1013,6 +1013,8 @@ ssh_principal_deletion_success = Prinsipaali on poistettu.
principal_state_desc = Tätä prinsipaalia on käytetty viimeisen seitsemän päivän aikana
regenerate_token_success = Poletti on luotu uudelleen. Sovellukset, jotka käyttivät polettia, eivät enää pääse tilillesi. Kyseiset sovellukset tulee päivittää uudella poletilla.
quota.sizes.assets.all = Resurssit
[repo]
owner=Omistaja
owner_helper=Jotkin organisaatiot eivät välttämättä näy pudotusvalikossa, koska tietovarastojen enimmäismäärää on rajoitettu.
@ -1072,7 +1074,7 @@ migrate_items_pullrequests=Vetopyynnöt
migrate_items_releases=Julkaisut
migrate_repo=Suorita tietovaraston migraatio
migrate.clone_address=Migraatio/kloonaus URL-osoitteesta
migrate.github_token_desc=Voit laittaa yhden tai useamman pääsypoletin pilkulla erotellen tähän nopeuttaaksesi migraatiota GitHubin rajapinnan tahtirajojen takia. VAROITUS: Tämän ominaisuuden väärinkäyttö voi rikkoa palveluntarjoajan ehtoja ja johtaa tilin estämiseen.
migrate.github_token_desc=Voit lisätä tähän yhden tai useamman tunnuksen pilkuilla erotettuna nopeuttaaksesi migraatiota kiertämällä GitHub API:n nopeusrajoituksen. Varoitus: Tämän ominaisuuden väärinkäyttö voi rikkoa palveluntarjoajan käytäntöä ja johtaa tiliesi sulkemiseen.
migrate.permission_denied=Paikallisten tietovarastojen tuominen ei ole sallittua.
migrate.failed=Migraatio epäonnistui: %v
migrate.migrate_items_options=Lisäkohteiden migraatiota varten vaaditaan pääsypoletti
@ -1081,7 +1083,7 @@ migrate.migrating_failed=Migraatio lähteestä <b>%s</b> epäonnistui.
migrate.migrating_git=Suoritetaan Git-datan migraatiota
mirror_from=peili kohteelle
forked_from=forkattu lähteestä
forked_from=forkattu tietovarastosta
unwatch=Lopeta tarkkailu
watch=Tarkkaile
unstar=Poista tähti
@ -1117,7 +1119,7 @@ file_permalink=Pysyväislinkki
video_not_supported_in_browser=Selaimesi ei tue HTML5:n video-tagia.
audio_not_supported_in_browser=Selaimesi ei tue HTML5:n audio-tagia.
blame=Blame
blame=Syyllistynyt
download_file=Lataa tiedosto
normal_view=Normaali näkymä
line=rivi
@ -1180,7 +1182,7 @@ projects.open=Avaa
projects.close=Sulje
issues.desc=Ongelmien, tehtävien ja merkkipaalujen hallinta.
issues.filter_assignees=Suodata käyttäjiä
issues.filter_assignees=Suodata vastuuhenkilö
issues.filter_milestones=Suodata merkkipaalu
issues.new=Uusi ongelma
issues.new.labels=Nimilaput
@ -1215,7 +1217,7 @@ issues.self_assign_at=`itse otti tämän käsittelyyn %s`
issues.change_title_at=`muutti otsikon <b><strike>%s</strike></b> otsikoksi <b>%s</b> %s`
issues.delete_branch_at=`poisti haaran <b>%s</b> %s`
issues.filter_label=Nimilappu
issues.filter_label_exclude=`Käytä <code>alt</code> + <code>napsautus/rivinvaihto</code> poissulkeaksesi nimilappuja`
issues.filter_label_exclude=Käytä <kbd>Alt</kbd> + <kbd>Click</kbd>-näppäinyhdistelmää nimiöiden poissulkemiseksi
issues.filter_label_no_select=Kaikki nimilaput
issues.filter_milestone=Merkkipaalu
issues.filter_project=Projekti
@ -1264,9 +1266,9 @@ issues.close_comment_issue=Kommentoi ja sulje
issues.reopen_issue=Avaa uudelleen
issues.reopen_comment_issue=Kommentoi ja avaa uudelleen
issues.create_comment=Kommentoi
issues.closed_at=`sulki tämän ongelman %s`
issues.reopened_at=`uudelleenavasi tämän ongelman %s`
issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa %s`
issues.closed_at=`sulki tämän tukipyynnön %s`
issues.reopened_at=`avasi tämän tukipyynnön uudelleen %s`
issues.commit_ref_at=`viittasi tähän tukipyyntöön sitoumuksesta %s`
issues.author=Tekijä
issues.role.owner=Omistaja
issues.role.member=Jäsen
@ -1547,7 +1549,7 @@ settings.web_hook_name_larksuite_only =Lark Suite
settings.web_hook_name_packagist=Packagist
settings.deploy_keys=Toimitusavaimet
settings.add_deploy_key=Lisää toimitusavain
settings.deploy_key_desc=Toimitusavaimilla on pelkkä lukuoikeus tietovarastoon.
settings.deploy_key_desc=Käyttöönottoavaimilla voi olla vain luku- tai luku-kirjoitusoikeudet tietovarastoon.
settings.is_writable_info=Salli tämän toimitusavaimen <strong>työntää</strong> tietovarastoon.
settings.no_deploy_keys=Toimitusavaimia ei ole käytössä vielä.
settings.title=Otsikko
@ -1586,7 +1588,7 @@ settings.archive.header=Arkistoi tämä tietovarasto
settings.archive.tagsettings_unavailable=Tagi-asetukset eivät ole käytettävissä arkistoiduissa tietovarastoissa.
settings.lfs=LFS
settings.lfs_filelist=Tähän tietovarastoon tallennetut LFS-tiedostot
settings.lfs_no_lfs_files=LFS-tiedostoja ei ole tallennettu tähän tietovarastoon.
settings.lfs_no_lfs_files=Tähän tietovarastoon ei ole tallennettu LFS-tiedostoja
settings.lfs_findcommits=Etsi kommitteja
settings.lfs_lfs_file_no_commits=Tälle LFS-tiedostolle ei löytynyt kommitteja
settings.lfs_noattribute=Tällä polulla ei ole lukittavaa attribuuttia oletushaarassa
@ -2148,7 +2150,7 @@ issues.add_label = lisäsi nimilapun %s %s
issues.due_date_added = lisäsi eräpäivän %s %s
issues.review.add_review_request = pyysi katselmointia käyttäjältä %[1]s %[2]s
issues.ref_pull_from = `<a href="%[2]s">viittasi tähän vetopyyntöön %[3]s</a> %[1]s`
pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %s`
pulls.commit_ref_at = `viittasi tähän vetopyyntöön sitoumuksesta %s`
issues.review.comment = katselmoi %s
issues.add_labels = lisäsi nimilaput %s %s
issues.review.add_review_requests = pyysi katselmointeja käyttäjiltä %[1]s %[2]s
@ -2329,9 +2331,9 @@ wiki.page_name_desc = Kirjoita tämän wikisivun nimi. Joitain erikoisnimiä ova
pulls.blocked_by_changed_protected_files_1 = Tämä vetopyyntö sisältää suojatun tiedoston ja on siksi estetty:
pulls.status_checks_warning = Jotkin tarkistukset raportoivat varoituksia
pulls.status_checks_error = Jotkin tarkistukset raportoivat virheitä
pulls.reopened_at = `avasi uudelleen tämän vetopyynnön %s`
pulls.reopened_at = `avasi tämän vetopyynnön uudelleen %s`
pulls.auto_merge_when_succeed = Yhdistä automaatisesti kun kaikki tarkistukset onnistuvat
signing.wont_sign.error = Tapahtui virhe tarkistaessa voiko kommitin allekirjoittaa.
signing.wont_sign.error = Tapahtui virhe tarkistettaessa, voiko sitoumus allekirjoittaa.
signing.wont_sign.twofa = Sinulla tulee olla kaksivaiheinen todennus käytössä, jotta kommitit voi allekirjoittaa.
pulls.data_broken = Tämä vetopyyntö on rikki johtuen puuttuvasta forkkitiedosta.
pulls.files_conflicted = Tämä vetopyyntö sisältää muutoksia, jotka ovat ristiriidassa kohdehaaran kanssa.
@ -3051,7 +3053,7 @@ auths.attribute_username_placeholder = Jätä tyhjäksi käyttääksesi Forgejo:
auths.oauth2_authURL = Valtuutuksen URL-osoite
auths.new_success = Todennus "%s" on lisätty.
users.still_own_repo = Tämä käyttäjä omistaa yhden tai useamman tietovaraston. Poista tai siirrä nämä tietovarastot ensin.
dashboard.cleanup_hook_task_table = Siivoa hook_task-taulu
dashboard.cleanup_hook_task_table = Siivoa koukku -_tehtävätaulukko
dashboard.delete_old_actions = Poista kaikki vanhat aktiviteetit tietokannasta
auths.attribute_mail = Sähköpostiosoitteen attribuutti
auths.attribute_ssh_public_key = Julkisen SSH-avaimen attribuutti
@ -3119,7 +3121,7 @@ dashboard.resync_all_sshprincipals = Päivitä ".ssh/authorized_principals"-tied
create_repo=loi tietovaraston <a href="%s">%s</a>
rename_repo=asetti tietovaraston <code>%[1]s</code> uudeksi nimeksi <a href="%[2]s">%[3]s</a>
transfer_repo=siirsi tietovaraston <code>%s</code> käyttäjälle <a href="%s">%s</a>
push_tag=työnsi tagin <a href="%[2]s">%[3]s</a> kohteeseen <a href="%[1]s">%[4]s</a>
push_tag=työnsi tagin <a href="%[2]s">%[3]s</a> tietovarastoon <a href="%[1]s">%[4]s</a>
delete_tag=poisti tagin %[2]s kohteesta <a href="%[1]s">%[3]s</a>
compare_commits_general=Vertaa kommitteja
create_branch=loi haaran <a href="%[2]s">%[3]s</a> tietovarastossa <a href="%[1]s">%[4]s</a>
@ -3143,6 +3145,8 @@ approve_pull_request = `hyväksyi <a href="%[1]s">%[3]s#%[2]s</a>`
starred_repo = lisäsi tähden tietovarastolle <a href="%[1]s">%[2]s</a>
reject_pull_request = `ehdotti muutoksia kohteeseen <a href="%[1]s">%[3]s#%[2]s</a>`
publish_release = `julkaisi <a href="%[2]s">%[4]s</a> tietovarastossa <a href="%[1]s">%[3]s</a>`
[tool]
now=nyt
1s=1 sekunti
@ -3233,7 +3237,7 @@ helm.install = Asenna paketti komennolla:
owner.settings.chef.keypair = Luo avainpari
settings.delete.error = Paketin poistaminen epäonnistui.
requirements = Vaatimukset
published_by_in = Julkaistu %[1]s, julkaisija <a href="%[2]s">%[3]s</a> projektissa <a href="%[4]s"><strong>%[5]s</strong></a>
published_by_in = Julkaistu %[1]s, julkaisija <a href="%[2]s">%[3]s</a> tietovarastossa <a href="%[4]s"><strong>%[5]s</strong></a>
pypi.requires = Vaatii Pythonin
alpine.install = Asenna paketti seuraavalla komennolla:
debian.repository.components = Komponentit
@ -3356,6 +3360,8 @@ conda.registry = Määritä tämä rekisteri Conda-tietovarastoksi <code>.condar
container.labels = Nimilaput
settings.link.description = Jos linkität paketin tietovarastoon, paketti listataan tietovaraston pakettilistalla.
assets = Resurssit
[secrets]
creation.failed = Salaisuuden lisääminen epäonnistui.
deletion = Poista salaisuus
@ -3523,4 +3529,4 @@ wiki.write = <b>Kirjoita:</b> Luo, päivitä ja poista integroidun wikin sivuja.
filepreview.truncated = Esikatselu on typistetty
[translation_meta]
test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :) :) :)
test = Tämä on testimerkkijono. Sitä ei näytetä Forgejo-käyttöliittymässä, mutta sitä käytetään testaustarkoituksiin. Voit vapaasti kirjoittaa "selvä" säästääksesi aikaa (tai käyttää hauskaa faktaa) ja saavuttaaksesi sen makean 100 %:n valmistumisrajan :)

View file

@ -225,3 +225,98 @@ admin_email = ईमेल एड्रेस
config_location_hint = ये सञ्चालन के तरीके यहाँ सेव होंगे :
install_btn_confirm = इनस्टॉल फॉरगेजो
test_git_failed = git कमांड टेस्ट नहीं हुई: %v
sqlite3_not_available = इस फॉरगेजो के वर्शन में SQLite३ नहीं है। कृपया डाउनलोड करें बाइनरी वर्शन यहाँ से %s (“gobuild” वर्शन नहीं करें)।
invalid_db_setting = ये डेटाबेस सेटिंग्स मान्य नहीं हैं %v
invalid_db_table = डेटाबेस टेबल “%s” मान्य नहीं: %v
invalid_repo_path = इस रिपॉजिटरी की मूल पथ मान्य नहीं है: %v
invalid_app_data_path = एप्प की डाटा पथ अमान्य है: %v
run_user_not_match = “यूजर को ऐसे चलाएं” उसरनाम अभी का उसरनाम नहीं है: %s -> %s
internal_token_failed = अंदरूनी टोकन बना नहीं पाए: %v
secret_key_failed = गुप्त चाभी बना नहीं पाए: %v
save_config_failed = संरूपण को सेव नहीं कर पाए: %v
enable_update_checker_helper_forgejo = ये समय-समय पर चेक करेगा फॉरगेजो वर्शन नया है की नहीं TXT DNS रिकॉर्ड से यहाँ release.forgejo.org।
invalid_admin_setting = प्रशासक अकाउंट सेटिंग अमान्य है: %v
invalid_log_root_path = लॉग का रास्ता अमान्य है: %v
no_reply_address = गुप्त ईमेल डोमेन
no_reply_address_helper = डोमेन का नाम उसेर्स के गुप्त ईमेल पते से हैं। जैसे की, जब यूजर “जोई” लॉग करेगा Git पे “जोई@noreply.example.org” अगर गुप्त ईमेल पता पड़ा है “noreply.example.org”।
password_algorithm = पासवर्ड का हैश कलन विधि (algorithm)
invalid_password_algorithm = अमान्य पासवर्ड का हैश कलन विधि (algorithm)
password_algorithm_helper = पासवर्ड हैश अल्गोरिथम सेट करें। अल्गोरिथ्म्स की अलग-अलग ज़रूरतें और मज़बूतियाँ हैं। आर्गन2 अल्गोरिथम मज़बूत है पर मेमोरी ज़्यादा लेता है और छोटे सिस्टम्स के लिए सही नहीं होता।
enable_update_checker = अपडेट चेकर चालू करें
env_config_keys = पर्यावरण संरूपण
env_config_keys_prompt = पर्यावरण वेरिएबल्स को संरूपण फाइल पर भी लागू किया जायेगा:
[home]
uname_holder = उसरनाम या ईमेल एड्रेस
switch_dashboard_context = डैशबोर्ड का सन्दर्भ बदलें
my_repos = रेपोसिटोरिएस
my_orgs = संस्थाएं
view_home = व्यू: %s
filter = बाकी फिल्टर्स
filter_by_team_repositories = फ़िल्टर करें टीम रेपोसिट्रीज़ से
feed_of = फीड इसकी "%s"
show_archived = संग्रहीत
show_both_archived_unarchived = संग्रहीत और असंग्रहीत देखिए
show_only_archived = सिर्फ संग्रहीत देखिए
show_only_unarchived = सिर्फ असंग्रहीत देखिए
show_private = निजी
show_both_private_public = निजी और सार्वजनिक देखिए
show_only_private = सिर्फ निजी देखिए
show_only_public = सिर्फ सार्वजनिक देखिए
issues.in_your_repos = आपकी रेपोसिटोरिस में
[explore]
repos = रेपोसिटोरिस
users = उसेर्स
stars_one = %d सितारा
stars_few = %d सितारे
forks_one = %d फोर्क
forks_few = %d फोर्कस
organizations = संस्थाएं
go_to = जाएं इधर
code = कोड
code_last_indexed_at = आखरी संस्थापित %s
relevant_repositories_tooltip = रेपोसिटोरिस जो की फोर्क्स या जिनका शीर्षक नहीं है, आइकॉन नहीं और विश्लेषण नहीं गुप्त हैं।
relevant_repositories = सिर्फ इस काम की रेपोस्टिरिस दिखाई जा रहीं हैं. <a href="%s">बिना फ़िल्टर के रिजल्ट दिखाएं</a>।
[auth]
create_new_account = अकाउंट रजिस्टर करें
disable_register_prompt = रजिस्ट्रेशन खुला नहीं। कृपया साइट प्रशासक से बात करें।
disable_register_mail = ईमेल से रजिस्ट्रेशन पक्का करना बंद हैं अभी।
manual_activation_only = अपने साइट प्रशासक बात करें एक्टिवेशन पूरा करने के लिए।
remember_me = इस डिवाइस को याद रखें
forgot_password_title = पासवर्ड भूल गए
forgot_password = पासवर्ड भूल गए?
hint_login = पहले से अकाउंट है? <a href="%s">अभी सिग्न इन करें!</a>
hint_register = अकाउंट चाहिए? <a href="%s">रजिस्टर करें</a>
sign_up_button = अभी रजिस्टर करें।
sign_up_successful = अकाउंट बन गया है। स्वागत!
back_to_sign_in = sign in में फिर से जाएं
sign_in_openid = OpenID के साथ आगे बढ़ें
[mail]
link_not_working_do_paste = क्या कड़ी चल नहीं पाई? उसे कॉपी करके URL बार में जोड़ें।
hi_user_x = नमस्ते <b>%s</b>,
activate_account = कृपया अपना खाता चालू करें
activate_account.text_1 = नमस्ते <b>%[1]s</b>, संपादित करने के लिए शुक्रिया यहां %[2]s!
activate_account.text_2 = कृपया यहां की कड़ी को क्लिक करें अपने अकाउंट को चालू करने के लिए <b>%s</b>:
activate_email = अपना ईमेल एड्रेस पुख्ता करें
activate_email.text = कृपया इस कड़ी को क्लिक करें अपना अकाउंट पुख्ता करने के लिए <b>%s</b>:
admin.new_user.subject = नया यूजर %s अभी sign up हुआ
admin.new_user.user_info = यूजर की जानकारी
admin.new_user.text = कृपया <a href="%s"> क्लिक करें यहाँ </a> अपने यूजर को प्रशासक पैनल से चलाने के लिए।
register_notify = स्वागत है %s
register_notify.text_1 = ये आपका रजिस्टर्ड ईमेल पुख्ता करने के लिए है %s!
register_notify.text_2 = आप अपने अकाउंट में हस्ताक्षर कर सकते हैं इस उसर-नाम के साथ: %s
register_notify.text_3 = अगर किसी और ने आपका अकाउंट बनाया है, तो आपको चाहिए <a href="%s"> पासवर्ड संरक्षित करें </a> पहले।
reset_password = अपना अकाउंट निकाल पाएं
reset_password.text = अगर ये आप थे, कृपया इस कड़ी को क्लिक करें अपने अकाउंट को फिर से चालू करने को <b>%s</b>:
password_change.subject = आपका पासवर्ड बदला गया
password_change.text_1 = आपके अकाउंट का पासवर्ड बदला गया है।
primary_mail_change.subject = आपका प्राथमिक ईमेल बदला गया है
primary_mail_change.text_1 = आपका प्राथमिक ईमेल बदला गया है इससे %[1]s. यानी इस ईमेल एड्रेस से आप ईमेल अधिसूचना प्राप्त नहीं कर पाएंगे।
totp_disabled.subject = टीओटीपी रोक दिया गया
totp_disabled.text_1 = समय निर्धारित एक बार के पासवर्ड (टीओटीपी) आपके अकाउंट पर रोक दिया गया।
totp_disabled.no_2fa = और कोई 2FA के तरीके संचालित नहीं हैं, यानी आपके अकाउंट पर 2FA से लॉगिन की ज़रुरत नहीं होगी।
removed_security_key.subject = सुरक्षक चाभी हटाई गई
removed_security_key.text_1 = सुरक्षक चाभी "%[1]s" अभी-अभी आपके अकाउंट से हटाई गई।

View file

@ -786,7 +786,7 @@ add_key=Aggiungi chiave
ssh_desc=Queste chiavi SSH pubbliche sono associate al tuo profilo. Le corrispondenti chiavi private consentono l'accesso completo ai tuoi progetti. Le chiavi SSH che sono state verificate possono essere usate per verificare commit Git firmati tramite SSH.
principal_desc=Queste entità di certificato SSH sono associate al tuo profilo e forniscono l'accesso completo ai tuoi repositori.
gpg_desc=Queste chiavi GPG pubbliche sono associate con il tuo profilo e sono usate per verificare i tuoi commit. Proteggi le tue chiavi private perché permettono di firmare i commit con la tua identità.
ssh_helper=<strong> Hai bisogno di aiuto?</strong> Dài un'occhiata alla guida per<a href="%s">creare le tue chiavi SSH </a> o risolvere quei <a href="%s"> problemi comuni </a> in cui potresti imbatterti utilizzando SSH.
ssh_helper=<strong> Hai bisogno di aiuto?</strong> Dai un'occhiata alla guida per <a href="%s">creare le tue chiavi SSH </a> o risolvere quei <a href="%s"> problemi comuni </a> in cui potresti imbatterti utilizzando SSH.
gpg_helper=<strong>Hai bisogno di aiuto?</strong> Dai un'occhiata alla guida di GitHub <a href="%s">riguardo il GPG</a>.
key_content_ssh_placeholder=Inizia con "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com", o "sk-ssh-ed25519@openssh.com"
key_content_gpg_placeholder=Inizia con "-----BEGIN PGP PUBLIC KEY BLOCK-----"
@ -2845,7 +2845,7 @@ members.membership_visibility=Visibilità appartenenza:
members.public=Visibile
members.public_helper=nascondi
members.private=Nascosto
members.private_helper=rendi visibile
members.private_helper=Rendi visibile
members.member_role=Ruolo del membro:
members.owner=Proprietario
members.member=Membro

View file

@ -1,6 +1,3 @@
[common]
home = Agejdan
username = Isem n useqdac
@ -43,7 +40,7 @@ captcha = CAPČA
passcode = Angal n wadduf
settings = Iɣewwaren
your_profile = Amaɣnu
your_settings = Iɣewwaren
your_settings = Tawila
activities = Irmad
ok = Ih
view = Wali
@ -56,6 +53,19 @@ concept_system_global = Amatu
concept_user_organization = Tuddsa
filter = Imsizdeg
your_starred = S yitran
new_repo.title = Akufi amaynut
new_repo.link = Akufi amaynut
issues = Uguren
preview = Taskant
loading = Aɛebbi…
sign_in = Qqen
sign_out = Senser tuqqna
link_account = Qqen amiḍan
email = Tansa imayl
access_token = Ajuṭu n unekcum
[user]
code = Tangalt
repositories = Ikufan
@ -69,9 +79,20 @@ following.title.one = Yeṭṭafaṛ
following.title.few = Yeṭṭafaṛ
unfollow = Ur ṭṭafar ara
overview = Tamuɣli s umata
[org]
code = Tanglat
org_desc = Aglam
team_desc = Aglam
lower_repositories = Ikufan
settings.options = Tuddsa
settings.website = Asmel web
members.remove = Kkes
teams.settings = Iɣewwaren
[repo]
release.source_code = Tangalt taɣbalut
settings.slack_username = Isem n useqdac
@ -125,6 +146,127 @@ projects.title = Azwel
projects.type.none = Ula yiwen
projects.template.desc = Tamudemt
repo_name = Isem n ukufi
repo_size = Tiddi n ukufi
create_repo = Snulfu-d akufi
mirror_sync = yemtawa
migrate_items_issues = Uguren
star = Rnu-yas itri
no_desc = Ur yesɛi ara aglam
branches = Tiṣeḍwa
issues = Uguren
projects.description = Aglam (d afrayan)
projects.column.edit_title = Isem
projects.column.new_title = Isem
projects.column.color = Ini
projects.open = Ldi
projects.close = Mdel
issues.new.labels = Tibzimin
issues.new.projects = Isenfaren
issues.choose.open_external_link = Ldi
issues.choose.blank = Amezwer
issues.new_label_desc_placeholder = Aglam
issues.filter_label = Tabzimt
issues.filter_project = Asenfar
issues.filter_poster = Ameskar
issues.filter_type = Tawsit
issues.filter_sort = Semyizwer
issues.action_open = Ldi
issues.action_close = Mdel
issues.action_label = Tabzimt
issues.previous = Uzwir
issues.next = Uḍfir
issues.all_title = Akk
issues.draft_title = Arewway
issues.context.edit = Ẓreg
issues.context.delete = Kkes
issues.reopen_issue = Ales tulya
issues.create_comment = Awennit
issues.author = Ameskar
issues.role.owner = Bab-is
issues.role.member = Aɛeggal
issues.role.contributor = Amɛiwen
issues.edit = Ẓreg
issues.cancel = Semmet
issues.save = Sekles
issues.label_title = Isem
issues.label_description = Aglam
issues.label_color = Ini
issues.label_edit = Ẓreg
issues.label_delete = Kkes
issues.label.filter_sort.alphabetically = S ugemmay
issues.delete = Kkes
issues.add_time_cancel = Semmet
issues.add_time_hours = Isragen
issues.due_date_form = yyyy-mm-dd
issues.due_date_form_edit = Ẓreg
issues.dependency.cancel = Semmet
issues.content_history.options = Tixtiṛiyin
pulls.made_using_agit = AGit
milestones.open = Ldi
milestones.close = Mdel
milestones.title = Azwel
milestones.desc = Aglam
milestones.clear = Sfeḍ
milestones.cancel = Semmet
milestones.filter_sort.name = Isem
wiki = Awiki
wiki.page = Asebter
wiki.new_page = Asebter
wiki.cancel = Semmet
wiki.edit_page_button = Ẓreg
wiki.pages = Isebtar
activity = Armud
activity.navbar.contributors = Iwiziwen
activity.overview = Tamuɣli s umata
settings.delete = Kkes akufi-a
settings.confirm_delete = Kkes akufi
settings.slack_color = Ini
all_branches = Akk tiseḍwa
editor.add_file = Rnu afaylu
editor.new_file = Afaylu amaynut
editor.upload_file = Sali afaylu
editor.edit_file = Ẓreg afaylu
editor.edit_this_file = Ẓreg afaylu
editor.delete_this_file = Kkes afaylu
commits.search_branch = Taseṭṭa-a
commits.search_all = Akk tiseḍwa
issues.filter_project_all = Akk isenfaren
issues.due_date_form_remove = Kkes
issues.dependency.remove = Kkes
pulls.filter_branch = Sizdeg taseṭṭa
activity.period.daily = 1 n wass
activity.period.halfweekly = 3 n wussan
activity.period.weekly = 1 n umalas
activity.period.monthly = 1 n wayyur
activity.period.quarterly = 3 n wayyuren
activity.period.semiyearly = 6 n wayyuren
activity.period.yearly = 1 n useggas
activity.git_stats_on_default_branch = Ɣef %s,
settings.site = Asmel web
settings.delete_collaborator = Kkes
settings.webhook.payload = Agbur
settings.event_issue_comment = Iwenniten
settings.event_pull_request_comment = Iwenniten
settings.web_hook_name_gitea = Gitea
settings.web_hook_name_forgejo = Forgejo
settings.web_hook_name_gogs = Gogs
settings.web_hook_name_slack = Slack
settings.web_hook_name_discord = Discord
settings.web_hook_name_dingtalk = DingTalk
settings.web_hook_name_telegram = Telegram
settings.web_hook_name_matrix = Matrix
settings.web_hook_name_feishu_only = Feishu
settings.web_hook_name_packagist = Packagist
settings.title = Azwel
settings.deploy_key_content = Agbur
settings.branches = Tiṣeḍwa
settings.edit_protected_branch = Ẓreg
release.edit = Ẓreg
release.cancel = Semmet
release.downloads = Isidar
[install]
admin_password = Awal n uɛeddi
password = Awal n uɛeddi
@ -135,6 +277,8 @@ db_schema = Azenziɣ
ssl_mode = SSL
path = Abrid
db_name = Isem n taffa n yisefka
[admin]
notices.type_1 = Akufi
packages.repository = Akufi
@ -142,6 +286,26 @@ config.db_user = Isem n useqdac
users.name = Isem n useqdac
emails.filter_sort.name = Isem n useqdac
orgs.name = Isem
repos.name = Isem
packages.name = Isem
auths.name = Isem
config.db_name = Isem
config.mailer_name = Isem
monitor.name = Isem
monitor.queue.name = Isem
notices.desc = Aglam
dashboard = Tafelwit n usenqed
organizations = Tuddsiwin
repositories = Ikufan
config = Tawila
config_summary = Agzul
config_settings = Iɣewwaren
dashboard.statistic = Agzul
users.2fa = 2FA
config.db_ssl_mode = SSL
[search]
code_kind = Nadi tangalt…
search = Nadi…
@ -160,11 +324,19 @@ table_modal.label.columns = Tigejda
link_modal.url = Url
link_modal.description = Aglam
table_modal.placeholder.header = Aqeṛṛu
table_modal.label.rows = Izirigen
buttons.new_table.tooltip = Rnu tafelwit
table_modal.header = Rnu tafelwit
[explore]
code = Tanglat
repos = Ikufan
users = Iseqdacen
organizations = Tuddsiwin
[form]
UserName = Isem n useqdac
Password = Awal n uɛeddi
@ -174,6 +346,12 @@ Content = Agbur
Biography = Tameddurt
Location = Asun
RepoName = Isem n ukufi
TeamName = Isem n terbaɛt
Email = Tansa imayl
Retype = Sentem awal n uɛeddi
[aria]
footer.links = Iseɣwan
footer = Aḍar n usebter
@ -184,14 +362,20 @@ more = Ugar
[startpage]
lightweight = D afessas
license = Aɣbalu yeldin
[home]
my_repos = Ikufan
show_private = Uslig
my_orgs = Tuddsiwin
[auth]
openid_connect_submit = Tuqqna
verify = Senqed
login_userpass = Qqen
[modal]
yes = Ih
no = Uhu
@ -235,3 +419,42 @@ quota.sizes.repos.all = Ikufan
quota.sizes.assets.attachments.all = Imeddayen
quota.sizes.assets.packages.all = Ikemmusen
quota.sizes.wiki = Wiki
appearance = Udem
avatar = Avataṛ
orgs = Tuddsiwin
organization = Tuddsiwin
quota = Amur
location = Asun
comment_type_group_reference = Tamsisɣelt
visibility.private = Uslig
quota.rule.no_limit = War talast
quota.sizes.assets.all = Igburen
update_language = Beddel tutlayt
language.title = Tutlayt tamezwarut
delete_email = Kkes
[packages]
alpine.repository.branches = Tiṣeḍwa
alpine.repository.repositories = Ikufan
arch.version.description = Aglam
settings.link.select = Fren akufi
alpine.repository.architectures = Tisegda
container.images.title = Tugniwin
container.details.platform = Tiɣerɣert
debian.repository.architectures = Tisegda
rpm.repository.architectures = Tisegda
alt.repository.architectures = Tisegda
[actions]
runners.name = Isem
runners.description = Aglam
runners.task_list.repository = Akufi
[projects]
type-2.display_name = Asenfar n ukufi
[error]
network_error = Tuccḍa deg uẓeṭṭa

View file

@ -1249,7 +1249,7 @@ file_history=Vēsture
file_view_source=Skatīt avotu
file_view_rendered=Skatīt atveidojumu
file_view_raw=Apskatīt neapstrādātu
file_permalink=Patstāvīgā saite
file_permalink=Pastāvīgā saite
file_too_large=Datne ir pārāk liela, lai to parādītu.
invisible_runes_header=Šī datne satur neredzamas unikoda rakstzīmes
invisible_runes_description=`Šī datne satur neredzamas unikoda rakstzīmes, kas ir neatšķiramas cilvēkiem, bet dators tās var apstrādāt atšķirīgi. Ja šķiet, ka tas ir ar nolūku, šo brīdinājumu var droši neņemt vērā. Jāizmanto atsoļa taustiņš (Esc), lai atklātu tās.`
@ -1921,7 +1921,7 @@ milestones.filter_sort.least_issues=Vismazāk pieteikumu
signing.will_sign=Šis iesūtījums tiks parakstīts ar atslēgu "%s".
signing.wont_sign.error=Atgadījās kļūda pārbaudot, vai iesūtījums var tikt parakstīts.
signing.wont_sign.nokey=Nav pieejamas atslēgas, ar ko parakstīt šo iesūtījumu.
signing.wont_sign.nokey=Šajā serverī nav atslēgas, ar ko parakstīt šo iesūtījumu.
signing.wont_sign.never=Iesūtījumi nekad netiek parakstīti.
signing.wont_sign.always=Iesūtījumi vienmēr tiek parakstīti.
signing.wont_sign.pubkey=Iesūtījums netiks parakstīts, jo kontam nav piesaistīta publiska atslēga.
@ -2307,7 +2307,7 @@ settings.packagist_api_token=API pilnvara
settings.packagist_package_url=Packagist pakotnes URL
settings.deploy_keys=Izvietošanas atslēgas
settings.add_deploy_key=Pievienot izvietošanas atslēgu
settings.deploy_key_desc=Izvietošanas atslēgām ir lasīšanas piekļuve glabātavai.
settings.deploy_key_desc=Izvietošanas atslēgām var būt tikai lasīšanas vai lasīšanas un rakstīšanas piekļuve glabātavai.
settings.is_writable=Iespējot rakstīšanas piekļuvi
settings.is_writable_info=Atļaut šai izvietošanas atslēgai <strong>aizgādāt</strong> uz glabātavu.
settings.no_deploy_keys=Pagaidām nav nevienas izvietošanas atslēgas.
@ -2364,7 +2364,7 @@ settings.protect_protected_file_patterns=Aizsargāto datņu paraugs (vairākus a
settings.protect_protected_file_patterns_desc=Aizsargātās datnes nav ļauts tiešā veidā mainīt, pat ja lietotājam šajā zarā ir tiesības pievienot, labot vai izdzēst datnes. Vairākus paraugus var atdalīt ar semikolu (";"). Paraugu pieraksts ir skatāms <a href="%[1]s">%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns=Neaizsargāto datņu paraugs (vairākus atdala ar semikolu ";")
settings.protect_unprotected_file_patterns_desc=Neaizsargātās datnes, kuras ir ļauts izmainīt tiešā veidā, apejot aizgādāšanas ierobežojumu, ja lietotājam ir rakstīšanas piekļuve. Vairāki paraugi ir atdalāmi ar semikolu (";"). Paraugu pierakstu skatīt <a href="%[1]s">%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.update_protect_branch_success=Zara aizsargāšanas kārtula "%s" tika atjaunināta.
settings.update_protect_branch_success=Zara aizsargāšanas kārtula “%s” tika atjaunināta.
settings.remove_protected_branch_success=Zara aizsargāšanas kārtula "%s" tika noņemta.
settings.remove_protected_branch_failed=Zara aizsargāšanas kārtulas "%s" noņemšana neizdevās.
settings.protected_branch_deletion=Izdzēst zara aizsargāšanu

View file

@ -178,7 +178,7 @@ contributions_format = {contributions} op {month} {day}, {year}
[editor]
buttons.heading.tooltip = Koptekst toevoegen
buttons.bold.tooltip = Vetgedrukte tekst toevoegen
buttons.bold.tooltip = Vetgedrukte tekst toevoegen (Ctrl+B / ⌘B)
buttons.quote.tooltip = Tekst citeren
buttons.code.tooltip = Code toevoegen
buttons.link.tooltip = Link toevoegen
@ -188,7 +188,7 @@ buttons.mention.tooltip = Vermeld een gebruiker of team
buttons.ref.tooltip = Verwijs naar een issue of pull verzoek
buttons.switch_to_legacy.tooltip = Gebruik in plaats daarvan de oude editor
buttons.enable_monospace_font = Lettertype monospace inschakelen
buttons.italic.tooltip = Schuingedrukte tekst toevoegen
buttons.italic.tooltip = Schuingedrukte tekst toevoegen (Ctrl+I / ⌘I)
buttons.list.task.tooltip = Een lijst met taken toevoegen
buttons.disable_monospace_font = Lettertype monospace uitschakelen
buttons.indent.tooltip = Items één niveau lager plaatsen
@ -2035,7 +2035,7 @@ settings.packagist_api_token=API token
settings.packagist_package_url=Packagist pakket URL
settings.deploy_keys=Deploy sleutels
settings.add_deploy_key=Deploy sleutel toevoegen
settings.deploy_key_desc=Deploy keys hebben alleen-lezen pull-toegang tot de repository.
settings.deploy_key_desc=Deploy sleutels kunnen alleen-lezen of alleen-lezen-schrijftoegang hebben tot de repository.
settings.is_writable=Schrijftoegang inschakelen
settings.is_writable_info=Sta deze deploy toets toe om te <strong>pushen</strong> naar de repository.
settings.no_deploy_keys=Er zijn nog geen deploy sleutels.

View file

@ -158,7 +158,7 @@ new_repo.link = Novo repositório
new_migrate.link = Nova migração
new_org.link = Nova organização
test = Teste
error413 = Você esgotou sua cota.
error413 = Você exauriu sua cota.
copy_path = Copiar caminho
[aria]
@ -222,7 +222,7 @@ platform=Multi-plataforma
lightweight=Leve e rápido
lightweight_desc=Forgejo utiliza poucos recursos e consegue mesmo rodar no barato Raspberry Pi. Economize energia elétrica da sua máquina!
license=Código aberto
license_desc=Está tudo no <a target="_blank" rel="noopener noreferrer" href="%[1]s">Forgejo</a>! Contribua e torne este projeto ainda melhor. Não tenha vergonha de contribuir!
license_desc=Está tudo no <a target="_blank" rel="noopener noreferrer" href="%[1]s">Forgejo</a>! Junte-se a nós e <a target="_blank" rel="noopener noreferrer" href="%[2]s">contribua</a> para tornar este projeto ainda melhor. Não tenha vergonha de contribuir!
install_desc = Apenas <a target="_blank" rel="noopener noreferrer" href="%[1]s">rode o binário</a> para a sua plataforma, execute-o com <a target="_blank" rel="noopener noreferrer" href="%[2]s">Docker</a>, ou obtenha-o <a target="_blank" rel="noopener noreferrer" href="%[3]s">empacotado</a>.
platform_desc = Forgejo roda em sistemas operacionais livres, como Linux e FreeBSD, assim como em diferentes arquiteturas de processador. Escolha o seu sistema preferido!
@ -528,7 +528,7 @@ totp_disabled.subject = A autenticação em dois fatores foi desabilitada
removed_security_key.subject = Uma chave de segurança foi removida
removed_security_key.text_1 = A chave de segurança "%[1]s" foi removida de sua conta.
account_security_caution.text_1 = Caso tenha sido você, este e-mail pode ser ignorado.
totp_enrolled.subject = Você ativou TOTP como método 2FA
totp_enrolled.subject = Ativação de TOTP como método de A2F
totp_disabled.text_1 = A senha de uso único baseada em tempo (TOTP) na sua conta foi desativada.
totp_disabled.no_2fa = Já não existem mais outros métodos de autenticação em dois fatores (2FA) configurados, ou seja, não é mais necessário acessar sua conta com 2FA.
removed_security_key.no_2fa = Já não existem mais outros métodos de autenticação em dois fatores (2FA) configurados, ou seja, não é mais necessário acessar sua conta com 2FA.
@ -644,7 +644,7 @@ email_domain_is_not_allowed = O domínio do endereço de email da conta <b>%s</b
[user]
change_avatar=Altere seu avatar...
change_avatar=Altere seu avatar
joined_on=Inscreveu-se em %s
repositories=Repositórios
activity=Atividade pública
@ -681,7 +681,7 @@ following.title.one = seguindo
followers.title.one = seguidor
followers.title.few = seguidores
public_activity.visibility_hint.self_private = Sua atividade está visível apenas para você e para os administradores da instância. <a href="%s">Configurar</a>.
public_activity.visibility_hint.self_public = Sua atividade está visível para todos, exceto interações em espaços privados. <a href="%s">Configurar</a>.
public_activity.visibility_hint.self_public = Sua atividade está visível para todos, exceto as interações em espaços privados. <a href="%s">Clique para configurar</a>.
public_activity.visibility_hint.admin_public = Sua atividade está visível para todos, mas como um administrador você também pode ver interações em espaços privados.
public_activity.visibility_hint.admin_private = Essa atividade está visível para você porque você é um administrador, mas o usuário dejesa que ela seja mantida em privado.
public_activity.visibility_hint.self_private_profile = Sua atividade só é visível para você e para os administradores do servidor porque seu perfil é privado. <a href="%s">Configurar</a>.
@ -992,7 +992,7 @@ pronouns_unspecified = Não especificado
language.title = Idioma padrão
additional_repo_units_hint = Sugira habilitar unidades de repositório adicionais
additional_repo_units_hint_description = Exibir uma sugestão para "Habilitar mais" em repositórios que não possuem todas as unidades disponíveis habilitadas.
update_hints = Dicas de atualização
update_hints = Atualizar dicas
update_hints_success = As dicas foram atualizadas.
keep_activity_private.description = A sua <a href="%s">atividade pública</a> estará visível apenas para si e para os administradores do servidor.
language.localization_project = Ajude-nos a traduzir Forgejo para o seu idioma! <a href="%s">Mais informações</a>.
@ -1303,14 +1303,14 @@ editor.patch=Aplicar correção
editor.patching=Corrigindo:
editor.fail_to_apply_patch=`Não foi possível aplicar a correção "%s"`
editor.new_patch=Novo patch
editor.commit_message_desc=Adicione uma descrição detalhada (opcional)...
editor.commit_message_desc=Adicione uma descrição detalhada (opcional)
editor.signoff_desc=Adicione um assinado-por-committer no final do log do commit.
editor.commit_directly_to_this_branch=Commit diretamente no branch <strong class="%[2]s">%[1]s</strong>.
editor.create_new_branch=Crie um <strong>novo branch</strong> para este commit e crie um pull request.
editor.create_new_branch_np=Crie um <strong>novo branch</strong> para este commit.
editor.propose_file_change=Propor alteração de arquivo
editor.new_branch_name=Nome do novo branch para este commit
editor.new_branch_name_desc=Novo nome do branch...
editor.new_branch_name_desc=Nome do novo ramo…
editor.cancel=Cancelar
editor.filename_cannot_be_empty=Nome do arquivo não pode ser em branco.
editor.filename_is_invalid=O nome do arquivo é inválido: "%s".
@ -1331,7 +1331,7 @@ editor.fail_to_update_file_summary=Mensagem de erro:
editor.push_rejected_no_message=A alteração foi rejeitada pelo servidor sem uma mensagem. Por favor, verifique os Git hooks .
editor.push_rejected=A alteração foi rejeitada pelo servidor. Por favor, verifique os Git hooks .
editor.push_rejected_summary=Mensagem completa de rejeição:
editor.add_subdir=Adicionar um subdiretório...
editor.add_subdir=Adicionar uma pasta…
editor.unable_to_upload_files=Ocorreu um erro ao enviar arquivos para "%s": %v
editor.upload_file_is_locked=Arquivo "%s" está bloqueado por %s.
editor.upload_files_to_dir=`Enviar arquivos para "%s"`
@ -1523,7 +1523,7 @@ issues.action_assignee_no_select=Sem responsável
issues.action_check=Marcar/Desmarcar
issues.action_check_all=Marcar/Desmarcar todos os itens
issues.opened_by=aberto por <a href="%[2]s">%[3]s</a> %[1]s
pulls.merged_by=por <a href="%[2]s">%[3]s</a> foi aplicado em %[1]s
pulls.merged_by=por <a href="%[2]s">%[3]s</a> foi aplicado %[1]s
pulls.merged_by_fake=por %[2]s foi aplicado %[1]s
issues.closed_by=por <a href="%[2]s">%[3]s</a> foi fechada %[1]s
issues.opened_by_fake=%[1]s abertas por %[2]s
@ -2258,7 +2258,7 @@ settings.packagist_api_token=Token de API
settings.packagist_package_url=URL do pacote do Packagist
settings.deploy_keys=Chaves de deploy
settings.add_deploy_key=Adicionar chave de deploy
settings.deploy_key_desc=As chaves de deploy possuem somente acesso de leitura (pull) ao repositório.
settings.deploy_key_desc=As chaves de deploy podem ter acesso ao repositório somente para leitura ou para leitura e escrita.
settings.is_writable=Habilitar acesso de escrita
settings.is_writable_info=Permitir que esta chave de deploy faça <strong>push</strong> para o repositório.
settings.no_deploy_keys=Não há nenhuma chave de deploy ainda.
@ -2321,7 +2321,7 @@ settings.block_outdated_branch_desc=O merge não será possível quando o branch
settings.default_branch_desc=Selecione um branch padrão para pull requests e commits de código:
settings.merge_style_desc=Estilos de merge
settings.default_merge_style_desc=Estilo de merge padrão
settings.choose_branch=Escolha um branch...
settings.choose_branch=Escolha um ramo…
settings.no_protected_branch=Não há branches protegidos.
settings.edit_protected_branch=Editar
settings.protected_branch_required_rule_name=Nome da regra é obrigatório
@ -2768,8 +2768,8 @@ issues.filter_no_results_placeholder = Tente ajustar seus filtros de pesquisa.
archive.nocomment = Não é possível comentar pois o repositório foi arquivado.
migrate.repo_desc_helper = Deixe em branco para importar a descrição existente
comment.blocked_by_user = Não é possível comentar pois você foi bloqueado pelo dono ou autor do repositório.
sync_fork.branch_behind_few = Este branch está %[1]d commits atrás de %[2]s
sync_fork.branch_behind_one = Este branch está %[1]d commit atrás de %[2]s
sync_fork.branch_behind_few = Este ramo está %[1]d commits atrás de %[2]s
sync_fork.branch_behind_one = Este ramo está %[1]d commit(s) atrás de %[2]s
sync_fork.button = Sincronizar
settings.event_header_action = Eventos de execução de Actions
settings.event_action_failure = Falha
@ -3001,9 +3001,9 @@ dashboard.delete_old_actions.started=A exclusão de todas as atividades antigas
dashboard.update_checker=Verificador de atualização
dashboard.delete_old_system_notices=Excluir todos os avisos de sistema antigos do banco de dados
dashboard.gc_lfs=Coletar lixos dos meta-objetos LFS
dashboard.stop_zombie_tasks=Parar tarefas de actions zumbi
dashboard.stop_endless_tasks=Parar tarefas infinitas de actions
dashboard.cancel_abandoned_jobs=Cancelar trabalhos abandonados de actions
dashboard.stop_zombie_tasks=Parar tarefas de Actions zumbis
dashboard.stop_endless_tasks=Parar tarefas de Actions intermináveis
dashboard.cancel_abandoned_jobs=Cancelar trabalhos abandonados de Actions
users.user_manage_panel=Gerenciar contas de usuário
users.new_account=Criar conta de usuário
@ -3406,7 +3406,7 @@ notices.delete_success=Os avisos do sistema foram excluídos.
identity_access = Identidade e acesso
settings = Configurações de administrador
users.bot = Robô
dashboard.start_schedule_tasks = Iniciar tarefas de actions programadas
dashboard.start_schedule_tasks = Iniciar tarefas de Actions programadas
users.reserved = Reservado
emails.change_email_text = Tem certeza de que deseja atualizar este endereço de e-mail?
self_check = Autoverificação
@ -3817,7 +3817,7 @@ variables.id_not_exist = A variável com o ID %d não existe.
variables.deletion.failed = Falha ao remover a variável.
variables.creation.failed = Falha ao adicionar a variável.
runs.no_results = Nenhum resultado.
variables.description = As variáveis serão passadas para certas ações e não poderão ser lidas de outra forma.
variables.description = As variáveis serão passadas para certas Actions e não poderão ser lidas de outra forma.
workflow.dispatch.trigger_found = Este workflow tem um disparador de evento <c>workflow_dispatch</c>.
workflow.dispatch.run = Executar workflow
runs.no_runs = O workflow ainda não foi executado.

View file

@ -2319,7 +2319,7 @@ settings.packagist_api_token=Código da API
settings.packagist_package_url=URL do pacote Packagist
settings.deploy_keys=Chaves de instalação
settings.add_deploy_key=Adicionar chave de instalação
settings.deploy_key_desc=Chaves de instalação têm acesso para puxar do repositório apenas em modo de leitura.
settings.deploy_key_desc=Chaves de instalação podem ter acesso ao repositório apenas para leitura ou de leitura e escrita.
settings.is_writable=Habilitar acesso de escrita
settings.is_writable_info=Permitir a esta chave de instalação <strong>enviar</strong> para o repositório.
settings.no_deploy_keys=Ainda não existem quaisquer chaves de instalação.

View file

@ -1092,7 +1092,7 @@ mirror_password_placeholder=(Неизменный)
mirror_password_blank_placeholder=(Отменено)
mirror_password_help=Смените имя пользователя для удаления пароля.
watchers=Наблюдатели
stargazers=Звездочеты
stargazers=Добавившие в избранное
stars_remove_warning=Данное действие удалит все звёзды из этого репозитория.
forks=Ответвления
reactions_more=и ещё %d
@ -2271,7 +2271,7 @@ settings.packagist_api_token=Токен API
settings.packagist_package_url=Ссылка на пакет Packagist
settings.deploy_keys=Ключи развёртывания
settings.add_deploy_key=Добавить ключ развёртывания
settings.deploy_key_desc=Ключи развёртывания предоставляют доступ к репозиторию только для чтения.
settings.deploy_key_desc=Ключи развёртывания дают доступ к просмотру репозитория и опционально к отправке изменений.
settings.is_writable=Разрешить запись
settings.is_writable_info=Может ли этот ключ быть использован для выполнения <strong>push</strong> в репозиторий? Ключи развёртывания всегда имеют доступ на pull.
settings.no_deploy_keys=Вы не добавляли ключи развёртывания.

View file

@ -182,8 +182,8 @@ buttons.quote.tooltip = Citera text
buttons.code.tooltip = Lägg till kod
buttons.link.tooltip = Lägg till en länk
buttons.heading.tooltip = Lägg till rubrik
buttons.bold.tooltip = Lägg till fetstilt text
buttons.italic.tooltip = Lägg till kursiv text
buttons.bold.tooltip = Lägg till fetstilt text (CTRL+B / ⌘B)
buttons.italic.tooltip = Lägg till kursiv text (CTRL+I / ⌘I)
buttons.list.unordered.tooltip = Lägg till en punktlista
buttons.list.ordered.tooltip = Lägg till en numrerad lista
buttons.list.task.tooltip = Lägg till en lista med sysslor
@ -203,6 +203,8 @@ buttons.disable_monospace_font = Avaktivera jämnbrett typsnitt
link_modal.paste_reminder = Tips: Med ett URL i ditt klippbord, kan du klistra in direkt i textredigeraren för att skapa en länk.
buttons.enable_monospace_font = Aktivera jämnbrett typsnitt
buttons.indent.tooltip = Inplacera föremål med en nivå
[filter]
string.asc = A - Ö
string.desc = Ö - A
@ -321,7 +323,7 @@ app_slogan_helper = Skriv in din slogan här. Lämna tom för att stänga av.
domain = Serverdomän
domain_helper = Domän eller värdadress för servern.
reinstall_error = Du försöker att installera i en existerande Forgejo-databas
password_algorithm_helper = Ställ in hashalgoritmen för lösenord. Algoritmer har olika krav och styrka. Argon2-algoritmen är ganska säker men använder mycket minne och kan vara olämplig för små system.
password_algorithm_helper = Ställ in hashalgoritmen för lösenord. Algoritmer har olika krav och styrkor. Argon2-algoritmen är ganska säker men använder mycket minne och kan vara olämplig för små system.
config_location_hint = Dessa konfigurationsinställningar kommer att sparas i:
invalid_db_table = Databastabellen "%s" är ogiltig: %v
secret_key_failed = Misslyckades att generera hemlig nyckel: %v
@ -335,6 +337,8 @@ invalid_password_algorithm = Ogiltig hashalgoritm för lösenord
env_config_keys_prompt = Följande miljövariabler kommer också att tillämpas på din konfigurationsfil:
smtp_from_invalid = "Skicka E-post som" adressen är ogiltig
reinstall_confirm_check_1 = Data krypterad av HEMLIG_NYCKEL i app.ini kan gå förlorad: användare kommer kanske inte att kunna logga in med 2FA/OTP & speglar funkar kanske inte korrekt. Genom att kryssa i rutan godkänner du att den nuvarande app.ini innehåller korrekta HEMLIG_NYCKEL.
[home]
uname_holder=Användarnamn eller e-postadress
switch_dashboard_context=Växla visad instrumentpanel
@ -650,7 +654,7 @@ activate_email=Skicka aktivering
activations_pending=Väntar på aktivering
delete_email=Ta Bort
email_deletion=Ta bort e-postadress
email_deletion_desc=Mejladressen och relaterad information kommer tas bort från ditt konto. Git-commits med denna mejladress förblir oförändrade. Vill du fortsätta?
email_deletion_desc=Denna mejladress och relaterad information kommer tas bort från ditt konto. Git-commits med denna mejladress förblir oförändrade. Vill du fortsätta?
email_deletion_success=Mejladressen har tagits bort.
theme_update_success=Ditt tema ändrades.
theme_update_error=Det valda temat finns inte.
@ -822,7 +826,7 @@ license_helper_desc=En licens styr vad andra kan och inte kan göra med din kod.
readme=README
readme_helper=Välj en mall för README-filen
readme_helper_desc=Här kan du skriva en fullständig beskrivning för ditt projekt.
auto_init=Initiera utvecklingskatalog (Lägger till .gitignore, License and README)
auto_init=Initiera utvecklingskatalog
create_repo=Skapa utvecklingskatalog
default_branch=Standardgren
default_branch_helper=Den förvalda grenen är bas-gren för pull requests och kod-commits.
@ -866,7 +870,7 @@ migrate_items_wiki=Wiki
migrate_items_milestones=Milstenar
migrate_items_labels=Etiketter
migrate_items_issues=Ärenden
migrate_items_pullrequests=Pull Requester
migrate_items_pullrequests=Pull-förfrågningar
migrate_items_merge_requests=Begäran om sammanslagning
migrate_items_releases=Releaser
migrate_repo=Migrera utvecklingskatalog
@ -966,7 +970,7 @@ editor.propose_file_change=Föreslå filändring
editor.new_branch_name_desc=Nytt branchnamn…
editor.cancel=Avbryt
editor.filename_cannot_be_empty=Filnamnet kan inte vara tomt.
editor.file_changed_while_editing=Filens innehåll har ändrats sedan du påbörjade din ändring.<a target="_blank" rel="noopener noreferrer" href="%s">Klicka här</a> för att se ändringarna eller <strong>commita ändringarna igen</strong> för att skriva över dem.
editor.file_changed_while_editing=Filens innehåll har ändrats sedan du öppnade filen.<a target="_blank" rel="noopener noreferrer" href="%s">Klicka här</a> för att se ändringarna eller <strong>commita ändringarna igen</strong> för att skriva över dem.
editor.commit_empty_file_header=Committa en tom fil
editor.commit_empty_file_text=Filen du vill committa är tom. Vill du fortsätta?
editor.no_changes_to_show=Det finns inga ändringar att visa.
@ -1062,7 +1066,7 @@ issues.remove_self_assignment=`tog bort sin tilldelning %s`
issues.change_title_at='ändrade titeln från <b><strike>%s</strike></b> till <b>%s</b> %s'
issues.delete_branch_at='tog bort grenen <b>%s</b> %s'
issues.filter_label=Etikett
issues.filter_label_exclude=`Använd <code>alt</code> + <code>klicka/enter</code> för att exkludera etiketter`
issues.filter_label_exclude=Använd <kbd>Alt</kbd> + <kbd>klicka/enter</kbd> för att exkludera etiketter
issues.filter_label_no_select=Alla etiketter
issues.filter_milestone=Milsten
issues.filter_project_none=Inget projekt
@ -2247,7 +2251,7 @@ team_kind = Sök team…
org_kind = Sök organisationer…
issue_kind = Sök ärenden…
regexp_tooltip = Tolka söktermen som ett reguljärt uttryck
code_search_unavailable = Kodsökning är för närvarande inte tillgänglig. Vänligen kontakta webbplatsadministratören.
code_search_unavailable = Kodsökning är för närvarande inte tillgängligt. Vänligen kontakta webbplatsadministratören.
fuzzy_tooltip = Inkludera resultat som är närliggande till söktermen
no_results = Inga matchande resultat hittades.
fuzzy = Ungefärlig

View file

@ -7,9 +7,9 @@ logo=Logo
sign_in=Giriş Yap
sign_in_with_provider=%s ile oturum aç
sign_in_or=veya
sign_out=Çıkış Yap
sign_out=Çıkış yap
sign_up=Kaydol
link_account=Bağlantı hesabı
link_account=Hesap Bağla
register=Üye Ol
version=Sürüm
powered_by=%s tarafından desteklenen
@ -159,7 +159,7 @@ toggle_menu = Menüyü aç-kapa
new_migrate.title = Yeni göç
new_migrate.link = Yeni göç
copy_path = Dizini kopyala
confirm_delete_artifact = "%s" adlı öğeyi silmek istediğinizden emin misiniz?
confirm_delete_artifact = "%s" adlı öğeyi silmek istediğinize emin misiniz?
[aria]
navbar=Gezinti çubuğu
@ -178,8 +178,8 @@ contributions_format = {day} {month} {year} tarihinde {contributions} katkı
[editor]
buttons.heading.tooltip=Başlık ekle
buttons.bold.tooltip=Kalın metin ekle
buttons.italic.tooltip=Eğik metin ekle
buttons.bold.tooltip=Kalın metin ekle (Ctrl+B / ⌘B)
buttons.italic.tooltip=Eğik metin ekle (Ctrl+I / ⌘I)
buttons.quote.tooltip=Metni alıntıla
buttons.code.tooltip=Kod ekle
buttons.link.tooltip=Bağlantı ekle
@ -228,7 +228,7 @@ platform_desc = Forgejo'nun Linux ve FreeBSD gibi özgür işletim sistemlerinde
[install]
install=Kurulum
title=Başlangıç Yapılandırması
title=Başlangıç yapılandırması
docker_helper=Eğer Forgejo'yı Docker içerisinde çalıştırıyorsanız, lütfen herhangi bir değişiklik yapmadan önce <a target="_blank" rel="noopener noreferrer" href="%s">belgeleri</a> okuyun.
require_db_desc=Forgejo MySQL, PostgreSQL, SQLite3 veya TiDB (MySQL protokolü) gerektirir.
db_title=Veritabanı ayarları
@ -257,7 +257,7 @@ err_admin_name_is_invalid=Yönetici kullanıcı adı geçersiz
general_title=Genel ayarlar
app_name=Site Başlığı
app_name_helper=Şirket adınızı buraya girebilirsiniz.
app_name_helper=Buraya örnek adınızı girin. Her sayfada görüntülenecektir.
repo_path=Depo kök dizini
repo_path_helper=Tüm uzak Git depoları bu dizine kaydedilecektir.
lfs_path=Git LFS kök dizini
@ -266,9 +266,9 @@ run_user=Şu Kullanıcı Olarak Çalıştır
run_user_helper=Forgejo'nin çalışacağı işletim sistemi kullanıcı adı. Bu kullanıcının depo kök yoluna erişiminin olması gerektiğini unutmayın.
domain=Sunucu alan adı
domain_helper=Sunucu için alan adı veya ana bilgisayar adresi.
ssh_port=SSH Sunucu Portu
ssh_port_helper=SSH sunucusunun dinleyeceği port numarası. Etkisizleştimek için boş bırakın.
http_port=Forgejo HTTP Dinleme Portu
ssh_port=SSH sunucu portu
ssh_port_helper=SSH sunucusunun dinleyeceği port numarası. SSH sunucusunu devre dışı bırakmak için boş bırakın.
http_port=HTTP dinleme portu
http_port_helper=Forgejo'nın web sunucusunun dinleyeceği port numarası.
app_url=Forgejo Kök URL
app_url_helper=HTTP(S) kopyalama URL'leri ve e-posta bildirimleri için temel adres.

View file

@ -473,7 +473,7 @@ register_notify.text_2=Ви можете ввійти до свого облік
register_notify.text_3=Якщо цей обліковий запис було створено не вами, будь ласка, спочатку <a href="%s">встановіть свій пароль</a>.
reset_password=Відновлення вашого облікового запису
reset_password.text=Перейдіть за цим посиланням, щоб відновити свій обліковий запис в <b>%s</b>:
reset_password.text=Перейдіть за посиланням (маєте <b>%s</b>), щоб відновити обліковий запис:
register_success=Реєстрація успішна
@ -776,7 +776,7 @@ ssh_desc=Ці відкриті ключі SSH повʼязані з вашим
principal_desc=Ці настройки SSH сертифікатів вказані у вашому обліковому записі та надають повний доступ до ваших репозиторіїв.
gpg_desc=Ці відкриті ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом.
ssh_helper=<strong>Потрібна допомога?</strong> Дивіться гід на GitHub з <a href="%s"> генерації ключів SSH</a> або виправлення <a href="%s">типових неполадок SSH</a>.
gpg_helper=<strong> Потрібна допомога? </strong> Перегляньте посібник GitHub <a href="%s"> про GPG </a>.
gpg_helper=<strong>Потрібна допомога?</strong> Перегляньте посібник <a href="%s">про GPG</a>.
key_content_ssh_placeholder=Починається з «ssh-ed25519», «ssh-rsa», «ecdsa-sha2-nistp256», «ecdsa-sha2-nistp384», «ecdsa-sha2-nistp521», «sk-ecdsa-sha2-nistp256@openssh.com» або «sk-ssh-ed25519@openssh.com»
key_content_gpg_placeholder=Починається з «-----BEGIN PGP PUBLIC KEY BLOCK-----»
add_new_principal=Додати принципал
@ -1030,7 +1030,7 @@ ssh_token_help_ssh_agent = або, якщо ви використовуєте а
[repo]
owner=Власник
owner_helper=Деякі організації можуть не відображатися у випадаючому списку через максимальну кількість репозиторііїв.
owner_helper=Деякі організації можуть не відображатися у випадаючому списку через обмеження на максимальну кількість репозиторіїв.
repo_name=Назва репозиторію
repo_name_helper=Хороші назви репозиторіїв використовують короткі, унікальні ключові слова що легко запам'ятати.
repo_size=Розмір репозиторію
@ -1039,7 +1039,7 @@ template_select=Виберіть шаблон
template_helper=Зробити репозиторій шаблоном
template_description=Шаблонні репозиторії дозволяють користувачам генерувати нові репозиторії із такою ж структурою директорій, файлами та додатковими налаштуваннями.
visibility=Видимість
visibility_description=Тільки власник або члени організації які мають віповідні права, зможуть побачити.
visibility_description=Тільки власник або члени організації, які мають відповідні права, зможуть побачити.
visibility_helper_forced=Адміністратор вашого сайту налаштував параметри: всі нові репозиторії будуть приватними.
visibility_fork_helper=(Буде змінено видимість усіх форків.)
clone_helper=Потрібна допомога у клонуванні? Відвідайте сторінку <a target="_blank" rel="noopener" href="%s">Допомога</a>.
@ -1384,7 +1384,7 @@ issues.filter_assignee=Виконавець
issues.filter_assginee_no_select=Всі виконавці
issues.filter_assginee_no_assignee=Немає виконавця
issues.filter_type=Тип
issues.filter_type.all_issues=Всі задачі
issues.filter_type.all_issues=Усі задачі
issues.filter_type.assigned_to_you=Призначене вам
issues.filter_type.created_by_you=Створено вами
issues.filter_type.mentioning_you=Вас згадано
@ -1508,7 +1508,7 @@ issues.add_time_minutes=Хвилини
issues.add_time_sum_to_small=Час не введено.
issues.time_spent_total=Загальний витрачений час
issues.time_spent_from_all_authors=`Загальний витрачений час: %s`
issues.due_date=Дата завершення
issues.due_date=Термін виконання
issues.push_commit_1=додає %d коміт %s
issues.push_commits_n=додає %d коміти(-ів) %s
issues.force_push_codes=`примусово залито %[1]s з <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> %[8]s до <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[9]s %[6]s`
@ -1517,10 +1517,10 @@ issues.due_date_form=рррр-мм-дд
issues.due_date_form_edit=Редагувати
issues.due_date_form_remove=Видалити
issues.due_date_not_set=Термін виконання не встановлено.
issues.due_date_added=додав(ла) дату завершення %s %s
issues.due_date_remove=видалив(ла) дату завершення %s %s
issues.due_date_added=додає термін виконання %s %s
issues.due_date_remove=видаляє термін виконання %s %s
issues.due_date_overdue=Прострочено
issues.due_date_invalid=Термін дії недійсний або знаходиться за межами допустимого діапазону. Будь ласка, використовуйте формат «рррр-мм-дд».
issues.due_date_invalid=Термін виконання недійсний або знаходиться за межами допустимого діапазону. Будь ласка, використовуйте формат «рррр-мм-дд».
issues.dependency.title=Залежності
issues.dependency.add=Додати залежність…
issues.dependency.cancel=Відмінити
@ -1548,17 +1548,17 @@ issues.dependency.add_error_cannot_create_circular=Ви не можете ств
issues.dependency.add_error_dep_not_same_repo=Обидві задачі повинні бути в одному репозиторії.
issues.review.self.approval=Ви не можете схвалити власний пулл-реквест.
issues.review.self.rejection=Ви не можете надіслати запит на зміну на власний пулл-реквест.
issues.review.approve=зміни затверджено %s
issues.review.comment=рецензовано %s
issues.review.dismissed=відхилено відгук %s %s
issues.review.approve=схвалює зміни %s
issues.review.comment=надає відгук %s
issues.review.dismissed=відхиляє відгук %s %s
issues.review.dismissed_label=Відхилено
issues.review.left_comment=додав коментар
issues.review.content.empty=Запрошуючи зміни, ви зобов'язані залишити коментар з поясненнями своїх побажань відносно Pull Request'а.
issues.review.reject=зробив запит змін %s
issues.review.reject=запитує зміни %s
issues.review.wait=попросив рецензію %s
issues.review.add_review_request=попросив рецензію від %s %s
issues.review.remove_review_request=видалив запит на рецензію до %s %s
issues.review.remove_review_request_self=відмовився рецензувати %s
issues.review.add_review_request=запитує відгук від %[1]s %[2]s
issues.review.remove_review_request=видаляє запит на відгук від %[1]s %[2]s
issues.review.remove_review_request_self=відмовляється рецензувати %s
issues.review.pending=Очікування
issues.review.reviewers=Рецензенти
issues.review.outdated=Застарілі
@ -1631,7 +1631,7 @@ pulls.no_merge_desc=Цей запити на злиття неможливо з
pulls.no_merge_helper=Увімкніть параметри злиття в налаштуваннях репозиторія або злийте запити на злиття вручну.
pulls.no_merge_wip=Цей пулл-реквест не можливо об'єднати, тому-що він вже виконується.
pulls.no_merge_not_ready=Цей запит не готовий до злиття, перевірте статус рецензіювання і статус перевірки.
pulls.no_merge_access=Ви не авторизовані, щоб виконати цей запит на злиття.
pulls.no_merge_access=У вас нема дозволу злити цей запит.
pulls.merge_pull_request=Створити коміт зі злиттям
pulls.rebase_merge_pull_request=Перебазувати, а потім виконати злиття перемотуванням
pulls.rebase_merge_commit_pull_request=Перебазувати, а потім створити коміт злиття
@ -1679,7 +1679,7 @@ milestones.completeness=%d%% завершено
milestones.create=Створити етап
milestones.title=Заголовок
milestones.desc=Опис
milestones.due_date=Дата завершення (опціонально)
milestones.due_date=Термін виконання (необов'язково)
milestones.clear=Очистити
milestones.invalid_due_date_format=Термін виконання має бути у форматі «рррр-мм-дд».
milestones.edit=Редагувати етап
@ -2406,7 +2406,7 @@ already_forked = Ви вже створили форк %s
fork_to_different_account = Створити форк до іншого облікового запису
fork_no_valid_owners = Неможливо створити форк цього репозиторію, оскільки тут немає дійсних власників.
pulls.agit_explanation = Створено через робочий потік AGit. AGit дозволяє учасникам пропонувати зміни за допомогою «git push» без створення форку або нової гілки.
diff.review.self_approve = Автори запитів на злиття не можуть схвалювати власні запити на злиття
diff.review.self_approve = Автори запитів на злиття не можуть схвалювати власні запити
settings.event_pull_request_approvals = Схвалення запитів на злиття
diff.git-notes.add = Додати примітку
diff.git-notes.remove-header = Видалити примітку
@ -2695,7 +2695,7 @@ issues.reaction.alt_many = %[1]s і ще %[2]d реагують %[3]s.
settings.pulls.default_allow_edits_from_maintainers = За замовчуванням дозволити редагування від супроводжувачів
pulls.blocked_by_changed_protected_files_1 = Цей запит на злиття заблоковано, оскільки він змінює захищений файл:
pulls.delete.text = Ви дійсно хочете видалити цей запит на злиття? (Весь його вміст буде остаточно видалено. Можливо, варто його закрити і зберегти в архіві)
pulls.blocked_by_rejection = Цей запит на злиття містить зміни, запропоновані офіційним рецензентом.
pulls.blocked_by_rejection = Цей запит на злиття містить запит змін від офіційного рецензента.
signing.wont_sign.parentsigned = Цей коміт не буде підписано, оскільки не підписано батьківський коміт.
settings.ignore_stale_approvals = Ігнорувати застарілі схвалення
pulls.blocked_by_approvals = Цей запит на злиття ще не має достатньої кількості схвалень. Отримано %d з %d схвалень.
@ -2738,6 +2738,20 @@ tree_path_not_found.tag = Шлях %[1]s не існує в тегу %[2]s
tree_path_not_found.branch = Шлях %[1]s не існує в гілці %[2]s
pulls.recently_pushed_new_branches = Ви надіслали зміни до гілки <a href="%[3]s"><strong>%[1]s</strong></a> %[2]s
issues.due_date_modified = змінює термін виконання з %[2]s на %[1]s %[3]s
milestones.filter_sort.earliest_due_data = Найближчий термін виконання
milestones.filter_sort.latest_due_date = Найдальший термін виконання
settings.mirror_settings.pushed_repository = Цільовий репозиторій
issues.label_exclusive_desc = Назвіть мітку <code>область/елемент</code>, щоб зробити її взаємовиключною з іншими мітками <code>область/</code>.
issues.label_exclusive_warning = Усі конфліктні мітки будуть видалені під час редагування міток задачі або запиту на злиття.
issues.review.add_review_requests = запитує відгуки від %[1]s %[2]s
issues.review.remove_review_requests = видаляє запити на відгук від %[1]s %[2]s
issues.review.add_remove_review_requests = запитує відгуки від %[1]s і видаляє запити на відгук від %[2]s %[3]s
settings.ignore_stale_approvals_desc = Не враховувати схвалення, зроблені для старих комітів (застарілі відгуки), до кількості схвалень, які має запит на злиття. Цей параметр не діє, якщо застарілі відгуки вже відхилено.
diff.review.self_reject = Автори запитів на злиття не можуть запитувати зміни у власних запитах
[graphs]
contributors.what = внески
component_loading_info = Це може зайняти деякий час…

View file

@ -173,8 +173,8 @@ contributions_zero=没有贡献
less=较少
more=较多
contributions_format = {year}年{month}{day}日有{contributions}
contributions_few = 贡献
contributions_one = 贡献
contributions_few = 贡献
contributions_one = 贡献
[editor]
buttons.heading.tooltip=添加标题
@ -328,7 +328,7 @@ default_allow_create_organization=默认情况下允许创建组织
default_allow_create_organization.description=默认允许新用户创建组织。禁用此选项时,管理员必须向新用户授予创建组织的权限。
default_enable_timetracking=默认情况下启用时间跟踪
default_enable_timetracking.description=默认允许新仓库使用时间跟踪功能。
no_reply_address=隐藏电子邮件
no_reply_address=隐藏电子邮件域名
no_reply_address_helper=用于设置隐藏电子邮件地址的用户使用的电子邮件域名。例如如果用于隐藏电子邮件地址的域名设为“noreply.example.org”则用户名 “joe” 在 Git 中将以 “joe@noreply.example.org” 表示。
password_algorithm=密码哈希算法
invalid_password_algorithm=无效的密码哈希算法
@ -445,8 +445,8 @@ password_pwned=此密码出现在 <a target="_blank" rel="noopener noreferrer" h
password_pwned_err=无法完成对 HaveIBeenPwned 的请求
last_admin=您不能删除最后一个管理员。必须至少保留一个管理员。
change_unconfirmed_email = 如果您在注册时提供了错误的邮箱地址,您可以在下方修改,激活邮件会发送到修改后的邮箱地址。
change_unconfirmed_email_summary = 修改用来接收激活邮件的邮箱地址。
change_unconfirmed_email_error = 无法修改邮箱地址:%v
change_unconfirmed_email_summary = 更改接收激活邮件的电子邮件地址。
change_unconfirmed_email_error = 无法更改电子邮件地址:%v
hint_login = 已经有账户了吗?<a href="%s">立即登录!</a>
back_to_sign_in = 返回登录
sign_in_openid = 继续使用 OpenID
@ -762,7 +762,7 @@ password_incorrect=当前密码不正确。
change_password_success=您的密码已更新。从现在开始请使用您的新密码登录。
password_change_disabled=非本地帐户不能通过 Forgejo 的 web 界面更改密码。
manage_emails=管理邮箱地址
manage_emails=管理电子邮件地址
manage_themes=默认主题
manage_openid=OpenID 地址
email_desc=您的主要电子邮件地址将用于通知、密码恢复基于网页界面的Git操作只要它不是设置为隐藏的
@ -783,15 +783,15 @@ theme_update_error=所选主题不存在。
openid_deletion=移除 OpenID 地址
openid_deletion_desc=删除此 OpenID 地址将会阻止你使用它进行登录。你确定要继续吗?
openid_deletion_success=OpenID地址已被移除。
add_new_email=添加邮箱地址
add_new_email=添加电子邮件地址
add_new_openid=添加新的 OpenID URI
add_email=加电子邮件地址
add_email=加电子邮件地址
add_openid=添加 OpenID URI
add_email_confirmation_sent=确认邮件已发送至“%s”。请检查您的收件箱并在接下来的 %s 内点击提供的链接以确认您的电子邮件地址。
add_email_success=新的电子邮件地址已添加。
email_preference_set_success=电子邮件首选项已成功设置。
add_openid_success=新的 OpenID 地址已添加。
keep_email_private=隐藏邮箱地址
keep_email_private=隐藏电子邮件地址
keep_email_private_popup=您的邮件地址不会在个人资料中显示,也不会成为通过网页界面提交的默认地址,例如文件上传、编辑和合并提交。相反,可以使用特殊地址 %s 将提交链接到您的账号。此选项不会影响现有提交。
openid_desc=OpenID 让你可以将认证转发到外部服务。
@ -1670,7 +1670,7 @@ issues.push_commit_1=于 %[2]s 推送了 %[1]d 个提交
issues.push_commits_n=于 %[2]s 推送了 %[1]d 个提交
issues.force_push_codes=`于 %[6]s 强制推送 %[1]s从 <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> %[8]s至 <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[9]s`
issues.force_push_compare=比较
issues.due_date_form=yyyy-mm-dd
issues.due_date_form=yyyy年mm月dd日
issues.due_date_form_edit=编辑
issues.due_date_form_remove=删除
issues.due_date_not_set=未设置到期时间。
@ -2315,7 +2315,7 @@ settings.packagist_api_token=API 令牌
settings.packagist_package_url=Packagist 软件包 URL
settings.deploy_keys=部署密钥
settings.add_deploy_key=添加部署密钥
settings.deploy_key_desc=部署密钥具有对仓库的只读拉取权限。
settings.deploy_key_desc=部署密钥对仓库可具有只读或读写访问权限。
settings.is_writable=启用写权限
settings.is_writable_info=允许此部署密钥 <strong>推送</strong> 提交到仓库。
settings.no_deploy_keys=没有部署密钥。
@ -3298,7 +3298,7 @@ config.enable_timetracking=启用时间跟踪
config.default_enable_timetracking=默认情况下启用时间跟踪
config.allow_dots_in_usernames = 允许用户在用户名中使用英文句号。不影响已有的帐户。
config.default_allow_only_contributors_to_track_time=仅允许成员跟踪时间
config.no_reply_address=隐藏电子邮件域名
config.no_reply_address=隐藏电子邮件域名
config.default_visibility_organization=新组织的默认可见性
config.default_enable_dependencies=默认情况下启用议题依赖
@ -3320,7 +3320,7 @@ config.mailer_sendmail_path=Sendmail 路径
config.mailer_sendmail_args=Sendmail 的额外参数
config.mailer_sendmail_timeout=Sendmail 超时
config.mailer_use_dummy=Dummy
config.test_email_placeholder=电子邮址(例如test@example.com
config.test_email_placeholder=电子邮址(例如test@example.com
config.send_test_mail=发送测试邮件
config.send_test_mail_submit=发送
config.test_mail_failed=发送测试邮件至 "%s" 时失败:%v

View file

@ -74,7 +74,7 @@
"keys.ssh.link": "Claus SSH",
"keys.gpg.link": "Claus GPG",
"admin.config.moderation_config": "Configuració de la moderació",
"admin.moderation.reports": "Informes",
"admin.moderation.reports": "Denúncies",
"admin.moderation.moderation_reports": "Informes de moderació",
"admin.moderation.no_open_reports": "No hi ha cap denúncia oberta.",
"moderation.report_abuse": "Denunciar abús",
@ -133,7 +133,7 @@
"admin.config.global_2fa_requirement.admin": "Administradors",
"settings.visibility.description": "La visibilitat del perfil afecta la capacitat dels altres per accedir als vostres repositoris no privats. <a href=\"%s\" target=\"_blank\">Saber-ne més</a>.",
"settings.twofa_unroll_unavailable": "El vostre compte requereix l'autenticació de doble factor i no es pot desactivar.",
"settings.twofa_reenroll.description": "Torneu-vos a inscriure a l'autenticació de doble factor",
"settings.twofa_reenroll.description": "Torneu-vos a inscriure a la vostra autenticació de doble factor",
"settings.must_enable_2fa": "Aquesta instància de Forgejo requereix que els usuaris habilitin l'autenticació de doble factor abans de poder accedir als seus comptes.",
"error.must_enable_2fa": "Aquesta instància de Forgejo requereix que els usuaris habilitin l'autenticació de doble factor abans de poder accedir als seus comptes. Habiliteu-la a: %s",
"avatar.constraints_hint": "Els avatars personalitzats no poden pesar més de %[1]s ni tenir una dimensió superior a %[2]dx%[3]d píxels",
@ -143,7 +143,24 @@
"migrate.pagure.incorrect_url": "S'ha proporcionat una URL del repositori font incorrecta",
"migrate.pagure.project_url": "URL del projecte de Pagure",
"migrate.pagure.project_example": "L'URL del projecte de pagure, per exemple: https://pagure.io/pagure",
"migrate.pagure.token_label": "Testimoni",
"migrate.pagure.token_label": "Testimoni de l'API de Pagure",
"migrate.pagure.token_body_a": "Proporcioneu un testimoni d'API de Pagure amb accés a les incidències privades per crear un repositori que contingui només les incidències privades",
"meta.last_line": "Molt estudiar anglès i vinga a fer espíquings i ràitings i tal... I què passa amb el català? Eh?! Me'l noto una mica oxidat! També és important."
"meta.last_line": "Molt estudiar anglès i vinga a fer espíquings i ràitings i tal... I què passa amb el català? Eh?! Me'l noto una mica oxidat! També és important.",
"relativetime.future": "en el futur",
"admin.dashboard.cleanup_offline_runners": "Neteja els executors fora de línia",
"settings.twofa_reenroll": "Torneu-vos a inscriure a l'autenticació de doble factor",
"user.ghost.tooltip": "Aquest usuari ha sigut eliminat, o no es pot trobar.",
"migrate.pagure.private_issues.summary": "Incidències privades (opcional)",
"migrate.pagure.private_issues.description": "Aquesta funció està pensada per crear un segon repositori amb només les incidències privades del vostre projecte de Pragure amb la finalitat d'arxivar-lo. Primer, feu una migració normal (sense cap testimoni) per importar tot el contingut públic. Després, si teniu incidències privades que voleu preservar, creeu un repositori separat emprant aquesta opció de testimoni per arxivar-les.",
"migrate.pagure.private_issues.warning": "Assegureu-vos de configurar la visibilitat del repositori com a privada si esteu usant la clau API per importar incidències privades. Això evitarà exposar accidentalment el contingut privat en un repositori públic.",
"migrate.pagure.token.placeholder": "Només per crear un arxiu d'incidències privades",
"actions.runs.run_attempt_label": "Intent d'execució #%[1]s (%[2]s)",
"actions.runs.viewing_out_of_date_run": "Esteu veient una execució desactualitzada d'aquesta tasca que va ser executada %[1]s.",
"actions.runs.view_most_recent_run": "Veure l'execució més recent",
"actions.workflow.job_parsing_error": "No s'han pogut analitzar les tasques al flux de treball: %v",
"actions.workflow.pre_execution_error": "No s'ha executat el flux de treball degut a un error que ha bloquejat l'intent d'execució.",
"stars.list.none": "Ningú ha marcat aquest repositori com a favorit.",
"mail.actions.run_info_trigger": "Activat per: %[1]s per: %[2]s",
"og.repo.summary_card.alt_description": "La targeta de resum del repositori %[1]s, descrita com a: %[2]s",
"actions.workflow.event_detection_error": "No s'han pogut analitzar els esdeveniments compatibles al flux de treball: %v"
}

View file

@ -130,7 +130,7 @@
"migrate.pagure.incorrect_url": "Byla zadána nesprávná adresa URL zdrojového repozitáře",
"migrate.pagure.project_url": "Adresa projektu Pagure",
"migrate.pagure.project_example": "Adresa URL projektu Pagure, např. https://pagure.io/pagure",
"migrate.pagure.token_label": "Token",
"migrate.pagure.token_label": "Token API Pagure",
"migrate.pagure.token_body_a": "Zadejte token API Pagure s přístupem k soukromým problémům pro vytvoření repozitáře obsahujícího pouze soukromé problémy",
"migrate.pagure.token_body_b": "Nezapomeňte nastavit repozitář jako soukromý výše, pokud chcete, aby byl repozitář soukromý",
"migrate.github.description": "Migrovat data z github.com nebo serveru GitHub Enterprise.",
@ -156,5 +156,12 @@
"migrate.form.error.url_credentials": "Adresa obsahuje údaje, zadejte je do odpovídajících polí uživatelského jména a hesla",
"actions.runs.viewing_out_of_date_run": "Prohlížíte si zastaralý běh této úlohy, který byl spuštěn %[1]s.",
"actions.runs.run_attempt_label": "Pokus o běh #%[1]s (%[2]s)",
"actions.runs.view_most_recent_run": "Zobrazit nejnovější běh"
"actions.runs.view_most_recent_run": "Zobrazit nejnovější běh",
"migrate.pagure.private_issues.summary": "Soukromé problémy (volitelné)",
"migrate.pagure.private_issues.description": "Tato funkce je navržená pro vytvoření druhého repozitáře obsahujícího pouze soukromé problémy z vašeho projektu Pagure pro účely archivace. Nejprve proveďte normální migraci (bez tokenu) pro importování veškerého veřejného obsahu. Poté, pokud máte soukromé problémy k uchování, vytvořte oddělený repozitář pomocí této možnosti tokenu k archivování daných soukromých problémů.",
"migrate.pagure.private_issues.warning": "Ujistěte se, že je viditelnost repozitáře výše nastavena na Soukromou, pokud používáte klíč API k importování soukromých problémů. Tím zabráníte nechtěnému zveřejněný soukromého obsahu ve veřejném repozitáři.",
"migrate.pagure.token.placeholder": "Pouze pro vytvoření archivu soukromých problémů",
"actions.workflow.job_parsing_error": "Nepodařilo se zpracovat úlohy ve workflow: %v",
"actions.workflow.event_detection_error": "Nepodařilo se zpracovat podporované události ve workflow: %v",
"actions.workflow.pre_execution_error": "Workflow nebyl vykonán z důvodu chyby, která zablokovala pokus o vykonání."
}

View file

@ -132,7 +132,7 @@
"migrate.pagure.token_body_a": "Stellen Sie ein Pagure-API-Token mit Zugriff auf die privaten Issues zur Verfügung, um ein Repository mit nur den privaten Issues darin zu erstellen",
"migrate.pagure.project_example": "Die URL des Pagure-Projekts, z. B. https://pagure.io/pagure",
"migrate.pagure.description": "Daten von pagure.io oder anderen Pagure-Instanzen migrieren.",
"migrate.pagure.token_label": "Token",
"migrate.pagure.token_label": "Pagure-API-Token",
"migrate.pagure.project_url": "Pagure-Projekt-URL",
"admin.config.security": "Sicherheitskonfiguration",
"settings.twofa_unroll_unavailable": "Für deinen Account wird die Zwei-Faktor-Authentifizierung benötigt; sie kann nicht deaktiviert werden.",
@ -148,5 +148,12 @@
"actions.runs.view_most_recent_run": "Letzten Run betrachten",
"actions.runs.viewing_out_of_date_run": "Sie sehen einen veralteten Run dieses Jobs, der %[1]s ausgeführt wurde.",
"actions.runs.run_attempt_label": "Run-Versuch #%[1]s (%[2]s)",
"migrate.form.error.url_credentials": "Die URL enthält Anmeldedaten; diese sollten in das jeweilige Benutzername- und Passwortfeld eingetragen werden"
"migrate.form.error.url_credentials": "Die URL enthält Anmeldedaten; diese sollten in das jeweilige Benutzername- und Passwortfeld eingetragen werden",
"migrate.pagure.private_issues.summary": "Private Issues (optional)",
"migrate.pagure.private_issues.description": "Dieses Feature wurde dafür entworfen, um ein zweites Repository zu erstellen, das nur private Issues von deinem Pagure-Projekt für Archivierungszwecke enthält. Nimm zuerst eine normale Migration (ohne ein Token) vor, um alle öffentliche Inhalte zu importieren. Anschließend, wenn du private Issues hast, die du aufbewahren willst, erstelle mit diesem Token ein separates Repository, um diese privaten Issues zu archivieren.",
"migrate.pagure.private_issues.warning": "Stell sicher, dass du die Repository-Sichtbarkeit oben auf Privat stellst, falls du den API-Schlüssel benutzt, um private Issues zu importieren. Damit verhindest du, dass du aus Versehen private Inhalte in einem öffentlichen Repository offenlegst.",
"migrate.pagure.token.placeholder": "Nur für die Erstellung eines privaten Issue-Archivs",
"actions.workflow.job_parsing_error": "Jobs im Workflow konnten nicht geparst werden: %v",
"actions.workflow.event_detection_error": "Unterstützte Ereignisse im Workflow konnten nicht geparst werden: %v",
"actions.workflow.pre_execution_error": "Der Workflow wurde aufgrund eines Fehlers, der den Ausführungsversuch blockierte, nicht ausgeführt."
}

View file

@ -142,11 +142,16 @@
"migrate.pagure.incorrect_url": "Incorrect source repository URL has been provided",
"migrate.pagure.project_url": "Pagure project URL",
"migrate.pagure.project_example": "The Pagure project URL, e.g. https://pagure.io/pagure",
"migrate.pagure.token_label": "Token",
"migrate.pagure.token_body_a": "Provide a Pagure API token with access to the private issues to create a repository with just the private issues in it",
"migrate.pagure.token_body_b": "Be sure to set the private repo flag above if you want this repo to be private",
"migrate.pagure.token_label": "Pagure API Token",
"migrate.pagure.private_issues.summary": "Private Issues (Optional)",
"migrate.pagure.private_issues.description": "This feature is designed to create a second repository containing only private issues from your Pagure project for archive purposes. First, perform a normal migration (without a token) to import all public content. Then, if you have private issues to preserve, create a separate repository using this token option to archive those private issues.",
"migrate.pagure.private_issues.warning": "Be sure to set the repository visibility above to Private if you are using the API key to import private issues. This prevents accidentally exposing private content in a public repository.",
"migrate.pagure.token.placeholder": "Only for creating private issues archive",
"actions.runs.run_attempt_label": "Run attempt #%[1]s (%[2]s)",
"actions.runs.viewing_out_of_date_run": "You are viewing an out-of-date run of this job that was executed %[1]s.",
"actions.runs.view_most_recent_run": "View most recent run",
"actions.workflow.job_parsing_error": "Unable to parse jobs in workflow: %v",
"actions.workflow.event_detection_error": "Unable to parse supported events in workflow: %v",
"actions.workflow.pre_execution_error": "Workflow was not executed due to an error that blocked the execution attempt.",
"meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it."
}

View file

@ -1,3 +1,38 @@
{
"search.milestone_kind": "Serĉi celojn..."
"search.milestone_kind": "Serĉi celojn…",
"home.welcome.no_activity": "Neniu aktiveco",
"home.explore_repos": "Esplori deponejojn",
"home.explore_users": "Esplori uzantojn",
"home.explore_orgs": "Esplori organizaĵojn",
"stars.list.none": "Neniu stelumis ĉi tiun deponejon.",
"relativetime.now": "nun",
"relativetime.1day": "hieraŭ",
"relativetime.2days": "antaŭ du tagoj",
"relativetime.1week": "je lasta semajno",
"relativetime.2weeks": "antaŭ du semajnoj",
"relativetime.1month": "je lasta monato",
"relativetime.2months": "antaŭ du monatoj",
"relativetime.1year": "je lasta jaro",
"relativetime.2years": "antaŭ du jaroj",
"migrate.github.description": "Migrigi datumojn el github.com aŭ Github Enterprise server.",
"migrate.git.description": "Migrigi nur deponejon el ajna Git servilo.",
"migrate.gitea.description": "Migrigi datumojn el gitea.com aŭ aliaj Gitea instancoj.",
"migrate.gitlab.description": "Migrigi datumojn el gitlab.com aŭ aliaj GitLab instancoj.",
"migrate.gogs.description": "Migrigi datumojn el notabug.org aŭ aliaj Gogs instancoj.",
"migrate.onedev.description": "Migrigi datumojn el code.onedev.io aŭ aliaj OneDev instancoj.",
"migrate.gitbucket.description": "Migrigi datumojn el GitBucket instancoj.",
"migrate.codebase.description": "Migrigi datumojn el codebasehq.com.",
"migrate.forgejo.description": "Migrigi datumojn el codeberg.org aŭ aliaj Forgejo instancoj.",
"repo.settings.push_mirror.branch_filter.label": "Branĉa filtrilo (malnepra)",
"themes.names.forgejo-auto": "Forgejo (konformi kun sistema etoso)",
"themes.names.forgejo-light": "Forgejo hela",
"themes.names.forgejo-dark": "Forgejo malhela",
"error.not_found.title": "Paĝo ne trovita",
"alert.range_error": " devas esti nombro inter %[1]s kaj %[2]s.",
"profile.edit.link": "Redakti profilon",
"feed.atom.link": "Atomfluo",
"keys.ssh.link": "SSH ŝlosiloj",
"keys.gpg.link": "GPG ŝlosiloj",
"admin.moderation.reports": "Raportoj",
"admin.moderation.no_open_reports": "Ne estas malfermataj raportoj."
}

View file

@ -132,5 +132,14 @@
"avatar.constraints_hint": "Mukautettu profiilikuva voi olla kooltaan enintään %[1]s tai enintään %[2]dx%[3]d pikseliä",
"repo.commit.load_tags_failed": "Tagien lataaminen epäonnistui sisäisen virheen vuoksi",
"actions.runs.run_attempt_label": "Suoritusyritys #%[1]s (%[2]s)",
"migrate.pagure.description": "Tee migraatio pagure.io:sta tai muista Pagure-instansseista."
"migrate.pagure.description": "Tee migraatio pagure.io:sta tai muista Pagure-instansseista.",
"repo.form.cannot_create": "Kaikki tilat, joihin voit luoda tietovarastoja, ovat saavuttaneet tietovarastojen rajan.",
"migrate.form.error.url_credentials": "URL-osoite sisältää valtuustiedot, laita ne vastaavasti käyttäjänimi- ja salasanakenttiin",
"warning.repository.out_of_sync": "Tämän tietotietovaraston tietokantaesitys ei ole synkronoitu. Jos tämä varoitus näkyy edelleen tämän tietovaraston sitoumuksen lähettämisen jälkeen, ota yhteyttä järjestelmänvalvojaan.",
"admin.moderation.deleted_content_ref": "Ilmoitettua sisältöä, jonka tyyppi on %[1]v ja tunnus %[2]d, ei enää ole olemassa",
"settings.twofa_reenroll.description": "Ota mukaan kaksivaiheinen todennus uudelleen",
"user.ghost.tooltip": "Tämä käyttäjä on poistettu tai häntä ei voida yhdistää.",
"og.repo.summary_card.alt_description": "Yhteenvetokortti tietovarastosta %[1]s, kuvattu seuraavasti: %[2]s",
"migrate.pagure.incorrect_url": "Virheellinen lähdetietovaraston URL-osoite on syötetty",
"actions.runs.viewing_out_of_date_run": "Katselet tämän työn vanhentunutta suorituskertaa, joka suoritettiin %[1]s."
}

View file

@ -120,7 +120,7 @@
"repo.pulls.already_merged": "Nabigo ang pagsasama: Naisama na ang hiling sa paghila na ito.",
"migrate.pagure.incorrect_url": "Maling pinagmulang URL ng repositoryo ang ibinigay",
"migrate.pagure.project_url": "URL ng Pagure na proyekto",
"migrate.pagure.token_label": "Token",
"migrate.pagure.token_label": "Pagure API Token",
"migrate.pagure.token_body_a": "Magbigay ng Pagure API token na may access sa mga pribadong isyu para gumawa ng repositoryo na may mga pribadong isyu lang",
"migrate.pagure.project_example": "Ang URL ng Pagure na proyekto, hal. https://pagure.io/pagure",
"migrate.pagure.token_body_b": "Siguraduhin na itakda ang pribadong repositoryo na flag sa itaas kung gusto mo na pribado ang repositoryong ito",
@ -148,5 +148,12 @@
"migrate.form.error.url_credentials": "Naglalaman ng mga kredensyal ang URL, ilagay ang mga ito sa mga patlang ng username at password ayon sa pagkakabanggit",
"actions.runs.viewing_out_of_date_run": "Tumitingin ka sa isang hindi napapanahong paktabo ng trabahong ito na na-execute %[1]s.",
"actions.runs.view_most_recent_run": "Tignan ang pinakabagong pagtakbo",
"actions.runs.run_attempt_label": "Tangka sa pagtakbo #%[1]s (%[2]s)"
"actions.runs.run_attempt_label": "Tangka sa pagtakbo #%[1]s (%[2]s)",
"migrate.pagure.private_issues.summary": "Mga Pribadong Isyu (Opsyonal)",
"migrate.pagure.private_issues.description": "Dinisenyo ang feature na ito na gumawa ng isa pang repositoryo na naglalaman lamang ng mga pribadong isyu mula sa iyong proyekto sa Pagure para sa layunin ng pag-archive. Una, magsagawa ng isang normal na pag-migrate (nang walang token) para i-import ang lahat ng mga nilalaman. At, kung may mga pribadong isyu na gusto mong panatilihin, gumawa ng isang hiwalay na repositoryo gamit ng opsyon ng token na ito para i-archive ang mga pribadong isyu.",
"migrate.pagure.private_issues.warning": "Siguraduhing itakda ang visibility ng repositoryo sa itaas sa Pribado kung ginagamit mo ang API key para i-import ang mga pribadong isyu. Pinipigilan nito ang paglalantag ng mga pribadong nilalaman sa isang pampublikong repositoryo.",
"migrate.pagure.token.placeholder": "Para lamang sa paggawa ng archive ng mga pribadong isyu",
"actions.workflow.job_parsing_error": "Hindi ma-parse ang mga trabaho sa workflow: %v",
"actions.workflow.event_detection_error": "Hindi ma-parse ang mga sinusuportahang event sa workflow: %v",
"actions.workflow.pre_execution_error": "Hindi na-execute ang workflow dahil sa isang error na hinarang ang pagtangka sa pag-execute."
}

View file

@ -156,5 +156,10 @@
"migrate.pagure.project_example": "L'URL du projet Pagure, ex. https://pagure.io/pagure",
"migrate.pagure.token_label": "Jeton",
"migrate.pagure.token_body_a": "Veuillez fournir un jeton API Pagure avec l'accès aux problèmes privés pour créer un dépôt contenant seulement les problèmes privés",
"migrate.pagure.token_body_b": "Assurez-vous de définir le drapeau dépôt privé ci-dessus si vous souhaitez que ce dépôt soit privé"
"migrate.pagure.token_body_b": "Assurez-vous de définir le drapeau dépôt privé ci-dessus si vous souhaitez que ce dépôt soit privé",
"migrate.pagure.private_issues.summary": "Tickets privés (Optionnel)",
"migrate.pagure.private_issues.description": "Cette fonctionnalité est conçue pour créer un deuxième dépôt contenant uniquement des tickets privés de votre projet Pagure à des fins d'archive. Premièrement, effectuer une migration normale (sans jeton) pour importer tout le contenu public. Ensuite, si vous avez des tickets privés à préserver, créez un dépôt séparé en utilisant cette option de jeton pour archiver ces tickets privés.",
"migrate.pagure.private_issues.warning": "Assurez-vous de définir la visibilité du dépôt ci-dessus à Privé si vous utilisez la clé API pour importer des tickets privés. Cela empêche d'exposer accidentellement du contenu privé dans un dépôt public.",
"migrate.pagure.token.placeholder": "Seulement pour créer des archives de tickets privés",
"actions.workflow.pre_execution_error": "Workflow n'a pas été exécuté en raison d'une erreur qui a bloqué la tentative d'exécution."
}

View file

@ -1,5 +1,133 @@
{
"repo.pulls.merged_title_desc": "commit %[1]d telah digabungkan dari <code>%[2]s</code> menjadi <code>%[3]s</code> %[4]s",
"repo.pulls.title_desc": "ingin menggabungkan komit %[1]d dari <code>%[2]s</code> menuju <code id=\"%[4]s\">%[3]s</code>",
"moderation.abuse_category.malware": "Perangkat pembahaya"
"moderation.abuse_category.malware": "Perangkat pembahaya",
"home.welcome.no_activity": "Tidak ada aktivitas",
"home.welcome.activity_hint": "Belum ada konten di feed Anda. Aktivitas dan tindakan Anda dari repositori yang Anda ikuti akan ditampilkan di sini.",
"home.explore_repos": "Jelajahi repositori",
"home.explore_users": "Jelajahi pengguna",
"home.explore_orgs": "Jelajahi organisasi",
"stars.list.none": "Tidak ada yang memberikan bintang pada repositori ini.",
"watch.list.none": "Tidak ada yang memantau repositori ini.",
"followers.incoming.list.self.none": "Tidak ada yang mengikuti profil Anda.",
"followers.incoming.list.none": "Tidak ada yang mengikuti pengguna ini.",
"followers.outgoing.list.self.none": "Anda tidak mengikuti siapa pun.",
"followers.outgoing.list.none": "%s tidak mengikuti siapa pun.",
"relativetime.now": "sekarang",
"relativetime.future": "di masa depan",
"relativetime.mins": "%d menit yang lalu",
"relativetime.hours": "%d jam yang lalu",
"relativetime.days": "%d hari yang lalu",
"relativetime.weeks": "%d minggu yang lalu",
"relativetime.months": "%d bulan yang lalu",
"relativetime.years": "%d tahun yang lalu",
"relativetime.1day": "kemarin",
"relativetime.2days": "dua hari yang lalu",
"relativetime.1week": "minggu lalu",
"relativetime.2weeks": "dua minggu yang lalu",
"relativetime.1month": "bulan lalu",
"relativetime.2months": "dua bulan yang lalu",
"relativetime.1year": "tahun lalu",
"relativetime.2years": "dua tahun yang lalu",
"repo.pulls.already_merged": "Penggabungan (merge) gagal: Permintaan pull (pull request) ini sudah digabungkan.",
"repo.form.cannot_create": "Semua ruang di mana Anda dapat membuat repositori telah mencapai batas jumlah repositori.",
"migrate.form.error.url_credentials": "URL tersebut mengandung kredensial, masukkan kredensial tersebut ke dalam kolom nama pengguna dan kata sandi masing-masing",
"migrate.github.description": "Migrasikan data dari github.com atau server GitHub Enterprise.",
"migrate.git.description": "Migrasikan repositori hanya dari layanan Git mana pun.",
"migrate.gitea.description": "Migrasikan data dari gitea.com atau instance Gitea lainnya.",
"migrate.gitlab.description": "Migrasikan data dari gitlab.com atau instance GitLab lainnya.",
"migrate.gogs.description": "Migrasikan data dari notabug.org atau instance Gogs lainnya.",
"migrate.onedev.description": "Migrasikan data dari code.onedev.io atau instance OneDev lainnya.",
"migrate.gitbucket.description": "Migrasikan data dari instance GitBucket.",
"migrate.codebase.description": "Migrasikan data dari codebasehq.com.",
"migrate.forgejo.description": "Migrasikan data dari codeberg.org atau instance Forgejo lainnya.",
"repo.issue_indexer.title": "Indeks Masalah",
"search.milestone_kind": "Cari milestone…",
"repo.settings.push_mirror.branch_filter.label": "Filter cabang (opsional)",
"repo.settings.push_mirror.branch_filter.description": "Cabang yang akan disalin. Biarkan kosong untuk menyalin semua cabang. Lihat <a href=\"%[1]s\">%[2]s dokumentasi</a> untuk sintaksis. Contoh: <code>main, release/*</code>",
"incorrect_root_url": "Instance Forgejo ini dikonfigurasi untuk diakses melalui “%s”. Saat ini Anda mengakses Forgejo melalui URL yang berbeda, yang dapat menyebabkan sebagian fungsi aplikasi tidak berfungsi dengan baik. URL kanonik dikendalikan oleh admin Forgejo melalui pengaturan ROOT_URL di berkas app.ini.",
"themes.names.forgejo-auto": "Forgejo (mengikuti tema sistem)",
"themes.names.forgejo-light": "Forgejo terang",
"themes.names.forgejo-dark": "Forgejo gelap",
"error.not_found.title": "Halaman tidak ditemukan",
"warning.repository.out_of_sync": "Representasi basis data repositori ini tidak sinkron. Jika peringatan ini masih ditampilkan setelah melakukan commit ke repositori ini, hubungi administrator.",
"alert.asset_load_failed": "Gagal memuat berkas aset dari {path}. Pastikan berkas aset dapat diakses.",
"alert.range_error": " Harus berupa angka antara %[1]s dan %[2]s.",
"install.invalid_lfs_path": "Tidak dapat membuat direktori root LFS di jalur yang ditentukan: %[1]s",
"profile.actions.tooltip": "Lebih banyak tindakan",
"profile.edit.link": "Edit profil",
"feed.atom.link": "Atom feed",
"keys.ssh.link": "Kunci SSH",
"keys.gpg.link": "Kunci GPG",
"admin.config.moderation_config": "Konfigurasi moderasi",
"admin.moderation.moderation_reports": "Konfigurasi moderasi",
"admin.moderation.reports": "Laporan",
"admin.moderation.no_open_reports": "Saat ini tidak ada laporan yang terbuka.",
"admin.moderation.deleted_content_ref": "Konten yang dilaporkan dengan tipe %[1]v dan ID %[2]d tidak lagi tersedia",
"moderation.report_abuse": "Laporkan penyalahgunaan",
"moderation.report_content": "Isi laporan",
"moderation.report_abuse_form.header": "Laporkan penyalahgunaan kepada administrator",
"moderation.report_abuse_form.details": "Formulir ini harus digunakan untuk melaporkan pengguna yang membuat profil spam, repositori, masalah, komentar, atau berperilaku tidak pantas.",
"moderation.report_abuse_form.invalid": "Argumen tidak valid",
"moderation.report_abuse_form.already_reported": "Anda sudah melaporkan konten ini",
"moderation.abuse_category": "Kategori",
"moderation.abuse_category.placeholder": "Pilih kategori",
"moderation.abuse_category.spam": "Spam",
"moderation.abuse_category.illegal_content": "Konten ilegal",
"moderation.abuse_category.other_violations": "Pelanggaran lain terhadap aturan platform",
"moderation.report_remarks": "Catatan",
"moderation.report_remarks.placeholder": "Silakan berikan beberapa detail mengenai penyalahgunaan yang Anda laporkan.",
"moderation.submit_report": "Kirimkan laporan",
"moderation.reporting_failed": "Tidak dapat kirimkan laporan penyalahgunaan baru: %v",
"moderation.reported_thank_you": "Terima kasih atas laporan yang Anda sampaikan. Pihak administrasi telah mengetahui hal tersebut.",
"mail.actions.successful_run_after_failure_subject": "Alur kerja %[1] telah dipulihkan di repositori %[2]",
"mail.actions.not_successful_run_subject": "Alur kerja %[1] gagal di repositori %[2]",
"mail.actions.successful_run_after_failure": "Alur kerja %[1] telah dipulihkan di repositori %[2]",
"mail.actions.not_successful_run": "Alur kerja %[1] gagal di repositori %[2]",
"mail.actions.run_info_cur_status": "Status Lari Ini: %[1]s (baru saja diperbarui dari %[2]s)",
"mail.actions.run_info_previous_status": "Status Eksekusi Sebelumnya: %[1]s",
"mail.actions.run_info_sha": "Komitmen: %[1]s",
"mail.actions.run_info_trigger": "Dipicu oleh: %[1]s oleh: %[2]s",
"repo.diff.commit.next-short": "Selanjutnya",
"repo.diff.commit.previous-short": "Sebelumnya",
"discussion.locked": "Diskusi ini telah dikunci. Komentar hanya dapat dilakukan oleh kontributor.",
"discussion.sidebar.reference": "Referensi",
"editor.textarea.tab_hint": "Baris sudah diindentasi. Tekan <kbd>Tab</kbd> lagi atau <kbd>Escape</kbd> untuk keluar dari editor.",
"editor.textarea.shift_tab_hint": "Tidak ada indentasi pada baris ini. Tekan <kbd>Shift</kbd> + <kbd>Tab</kbd> lagi atau <kbd>Escape</kbd> untuk keluar dari editor.",
"admin.auths.allow_username_change": "Izinkan perubahan nama pengguna",
"admin.auths.allow_username_change.description": "Izinkan pengguna untuk mengubah nama pengguna mereka di pengaturan profil",
"admin.dashboard.cleanup_offline_runners": "Pembersihan pelari offline",
"admin.dashboard.remove_resolved_reports": "Hapus laporan yang telah diselesaikan",
"admin.config.security": "Konfigurasi keamanan",
"admin.config.global_2fa_requirement.title": "Persyaratan dua faktor global",
"admin.config.global_2fa_requirement.none": "Tidak",
"admin.config.global_2fa_requirement.all": "Semua pengguna",
"admin.config.global_2fa_requirement.admin": "Administrator",
"settings.visibility.description": "Visibilitas profil memengaruhi kemampuan orang lain untuk mengakses repositori non-pribadi Anda. <a href=\"%s\" target=\"_blank\">Pelajari lebih lanjut</a>.",
"settings.twofa_unroll_unavailable": "Otentikasi dua faktor diwajibkan untuk akun Anda dan tidak dapat dinonaktifkan.",
"settings.twofa_reenroll": "Aktifkan kembali otentikasi dua faktor",
"settings.twofa_reenroll.description": "Daftarkan ulang otentikasi dua faktor Anda",
"settings.must_enable_2fa": "Instance Forgejo ini mengharuskan pengguna untuk mengaktifkan otentikasi dua faktor sebelum mereka dapat mengakses akun mereka.",
"error.must_enable_2fa": "Instance Forgejo ini mengharuskan pengguna untuk mengaktifkan otentikasi dua faktor sebelum mereka dapat mengakses akun mereka. Aktifkan di: %s",
"avatar.constraints_hint": "Avatar kustom tidak boleh melebihi %[1]s dalam ukuran atau lebih besar dari %[2]dx%[3]d piksel",
"user.ghost.tooltip": "Pengguna ini telah dihapus, atau tidak dapat diidentifikasi.",
"og.repo.summary_card.alt_description": "Kartu ringkasan repositori %[1]s, dijelaskan sebagai: %[2]s",
"repo.commit.load_tags_failed": "Pemuatan tag gagal karena kesalahan internal",
"compare.branches.title": "Bandingkan cabang",
"migrate.pagure.description": "Migrasikan data dari pagure.io atau instance Pagure lainnya.",
"migrate.pagure.incorrect_url": "URL repositori sumber yang salah telah disediakan",
"migrate.pagure.project_url": "URL Proyek Pagure",
"migrate.pagure.project_example": "URL proyek Pagure, misalnya https://pagure.io/pagure",
"migrate.pagure.token_label": "Token API Pagure",
"migrate.pagure.private_issues.summary": "Masalah Pribadi (Opsional)",
"migrate.pagure.private_issues.description": "Fitur ini dirancang untuk membuat repositori kedua yang hanya berisi masalah pribadi dari proyek Pagure Anda untuk tujuan arsip. Pertama, lakukan migrasi normal (tanpa token) untuk mengimpor semua konten publik. Kemudian, jika Anda memiliki masalah pribadi yang ingin disimpan, buat repositori terpisah menggunakan opsi token ini untuk mengarsipkan masalah pribadi tersebut.",
"migrate.pagure.private_issues.warning": "Pastikan untuk mengatur visibilitas repositori di atas menjadi Pribadi jika Anda menggunakan kunci API untuk mengimpor masalah pribadi. Hal ini mencegah pengungkapan konten pribadi secara tidak sengaja di repositori publik.",
"migrate.pagure.token.placeholder": "Hanya untuk membuat arsip masalah pribadi",
"actions.runs.run_attempt_label": "Upaya eksekusi #%[1]s (%[2]s)",
"actions.runs.viewing_out_of_date_run": "Anda sedang melihat hasil eksekusi yang sudah kadaluwarsa dari tugas ini yang dijalankan pada %[1]s.",
"actions.runs.view_most_recent_run": "Lihat hasil terbaru",
"actions.workflow.job_parsing_error": "Tidak dapat memproses pekerjaan dalam alur kerja: %v",
"actions.workflow.event_detection_error": "Tidak dapat memproses peristiwa yang didukung dalam alur kerja: %v",
"actions.workflow.pre_execution_error": "Alur kerja tidak dijalankan karena terjadi kesalahan yang menghalangi upaya eksekusi.",
"meta.last_line": "Terima kasih telah menerjemahkan Forgejo! Baris ini tidak terlihat oleh pengguna, tetapi memiliki fungsi lain dalam manajemen terjemahan. Anda dapat menambahkan fakta menarik dalam terjemahan alih-alih menerjemahkannya."
}

View file

@ -156,5 +156,6 @@
"migrate.pagure.project_url": "URL del progetto Pagure",
"migrate.pagure.project_example": "L'URL del progetto Pagure, ad esempio https://pagure.io/pagure",
"mail.actions.not_successful_run": "Flusso di lavoro %[1]s non riuscito nel repositorio %[2]s",
"settings.twofa_reenroll.description": "Reinserisci la verifica in due passaggi"
"settings.twofa_reenroll.description": "Reinserisci la verifica in due passaggi",
"migrate.pagure.private_issues.summary": "Segnalazioni Private (opzionale)"
}

View file

@ -9,5 +9,25 @@
"repo.diff.commit.previous-short": "Uzwir",
"admin.config.global_2fa_requirement.none": "Uhu",
"admin.config.global_2fa_requirement.admin": "Inedbalen",
"migrate.pagure.token_label": "Ajiṭun"
"migrate.pagure.token_label": "Ajiṭun API n Pagure",
"home.explore_repos": "Snirem ikufan",
"home.explore_users": "Snirem iseqdacen",
"relativetime.2days": "sin n wussan aya",
"relativetime.1week": "dduṛt yezrin",
"relativetime.1month": "ayyur yezrin",
"relativetime.2months": "sin n wayyuren aya",
"relativetime.1year": "ilindi",
"relativetime.2years": "sin n iseggasen aya",
"themes.names.forgejo-auto": "Forgejo (akken i yella usentel n unagraw)",
"themes.names.forgejo-light": "Forgejo aceɛlal",
"themes.names.forgejo-dark": "Forgejo ubrik",
"error.not_found.title": "Asebtar ulac-it",
"profile.actions.tooltip": "Ugar n tigawin",
"profile.edit.link": "Ẓreg amaɣnu",
"keys.ssh.link": "Tasarut SSH",
"keys.gpg.link": "Tisura GPG",
"moderation.abuse_category.placeholder": "Fren taggayt",
"admin.config.security": "Tawila n tɣellist",
"admin.config.global_2fa_requirement.all": "Akk iseqdacen",
"migrate.pagure.project_url": "Tansa URL n usenfar Pagure"
}

View file

@ -129,7 +129,7 @@
"migrate.pagure.incorrect_url": "Ir norādīts nepareizs avota glabātavas URL",
"migrate.pagure.project_url": "Pagure projekta URL",
"migrate.pagure.project_example": "Pagure projekta URL, piem., https://pagure.io/pagure",
"migrate.pagure.token_label": "Pilnvara",
"migrate.pagure.token_label": "Pagure API ilnvara",
"migrate.pagure.token_body_a": "Jānorāda Pagura API pilnvara ar piekļuvi privātajiem pieteikumiem, lai izveidotu glabātavu tikai ar privātiem pieteikumiem tajā",
"migrate.pagure.token_body_b": "Jāpārliecinās, ka augstāk ir iestatīts privātas glabātavas karogs, ja vēlies, lai šī glabātava būtu privāta",
"migrate.github.description": "Pārcelt datus no github.com vai GitHub Enterprise servera.",
@ -156,5 +156,12 @@
"migrate.form.error.url_credentials": "URL satur pieteikšanās datus, tie ir attiecīgi jāievieto lietotājvārda un paroles laukā",
"actions.runs.view_most_recent_run": "Apskatīt visnesenāko palaišanu",
"actions.runs.run_attempt_label": "Palaišanas mēģinājums #%[1]s (%[2]s)",
"actions.runs.viewing_out_of_date_run": "Tu skati novecojušu šī darba palaišanu, kas tika izpildīta %[1]s."
"actions.runs.viewing_out_of_date_run": "Tu skati novecojušu šī darba palaišanu, kas tika izpildīta %[1]s.",
"migrate.pagure.private_issues.summary": "Privātie pieteikumi (izvēles)",
"migrate.pagure.private_issues.description": "Šī iespēja ir izstrādāta, lai izveidotu otrēju glabātavu, kurā arhivēšanas nolūkā atrodas tikai privātie pieteikumi no Pagure projekta. Vispirms jāveic parasta pārcelšana (bez pilnvaras), lai ievietotu visu publisko saturu. Tad, ja ir saglabājami privāti pieteikumi, jāizveido atsevišķa glabātava, izmantojot šo pilnvaras iespēju, lai arhivētu privātos pieteikumus.",
"migrate.pagure.private_issues.warning": "Jāpārliecinās, ka glabātavas redzamība augstāk ir iestatīta kā privāta, ja izmanto API atslēgu, lai ievietotu privātos pieteikumus. Tas novērš nejaušu privāta satura atklāšanu publiskā glabātavā.",
"migrate.pagure.token.placeholder": "Tikai privātu pieteikumu arhīva izveidošanai",
"actions.workflow.job_parsing_error": "Nebija iespējams apstrādāt darbus darbplūsmā: %v",
"actions.workflow.event_detection_error": "Nebija iespējams apstrādāt atbalstītos notikumus darbplūsmā: %v",
"actions.workflow.pre_execution_error": "Darbplūsma netika izpildīta kļūdas, kura aizturēja izpildes mēģinājumu, dēļ."
}

View file

@ -121,7 +121,7 @@
"migrate.pagure.description": "Daten vun pagure.io of anner Pagure-Instanzen umtrecken.",
"migrate.pagure.project_url": "Pagure-Projekt-URL",
"migrate.pagure.project_example": "De URL vum Projekt up Pagure, ton Bispööl https://pagure.io/pagure",
"migrate.pagure.token_label": "Teken",
"migrate.pagure.token_label": "Pagure-API-Teken",
"migrate.pagure.token_body_b": "Wees wiss, boven the Flagg för een privaates Repo to setten, wenn du willst, dat deeses Repo privaat wesen sall",
"migrate.pagure.incorrect_url": "Ungültige Quell-Repositoriums-URL is angeven worden",
"migrate.pagure.token_body_a": "Giff een Pagure-API-Teken mit Togang to de privaaten Gefallens an, um een Repositorium mit blots the privaaten Gefallens daarin to maken",
@ -148,5 +148,12 @@
"migrate.form.error.url_credentials": "De URL enthollt Anmell-Daten, legg se in de Felden för Brukernaam un Passwoord",
"actions.runs.viewing_out_of_date_run": "Du bekiekst eenen verollten Loop vun deeser Upgaav, wat %[1]s utföhrt worden is.",
"actions.runs.view_most_recent_run": "Neeisten Loop ankieken",
"actions.runs.run_attempt_label": "Loop-Versöök #%[1]s (%[2]s)"
"actions.runs.run_attempt_label": "Loop-Versöök #%[1]s (%[2]s)",
"migrate.pagure.private_issues.summary": "Privaate Gefallens (wenn du willst)",
"migrate.pagure.private_issues.description": "Deese Funktioon lett di een twedes Repositorium maken, wat blots privaate Gefallens ut dienem Pagure-Projekt för Archivzwecken enthollt. Maak toeerst eenen normaalen Umtreck (sünner een Teken), um alle publiken Inhollen to importeren. Dann, wenn du privaate Gefallens hest, wat du behollen willst, maak noch een anner Repositorium mit deesem Teken, um deese privaaten Gefallens to archiveren.",
"migrate.pagure.private_issues.warning": "Wees wiss, de Sichtbaarkeid vun de Repositorium up Privaat to setten, wenn du de API-Slötel bruukst, um privaate Gefallens to importeren. Dat verhinnert, dat du privaaten Inholl ut Versehn in eenem publiken Repositorium blootmaakst.",
"migrate.pagure.token.placeholder": "Blots, um een Archiv vun privaaten Gefallens to maken",
"actions.workflow.job_parsing_error": "Kann de Upgaven in de Warkwies nich lesen: %v",
"actions.workflow.event_detection_error": "Kann de unnerstütt Vörfallen in de Warkwies nich lesen: %v",
"actions.workflow.pre_execution_error": "Warkwies is nich utföhrt worden, denn een Fehler is uptreden, wat de Utföhrens-Versöök blockeert hett."
}

View file

@ -122,7 +122,7 @@
"migrate.pagure.incorrect_url": "Er is een onjuiste URL voor de bronrepository opgegeven",
"migrate.pagure.project_url": "Pagure-project URL",
"migrate.pagure.project_example": "De url van het Pagure-project, bijvoorbeeld https://pagure.io/pagure",
"migrate.pagure.token_label": "Token",
"migrate.pagure.token_label": "Pagure API-token",
"migrate.pagure.token_body_a": "Geef een Pagure API-token met toegang tot de privé-issues om een repository te maken met alleen de privé-issues erin",
"migrate.pagure.token_body_b": "Zorg ervoor dat u de vlag voor privé-repository hierboven instelt als u wilt dat deze repository privé is",
"migrate.github.description": "Migreer gegevens van github.com of GitHub Enterprise server.",
@ -146,5 +146,14 @@
"user.ghost.tooltip": "Deze gebruiker is verwijderd of kan niet worden gevonden.",
"error.must_enable_2fa": "Deze Forgejo-instantie vereist dat gebruikers tweefactorauthenticatie inschakelen voordat ze toegang krijgen tot hun accounts. Schakel dit in op: %s",
"migrate.form.error.url_credentials": "De URL bevat inloggegevens. Vul deze in de velden voor gebruikersnaam en wachtwoord respectievelijk",
"actions.runs.view_most_recent_run": "Bekijk de meest recente run"
"actions.runs.view_most_recent_run": "Bekijk de meest recente run",
"actions.runs.run_attempt_label": "Uitvoerpoging #%[1]s (%[2]s)",
"actions.runs.viewing_out_of_date_run": "U bekijkt een verouderde uitvoer van deze opdracht die %[1]s is uitgevoerd.",
"migrate.pagure.private_issues.summary": "Privé-issues (optioneel)",
"migrate.pagure.private_issues.description": "Deze functie is ontworpen om een tweede repository aan te maken met alleen privé-issues van je Pagure project voor archiefdoeleinden. Voer eerst een normale migratie uit (zonder token) om alle publieke content te importeren. Als u vervolgens privé-issues wilt bewaren, maak dan een aparte repository aan met deze tokenoptie om die privé-issues te archiveren.",
"migrate.pagure.private_issues.warning": "Zorg ervoor dat je de repository zichtbaarheid hierboven op Privé zet als je de API sleutel gebruikt om privézaken te importeren. Dit voorkomt dat je per ongeluk privé-inhoud blootstelt in een openbare repository.",
"migrate.pagure.token.placeholder": "Alleen voor het aanmaken van een privé-archief met issues",
"actions.workflow.job_parsing_error": "Taken in workflow kunnen niet worden geparseerd: %v",
"actions.workflow.event_detection_error": "Ondersteunde gebeurtenissen in workflow kunnen niet worden geparseerd: %v",
"actions.workflow.pre_execution_error": "Workflow is niet uitgevoerd vanwege een fout die de uitvoeringspoging blokkeerde."
}

View file

@ -11,7 +11,7 @@
},
"search.milestone_kind": "Pesquisar marcos…",
"home.welcome.no_activity": "Sem atividade",
"home.welcome.activity_hint": "Ainda não tem nada no seu feed. Suas ações e atividade dos seus repositórios vigiados aparecerão aqui.",
"home.welcome.activity_hint": "Ainda não tem nada no seu feed. Suas Actions e atividade dos seus repositórios vigiados aparecerão aqui.",
"home.explore_repos": "Explorar repositórios",
"home.explore_users": "Explorar usuários",
"home.explore_orgs": "Explorar organizações",
@ -140,7 +140,7 @@
"migrate.pagure.incorrect_url": "Uma URL incorreta do repositório fonte foi fornecida",
"migrate.pagure.project_example": "URL do projeto Pagure, por exemplo: https://pagure.io/pagure",
"migrate.pagure.project_url": "URL do projeto Pagure",
"migrate.pagure.token_label": "Token",
"migrate.pagure.token_label": "Token de API Pagure",
"migrate.pagure.token_body_b": "Certifique-se de definir o sinalizador de repositório privado acima se você deseja que este repositório seja privado",
"admin.config.global_2fa_requirement.title": "Exigência global de segundo fator",
"admin.config.global_2fa_requirement.none": "Não",
@ -156,5 +156,12 @@
"migrate.form.error.url_credentials": "A URL contém credenciais, coloque-as nos campos de usuário e senha respectivamente",
"actions.runs.viewing_out_of_date_run": "Você está visualizando uma execução desatualizada deste trabalho que foi executada %[1]s.",
"actions.runs.view_most_recent_run": "Ver execução mais recente",
"actions.runs.run_attempt_label": "Tentativa de execução #%[1]s (%[2]s)"
"actions.runs.run_attempt_label": "Tentativa de execução #%[1]s (%[2]s)",
"migrate.pagure.private_issues.summary": "Questões Privadas (Opcional)",
"migrate.pagure.private_issues.description": "Esta funcionalidade foi projetada para criar um segundo repositório contendo somente questões privadas do seu projeto Pagure para a finalidade de arquivo. Primeiro, faça uma migração normal (sem token) para importar todo o conteúdo público. Depois, se você tem questões privadas que deseja preservar, crie um repositório separado usando esta opção de token para arquivar estas questões privadas.",
"migrate.pagure.private_issues.warning": "Certifique-se de definir a visibilidade do repositório acima para Privado se você está usando a chave de API para importar questões privadas. Isso evita expor acidentalmente conteúdo privado em um repositório público.",
"migrate.pagure.token.placeholder": "Somente para criar um arquivo de questões privadas",
"actions.workflow.job_parsing_error": "Não foi possível processar alguns trabalhos do workflow: %v",
"actions.workflow.event_detection_error": "Não foi possível processar eventos suportados no workflow: %v",
"actions.workflow.pre_execution_error": "Workflow não foi executado devido a um erro que bloqueou a tentativa de execução."
}

View file

@ -140,7 +140,7 @@
"migrate.pagure.incorrect_url": "Foi fornecida uma URL incorreta do repositório de origem",
"migrate.pagure.project_url": "URL do projeto Pagure",
"migrate.pagure.project_example": "URL do projeto Pagure, por exemplo, https://pagure.io/pagure",
"migrate.pagure.token_label": "Token",
"migrate.pagure.token_label": "Código da API do Pagure",
"migrate.pagure.token_body_a": "Forneça um token da API Pagure com acesso às questões privadas para criar um repositório contendo apenas as questões privadas",
"migrate.pagure.token_body_b": "Certifique-se de definir o marcador de repositório privado acima se desejar que este repositório seja privado",
"migrate.github.description": "Migrar dados do github.com ou do GitHub Enterprise server.",
@ -156,5 +156,12 @@
"user.ghost.tooltip": "Este utilizador foi eliminado ou não é possível encontrar uma correspondência.",
"actions.runs.run_attempt_label": "Tentativa de execução #%[1]s (%[2]s)",
"actions.runs.viewing_out_of_date_run": "Está a visualizar uma execução desatualizada deste trabalho que foi executado %[1]s.",
"actions.runs.view_most_recent_run": "Ver execução mais recente"
"actions.runs.view_most_recent_run": "Ver execução mais recente",
"migrate.pagure.private_issues.summary": "Questões privadas (opcional)",
"migrate.pagure.private_issues.description": "Esta funcionalidade está desenhada para criar um segundo repositório contendo apenas questões privadas do seu repositório Pagure para efeitos de arquivo. Primeiro, faça uma migração normal (sem um código) para importar todo o conteúdo público. Depois, se tiver questões privadas a preservar, crie um repositório separado usando esta opção com código para arquivar essas questões privadas.",
"migrate.pagure.private_issues.warning": "Certifique-se que passa a visibilidade do repositório para Privado se estiver a usar a chave de API para importar questões privadas. Isso irá evitar que exponha acidentalmente conteúdo privado num repositório público.",
"migrate.pagure.token.placeholder": "Somente para criar arquivo com questões privadas",
"actions.workflow.job_parsing_error": "Não foi possível analisar os trabalhos na sequência de trabalho: %v",
"actions.workflow.event_detection_error": "Não foi possível analisar eventos suportados na sequência de trabalho: %v",
"actions.workflow.pre_execution_error": "A sequência de trabalho não foi executada por causa de um erro que bloqueou a tentativa de execução."
}

View file

@ -23,7 +23,7 @@
"alert.asset_load_failed": "Не удалось получить ресурсы из {path}. Убедитесь, что файлы ресурсов доступны.",
"install.invalid_lfs_path": "Не удалось расположить корень LFS по указанному пути: %[1]s",
"alert.range_error": " - число должно быть в диапазоне от %[1]s-%[2]s.",
"meta.last_line": "This magic string will cause Codeberg Translate to create a new pull request in the Forgejo repository. Test.",
"meta.last_line": "This magic string will cause Codeberg Translate to create a new pull request in the Forgejo repository.",
"mail.actions.not_successful_run_subject": "Провал раб. потока %[1]s в репозитории %[2]s",
"mail.actions.successful_run_after_failure_subject": "Возобновление раб. потока %[1]s в репозитории %[2]s",
"mail.actions.run_info_trigger": "Причина срабатывания: %[1]s by: %[2]s",
@ -140,7 +140,7 @@
"migrate.pagure.incorrect_url": "Введена неправильная ссылка на источник",
"migrate.pagure.project_url": "Ссылка проекта на Pagure",
"migrate.pagure.project_example": "Ссылка проекта. Например, https://pagure.io/pagure",
"migrate.pagure.token_label": "Токен",
"migrate.pagure.token_label": "Токен API Pagure",
"migrate.pagure.token_body_a": "Если предоставить токен API Pagure с доступом к секретным задачам проекта, будет создан отдельный репозиторий, в котором они будут размещены",
"migrate.pagure.token_body_b": "Поставьте флажок на пункте видимости репозитория выше, если хотите, чтобы этот репозиторий был частным",
"migrate.github.description": "Перенести данные с github.com или сервера GitHub Enterprise.",
@ -156,5 +156,12 @@
"migrate.form.error.url_credentials": "Ссылка содержит данные авторизации. Поместите их в соответствующие поля",
"actions.runs.run_attempt_label": "Попытка №%[1]s (%[2]s)",
"actions.runs.viewing_out_of_date_run": "Это устаревший результат. Задание было выполнено %[1]s.",
"actions.runs.view_most_recent_run": "Перейти к актуальному"
"actions.runs.view_most_recent_run": "Перейти к актуальному",
"migrate.pagure.private_issues.summary": "Скрытые задачи (необязательно)",
"migrate.pagure.private_issues.description": "Эта функция позволяет создать дополнительный репозиторий для архивации в нём скрытых задач из вашего проекта Pagure. Сперва выполните нормальный перенос репозитория без токена, чтобы скопировать всё публичное содержимое. Затем, если нужно, выполните ещё один перенос и укажите токен, чтобы разместить скрытые задачи в дополнительном репозитории.",
"migrate.pagure.private_issues.warning": "Не забудьте указать видимость репозитория как Частную при переносе скрытых задач, чтобы их никто не увидел.",
"migrate.pagure.token.placeholder": "Только для переноса скрытых задач",
"actions.workflow.job_parsing_error": "Не удалось прочитать задачи в раб. потоке: %v",
"actions.workflow.event_detection_error": "Не удалось прочитать поддерживаемые события в раб. потоке: %v",
"actions.workflow.pre_execution_error": "Рабочий поток не был выполнен из-за ошибки, возникшей при попытке начать выполнение."
}

View file

@ -93,7 +93,7 @@
"moderation.abuse_category.placeholder": "Välj en kategori",
"moderation.abuse_category.spam": "Skräppost",
"moderation.abuse_category.malware": "Skadlig kod",
"settings.visibility.description": "Profilens synlighet påverkar andras möjlighet att komma åt dina icke-privata förråd. <a href=\"%s\" target=\"_blank\">Läs mer</a>",
"settings.visibility.description": "Profilens synlighet påverkar andras möjlighet att komma åt dina icke-privata förråd. <a href=\"%s\" target=\"_blank\">Läs mer</a>.",
"avatar.constraints_hint": "Anpassade avatarer får inte vara större än %[1] eller %[2]dx%[3] bildpunkter",
"og.repo.summary_card.alt_description": "Sammanfattningskort för arkivet %[1]s, beskrivet som: %[2]s",
"profile.actions.tooltip": "Fler åtgärder",
@ -102,5 +102,56 @@
"keys.ssh.link": "SSH-nycklar",
"repo.diff.commit.next-short": "Nästa",
"repo.diff.commit.previous-short": "Föreg",
"feed.atom.link": "Atom-flöde"
"feed.atom.link": "Atom-flöde",
"repo.pulls.already_merged": "Sammanfogning misslyckades: Denna pulka förfrågning har redan blivit sammanfogad.",
"migrate.form.error.url_credentials": "URL:en innehåller inloggningsuppgifter, sätt dem i respektive användarnamn- och lösenordsfält",
"migrate.github.description": "Migrera data från github.com eller GitHub Enterprise server.",
"migrate.git.description": "Migrera endast utvecklingskatalogen från vilken Git-tjänst som helst.",
"migrate.gitea.description": "Migrera data från gitea.com eller andra Gitea-instanser.",
"migrate.gitlab.description": "Migrera data från gitlab.com eller annan GitLab-instans.",
"migrate.gogs.description": "Migrera data från notabug.org eller annan Gogs-instans.",
"migrate.onedev.description": "Migrera data från code.onedev.io eller annan OneDev-instans.",
"migrate.gitbucket.description": "Migrera data från GitBucket-instans.",
"migrate.codebase.description": "Migrera data från codebasehq.com.",
"migrate.forgejo.description": "Migrera data från codeberg.org eller annan Forgejo-instans.",
"repo.settings.push_mirror.branch_filter.label": "Grenfilter (frivilligt)",
"repo.settings.push_mirror.branch_filter.description": "Grenar att speglas. Lämna tom för att spegla alla grenar. Se <a href=\"%[1]s\">%[2]s dokumentation</a> för syntax. Exempel: <code>main, release/*</code>",
"admin.moderation.moderation_reports": "Moderationsrapporter",
"admin.moderation.reports": "Rapporter",
"admin.moderation.no_open_reports": "Det finns för tillfället inga öppna rapporter.",
"mail.actions.run_info_sha": "Commit: %[1]s",
"discussion.sidebar.reference": "Referens",
"admin.auths.allow_username_change": "Tillåt användarnamnsändring",
"admin.auths.allow_username_change.description": "Tillåt användare att ändra sitt användarnamn i profilinställningarna",
"admin.dashboard.remove_resolved_reports": "Ta bort lösta rapporter",
"admin.config.security": "Säkerhetskonfiguration",
"admin.config.global_2fa_requirement.title": "Globalt tvåfaktorskrav",
"admin.config.global_2fa_requirement.none": "Nej",
"admin.config.global_2fa_requirement.all": "Alla användare",
"admin.config.global_2fa_requirement.admin": "Administratörer",
"settings.twofa_unroll_unavailable": "Tvåfaktorsautentisering krävs för ditt konto och kan inte inaktiveras.",
"settings.must_enable_2fa": "Denna Forgejo-instans kräver användare att aktivera tvåfaktorsautentisering innan de kan komma at deras konto.",
"error.must_enable_2fa": "Denna Forgejo-instans kräver användare att aktivera tvåfaktorsautentisering innan de kan komma åt deras konto. Aktivera det på: %s",
"repo.commit.load_tags_failed": "Laddning av taggar misslyckades på grund av internt fel",
"compare.branches.title": "Jämför grenar",
"migrate.pagure.description": "Migrera data från pagure.io eller andra Pagure-instanser.",
"migrate.pagure.project_url": "Pagure projekt-URL",
"migrate.pagure.project_example": "Pagure-projektets URL, t.ex. https://pagure.io/pagure",
"migrate.pagure.token_label": "Pagure API-token",
"actions.runs.run_attempt_label": "Kör försök #%[1]s (%[2]s)",
"warning.repository.out_of_sync": "Databasrepresentationen av detta arkiv är inte synkroniserad. Om denna varning fortfarande visas efter att du har skickat en commit till detta arkiv, kontakta administratören.",
"admin.moderation.deleted_content_ref": "Rapporterat innehåll med typ %[1]v och id %[2]d finns inte längre",
"settings.twofa_reenroll": "Registrera om tvåfaktorsautentisering",
"settings.twofa_reenroll.description": "Registrera om din tvåfaktorsautentisering",
"user.ghost.tooltip": "Denna användare har tagits bort eller kan inte matchas.",
"migrate.pagure.incorrect_url": "Felaktig URL till källarkivet har angetts",
"migrate.pagure.private_issues.summary": "Privata ärenden (valfritt)",
"migrate.pagure.private_issues.description": "Denna funktion är utformad för att skapa ett andra arkiv som endast innehåller privata ärenden från ditt Pagure-projekt för arkiveringsändamål. Utför först en normal migrering (utan token) för att importera allt publika innehåll. Om du har privata ärenden som du vill bevara skapar du sedan ett separat arkiv med hjälp av denna token-option för att arkivera dessa privata ärenden.",
"migrate.pagure.private_issues.warning": "Se till att ställa in synligheten för arkivet ovan till Privat om du använder API-nyckeln för att importera privata ärenden. Detta förhindrar att privat innehåll av misstag exponeras i ett publikt arkiv.",
"migrate.pagure.token.placeholder": "Endast för att skapa privata ärendearkiv",
"actions.runs.viewing_out_of_date_run": "Du tittar på en föråldrad körning av detta jobb som utfördes %[1]s.",
"actions.runs.view_most_recent_run": "Visa senaste körning",
"actions.workflow.job_parsing_error": "Kunde inte tolka jobb i arbetsflöde: %v",
"actions.workflow.event_detection_error": "Det går inte att tolka stödda händelser i arbetsflödet: %v",
"actions.workflow.pre_execution_error": "Arbetsflödet kördes inte på grund av ett fel som blockerade körningsförsöket."
}

View file

@ -127,7 +127,7 @@
"warning.repository.out_of_sync": "База даних цього репозиторію не синхронізована. Якщо ви знову бачите це попередження після відправлення коміту в цей репозиторій, зверніться до адміністратора.",
"repo.pulls.already_merged": "Не вдалося об'єднати: цей запит на злиття вже об'єднано.",
"migrate.pagure.description": "Перенести дані з pagure.io або інших екземплярів Pagure.",
"migrate.pagure.token_label": "Токен",
"migrate.pagure.token_label": "Токен API Pagure",
"migrate.pagure.project_url": "URL-адреса проєкту Pagure",
"migrate.pagure.project_example": "URL-адреса проєкту Pagure, наприклад, https://pagure.io/pagure",
"migrate.pagure.token_body_b": "Якщо хочете зробити цей репозиторій приватним, обов'язково встановіть прапорець «Приватний репозиторій» вище",
@ -156,5 +156,12 @@
"migrate.form.error.url_credentials": "URL-адреса містить дані для входу, введіть їх у поля «Ім'я користувача» і «Пароль» відповідно",
"actions.runs.run_attempt_label": "Спроба запуску №%[1]s (%[2]s)",
"actions.runs.view_most_recent_run": "Переглянути останній запуск",
"actions.runs.viewing_out_of_date_run": "Ви переглядаєте застарілий запуск цього завдання, який було виконано %[1]s."
"actions.runs.viewing_out_of_date_run": "Ви переглядаєте застарілий запуск цього завдання, який було виконано %[1]s.",
"migrate.pagure.private_issues.summary": "Приватні задачі (необов'язково)",
"migrate.pagure.private_issues.description": "Ця функція дозволяє створити додатковий репозиторій для архівування в ньому приватних задач із вашого проєкту Pagure. Спочатку виконайте звичайну міграцію (без токена), щоб імпортувати весь публічний вміст. Потім, якщо у вас є приватні задачі, які потрібно зберегти, створіть окремий репозиторій, використовуючи токен, щоб архівувати ці приватні задачі.",
"migrate.pagure.private_issues.warning": "Обов'язково встановіть видимість репозиторію на «Приватний», якщо використовуєте ключ API для імпорту приватних задач. Це запобігає випадковому розкриттю приватного вмісту в публічному репозиторії.",
"migrate.pagure.token.placeholder": "Тільки для створення архіву приватних задач",
"actions.workflow.job_parsing_error": "Не вдалося розібрати завдання в робочому потоці: %v",
"actions.workflow.event_detection_error": "Не вдалося розібрати підтримувані події в робочому потоці: %v",
"actions.workflow.pre_execution_error": "Спробу виконання робочого потоку було заблоковано через помилку."
}

View file

@ -1 +1,21 @@
{}
{
"home.explore_repos": "Omborlarni kashf etish",
"home.explore_users": "Foydalanuvchilarni kashf etish",
"home.explore_orgs": "`tashkilotlarni` kashf etish",
"stars.list.none": "Hech kim bu omborga yulduz bosmagan.",
"watch.list.none": "Bu ombor hech kimning ko'ruv'ida emas",
"followers.incoming.list.self.none": "Hech kim sizning profilingizga tirkalmagan.",
"followers.incoming.list.none": "Hech kim bu foydalanuvchiga tirkalmagan.",
"followers.outgoing.list.self.none": "Siz hech kimga tirkalmagansiz.",
"followers.outgoing.list.none": "%s hech kimga tirkalmagan.",
"relativetime.now": "hozir",
"relativetime.future": "kelajakda",
"relativetime.1day": "kecha",
"relativetime.2days": "2 kun oldin",
"relativetime.1week": "oxirgi hafta",
"relativetime.2weeks": "ikki hafta oldin",
"relativetime.1month": "oxirgi oy",
"relativetime.2months": "ikki oy oldin",
"relativetime.1year": "oxirgi yil",
"relativetime.2years": "ikki yil oldin"
}

View file

@ -15,7 +15,7 @@
"alert.asset_load_failed": "无法从 {path} 加载资源文件。请确保资源文件可被访问。",
"install.invalid_lfs_path": "无法在指定路径创建 LFS 根目录:%[1]s",
"alert.range_error": " 必须是一个介于 %[1]s 和 %[2]s 之间的数字。",
"meta.last_line": "感谢各位对Forgejo翻译的支持和帮助不需要翻译这个。",
"meta.last_line": "感谢各位对Forgejo翻译的支持和帮助不需要翻译这个。(三维鱼)。",
"mail.actions.successful_run_after_failure_subject": "仓库 %[2]s 中的工作流 %[1]s 已恢复",
"mail.actions.not_successful_run_subject": "仓库 %[2]s 中的工作流 %[1]s 已失败",
"mail.actions.successful_run_after_failure": "仓库 %[2]s 中的工作流 %[1]s 已恢复",
@ -116,7 +116,7 @@
"migrate.pagure.incorrect_url": "提供了错误的源仓库URL",
"migrate.pagure.project_url": "Pagure 项目 URL",
"migrate.pagure.project_example": "Pagure 项目 URL例如https://pagure.io/pagure",
"migrate.pagure.token_label": "令牌",
"migrate.pagure.token_label": "Pagure API 令牌",
"migrate.pagure.token_body_a": "提供可访问私有议题的 Pagure API 令牌以创建一个只有私有议题的仓库",
"migrate.pagure.token_body_b": "如果你希望此仓库为私有,记得选择上面的私有仓库选项!",
"repo.pulls.already_merged": "合并失败:此合并请求已被合并。",
@ -124,5 +124,12 @@
"migrate.form.error.url_credentials": "URL 包含凭据,请分别将凭据填入用户名和密码字段",
"actions.runs.view_most_recent_run": "查看最新运行",
"actions.runs.run_attempt_label": "运行尝试 #%[1]s%[2]s",
"actions.runs.viewing_out_of_date_run": "您正在查看于 %[1]s 执行的过期运行。"
"actions.runs.viewing_out_of_date_run": "您正在查看于 %[1]s 执行的过期运行。",
"migrate.pagure.private_issues.summary": "私有议题(可选)",
"migrate.pagure.private_issues.description": "此功能将创建一个单独的、只包含私有议题的仓库,以供存档。首先,不带令牌进行一次普通迁移以导入公开内容,然后若有需要,填写令牌并再次迁移以创建一个新的仓库并存档私有议题。",
"migrate.pagure.private_issues.warning": "请确保在导入私有议题时将仓库可见性设置为私有,以避免意外将私有内容泄漏至公开仓库。",
"migrate.pagure.token.placeholder": "仅用于创建私有议题归档",
"actions.workflow.job_parsing_error": "无法解析工作流中的任务:%v",
"actions.workflow.event_detection_error": "无法解析工作流中受支持的事件:%v",
"actions.workflow.pre_execution_error": "因有错误阻止了执行尝试,工作流未被执行。"
}

1
release-notes/9689.md Normal file
View file

@ -0,0 +1 @@
Uploaded avatar images can sometimes contain unexpected metadata such as the location where the image was created, or the device the image was created with, stored in a format called EXIF. Forgejo now removes EXIF data when custom user and repository images are uploaded in order to reduce the risk of personally identifiable information being leaked unexpectedly. A new CLI subcommand `forgejo doctor avatar-strip-exif` can be used to strip EXIF information from all existing avatars; we recommend that administrators run this command once after upgrade in order to minimize this risk for existing stored files.

4
release-notes/9849.md Normal file
View file

@ -0,0 +1,4 @@
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/8885844e72fedc3585a1f42f16c430ab0cbeb90f) prevent commit API from leaking user's hidden email address on valid GPG signed commits
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/449b5bf10e3ba4baf2ef33b535b5e8ba15711838) prevent writing to out-of-repo symlink destinations while evaluating template repos
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/afbf1efe028a33fb79c1af208d28a993db8ca2af) prevent .forgejo/template from being out-of-repo content
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/fa1a2ba669301238cf3da6a3e746912d76e47f36) return on error if an LFS token cannot be parsed

View file

@ -631,7 +631,7 @@ func CommonRoutes() *web.Route {
baseURLPattern = regexp.MustCompile(`\A(.*?)\.repo\z`)
uploadPattern = regexp.MustCompile(`\A(.*?)/upload\z`)
baseRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\/base/(\S+)`)
rpmsRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\.(\S+)\/([a-zA-Z0-9_-]+)-([\d.]+-[a-zA-Z0-9_.-]+)\.(\S+)\.rpm`)
rpmsRepoPattern = regexp.MustCompile(`\A/(.+?)\.repo/([^/]+)/RPMS\.([^/]+)/(.+)-([0-9][^-]*-[^-]*)\.([^.]+)\.rpm\z`)
)
r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) {

View file

@ -61,7 +61,7 @@ func ForgotPasswdPost(ctx *context.Context) {
email := ctx.FormString("email")
ctx.Data["Email"] = email
u, err := user_model.GetUserByEmail(ctx, email)
u, err := user_model.GetUserByEmailSimple(ctx, email)
if err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale)

View file

@ -178,6 +178,7 @@ type ViewRunInfo struct {
Done bool `json:"done"`
Jobs []*ViewJob `json:"jobs"`
Commit ViewCommit `json:"commit"`
PreExecutionError string `json:"preExecutionError"`
}
type ViewCurrentJob struct {
@ -285,6 +286,7 @@ func getViewResponse(ctx *context_module.Context, req *ViewRequest, runIndex, jo
resp.State.Run.CanDeleteArtifact = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions)
resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead of 'null' in json
resp.State.Run.Status = run.Status.String()
resp.State.Run.PreExecutionError = run.PreExecutionError
// It's possible for the run to be marked with a finalized status (eg. failure) because of a single job within the
// run; eg. one job fails, the run fails. But other jobs can still be running. The frontend RepoActionView uses the

View file

@ -172,7 +172,8 @@ func SetRulePreviewContext(ctx *context.Context, owner *user_model.User) {
ctx.ServerError("SearchVersions", err)
return
}
for _, pv := range pvs[pcr.KeepCount:] {
keep := min(len(pvs), pcr.KeepCount)
for _, pv := range pvs[keep:] {
if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
ctx.ServerError("ShouldBeSkipped", err)
return

View file

@ -26,6 +26,7 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/setting"
api "forgejo.org/modules/structs"
"forgejo.org/modules/translation"
"forgejo.org/modules/util"
webhook_module "forgejo.org/modules/webhook"
"forgejo.org/services/convert"
@ -378,13 +379,25 @@ func handleWorkflows(
continue
}
jobs, err := jobParser(dwf.Content, jobparser.WithVars(vars))
if err != nil {
var jobs []*jobparser.SingleWorkflow
if dwf.EventDetectionError != nil { // don't even bother trying to parse jobs due to event detection error
tr := translation.NewLocale(input.Doer.Language)
run.PreExecutionError = tr.TrString("actions.workflow.event_detection_error", dwf.EventDetectionError)
run.Status = actions_model.StatusFailure
log.Info("jobparser.Parse: invalid workflow, setting job status to failed: %v", err)
jobs = []*jobparser.SingleWorkflow{{
Name: dwf.EntryName,
}}
} else {
jobs, err = jobParser(dwf.Content, jobparser.WithVars(vars))
if err != nil {
log.Info("jobparser.Parse: invalid workflow, setting job status to failed: %v", err)
tr := translation.NewLocale(input.Doer.Language)
run.PreExecutionError = tr.TrString("actions.workflow.job_parsing_error", err)
run.Status = actions_model.StatusFailure
jobs = []*jobparser.SingleWorkflow{{
Name: dwf.EntryName,
}}
}
}
// cancel running jobs if the event is push or pull_request_sync

View file

@ -4,6 +4,7 @@
package actions
import (
"errors"
"testing"
actions_model "forgejo.org/models/actions"
@ -144,3 +145,88 @@ func Test_OpenForkPullRequestEvent(t *testing.T) {
assert.Equal(t, webhook_module.HookEventPullRequest, runs[0].Event)
assert.True(t, runs[0].IsForkPullRequest)
}
func TestActionsPreExecutionErrorInvalidJobs(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 3})
commit := &git.Commit{
ID: git.MustIDFromString("0000000000000000000000000000000000000000"),
CommitMessage: "test",
}
detectedWorkflows := []*actions_module.DetectedWorkflow{
{
EntryName: "test.yml",
TriggerEvent: &jobparser.Event{
Name: "pull_request",
},
Content: []byte("{ on: pull_request, jobs: 'hello, I am the jobs!' }"),
},
}
input := &notifyInput{
Repo: repo,
Doer: doer,
Event: webhook_module.HookEventPullRequestSync,
PullRequest: pr,
Payload: &api.PullRequestPayload{},
}
err := handleWorkflows(db.DefaultContext, detectedWorkflows, commit, input, "refs/head/main")
require.NoError(t, err)
runs, err := db.Find[actions_model.ActionRun](db.DefaultContext, actions_model.FindRunOptions{
RepoID: repo.ID,
})
require.NoError(t, err)
require.Len(t, runs, 1)
createdRun := runs[0]
assert.Equal(t, actions_model.StatusFailure, createdRun.Status)
assert.Contains(t, createdRun.PreExecutionError, "actions.workflow.job_parsing_error%!(EXTRA *fmt.wrapError=")
}
func TestActionsPreExecutionEventDetectionError(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 3})
commit := &git.Commit{
ID: git.MustIDFromString("0000000000000000000000000000000000000000"),
CommitMessage: "test",
}
detectedWorkflows := []*actions_module.DetectedWorkflow{
{
EntryName: "test.yml",
TriggerEvent: &jobparser.Event{
Name: "pull_request",
},
Content: []byte("{ on: nothing, jobs: { j1: {} }}"),
EventDetectionError: errors.New("nothing is not a valid event"),
},
}
input := &notifyInput{
Repo: repo,
Doer: doer,
Event: webhook_module.HookEventPullRequestSync,
PullRequest: pr,
Payload: &api.PullRequestPayload{},
}
err := handleWorkflows(db.DefaultContext, detectedWorkflows, commit, input, "refs/head/main")
require.NoError(t, err)
runs, err := db.Find[actions_model.ActionRun](db.DefaultContext, actions_model.FindRunOptions{
RepoID: repo.ID,
})
require.NoError(t, err)
require.Len(t, runs, 1)
createdRun := runs[0]
assert.Equal(t, actions_model.StatusFailure, createdRun.Status)
assert.Equal(t, "actions.workflow.event_detection_error%!(EXTRA *errors.errorString=nothing is not a valid event)", createdRun.PreExecutionError)
}

View file

@ -139,7 +139,6 @@ func (ctx *Context) validateTwoFactorRequirement() {
hasTwoFactor, err := auth.HasTwoFactorByUID(ctx, ctx.Doer.ID)
if err != nil {
log.ErrorWithSkip(2, "Error getting 2fa: %s", err)
// fallthrough to set the variables
}
ctx.Data["MustEnableTwoFactor"] = !hasTwoFactor
ctx.Data["HideNavbarLinks"] = !hasTwoFactor

View file

@ -236,7 +236,7 @@ func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerifi
if verif.SigningUser != nil {
commitVerification.Signer = &api.PayloadUser{
Name: verif.SigningUser.Name,
Email: verif.SigningUser.Email,
Email: verif.SigningEmail,
}
}
return commitVerification

View file

@ -0,0 +1,105 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package convert
import (
"testing"
"forgejo.org/models/db"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
"forgejo.org/modules/git"
api "forgejo.org/modules/structs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestToVerification(t *testing.T) {
defer unittest.OverrideFixtures("models/fixtures/TestParseCommitWithSSHSignature")()
require.NoError(t, unittest.PrepareTestDatabase())
// Change the user's primary email address to ensure this value isn't ambiguous with any other return value from
// signature verification.
userModel := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
userModel.Email = "secret-email@example.com"
db.GetEngine(t.Context()).ID(userModel.ID).Cols("email").Update(userModel)
t.Run("SSH Key Signature", func(t *testing.T) {
commit := &git.Commit{
Committer: &git.Signature{
Email: "user2@example.com",
},
Signature: &git.ObjectSignature{
Payload: `tree 853694aae8816094a0d875fee7ea26278dbf5d0f
parent c2780d5c313da2a947eae22efd7dacf4213f4e7f
author user2 <user2@example.com> 1699707877 +0100
committer user2 <user2@example.com> 1699707877 +0100
Add content
`,
Signature: `-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95
f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQBe2Fwk/FKY3SBCnG6jSYcO6ucyahp2SpQ/0P+otslzIHpWNW8cQ0fGLdhhaFynJXQ
fs9cMpZVM9BfIKNUSO8QY=
-----END SSH SIGNATURE-----
`,
},
}
commitVerification := ToVerification(t.Context(), commit)
require.NotNil(t, commitVerification)
assert.Equal(t, &api.PayloadCommitVerification{
Verified: true,
Reason: "user2 / SHA256:TKfwbZMR7e9OnlV2l1prfah1TXH8CmqR0PvFEXVCXA4",
Signature: "-----BEGIN SSH SIGNATURE-----\nU1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95\nf5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5\nAAAAQBe2Fwk/FKY3SBCnG6jSYcO6ucyahp2SpQ/0P+otslzIHpWNW8cQ0fGLdhhaFynJXQ\nfs9cMpZVM9BfIKNUSO8QY=\n-----END SSH SIGNATURE-----\n",
Signer: &api.PayloadUser{
Name: "user2",
Email: "user2@example.com", // expected email will match the commit's committer's email, regardless of `KeepEmailPrivate`.
},
Payload: "tree 853694aae8816094a0d875fee7ea26278dbf5d0f\nparent c2780d5c313da2a947eae22efd7dacf4213f4e7f\nauthor user2 <user2@example.com> 1699707877 +0100\ncommitter user2 <user2@example.com> 1699707877 +0100\n\nAdd content\n",
}, commitVerification)
})
t.Run("GPG Signature", func(t *testing.T) {
commit := &git.Commit{
ID: git.MustIDFromString("e20aa0bcd2878f65a93de68a3eed9045d6efdd74"),
Committer: &git.Signature{
Email: "user2@example.com",
},
Signature: &git.ObjectSignature{
Payload: `tree e20aa0bcd2878f65a93de68a3eed9045d6efdd74
parent 5cd9b9847563eb730d63d23c1f1b84868e52ae7d
author user2 <user2+committer@example.com> 1759956520 -0600
committer user2 <user2+committer@example.com> 1759956520 -0600
Add content
`,
Signature: `-----BEGIN PGP SIGNATURE-----
iQEzBAABCgAdFiEEdlqhn25IEoMmvK5vmDaXTfEZWRMFAmjmzigACgkQmDaXTfEZ
WROC4ggAs8mD8csA6FV5e2v/4HcxuaZKCN+D8Gvku2JUigODQCA+NOX0FF2jDnCh
tXylBPB4HJw1spKkDLtOpnCUSOniBdl9NcZjnBt6sP/OSnEfLznXFra+9fCHzsu0
9uhDn3Wn1iHWXQ2ZglUwVS0ja6pNgEip8wNZBysv8+XbO1CEEW0m7zQA6tunzIwp
yiPZDUJrKtpKAK0+v19EccT2VjYAa+Vo+p3/E0piaTYNbsTqtFRy63tdjDkf+mo+
l/PaPhrMqdnbxv3/sd/63VCNdvPH3f0+OuydcC7mXyysmvap99EC+QKnpsrm7RAP
uf51WIBywxztet6vi+jYJK1jFoY4iA==
=Lnrt
-----END PGP SIGNATURE-----`,
},
}
commitVerification := ToVerification(t.Context(), commit)
require.NotNil(t, commitVerification)
assert.Equal(t, &api.PayloadCommitVerification{
Verified: true,
Reason: "user2 / 9836974DF1195913",
Signature: "-----BEGIN PGP SIGNATURE-----\n\niQEzBAABCgAdFiEEdlqhn25IEoMmvK5vmDaXTfEZWRMFAmjmzigACgkQmDaXTfEZ\nWROC4ggAs8mD8csA6FV5e2v/4HcxuaZKCN+D8Gvku2JUigODQCA+NOX0FF2jDnCh\ntXylBPB4HJw1spKkDLtOpnCUSOniBdl9NcZjnBt6sP/OSnEfLznXFra+9fCHzsu0\n9uhDn3Wn1iHWXQ2ZglUwVS0ja6pNgEip8wNZBysv8+XbO1CEEW0m7zQA6tunzIwp\nyiPZDUJrKtpKAK0+v19EccT2VjYAa+Vo+p3/E0piaTYNbsTqtFRy63tdjDkf+mo+\nl/PaPhrMqdnbxv3/sd/63VCNdvPH3f0+OuydcC7mXyysmvap99EC+QKnpsrm7RAP\nuf51WIBywxztet6vi+jYJK1jFoY4iA==\n=Lnrt\n-----END PGP SIGNATURE-----",
Signer: &api.PayloadUser{
Name: "user2",
Email: "user2+signingkey@example.com", // expected email will match the signing key's email
},
Payload: "tree e20aa0bcd2878f65a93de68a3eed9045d6efdd74\nparent 5cd9b9847563eb730d63d23c1f1b84868e52ae7d\nauthor user2 <user2+committer@example.com> 1759956520 -0600\ncommitter user2 <user2+committer@example.com> 1759956520 -0600\n\nAdd content\n",
}, commitVerification)
})
}

View file

@ -580,9 +580,6 @@ func authenticate(ctx *context.Context, repository *repo_model.Repository, autho
}
func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repository, mode perm.AccessMode) (*user_model.User, error) {
if !strings.Contains(tokenSHA, ".") {
return nil, nil
}
token, err := jwt.ParseWithClaims(tokenSHA, &Claims{}, func(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
@ -590,7 +587,7 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo
return setting.LFS.JWTSecretBytes, nil
})
if err != nil {
return nil, nil
return nil, errors.New("invalid token")
}
claims, claimsOk := token.Claims.(*Claims)

View file

@ -0,0 +1,82 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package lfs
import (
"fmt"
"strings"
"testing"
"time"
perm_model "forgejo.org/models/perm"
repo_model "forgejo.org/models/repo"
"forgejo.org/models/unittest"
"forgejo.org/modules/setting"
"forgejo.org/services/contexttest"
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
unittest.MainTest(m)
}
type authTokenOptions struct {
Op string
UserID int64
RepoID int64
}
func getLFSAuthTokenWithBearer(opts authTokenOptions) (string, error) {
now := time.Now()
claims := Claims{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
NotBefore: jwt.NewNumericDate(now),
},
RepoID: opts.RepoID,
Op: opts.Op,
UserID: opts.UserID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Sign and get the complete encoded token as a string using the secret
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
if err != nil {
return "", fmt.Errorf("failed to sign LFS JWT token: %w", err)
}
return "Bearer " + tokenString, nil
}
func TestAuthenticate(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
token2, _ := getLFSAuthTokenWithBearer(authTokenOptions{Op: "download", UserID: 2, RepoID: 1})
_, token2, _ = strings.Cut(token2, " ")
ctx, _ := contexttest.MockContext(t, "/")
t.Run("handleLFSToken", func(t *testing.T) {
u, err := handleLFSToken(ctx, "", repo1, perm_model.AccessModeRead)
require.Error(t, err)
assert.Nil(t, u)
u, err = handleLFSToken(ctx, "invalid", repo1, perm_model.AccessModeRead)
require.Error(t, err)
assert.Nil(t, u)
u, err = handleLFSToken(ctx, token2, repo1, perm_model.AccessModeRead)
require.NoError(t, err)
assert.EqualValues(t, 2, u.ID)
})
t.Run("authenticate", func(t *testing.T) {
const prefixBearer = "Bearer "
assert.False(t, authenticate(ctx, repo1, "", true, false))
assert.False(t, authenticate(ctx, repo1, prefixBearer+"invalid", true, false))
assert.True(t, authenticate(ctx, repo1, prefixBearer+token2, true, false))
})
}

View file

@ -16,6 +16,7 @@ import (
"forgejo.org/modules/log"
base "forgejo.org/modules/migration"
"forgejo.org/modules/proxy"
"forgejo.org/modules/setting"
"forgejo.org/modules/structs"
"forgejo.org/modules/util"
)
@ -279,6 +280,11 @@ func (d *PagureDownloader) callAPI(endpoint string, parameter map[string]string,
if err != nil {
return err
}
// pagure.io is protected by Anubis and requires proper headers
req.Header.Add("Accept", "*/*")
req.Header.Add("User-Agent", "Forgejo/"+setting.AppVer)
if d.privateIssuesOnlyRepo {
req.Header.Set("Authorization", "token "+d.token)
}
@ -344,7 +350,7 @@ func (d *PagureDownloader) GetMilestones() ([]*base.Milestone, error) {
func (d *PagureDownloader) GetLabels() ([]*base.Label, error) {
rawLabels := PagureLabelsList{}
err := d.callAPI("/api/0/"+d.repoName+"/tags", nil, &rawLabels)
err := d.callAPI("/api/0/"+d.repoName+"/tags/", nil, &rawLabels)
if err != nil {
return nil, err
}

View file

@ -69,7 +69,8 @@ func ExecuteCleanupRules(outerCtx context.Context) error {
return fmt.Errorf("CleanupRule [%d]: SearchVersions failed: %w", pcr.ID, err)
}
versionDeleted := false
for _, pv := range pvs[pcr.KeepCount:] {
keep := min(len(pvs), pcr.KeepCount)
for _, pv := range pvs[keep:] {
if pcr.Type == packages_model.TypeContainer {
if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil {
return fmt.Errorf("CleanupRule [%d]: container.ShouldBeSkipped failed: %w", pcr.ID, err)

View file

@ -44,7 +44,7 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error {
// If the line got changed the comment is going to be invalidated.
func checkInvalidation(ctx context.Context, c *issues_model.Comment, repo *git.Repository, branch string) error {
// FIXME differentiate between previous and proposed line
commit, err := repo.LineBlame(branch, c.TreePath, c.UnsignedLine())
commit, _, err := repo.LineBlame(branch, c.TreePath, c.UnsignedLine())
if err != nil && (errors.Is(err, git.ErrBlameFileDoesNotExist) || errors.Is(err, git.ErrBlameFileNotEnoughLines)) {
c.Invalidated = true
return issues_model.UpdateCommentInvalidate(ctx, c)
@ -178,7 +178,8 @@ func CreateCodeComment(ctx context.Context, doer *user_model.User, gitRepo *git.
// CreateCodeCommentKnownReviewID creates a plain code comment at the specified line / path
func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, treePath string, line, reviewID int64, attachments []string) (*issues_model.Comment, error) {
var commitID, patch string
var commitID, blamedCommitID, patch string
blamedLine := line
if err := issue.LoadPullRequest(ctx); err != nil {
return nil, fmt.Errorf("LoadPullRequest: %w", err)
}
@ -226,12 +227,15 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User,
// FIXME validate treePath
// Get latest commit referencing the commented line
// No need for get commit for base branch changes
commit, err := gitRepo.LineBlame(head, treePath, uint64(line))
commit, lineres, err := gitRepo.LineBlame(head, treePath, uint64(line))
if err == nil {
commitID = commit.ID.String()
blamedCommitID = commit.ID.String()
blamedLine = int64(lineres)
} else if !errors.Is(err, git.ErrBlameFileDoesNotExist) && !errors.Is(err, git.ErrBlameFileNotEnoughLines) {
return nil, fmt.Errorf("LineBlame[%s, %s, %s, %d]: %w", pr.GetGitRefName(), gitRepo.Path, treePath, line, err)
}
} else {
blamedCommitID = commitID
}
}
@ -243,6 +247,9 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User,
return nil, fmt.Errorf("GetRefCommitID[%s]: %w", head, err)
}
}
if len(blamedCommitID) == 0 {
blamedCommitID = commitID
}
reader, writer := io.Pipe()
defer func() {
_ = reader.Close()
@ -268,9 +275,9 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User,
Repo: repo,
Issue: issue,
Content: content,
LineNum: line,
LineNum: blamedLine,
TreePath: treePath,
CommitSHA: commitID,
CommitSHA: blamedCommitID,
ReviewID: reviewID,
Patch: patch,
Invalidated: invalidated,

View file

@ -69,7 +69,7 @@ func TestCheckUnadoptedRepositories(t *testing.T) {
func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
username := "user2"
unadoptedList := []string{path.Join(username, "unadopted1"), path.Join(username, "unadopted2")}
unadoptedList := []string{path.Join(username, "rendering-test"), path.Join(username, "unadopted1"), path.Join(username, "unadopted2")}
for _, unadopted := range unadoptedList {
_ = os.Mkdir(path.Join(setting.RepoRootPath, unadopted+".git"), 0o755)
}
@ -77,13 +77,13 @@ func TestListUnadoptedRepositories_ListOptions(t *testing.T) {
opts := db.ListOptions{Page: 1, PageSize: 1}
repoNames, count, err := ListUnadoptedRepositories(db.DefaultContext, "", &opts)
require.NoError(t, err)
assert.Equal(t, 2, count)
assert.Equal(t, 3, count)
assert.Equal(t, unadoptedList[0], repoNames[0])
opts = db.ListOptions{Page: 2, PageSize: 1}
repoNames, count, err = ListUnadoptedRepositories(db.DefaultContext, "", &opts)
require.NoError(t, err)
assert.Equal(t, 2, count)
assert.Equal(t, 3, count)
assert.Equal(t, unadoptedList[1], repoNames[0])
}
@ -97,7 +97,7 @@ func TestAdoptRepository(t *testing.T) {
path.Join(setting.RepoRootPath, username, unadopted+".git"),
))
opts := db.ListOptions{Page: 1, PageSize: 1}
opts := db.ListOptions{Page: 2, PageSize: 1}
repoNames, _, err := ListUnadoptedRepositories(db.DefaultContext, "", &opts)
require.NoError(t, err)
require.Contains(t, repoNames, path.Join(username, unadopted))

View file

@ -43,6 +43,7 @@ type ChangeRepoFile struct {
ContentReader io.ReadSeeker
SHA string
Options *RepoFileOptions
Symlink bool
}
// ChangeRepoFilesOptions holds the repository files update options
@ -62,6 +63,7 @@ type RepoFileOptions struct {
treePath string
fromTreePath string
executable bool
symlink bool
}
// ChangeRepoFiles adds, updates or removes multiple files in the given repository
@ -116,6 +118,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
treePath: treePath,
fromTreePath: fromTreePath,
executable: false,
symlink: file.Symlink,
}
treePaths = append(treePaths, treePath)
}
@ -427,14 +430,14 @@ func CreateOrUpdateFile(ctx context.Context, t *TemporaryUploadRepository, file
}
// Add the object to the index
mode := "100644" // regular file
if file.Options.executable {
if err := t.AddObjectToIndex("100755", objectHash, file.Options.treePath); err != nil {
return err
}
} else {
if err := t.AddObjectToIndex("100644", objectHash, file.Options.treePath); err != nil {
return err
}
mode = "100755"
} else if file.Options.symlink {
mode = "120644"
}
if err := t.AddObjectToIndex(mode, objectHash, file.Options.treePath); err != nil {
return err
}
if lfsMetaObject != nil {

View file

@ -9,8 +9,6 @@ import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
@ -121,143 +119,6 @@ func (gt *GiteaTemplate) Globs() []glob.Glob {
return gt.globs
}
func checkGiteaTemplate(tmpDir string) (*GiteaTemplate, error) {
configDirs := []string{".forgejo", ".gitea"}
var templateFilePath string
for _, dir := range configDirs {
candidatePath := filepath.Join(tmpDir, dir, "template")
if _, err := os.Stat(candidatePath); err == nil {
templateFilePath = candidatePath
break
} else if !os.IsNotExist(err) {
return nil, err
}
}
if templateFilePath == "" {
return nil, nil
}
content, err := os.ReadFile(templateFilePath)
if err != nil {
return nil, err
}
return &GiteaTemplate{
Path: templateFilePath,
Content: content,
}, nil
}
func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *repo_model.Repository, tmpDir string) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+authorSig.Name,
"GIT_AUTHOR_EMAIL="+authorSig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+authorSig.Name,
"GIT_COMMITTER_EMAIL="+authorSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
// Clone to temporary path and do the init commit.
templateRepoPath := templateRepo.RepoPath()
if err := git.Clone(ctx, templateRepoPath, tmpDir, git.CloneRepoOptions{
Depth: 1,
Branch: templateRepo.DefaultBranch,
}); err != nil {
return fmt.Errorf("git clone: %w", err)
}
if err := util.RemoveAll(path.Join(tmpDir, ".git")); err != nil {
return fmt.Errorf("remove git dir: %w", err)
}
// Variable expansion
gt, err := checkGiteaTemplate(tmpDir)
if err != nil {
return fmt.Errorf("checkGiteaTemplate: %w", err)
}
if gt != nil {
if err := util.Remove(gt.Path); err != nil {
return fmt.Errorf("remove .giteatemplate: %w", err)
}
// Avoid walking tree if there are no globs
if len(gt.Globs()) > 0 {
tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
if err := filepath.WalkDir(tmpDirSlash, func(path string, d os.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() {
return nil
}
base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash)
for _, g := range gt.Globs() {
if g.Match(base) {
content, err := os.ReadFile(path)
if err != nil {
return err
}
if err := os.WriteFile(path,
[]byte(generateExpansion(string(content), templateRepo, generateRepo, false)),
0o644); err != nil {
return err
}
substPath := filepath.FromSlash(filepath.Join(tmpDirSlash,
generateExpansion(base, templateRepo, generateRepo, true)))
// Create parent subdirectories if needed or continue silently if it exists
if err := os.MkdirAll(filepath.Dir(substPath), 0o755); err != nil {
return err
}
// Substitute filename variables
if err := os.Rename(path, substPath); err != nil {
return err
}
break
}
}
return nil
}); err != nil {
return err
}
}
}
if err := git.InitRepository(ctx, tmpDir, false, templateRepo.ObjectFormatName); err != nil {
return err
}
repoPath := repo.RepoPath()
if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin").AddDynamicArguments(repoPath).
SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)).
RunStdString(&git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git remote add: %w", err)
}
// set default branch based on whether it's specified in the newly generated repo or not
defaultBranch := repo.DefaultBranch
if strings.TrimSpace(defaultBranch) == "" {
defaultBranch = templateRepo.DefaultBranch
}
return initRepoCommit(ctx, tmpDir, repo, repo.Owner, defaultBranch)
}
func generateGitContent(ctx context.Context, repo, templateRepo, generateRepo *repo_model.Repository) (err error) {
tmpDir, err := os.MkdirTemp(os.TempDir(), "gitea-"+repo.Name)
if err != nil {

View file

@ -0,0 +1,178 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build go1.25
package repository
import (
"context"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"time"
repo_model "forgejo.org/models/repo"
"forgejo.org/modules/git"
"forgejo.org/modules/log"
"forgejo.org/modules/util"
)
func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *repo_model.Repository, tmpDir string) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
// Because this may call hooks we should pass in the environment
env := append(os.Environ(),
"GIT_AUTHOR_NAME="+authorSig.Name,
"GIT_AUTHOR_EMAIL="+authorSig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr,
"GIT_COMMITTER_NAME="+authorSig.Name,
"GIT_COMMITTER_EMAIL="+authorSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
// Clone to temporary path and do the init commit.
templateRepoPath := templateRepo.RepoPath()
if err := git.Clone(ctx, templateRepoPath, tmpDir, git.CloneRepoOptions{
Depth: 1,
Branch: templateRepo.DefaultBranch,
}); err != nil {
return fmt.Errorf("git clone: %w", err)
}
if err := util.RemoveAll(path.Join(tmpDir, ".git")); err != nil {
return fmt.Errorf("remove git dir: %w", err)
}
// Variable expansion
gt, err := checkGiteaTemplate(tmpDir)
if err != nil {
return fmt.Errorf("checkGiteaTemplate: %w", err)
}
if gt != nil {
if err := util.Remove(gt.Path); err != nil {
return fmt.Errorf("remove .giteatemplate: %w", err)
}
// Avoid walking tree if there are no globs
if len(gt.Globs()) > 0 {
// All file access should be done through `root` to avoid file traversal attacks, especially with symlinks
root, err := os.OpenRoot(tmpDir)
if err != nil {
return fmt.Errorf("open root: %w", err)
}
defer root.Close()
tmpDirSlash := strings.TrimSuffix(filepath.ToSlash(tmpDir), "/") + "/"
if err := filepath.WalkDir(tmpDirSlash, func(path string, d os.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() {
return nil
}
base := strings.TrimPrefix(filepath.ToSlash(path), tmpDirSlash)
for _, g := range gt.Globs() {
if g.Match(base) {
// `path` will be an absolute filepath from `WalkDir`, but `os.Root` requires all accesses are
// relative file paths from the root -- use `relPath` from here out.
relPath, err := filepath.Rel(tmpDir, path)
if err != nil {
return err
}
content, err := root.ReadFile(relPath)
if err != nil {
return err
}
if err := root.WriteFile(relPath,
[]byte(generateExpansion(string(content), templateRepo, generateRepo, false)),
0o644); err != nil {
return err
}
substPath := generateExpansion(relPath, templateRepo, generateRepo, true)
// Create parent subdirectories if needed or continue silently if it exists
if err := root.MkdirAll(filepath.Dir(substPath), 0o755); err != nil {
return err
}
// Substitute filename variables
if err := root.Rename(relPath, substPath); err != nil {
return err
}
break
}
}
return nil
}); err != nil {
return err
}
}
}
if err := git.InitRepository(ctx, tmpDir, false, templateRepo.ObjectFormatName); err != nil {
return err
}
repoPath := repo.RepoPath()
if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin").AddDynamicArguments(repoPath).
SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)).
RunStdString(&git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)
return fmt.Errorf("git remote add: %w", err)
}
// set default branch based on whether it's specified in the newly generated repo or not
defaultBranch := repo.DefaultBranch
if strings.TrimSpace(defaultBranch) == "" {
defaultBranch = templateRepo.DefaultBranch
}
return initRepoCommit(ctx, tmpDir, repo, repo.Owner, defaultBranch)
}
func checkGiteaTemplate(tmpDir string) (*GiteaTemplate, error) {
configDirs := []string{".forgejo", ".gitea"}
var templateFilePath string
// All file access should be done through `root` to avoid file traversal attacks, especially with symlinks
root, err := os.OpenRoot(tmpDir)
if err != nil {
return nil, fmt.Errorf("open root: %w", err)
}
defer root.Close()
for _, dir := range configDirs {
candidatePath := filepath.Join(dir, "template")
if _, err := root.Stat(candidatePath); err == nil {
templateFilePath = candidatePath
break
} else if !os.IsNotExist(err) {
return nil, err
}
}
if templateFilePath == "" {
return nil, nil
}
content, err := root.ReadFile(templateFilePath)
if err != nil {
return nil, err
}
return &GiteaTemplate{
Path: templateFilePath,
Content: content,
}, nil
}

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