From 43fb63a06388160831e97d0ebb858b8498b98b61 Mon Sep 17 00:00:00 2001 From: Beowulf Date: Thu, 29 May 2025 19:55:57 +0200 Subject: [PATCH 001/297] fix(ui): release: name is overridden with tag name on edit Fixes #8001 Regression: f66a6b12cda96807c29f3c6b2fc851c49f24d739 --- templates/repo/release/new.tmpl | 2 +- tests/e2e/release.test.e2e.ts | 170 ++++++++++++++++++-------------- 2 files changed, 97 insertions(+), 75 deletions(-) diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl index 788d7f015c..c5c7eb23fa 100644 --- a/templates/repo/release/new.tmpl +++ b/templates/repo/release/new.tmpl @@ -46,7 +46,7 @@
- +
{{template "shared/combomarkdowneditor" (dict diff --git a/tests/e2e/release.test.e2e.ts b/tests/e2e/release.test.e2e.ts index 044e7b93ab..a4303a7320 100644 --- a/tests/e2e/release.test.e2e.ts +++ b/tests/e2e/release.test.e2e.ts @@ -14,78 +14,100 @@ import {validate_form} from './shared/forms.ts'; test.use({user: 'user2'}); -test.describe.configure({ - timeout: 30000, -}); - -test('External Release Attachments', async ({page, isMobile}) => { - test.skip(isMobile); - - // Click "New Release" - await page.goto('/user2/repo2/releases'); - await page.click('.button.small.primary'); - - // Fill out form and create new release - await expect(page).toHaveURL('/user2/repo2/releases/new'); - await validate_form({page}, 'fieldset'); - const textarea = page.locator('input[name=tag_name]'); - await textarea.pressSequentially('2.0'); - await expect(page.locator('input[name=title]')).toHaveValue('2.0'); - await page.click('#add-external-link'); - await page.click('#add-external-link'); - await page.fill('input[name=attachment-new-name-2]', 'Test'); - await page.fill('input[name=attachment-new-exturl-2]', 'https://forgejo.org/'); - await page.click('.remove-rel-attach'); - await save_visual(page); - await page.click('.button.small.primary'); - - // Validate release page and click edit - await expect(page).toHaveURL('/user2/repo2/releases'); - await expect(page.locator('.download[open] li')).toHaveCount(3); - - await expect(page.locator('.download[open] li:nth-of-type(1)')).toContainText('Source code (ZIP)'); - await expect(page.locator('.download[open] li:nth-of-type(1) span[data-tooltip-content]')).toHaveAttribute('data-tooltip-content', 'This attachment is automatically generated.'); - await expect(page.locator('.download[open] li:nth-of-type(1) a')).toHaveAttribute('href', '/user2/repo2/archive/2.0.zip'); - await expect(page.locator('.download[open] li:nth-of-type(1) a')).toHaveAttribute('type', 'application/zip'); - - await expect(page.locator('.download[open] li:nth-of-type(2)')).toContainText('Source code (TAR.GZ)'); - await expect(page.locator('.download[open] li:nth-of-type(2) span[data-tooltip-content]')).toHaveAttribute('data-tooltip-content', 'This attachment is automatically generated.'); - await expect(page.locator('.download[open] li:nth-of-type(2) a')).toHaveAttribute('href', '/user2/repo2/archive/2.0.tar.gz'); - await expect(page.locator('.download[open] li:nth-of-type(2) a')).toHaveAttribute('type', 'application/gzip'); - - await expect(page.locator('.download[open] li:nth-of-type(3)')).toContainText('Test'); - await expect(page.locator('.download[open] li:nth-of-type(3) a')).toHaveAttribute('href', 'https://forgejo.org/'); - await save_visual(page); - await page.locator('.octicon-pencil').first().click(); - - // Validate edit page and edit the release - await expect(page).toHaveURL('/user2/repo2/releases/edit/2.0'); - await validate_form({page}, 'fieldset'); - await expect(page.locator('.attachment_edit:visible')).toHaveCount(2); - await expect(page.locator('.attachment_edit:visible').nth(0)).toHaveValue('Test'); - await expect(page.locator('.attachment_edit:visible').nth(1)).toHaveValue('https://forgejo.org/'); - await page.locator('.attachment_edit:visible').nth(0).fill('Test2'); - await page.locator('.attachment_edit:visible').nth(1).fill('https://gitea.io/'); - await page.click('#add-external-link'); - await expect(page.locator('.attachment_edit:visible')).toHaveCount(4); - await page.locator('.attachment_edit:visible').nth(2).fill('Test3'); - await page.locator('.attachment_edit:visible').nth(3).fill('https://gitea.com/'); - await save_visual(page); - await page.click('.button.small.primary'); - - // Validate release page and click edit - await expect(page).toHaveURL('/user2/repo2/releases'); - await expect(page.locator('.download[open] li')).toHaveCount(4); - await expect(page.locator('.download[open] li:nth-of-type(3)')).toContainText('Test2'); - await expect(page.locator('.download[open] li:nth-of-type(3) a')).toHaveAttribute('href', 'https://gitea.io/'); - await expect(page.locator('.download[open] li:nth-of-type(4)')).toContainText('Test3'); - await expect(page.locator('.download[open] li:nth-of-type(4) a')).toHaveAttribute('href', 'https://gitea.com/'); - await save_visual(page); - await page.locator('.octicon-pencil').first().click(); - - // Delete release - await expect(page).toHaveURL('/user2/repo2/releases/edit/2.0'); - await page.click('.delete-button'); - await page.click('.button.ok'); - await expect(page).toHaveURL('/user2/repo2/releases'); +test.describe('repo branch protection settings', () => { + test('External Release Attachments', async ({page, isMobile}, workerInfo) => { + test.skip(isMobile || workerInfo.project.name === 'webkit'); + + // Click "New Release" + await page.goto('/user2/repo2/releases'); + await page.click('.button.small.primary'); + + // Fill out form and create new release + await expect(page).toHaveURL('/user2/repo2/releases/new'); + await validate_form({page}, 'fieldset'); + const textarea = page.locator('input[name=tag_name]'); + await textarea.pressSequentially('2.0'); + await expect(page.locator('input[name=title]')).toHaveValue('2.0'); + await page.click('#add-external-link'); + await page.click('#add-external-link'); + await page.fill('input[name=attachment-new-name-2]', 'Test'); + await page.fill('input[name=attachment-new-exturl-2]', 'https://forgejo.org/'); + await page.click('.remove-rel-attach'); + await save_visual(page); + await page.click('.button.small.primary'); + + // Validate release page and click edit + await expect(page).toHaveURL('/user2/repo2/releases'); + await expect(page.locator('.download[open] li')).toHaveCount(3); + + await expect(page.locator('.download[open] li:nth-of-type(1)')).toContainText('Source code (ZIP)'); + await expect(page.locator('.download[open] li:nth-of-type(1) span[data-tooltip-content]')).toHaveAttribute('data-tooltip-content', 'This attachment is automatically generated.'); + await expect(page.locator('.download[open] li:nth-of-type(1) a')).toHaveAttribute('href', '/user2/repo2/archive/2.0.zip'); + await expect(page.locator('.download[open] li:nth-of-type(1) a')).toHaveAttribute('type', 'application/zip'); + + await expect(page.locator('.download[open] li:nth-of-type(2)')).toContainText('Source code (TAR.GZ)'); + await expect(page.locator('.download[open] li:nth-of-type(2) span[data-tooltip-content]')).toHaveAttribute('data-tooltip-content', 'This attachment is automatically generated.'); + await expect(page.locator('.download[open] li:nth-of-type(2) a')).toHaveAttribute('href', '/user2/repo2/archive/2.0.tar.gz'); + await expect(page.locator('.download[open] li:nth-of-type(2) a')).toHaveAttribute('type', 'application/gzip'); + + await expect(page.locator('.download[open] li:nth-of-type(3)')).toContainText('Test'); + await expect(page.locator('.download[open] li:nth-of-type(3) a')).toHaveAttribute('href', 'https://forgejo.org/'); + await save_visual(page); + await page.locator('.octicon-pencil').first().click(); + + // Validate edit page and edit the release + await expect(page).toHaveURL('/user2/repo2/releases/edit/2.0'); + await validate_form({page}, 'fieldset'); + await expect(page.locator('.attachment_edit:visible')).toHaveCount(2); + await expect(page.locator('.attachment_edit:visible').nth(0)).toHaveValue('Test'); + await expect(page.locator('.attachment_edit:visible').nth(1)).toHaveValue('https://forgejo.org/'); + await page.locator('.attachment_edit:visible').nth(0).fill('Test2'); + await page.locator('.attachment_edit:visible').nth(1).fill('https://gitea.io/'); + await page.click('#add-external-link'); + await expect(page.locator('.attachment_edit:visible')).toHaveCount(4); + await page.locator('.attachment_edit:visible').nth(2).fill('Test3'); + await page.locator('.attachment_edit:visible').nth(3).fill('https://gitea.com/'); + await save_visual(page); + await page.click('.button.small.primary'); + + // Validate release page and click edit + await expect(page).toHaveURL('/user2/repo2/releases'); + await expect(page.locator('.download[open] li')).toHaveCount(4); + await expect(page.locator('.download[open] li:nth-of-type(3)')).toContainText('Test2'); + await expect(page.locator('.download[open] li:nth-of-type(3) a')).toHaveAttribute('href', 'https://gitea.io/'); + await expect(page.locator('.download[open] li:nth-of-type(4)')).toContainText('Test3'); + await expect(page.locator('.download[open] li:nth-of-type(4) a')).toHaveAttribute('href', 'https://gitea.com/'); + await save_visual(page); + await page.locator('.octicon-pencil').first().click(); + }); + + test('Release name equals tag name if created from tag', async ({page}) => { + await page.goto('/user2/repo2/releases/new?tag=v1.1'); + + await expect(page.locator('input[name=title]')).toHaveValue('v1.1'); + }); + + test('Release name equals release name if edit', async ({page, isMobile}) => { + test.skip(isMobile); + + await page.goto('/user2/repo2/releases/new'); + + await page.locator('input[name=title]').pressSequentially('v2.0'); + await page.locator('input[name=tag_name]').pressSequentially('2.0'); + await page.click('.button.small.primary'); + + await page.goto('/user2/repo2/releases/edit/2.0'); + + await expect(page.locator('input[name=title]')).toHaveValue('v2.0'); + }); + + test.afterEach(async ({page}) => { + // Delete release + const response = await page.goto('/user2/repo2/releases/edit/2.0'); + test.skip(response.status() === 404, 'No release to delete'); + + await page.locator('.delete-button').dispatchEvent('click'); + await page.locator('.button.ok').click(); + await expect(page).toHaveURL('/user2/repo2/releases'); + }); }); From 7a6b5b6dd9ef5bbaa9a48424639e44791f330e38 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Tue, 17 Jun 2025 15:30:53 +0200 Subject: [PATCH 002/297] blame: count lines without reading blob --- routers/web/repo/blame.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index ccdd59f2dd..f4cc2a2cea 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -82,19 +82,19 @@ func RefBlame(ctx *context.Context) { return } - ctx.Data["NumLinesSet"] = true - ctx.Data["NumLines"], err = blob.GetBlobLineCount() - if err != nil { - ctx.ServerError("GetBlobLineCount", err) - return - } - result, err := performBlame(ctx, ctx.Repo.Commit, ctx.Repo.TreePath, ctx.FormBool("bypass-blame-ignore")) if err != nil { ctx.ServerError("performBlame", err) return } + ctx.Data["NumLinesSet"] = true + numLines := 0 + for _, p := range result.Parts { + numLines += len(p.Lines) + } + ctx.Data["NumLines"] = numLines + ctx.Data["UsesIgnoreRevs"] = result.UsesIgnoreRevs ctx.Data["FaultyIgnoreRevsFile"] = result.FaultyIgnoreRevsFile From 744363597d05dab677b94d9754927b9f27e155c6 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Tue, 17 Jun 2025 15:56:53 +0200 Subject: [PATCH 003/297] test: before refatoring count function --- services/gitdiff/gitdiff_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index 3d3c8432c4..695b177b8b 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -712,6 +712,8 @@ func TestGetDiffFull(t *testing.T) { assert.Equal(t, ".gitattributes", diff.Files[0].Name) assert.Equal(t, "24139dae656713ba861751fb2c2ac38839349a7a", diff.Files[0].NameHash) + assert.Len(t, diff.Files[0].Sections, 2) + assert.Equal(t, 4, diff.Files[0].Sections[1].Lines[0].SectionInfo.LeftIdx) }) } From 6ed62c14d3d1d9a693ad68262ae5f740f8aa1506 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Tue, 17 Jun 2025 16:06:10 +0200 Subject: [PATCH 004/297] move blobLineCount to disencentive its usage --- modules/git/blob.go | 27 --------------------------- services/gitdiff/gitdiff.go | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/modules/git/blob.go b/modules/git/blob.go index 8c5c275146..30615afe32 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -172,33 +172,6 @@ func (b *Blob) GetBlobContent(limit int64) (string, error) { return string(buf), err } -// GetBlobLineCount gets line count of the blob -func (b *Blob) GetBlobLineCount() (int, error) { - reader, err := b.DataAsync() - if err != nil { - return 0, err - } - defer reader.Close() - buf := make([]byte, 32*1024) - count := 1 - lineSep := []byte{'\n'} - - c, err := reader.Read(buf) - if c == 0 && err == io.EOF { - return 0, nil - } - for { - count += bytes.Count(buf[:c], lineSep) - switch { - case err == io.EOF: - return count, nil - case err != nil: - return count, err - } - c, err = reader.Read(buf) - } -} - // GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string func (b *Blob) GetBlobContentBase64() (string, error) { dataRc, err := b.DataAsync() diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 989f69d4f4..6835dfbf36 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -440,11 +440,29 @@ func getCommitFileLineCount(commit *git.Commit, filePath string) int { if err != nil { return 0 } - lineCount, err := blob.GetBlobLineCount() + reader, err := blob.DataAsync() if err != nil { return 0 } - return lineCount + defer reader.Close() + buf := make([]byte, 32*1024) + count := 1 + lineSep := []byte{'\n'} + + c, err := reader.Read(buf) + if c == 0 && err == io.EOF { + return 0 + } + for { + count += bytes.Count(buf[:c], lineSep) + switch { + case err == io.EOF: + return count + case err != nil: + return count + } + c, err = reader.Read(buf) + } } // Diff represents a difference between two git trees. From 8844b6b8e50826d9a5a33ec986332d594d3c6c84 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 25 Jun 2025 06:41:35 +0200 Subject: [PATCH 005/297] chore: 12.0 is now stable (take 2) --- release-notes-published/{13.0.0.md => 12.0.0.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename release-notes-published/{13.0.0.md => 12.0.0.md} (100%) diff --git a/release-notes-published/13.0.0.md b/release-notes-published/12.0.0.md similarity index 100% rename from release-notes-published/13.0.0.md rename to release-notes-published/12.0.0.md From 2bca029f6fe0812c2c1b6741a9e8a4ae75e8c0a0 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Wed, 25 Jun 2025 15:58:55 +0200 Subject: [PATCH 006/297] chore(ci): testSleep: show actual times on failures (#8271) I just experienced a spurious error on `testSleep`. The current assertion only showed `expected false to be truthy`. With this PR it should show the actual elapsed time (to be able to knowingly adjust the expected delay). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8271 Reviewed-by: floss4good Reviewed-by: Beowulf Co-authored-by: oliverpool Co-committed-by: oliverpool --- web_src/js/utils.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web_src/js/utils.test.js b/web_src/js/utils.test.js index 535aae874a..365eb63e30 100644 --- a/web_src/js/utils.test.js +++ b/web_src/js/utils.test.js @@ -1,3 +1,4 @@ +import {expect, test} from 'vitest'; import { basename, extname, isObject, stripTags, parseIssueHref, parseUrl, translateMonth, translateDay, blobToDataURI, @@ -182,5 +183,5 @@ async function testSleep(ms) { await sleep(ms); const endTime = Date.now(); // Record the end time const actualSleepTime = endTime - startTime; - expect(actualSleepTime >= ms).toBeTruthy(); + expect(actualSleepTime).toBeGreaterThanOrEqual(ms); } From 7ab27a7a7f7444472805da477342d1293469e90f Mon Sep 17 00:00:00 2001 From: Bente Groh Date: Wed, 25 Jun 2025 18:31:03 +0200 Subject: [PATCH 007/297] fix(ui): add missing lazy load attribute to images (#8246) closes #8076 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8246 Reviewed-by: Beowulf Co-authored-by: Bente Groh Co-committed-by: Bente Groh --- modules/markup/markdown/markdown_test.go | 104 +++++++++--------- modules/markup/markdown/transform_image.go | 1 + modules/markup/sanitizer.go | 3 + modules/markup/sanitizer_test.go | 4 + modules/templates/util_render_test.go | 4 +- templates/repo/issue/card.tmpl | 2 +- .../repo/issue/view_content/attachments.tmpl | 2 +- .../repo/issue/view_content/comments.tmpl | 2 +- templates/user/dashboard/feeds.tmpl | 2 +- web_src/js/components/RepoContributors.vue | 2 +- 10 files changed, 67 insertions(+), 59 deletions(-) diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index e229ee4c65..f7955115e0 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) { test( "!["+title+"]("+url+")", - `

`+title+`

`) + `

`+title+`

`) test( "[["+title+"|"+url+"]]", @@ -115,7 +115,7 @@ func TestRender_Images(t *testing.T) { test( "!["+title+"]("+url+")", - `

`+title+`

`) + `

`+title+`

`) test( "[["+title+"|"+url+"]]", @@ -412,8 +412,8 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) { testcase := `![image1](/image1) ![image2](/image2) ` - expected := `

image1
-image2

+ expected := `

image1
+image2

` res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase) require.NoError(t, err) @@ -845,10 +845,10 @@ mail@domain.com remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -872,10 +872,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -901,10 +901,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -930,10 +930,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -959,10 +959,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -988,10 +988,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1018,10 +1018,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1048,10 +1048,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1078,10 +1078,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1108,10 +1108,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1139,10 +1139,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
@@ -1170,10 +1170,10 @@ space

remote link
local link
remote link
-local image
-local image
-local image
-remote image
+local image
+local image
+local image
+remote image


https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go index 0f9c69cae6..b86c9e3d41 100644 --- a/modules/markup/markdown/transform_image.go +++ b/modules/markup/markdown/transform_image.go @@ -44,6 +44,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) for _, attr := range v.Attributes() { image.SetAttribute(attr.Name, attr.Value) } + image.SetAttributeString("loading", []byte("lazy")) for child := v.FirstChild(); child != nil; { next := child.NextSibling() image.AppendChild(image, child) diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 384dd1fe94..aacc2536bf 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -108,6 +108,9 @@ func createDefaultPolicy() *bluemonday.Policy { // Allow classes for emojis policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img") + // Allow attributes for images + policy.AllowAttrs("loading").Matching(regexp.MustCompile(`^lazy$`)).OnElements("img") + // Allow icons, emojis, chroma syntax and keyword markup on span policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span") diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go index 9805a34910..a0faff0494 100644 --- a/modules/markup/sanitizer_test.go +++ b/modules/markup/sanitizer_test.go @@ -75,6 +75,10 @@ func Test_Sanitizer(t *testing.T) { // Emoji `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, `THUMBS UP`, + + // Images lazy loading + `image1`, `image1`, + `image1`, `image1`, } for i := 0; i < len(testCases); i += 2 { diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index b75b061218..62e063213c 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -192,8 +192,8 @@ func TestRenderMarkdownToHtml(t *testing.T) { remote link local link remote link -local image -remote image +local image +remote image 88fc37a3c0...12fc37a3c0 (hash) diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index 8646562ca8..6d2f441793 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -4,7 +4,7 @@ {{if $attachments}}
{{range $attachments}} - {{.Name}} + {{.Name}} {{end}}
{{end}} diff --git a/templates/repo/issue/view_content/attachments.tmpl b/templates/repo/issue/view_content/attachments.tmpl index 79085df3ab..8b5094771a 100644 --- a/templates/repo/issue/view_content/attachments.tmpl +++ b/templates/repo/issue/view_content/attachments.tmpl @@ -31,7 +31,7 @@ {{if FilenameIsImage .Name}} {{if not (StringUtils.Contains (StringUtils.ToString $.RenderedContent) .UUID)}} - {{.Name}} + {{.Name}} {{end}} {{end}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 2e9ba3dcd7..3bc4cd0773 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -634,7 +634,7 @@
- + {{svg "octicon-x" 16}} diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index d0ecb1fc36..cdf0429714 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -90,7 +90,7 @@
{{range $push.Commits}}
- + {{template "repo/shabox" (dict "sha1" .Sha1 "commitLink" (printf "%s/commit/%s" $repoLink .Sha1) diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue index 07ad336cf7..5e03019ef1 100644 --- a/web_src/js/components/RepoContributors.vue +++ b/web_src/js/components/RepoContributors.vue @@ -391,7 +391,7 @@ export default {
#{{ index + 1 }} - +

{{ contributor.name }}

From 69bd7a1f1bcd2529f3cea2c49cb9d573ce4c2f85 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 25 Jun 2025 23:58:26 +0200 Subject: [PATCH 008/297] Update dependency mermaid to v11.7.0 (forgejo) (#8249) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8249 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9de06a8055..e033b28f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", + "mermaid": "11.7.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", @@ -2088,9 +2088,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", - "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.5.0.tgz", + "integrity": "sha512-AiaN7+VjXC+3BYE+GwNezkpjIcCI2qIMB/K4S2/vMWe0q/XJCBbx5+K7iteuz7VyltX9iAK4FmVTvGc9kjOV4w==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -10562,14 +10562,14 @@ } }, "node_modules/mermaid": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", - "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.7.0.tgz", + "integrity": "sha512-/1/5R0rt0Z1Ak0CuznAnCF3HtQgayRXUz6SguzOwN4L+DuCobz0UxnQ+ZdTSZ3AugKVVh78tiVmsHpHWV25TCw==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.4.0", + "@mermaid-js/parser": "^0.5.0", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", @@ -10578,7 +10578,7 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.13", - "dompurify": "^3.2.4", + "dompurify": "^3.2.5", "katex": "^0.16.9", "khroma": "^2.1.0", "lodash-es": "^4.17.21", diff --git a/package.json b/package.json index f7df1b3f38..3d71e94cd3 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", + "mermaid": "11.7.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", From 507a12bf82598e29ccfad14211279a5ac5e39307 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 26 Jun 2025 00:36:18 +0200 Subject: [PATCH 009/297] Revert "fix(api): document `is_system_webhook` field (#7784)" (#8286) The field is not part of the struct, it is instead part of the config field. See https://codeberg.org/forgejo/forgejo/pulls/7784#issuecomment-5511212 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8286 Reviewed-by: Beowulf Co-authored-by: Gusted Co-committed-by: Gusted --- modules/structs/hook.go | 3 +-- templates/swagger/v1_json.tmpl | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 5adcad0881..11372ca6e1 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -53,8 +53,7 @@ type CreateHookOption struct { BranchFilter string `json:"branch_filter" binding:"GlobPattern"` AuthorizationHeader string `json:"authorization_header"` // default: false - Active bool `json:"active"` - IsSystemWebhook bool `json:"is_system_webhook"` + Active bool `json:"active"` } // EditHookOption options when modify one hook diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 3ef712c464..59c13cd9e6 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -22701,10 +22701,6 @@ }, "x-go-name": "Events" }, - "is_system_webhook": { - "type": "boolean", - "x-go-name": "IsSystemWebhook" - }, "type": { "type": "string", "enum": [ From d3c712fe2a34446441a97495c2916faacea51f5d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 26 Jun 2025 02:27:54 +0200 Subject: [PATCH 010/297] Lock file maintenance (forgejo) (#8257) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8257 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 380 ++++++++++++++--------------- web_src/fomantic/package-lock.json | 42 ++-- 2 files changed, 211 insertions(+), 211 deletions(-) diff --git a/package-lock.json b/package-lock.json index e033b28f22..604ff38c18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2269,9 +2269,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz", - "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", + "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", "cpu": [ "arm" ], @@ -2283,9 +2283,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz", - "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", + "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", "cpu": [ "arm64" ], @@ -2297,9 +2297,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz", - "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", + "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", "cpu": [ "arm64" ], @@ -2311,9 +2311,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz", - "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", + "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", "cpu": [ "x64" ], @@ -2325,9 +2325,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz", - "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", + "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", "cpu": [ "arm64" ], @@ -2339,9 +2339,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz", - "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", + "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", "cpu": [ "x64" ], @@ -2353,9 +2353,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz", - "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", + "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", "cpu": [ "arm" ], @@ -2367,9 +2367,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz", - "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", + "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", "cpu": [ "arm" ], @@ -2381,9 +2381,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz", - "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", + "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", "cpu": [ "arm64" ], @@ -2395,9 +2395,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz", - "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", + "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", "cpu": [ "arm64" ], @@ -2409,9 +2409,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz", - "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", + "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", "cpu": [ "loong64" ], @@ -2423,9 +2423,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz", - "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", + "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", "cpu": [ "ppc64" ], @@ -2437,9 +2437,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz", - "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", + "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", "cpu": [ "riscv64" ], @@ -2451,9 +2451,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz", - "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", + "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", "cpu": [ "riscv64" ], @@ -2465,9 +2465,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz", - "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", + "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", "cpu": [ "s390x" ], @@ -2479,9 +2479,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz", - "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", + "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", "cpu": [ "x64" ], @@ -2493,9 +2493,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz", - "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", + "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", "cpu": [ "x64" ], @@ -2507,9 +2507,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz", - "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", + "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", "cpu": [ "arm64" ], @@ -2521,9 +2521,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz", - "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", + "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", "cpu": [ "ia32" ], @@ -2535,9 +2535,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz", - "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", + "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", "cpu": [ "x64" ], @@ -3509,9 +3509,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", - "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", + "version": "20.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.1.tgz", + "integrity": "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3825,9 +3825,9 @@ } }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.0.tgz", - "integrity": "sha512-h1T2c2Di49ekF2TE8ZCoJkb+jwETKUIPDJ/nO3tJBKlLFPu+fyd93f0rGP/BvArKx2k2HlRM4kqkNarj3dvZlg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.1.tgz", + "integrity": "sha512-dd7yIp1hfJFX9ZlVLQRrh/Re9WMUHHmF9hrKD1yIvxcyNr2BhQ3xc1upAVhy8NijadnCswAxWQu8MkkSMC1qXQ==", "cpu": [ "arm" ], @@ -3839,9 +3839,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.0.tgz", - "integrity": "sha512-sG1NHtgXtX8owEkJ11yn34vt0Xqzi3k9TJ8zppDmyG8GZV4kVWw44FHwKwHeEFl07uKPeC4ZoyuQaGh5ruJYPA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.1.tgz", + "integrity": "sha512-EzUPcMFtDVlo5yrbzMqUsGq3HnLXw+3ZOhSd7CUaDmbTtnrzM+RO2ntw2dm2wjbbc5djWj3yX0wzbbg8pLhx8g==", "cpu": [ "arm64" ], @@ -3853,9 +3853,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-nJ9z47kfFnCxN1z/oYZS7HSNsFh43y2asePzTEZpEvK7kGyuShSl3RRXnm/1QaqFL+iP+BjMwuB+DYUymOkA5A==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.1.tgz", + "integrity": "sha512-nB+dna3q4kOleKFcSZJ/wDXIsAd1kpMO9XrVAt8tG3RDWJ6vi+Ic6bpz4cmg5tWNeCfHEY4KuqJCB+pKejPEmQ==", "cpu": [ "arm64" ], @@ -3867,9 +3867,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.0.tgz", - "integrity": "sha512-TK+UA1TTa0qS53rjWn7cVlEKVGz2B6JYe0C++TdQjvWYIyx83ruwh0wd4LRxYBM5HeuAzXcylA9BH2trARXJTw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.1.tgz", + "integrity": "sha512-aKWHCrOGaCGwZcekf3TnczQoBxk5w//W3RZ4EQyhux6rKDwBPgDU9Y2yGigCV1Z+8DWqZgVGQi+hdpnlSy3a1w==", "cpu": [ "x64" ], @@ -3881,9 +3881,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.0.tgz", - "integrity": "sha512-6uZwzMRFcD7CcCd0vz3Hp+9qIL2jseE/bx3ZjaLwn8t714nYGwiE84WpaMCYjU+IQET8Vu/+BNAGtYD7BG/0yA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.1.tgz", + "integrity": "sha512-4dIEMXrXt0UqDVgrsUd1I+NoIzVQWXy/CNhgpfS75rOOMK/4Abn0Mx2M2gWH4Mk9+ds/ASAiCmqoUFynmMY5hA==", "cpu": [ "x64" ], @@ -3895,9 +3895,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.0.tgz", - "integrity": "sha512-bPUBksQfrgcfv2+mm+AZinaKq8LCFvt5PThYqRotqSuuZK1TVKkhbVMS/jvSRfYl7jr3AoZLYbDkItxgqMKRkg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.1.tgz", + "integrity": "sha512-vtvS13IXPs1eE8DuS/soiosqMBeyh50YLRZ+p7EaIKAPPeevRnA9G/wu/KbVt01ZD5qiGjxS+CGIdVC7I6gTOw==", "cpu": [ "arm" ], @@ -3909,9 +3909,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.0.tgz", - "integrity": "sha512-uT6E7UBIrTdCsFQ+y0tQd3g5oudmrS/hds5pbU3h4s2t/1vsGWbbSKhBSCD9mcqaqkBwoqlECpUrRJCmldl8PA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.1.tgz", + "integrity": "sha512-BfdnN6aZ7NcX8djW8SR6GOJc+K+sFhWRF4vJueVE0vbUu5N1bLnBpxJg1TGlhSyo+ImC4SR0jcNiKN0jdoxt+A==", "cpu": [ "arm" ], @@ -3923,9 +3923,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.0.tgz", - "integrity": "sha512-vdqBh911wc5awE2bX2zx3eflbyv8U9xbE/jVKAm425eRoOVv/VseGZsqi3A3SykckSpF4wSROkbQPvbQFn8EsA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.1.tgz", + "integrity": "sha512-Jhge7lFtH0QqfRz2PyJjJXWENqywPteITd+nOS0L6AhbZli+UmEyGBd2Sstt1c+l9C+j/YvKTl9wJo9PPmsFNg==", "cpu": [ "arm64" ], @@ -3937,9 +3937,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.0.tgz", - "integrity": "sha512-/8JFZ/SnuDr1lLEVsxsuVwrsGquTvT51RZGvyDB/dOK3oYK2UqeXzgeyq6Otp8FZXQcEYqJwxb9v+gtdXn03eQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.1.tgz", + "integrity": "sha512-ofdK/ow+ZSbSU0pRoB7uBaiRHeaAOYQFU5Spp87LdcPL/P1RhbCTMSIYVb61XWzsVEmYKjHFtoIE0wxP6AFvrA==", "cpu": [ "arm64" ], @@ -3951,9 +3951,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.0.tgz", - "integrity": "sha512-FkJjybtrl+rajTw4loI3L6YqSOpeZfDls4SstL/5lsP2bka9TiHUjgMBjygeZEis1oC8LfJTS8FSgpKPaQx2tQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.1.tgz", + "integrity": "sha512-eC8SXVn8de67HacqU7PoGdHA+9tGbqfEdD05AEFRAB81ejeQtNi5Fx7lPcxpLH79DW0BnMAHau3hi4RVkHfSCw==", "cpu": [ "ppc64" ], @@ -3965,9 +3965,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.0.tgz", - "integrity": "sha512-w/NZfHNeDusbqSZ8r/hp8iL4S39h4+vQMc9/vvzuIKMWKppyUGKm3IST0Qv0aOZ1rzIbl9SrDeIqK86ZpUK37w==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.1.tgz", + "integrity": "sha512-fIkwvAAQ41kfoGWfzeJ33iLGShl0JEDZHrMnwTHMErUcPkaaZRJYjQjsFhMl315NEQ4mmTlC+2nfK/J2IszDOw==", "cpu": [ "riscv64" ], @@ -3979,9 +3979,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.0.tgz", - "integrity": "sha512-bEPBosut8/8KQbUixPry8zg/fOzVOWyvwzOfz0C0Rw6dp+wIBseyiHKjkcSyZKv/98edrbMknBaMNJfA/UEdqw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.1.tgz", + "integrity": "sha512-RAAszxImSOFLk44aLwnSqpcOdce8sBcxASledSzuFAd8Q5ZhhVck472SisspnzHdc7THCvGXiUeZ2hOC7NUoBQ==", "cpu": [ "riscv64" ], @@ -3993,9 +3993,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.0.tgz", - "integrity": "sha512-LDtMT7moE3gK753gG4pc31AAqGUC86j3AplaFusc717EUGF9ZFJ356sdQzzZzkBk1XzMdxFyZ4f/i35NKM/lFA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.1.tgz", + "integrity": "sha512-QoP9vkY+THuQdZi05bA6s6XwFd6HIz3qlx82v9bTOgxeqin/3C12Ye7f7EOD00RQ36OtOPWnhEMMm84sv7d1XQ==", "cpu": [ "s390x" ], @@ -4007,9 +4007,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.0.tgz", - "integrity": "sha512-WmFd5KINHIXj8o1mPaT8QRjA9HgSXhN1gl9Da4IZihARihEnOylu4co7i/yeaIpcfsI6sYs33cNZKyHYDh0lrA==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.1.tgz", + "integrity": "sha512-/p77cGN/h9zbsfCseAP5gY7tK+7+DdM8fkPfr9d1ye1fsF6bmtGbtZN6e/8j4jCZ9NEIBBkT0GhdgixSelTK9g==", "cpu": [ "x64" ], @@ -4021,9 +4021,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.0.tgz", - "integrity": "sha512-CYuXbANW+WgzVRIl8/QvZmDaZxrqvOldOwlbUjIM4pQ46FJ0W5cinJ/Ghwa/Ng1ZPMJMk1VFdsD/XwmCGIXBWg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.1.tgz", + "integrity": "sha512-wInTqT3Bu9u50mDStEig1v8uxEL2Ht+K8pir/YhyyrM5ordJtxoqzsL1vR/CQzOJuDunUTrDkMM0apjW/d7/PA==", "cpu": [ "x64" ], @@ -4035,9 +4035,9 @@ ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.0.tgz", - "integrity": "sha512-6Rp2WH0OoitMYR57Z6VE8Y6corX8C6QEMWLgOV6qXiJIeZ1F9WGXY/yQ8yDC4iTraotyLOeJ2Asea0urWj2fKQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.1.tgz", + "integrity": "sha512-eNwqO5kUa+1k7yFIircwwiniKWA0UFHo2Cfm8LYgkh9km7uMad+0x7X7oXbQonJXlqfitBTSjhA0un+DsHIrhw==", "cpu": [ "wasm32" ], @@ -4052,9 +4052,9 @@ } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.0.tgz", - "integrity": "sha512-rknkrTRuvujprrbPmGeHi8wYWxmNVlBoNW8+4XF2hXUnASOjmuC9FNF1tGbDiRQWn264q9U/oGtixyO3BT8adQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.1.tgz", + "integrity": "sha512-Eaz1xMUnoa2mFqh20mPqSdbYl6crnk8HnIXDu6nsla9zpgZJZO8w3c1gvNN/4Eb0RXRq3K9OG6mu8vw14gIqiA==", "cpu": [ "arm64" ], @@ -4066,9 +4066,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.0.tgz", - "integrity": "sha512-Ceymm+iBl+bgAICtgiHyMLz6hjxmLJKqBim8tDzpX61wpZOx2bPK6Gjuor7I2RiUynVjvvkoRIkrPyMwzBzF3A==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.1.tgz", + "integrity": "sha512-H/+d+5BGlnEQif0gnwWmYbYv7HJj563PUKJfn8PlmzF8UmF+8KxdvXdwCsoOqh4HHnENnoLrav9NYBrv76x1wQ==", "cpu": [ "ia32" ], @@ -4080,9 +4080,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.0.tgz", - "integrity": "sha512-k59o9ZyeyS0hAlcaKFezYSH2agQeRFEB7KoQLXl3Nb3rgkqT1NY9Vwy+SqODiLmYnEjxWJVRE/yq2jFVqdIxZw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.1.tgz", + "integrity": "sha512-rS86wI4R6cknYM3is3grCb/laE8XBEbpWAMSIPjYfmYp75KL5dT87jXF2orDa4tQYg5aajP5G8Fgh34dRyR+Rw==", "cpu": [ "x64" ], @@ -5346,9 +5346,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", + "version": "1.0.30001724", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", + "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", "funding": [ { "type": "opencollective", @@ -6916,9 +6916,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", + "version": "1.5.171", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", + "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7927,9 +7927,9 @@ } }, "node_modules/exsolve": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", - "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", "license": "MIT" }, "node_modules/fast-deep-equal": { @@ -10251,9 +10251,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", + "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", "dev": true, "license": "MIT" }, @@ -14376,9 +14376,9 @@ } }, "node_modules/terser": { - "version": "5.42.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.42.0.tgz", - "integrity": "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -14611,9 +14611,9 @@ } }, "node_modules/tinypool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", - "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -14955,9 +14955,9 @@ } }, "node_modules/unrs-resolver": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.0.tgz", - "integrity": "sha512-wqaRu4UnzBD2ABTC1kLfBjAqIDZ5YUTr/MLGa7By47JV1bJDSW7jq/ZSLigB7enLe7ubNaJhtnBXgrc/50cEhg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.1.tgz", + "integrity": "sha512-4AZVxP05JGN6DwqIkSP4VKLOcwQa5l37SWHF/ahcuqBMbfxbpN1L1QKafEhWCziHhzKex9H/AR09H0OuVyU+9g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -14968,25 +14968,25 @@ "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.9.0", - "@unrs/resolver-binding-android-arm64": "1.9.0", - "@unrs/resolver-binding-darwin-arm64": "1.9.0", - "@unrs/resolver-binding-darwin-x64": "1.9.0", - "@unrs/resolver-binding-freebsd-x64": "1.9.0", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.0", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.0", - "@unrs/resolver-binding-linux-arm64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-arm64-musl": "1.9.0", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-riscv64-musl": "1.9.0", - "@unrs/resolver-binding-linux-s390x-gnu": "1.9.0", - "@unrs/resolver-binding-linux-x64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-x64-musl": "1.9.0", - "@unrs/resolver-binding-wasm32-wasi": "1.9.0", - "@unrs/resolver-binding-win32-arm64-msvc": "1.9.0", - "@unrs/resolver-binding-win32-ia32-msvc": "1.9.0", - "@unrs/resolver-binding-win32-x64-msvc": "1.9.0" + "@unrs/resolver-binding-android-arm-eabi": "1.9.1", + "@unrs/resolver-binding-android-arm64": "1.9.1", + "@unrs/resolver-binding-darwin-arm64": "1.9.1", + "@unrs/resolver-binding-darwin-x64": "1.9.1", + "@unrs/resolver-binding-freebsd-x64": "1.9.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.9.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.9.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.9.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.9.1", + "@unrs/resolver-binding-linux-x64-musl": "1.9.1", + "@unrs/resolver-binding-wasm32-wasi": "1.9.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.9.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.9.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.9.1" } }, "node_modules/update-browserslist-db": { @@ -15198,9 +15198,9 @@ "license": "BSD-2-Clause" }, "node_modules/vite/node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, @@ -15248,13 +15248,13 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz", - "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", + "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -15264,26 +15264,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.43.0", - "@rollup/rollup-android-arm64": "4.43.0", - "@rollup/rollup-darwin-arm64": "4.43.0", - "@rollup/rollup-darwin-x64": "4.43.0", - "@rollup/rollup-freebsd-arm64": "4.43.0", - "@rollup/rollup-freebsd-x64": "4.43.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", - "@rollup/rollup-linux-arm-musleabihf": "4.43.0", - "@rollup/rollup-linux-arm64-gnu": "4.43.0", - "@rollup/rollup-linux-arm64-musl": "4.43.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-musl": "4.43.0", - "@rollup/rollup-linux-s390x-gnu": "4.43.0", - "@rollup/rollup-linux-x64-gnu": "4.43.0", - "@rollup/rollup-linux-x64-musl": "4.43.0", - "@rollup/rollup-win32-arm64-msvc": "4.43.0", - "@rollup/rollup-win32-ia32-msvc": "4.43.0", - "@rollup/rollup-win32-x64-msvc": "4.43.0", + "@rollup/rollup-android-arm-eabi": "4.44.0", + "@rollup/rollup-android-arm64": "4.44.0", + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-freebsd-arm64": "4.44.0", + "@rollup/rollup-freebsd-x64": "4.44.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", + "@rollup/rollup-linux-arm-musleabihf": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-musl": "4.44.0", + "@rollup/rollup-linux-s390x-gnu": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-ia32-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" } }, @@ -15708,9 +15708,9 @@ } }, "node_modules/webpack/node_modules/webpack-sources": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", - "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "license": "MIT", "engines": { "node": ">=10.13.0" diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index 7d95ad2e1b..cea707672e 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -182,9 +182,9 @@ "peer": true }, "node_modules/@octokit/core/node_modules/@octokit/request": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz", - "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz", + "integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==", "license": "MIT", "peer": true, "dependencies": { @@ -289,9 +289,9 @@ "peer": true }, "node_modules/@octokit/graphql/node_modules/@octokit/request": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz", - "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz", + "integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==", "license": "MIT", "peer": true, "dependencies": { @@ -494,9 +494,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", - "integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", + "version": "24.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz", + "integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==", "license": "MIT", "dependencies": { "undici-types": "~7.8.0" @@ -1249,9 +1249,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", + "version": "1.0.30001724", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", + "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", "funding": [ { "type": "opencollective", @@ -2005,9 +2005,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", + "version": "1.5.171", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", + "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -2017,9 +2017,9 @@ "license": "MIT" }, "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -2787,9 +2787,9 @@ } }, "node_modules/get-stream/node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", From aee5e1fb94dda5a49b0b0b155133c2b00c7cb5a7 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 26 Jun 2025 08:49:20 +0200 Subject: [PATCH 011/297] Update module github.com/jhillyerd/enmime/v2 to v2.2.0 (forgejo) (#8254) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [github.com/jhillyerd/enmime/v2](https://github.com/jhillyerd/enmime) | `v2.1.0` -> `v2.2.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fjhillyerd%2fenmime%2fv2/v2.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fjhillyerd%2fenmime%2fv2/v2.1.0/v2.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
jhillyerd/enmime (github.com/jhillyerd/enmime/v2) ### [`v2.2.0`](https://github.com/jhillyerd/enmime/releases/tag/v2.2.0) [Compare Source](https://github.com/jhillyerd/enmime/compare/v2.1.0...v2.2.0) #### What's Changed - chore: bump golangci to last 1.x release by [@​jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/368 - chore: fix linter warnings by [@​jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/370 - chore: bump golangci to 2.x by [@​jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/369 - chore: bump go deps by [@​jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/371 - build(deps): bump golangci/golangci-lint-action from 7 to 8 by [@​dependabot](https://github.com/dependabot) in https://github.com/jhillyerd/enmime/pull/373 - Switch to fork of html2text for tablewriter 1.0 by [@​jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/375 - Release for go 1.23 support by [@​jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/376 **Full Changelog**: https://github.com/jhillyerd/enmime/compare/v2.1.0...v2.2.0
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8254 Reviewed-by: Earl Warren Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- assets/go-licenses.json | 14 ++++++++++++-- go.mod | 12 +++++++----- go.sum | 27 ++++++++++++++------------- routers/web/feed/convert.go | 2 +- services/mailer/mailer.go | 2 +- 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index fb6c201a5e..c3b261320c 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -595,8 +595,8 @@ "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Huan Du\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, { - "name": "github.com/jaytaylor/html2text", - "path": "github.com/jaytaylor/html2text/LICENSE", + "name": "github.com/inbucket/html2text", + "path": "github.com/inbucket/html2text/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Jay Taylor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, { @@ -779,6 +779,16 @@ "path": "github.com/nwaples/rardecode/LICENSE", "licenseText": "Copyright (c) 2015, Nicholas Waples\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, + { + "name": "github.com/olekukonko/errors", + "path": "github.com/olekukonko/errors/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, + { + "name": "github.com/olekukonko/ll", + "path": "github.com/olekukonko/ll/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, { "name": "github.com/olekukonko/tablewriter", "path": "github.com/olekukonko/tablewriter/LICENSE.md", diff --git a/go.mod b/go.mod index bb2be827eb..510ec9c3ae 100644 --- a/go.mod +++ b/go.mod @@ -63,8 +63,8 @@ require ( github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.5.0 - github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 - github.com/jhillyerd/enmime/v2 v2.1.0 + github.com/inbucket/html2text v0.9.0 + github.com/jhillyerd/enmime/v2 v2.2.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/klauspost/compress v1.18.0 @@ -158,7 +158,7 @@ require ( github.com/dlclark/regexp2 v1.11.5 // 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.16.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect @@ -192,7 +192,7 @@ require ( github.com/libdns/libdns v1.0.0-beta.1 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mholt/acmez/v3 v3.1.2 // indirect github.com/miekg/dns v1.1.63 // indirect @@ -205,7 +205,9 @@ require ( github.com/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nwaples/rardecode v1.1.3 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.0.9 // indirect + github.com/olekukonko/tablewriter v1.0.7 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect diff --git a/go.sum b/go.sum index 639880e2ce..53558fddd7 100644 --- a/go.sum +++ b/go.sum @@ -192,8 +192,8 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -341,12 +341,12 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= -github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= +github.com/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+qks= +github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE= github.com/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/jhillyerd/enmime/v2 v2.1.0 h1:c8Qwi5Xq5EdtMN6byQWoZ/8I2RMTo6OJ7Xay+s1oPO0= -github.com/jhillyerd/enmime/v2 v2.1.0/go.mod h1:EJ74dcRbBcqHSP2TBu08XRoy6y3Yx0cevwb1YkGMEmQ= +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= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -389,12 +389,10 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o= github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8= github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= @@ -436,8 +434,12 @@ github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWk github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= +github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw= +github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs= github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -645,7 +647,6 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 01e663a672..7b09c92ee5 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -25,7 +25,7 @@ import ( "forgejo.org/services/context" "github.com/gorilla/feeds" - "github.com/jaytaylor/html2text" + "github.com/inbucket/html2text" ) func toBranchLink(ctx *context.Context, act *activities_model.Action) string { diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go index ca5c645e0c..d8646d9ddd 100644 --- a/services/mailer/mailer.go +++ b/services/mailer/mailer.go @@ -29,7 +29,7 @@ import ( notify_service "forgejo.org/services/notify" ntlmssp "github.com/Azure/go-ntlmssp" - "github.com/jaytaylor/html2text" + "github.com/inbucket/html2text" "gopkg.in/gomail.v2" ) From 414199fc66644cdd8b73c03a11ce09a1ed27d213 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Thu, 26 Jun 2025 10:44:26 +0200 Subject: [PATCH 012/297] i18n: update of translations from Codeberg Translate (#8238) Translations update from [Codeberg Translate](https://translate.codeberg.org) for [Forgejo/forgejo](https://translate.codeberg.org/projects/forgejo/forgejo/). It also includes following components: * [Forgejo/forgejo-next](https://translate.codeberg.org/projects/forgejo/forgejo-next/) Current translation status: ![Weblate translation status](https://translate.codeberg.org/widget/forgejo/forgejo/horizontal-auto.svg) ## Release notes - Localization - [PR](https://codeberg.org/forgejo/forgejo/pulls/8238): i18n: update of translations from Codeberg Translate Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8238 Reviewed-by: Earl Warren Reviewed-by: Robert Wolff Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_ar.ini | 20 +-- options/locale/locale_bg.ini | 20 +-- options/locale/locale_cs-CZ.ini | 26 ++-- options/locale/locale_da.ini | 20 +-- options/locale/locale_de-DE.ini | 26 ++-- options/locale/locale_el-GR.ini | 20 +-- options/locale/locale_es-ES.ini | 20 +-- options/locale/locale_fa-IR.ini | 18 +-- options/locale/locale_fi-FI.ini | 14 +- options/locale/locale_fil.ini | 93 +++++------ options/locale/locale_fr-FR.ini | 28 ++-- options/locale/locale_ga-IE.ini | 14 +- options/locale/locale_hu-HU.ini | 2 +- options/locale/locale_id-ID.ini | 2 +- options/locale/locale_is-IS.ini | 4 +- options/locale/locale_it-IT.ini | 215 ++++++++++++++++++++++---- options/locale/locale_ja-JP.ini | 20 +-- options/locale/locale_jbo.ini | 10 +- options/locale/locale_ko-KR.ini | 4 +- options/locale/locale_lv-LV.ini | 20 +-- options/locale/locale_nds.ini | 22 +-- options/locale/locale_nl-NL.ini | 20 +-- options/locale/locale_pl-PL.ini | 20 +-- options/locale/locale_pt-BR.ini | 31 ++-- options/locale/locale_pt-PT.ini | 20 +-- options/locale/locale_ru-RU.ini | 22 +-- options/locale/locale_si-LK.ini | 16 +- options/locale/locale_sr-SP.ini | 2 +- options/locale/locale_sv-SE.ini | 14 +- options/locale/locale_tr-TR.ini | 18 +-- options/locale/locale_uk-UA.ini | 39 ++--- options/locale/locale_zh-CN.ini | 31 ++-- options/locale/locale_zh-HK.ini | 2 +- options/locale/locale_zh-TW.ini | 22 +-- options/locale_next/locale_de-DE.json | 7 +- options/locale_next/locale_fil.json | 12 +- options/locale_next/locale_fr-FR.json | 2 +- options/locale_next/locale_it-IT.json | 80 +++++++++- options/locale_next/locale_nds.json | 7 +- options/locale_next/locale_pt-BR.json | 4 +- options/locale_next/locale_ru-RU.json | 11 +- options/locale_next/locale_uk-UA.json | 7 +- options/locale_next/locale_zh-CN.json | 9 +- options/locale_next/locale_zh-TW.json | 7 +- 44 files changed, 654 insertions(+), 367 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index f4ac1a0e3d..15d614e8bc 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -699,7 +699,7 @@ issues.filter_milestone_all = كل الأهداف issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل. issues.num_participants_few = %d متحاور release.title = عنوان الإصدار -issues.closed_at = `أغلق هذه المسألة %[2]s` +issues.closed_at = `أغلق هذه المسألة %s` issues.lock.title = إقفال التحاور في هذه المسألة. issues.new.no_label = بلا تصنيف issues.filter_sort.mostforks = الأعلى اشتقاقا @@ -759,7 +759,7 @@ branch.renamed = غُيّر اسم الفرع %s إلى %s. delete_preexisting = احذف الملفات الموجودة سابقا branch.included_desc = هذا الفرع جزء من الفرع المبدئي trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع -issues.reopened_at = `أعاد فتح هذه المسألة %[2]s` +issues.reopened_at = `أعاد فتح هذه المسألة %s` issues.action_milestone = هدف issues.new.assignees = المكلَّفون release.tag_name_protected = اسم الوسم محمي. @@ -1166,7 +1166,7 @@ pulls.status_checking = في انتظار بعض الفحوص pulls.status_checks_failure = بعض الفحوص فشلت pulls.status_checks_success = جميع الفحوص ناجحة pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات -pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %[2]s` +pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %s` pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.` pulls.cmd_instruction_checkout_title = اسحب pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات. @@ -1257,8 +1257,8 @@ pulls.status_checks_details = تفاصيل pulls.status_checks_hide_all = أخفِ كل الفحوص pulls.status_checks_show_all = أظهر كل الفحوص pulls.close = أغلق طلب الدمج -pulls.closed_at = `أغلق طلب الدمج %[2]s` -pulls.reopened_at = `أعاد فتح طلب الدمج %[2]s` +pulls.closed_at = `أغلق طلب الدمج %s` +pulls.reopened_at = `أعاد فتح طلب الدمج %s` milestones.title = العنوان milestones.desc = الوصف milestones.edit = عدّل الهدف @@ -1302,11 +1302,11 @@ issues.closed_by_fake = من %[2]s أُغلقت %[1]s issues.num_comments_1 = %d تعليق issues.num_comments = %d تعليقا issues.commented_at = `علّق %s` -issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %[2]s` -issues.ref_issue_from = `أشار إلى هذه المسألة %[4]s %[2]s` -issues.ref_pull_from = `أشار إلى هذا الطلب %[4]s %[2]s` -issues.ref_closing_from = `أشار إلى طلب دمج %[4]s سيغلق هذه المسألة %[2]s` -issues.ref_reopening_from = `أشار إلى طلب دمج %[4]s سيعيد فتح هذه المسألة %[2]s` +issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %s` +issues.ref_issue_from = `أشار إلى هذه المسألة %[3]s %[1]s` +issues.ref_pull_from = `أشار إلى هذا الطلب %[3]s %[1]s` +issues.ref_closing_from = `أشار إلى طلب دمج %[3]s سيغلق هذه المسألة %[1]s` +issues.ref_reopening_from = `أشار إلى طلب دمج %[3]s سيعيد فتح هذه المسألة %[1]s` issues.ref_closed_from = `أغلق هذه المسألة %[4]s %[2]s` issues.ref_reopened_from = `أعاد فتح هذه المسألة %[4]s %[2]s` issues.reference_issue.body = المحتوى diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index abce4f1133..1b9767f674 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -749,7 +749,7 @@ settings.admin_settings = Администраторски настройки issues.role.owner = Притежател settings.transfer.title = Прехвърляне на притежанието issues.author = Автор -issues.closed_at = `затвори тази задача %[2]s` +issues.closed_at = `затвори тази задача %s` settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване? commits.message = Съобщение issues.due_date_not_set = Няма зададен краен срок. @@ -773,9 +773,9 @@ issues.filter_type.all_issues = Всички задачи issues.filter_poster_no_select = Всички автори issues.opened_by = отворена %[1]s от %[3]s issues.action_open = Отваряне -pulls.closed_at = `затвори тази заявка за сливане %[2]s` -pulls.reopened_at = `отвори наново тази заявка за сливане %[2]s` -issues.reopened_at = `отвори наново тази задача %[2]s` +pulls.closed_at = `затвори тази заявка за сливане %s` +pulls.reopened_at = `отвори наново тази заявка за сливане %s` +issues.reopened_at = `отвори наново тази задача %s` projects.column.edit = Редактиране на колоната issues.close = Затваряне на задачата issues.ref_reopened_from = `отвори наново тази задача %[4]s %[2]s` @@ -1205,7 +1205,7 @@ issues.dependency.cancel = Отказ issues.dependency.add_error_dep_exists = Зависимостта вече съществува. issues.dependency.add_error_dep_not_exist = Зависимостта не съществува. issues.remove_ref_at = `премахна препратката %s %s` -issues.ref_pull_from = `спомена тази заявка за сливане %[4]s %[2]s` +issues.ref_pull_from = `спомена тази заявка за сливане %[3]s %[1]s` issues.dependency.pr_no_dependencies = Няма зададени зависимости. issues.dependency.remove_info = Премахване на тази зависимост issues.dependency.removed_dependency = `премахна зависимостта %s` @@ -1230,11 +1230,11 @@ issues.dependency.title = Зависимости issues.dependency.issue_no_dependencies = Няма зададени зависимости. issues.dependency.pr_close_blocked = Трябва да затворите всички задачи, блокиращи тази заявка за сливане, преди да можете да я слеете. issues.dependency.pr_close_blocks = Тази заявка за сливане блокира затварянето на следните задачи -issues.ref_issue_from = `спомена тази задача %[4]s %[2]s` -issues.commit_ref_at = `спомена тази задача в подаване %[2]s` +issues.ref_issue_from = `спомена тази задача %[3]s %[1]s` +issues.commit_ref_at = `спомена тази задача в подаване %s` issues.add_ref_at = `добави препратка %s %s` pulls.merged_info_text = Клонът %s вече може да бъде изтрит. -pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %[2]s` +pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %s` issues.change_ref_at = `промени препратката от %s на %s %s` diff.review.reject = Поискване на промени diff.bin_not_shown = Двоичният файл не е показан. @@ -1299,9 +1299,9 @@ branch.create_new_branch = Създаване на клон от клон: pulls.status_checks_show_all = Показване на всички проверки size_format = %[1]s: %[2]s; %[3]s: %[4]s pulls.filter_changes_by_commit = Филтриране по подаване -issues.ref_closing_from = `спомена тази задача в заявка за сливане %[4]s, която ще я затвори, %[2]s` +issues.ref_closing_from = `спомена тази задача в заявка за сливане %[3]s, която ще я затвори, %[1]s` issues.ref_from = `от %[1]s` -issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[4]s, която ще я отвори наново , %[2]s` +issues.ref_reopening_from = `спомена тази задача в заявка за сливане %[3]s, която ще я отвори наново , %[1]s` issues.draft_title = Чернова pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане. pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит. diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 9448dd8e7e..830065fb64 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1633,13 +1633,13 @@ issues.opened_by_fake=otevřeno %[1]s uživatelem %[2]s issues.closed_by_fake=od %[2]s byl uzavřen %[1]s issues.previous=Předchozí issues.next=Další -issues.open_title=Otevřeno -issues.closed_title=Uzavřeno +issues.open_title=Otevřené +issues.closed_title=Uzavřené issues.draft_title=Koncept issues.num_comments_1=%d komentář issues.num_comments=%d komentářů issues.commented_at=`okomentoval/a %s` -issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář? +issues.delete_comment_confirm=Opravdu chcete smazat tento komentář? issues.context.copy_link=Kopírovat odkaz issues.context.quote_reply=Citovat odpověď issues.context.reference_issue=Odkázat v novém problému @@ -1653,13 +1653,13 @@ issues.close_comment_issue=Zavřít s komentářem issues.reopen_issue=Znovu otevřít issues.reopen_comment_issue=Znovu otevřít s komentářem issues.create_comment=Komentovat -issues.closed_at=`uzavřel/a tento problém %[2]s` -issues.reopened_at=`znovu otevřel/a tento problém %[2]s` -issues.commit_ref_at=`odkázal/a na tento problém z revize %[2]s` -issues.ref_issue_from=`odkázal/a na tento problém %[4]s %[2]s` -issues.ref_pull_from=`odkázal/a na tuto žádost o sloučení %[4]s %[2]s` -issues.ref_closing_from=`odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej uzavře, %[2]s` -issues.ref_reopening_from=`odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej znovu otevře, %[2]s` +issues.closed_at=`uzavřel/a tento problém %s` +issues.reopened_at=`znovu otevřel/a tento problém %s` +issues.commit_ref_at=`odkázal/a na tento problém z revize %s` +issues.ref_issue_from=`odkázal/a na tento problém %[3]s %[1]s` +issues.ref_pull_from=`odkázal/a na tuto žádost o sloučení %[3]s %[1]s` +issues.ref_closing_from=`odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej uzavře, %[1]s` +issues.ref_reopening_from=`odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej znovu otevře, %[1]s` issues.ref_closed_from=`uzavřel/a tento problém %[4]s %[2]s` issues.ref_reopened_from=`znovu otevřel/a tento problém %[4]s %[2]s` issues.ref_from=`z %[1]s` @@ -1966,8 +1966,8 @@ pulls.update_branch_success=Aktualizace větve byla úspěšná pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi pulls.close=Zavřít žádost o sloučení -pulls.closed_at=`uzavřel/a tuto žádost o sloučení %[2]s` -pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %[2]s` +pulls.closed_at=`uzavřel/a tuto žádost o sloučení %s` +pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %s` pulls.cmd_instruction_hint=Zobrazit instrukce příkazové řádky pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny. pulls.cmd_instruction_merge_title=Sloučit @@ -2758,7 +2758,7 @@ settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Tuto ak settings.new_owner_blocked_doer = Nový majitel vás zablokoval. settings.mirror_settings.pushed_repository = Odeslaný repozitář settings.add_collaborator_blocked_our = Nepodařilo se přidat spolupracovníka, jelikož byl zablokován majitelem repozitáře. -pulls.commit_ref_at = `se odkázal/a na tuto žádost o sloučení z revize %[2]s` +pulls.commit_ref_at = `odkázal/a na tuto žádost o sloučení z revize %s` settings.wiki_rename_branch_main = Normalizovat název větve wiki settings.wiki_rename_branch_main_desc = Přejmenovat větev interně používanou pro wiki na „%s“. Tato změna je trvalá a nelze ji vrátit. pulls.fast_forward_only_merge_pull_request = Pouze zrychlené diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index ea22f49e77..c82779ab60 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -1520,15 +1520,15 @@ issues.add_labels = tilføjede %s etiketterne %s issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s issues.add_milestone_at = `føjede dette til %s milepælen %s` issues.add_project_at = `føjede dette til %s- projektet %s` -issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[4]s, der vil genåbne den, %[2]s` +issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne den, %[1]s` issues.ref_closed_from = `lukkede dette problem %[4]s %[2 ]s` issues.ref_reopened_from = `genåbnede dette problem %[4]s %[2 ]s` issues.ref_from = `fra %[1]s` issues.author = Forfatter -issues.commit_ref_at = `henviste til dette problem fra en commit %[2]s` -issues.ref_issue_from = `henviste til dette problem %[4]s %[2 ]s` -issues.ref_pull_from = `henviste til denne pull-anmodning %[4]s %[ 2]s` -issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[4]s, der vil lukke det, %[2]s` +issues.commit_ref_at = `henviste til dette problem fra en commit %s` +issues.ref_issue_from = `henviste til dette problem %[3]s %[2 ]s` +issues.ref_pull_from = `henviste til denne pull-anmodning %[3]s %[1]s` +issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil lukke det, %[1]s` issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem. issues.author.tooltip.pr = Denne bruger er forfatteren af denne pull-anmodning. issues.role.owner = Ejer @@ -1564,8 +1564,8 @@ issues.reaction.alt_add = Tilføj %[1]s reaktion til kommentar. issues.context.menu = Kommentar menu issues.reopen_comment_issue = Genåbner med kommentar issues.create_comment = Kommentar -issues.closed_at = `lukkede dette problem %[2]s` -issues.reopened_at = `genåbnede dette problem %[2]s` +issues.closed_at = `lukkede dette problem %s` +issues.reopened_at = `genåbnede dette problem %s` issues.remove_label = fjernede %s etiketten %s issues.remove_labels = fjernede %s etiketterne %s issues.change_project_at = `modificerede projektet fra %s til %s %s` @@ -1911,10 +1911,10 @@ pulls.editable_explanation = Denne pull-anmodning tillader redigeringer fra vedl pulls.auto_merge_button_when_succeed = (Når kontroller lykkes) pulls.status_checks_requested = Påkrævet pulls.close = Luk pull anmodning -pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %[2]s` +pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %s` pulls.cmd_instruction_hint = Se instruktionerne på kommandolinjen -pulls.reopened_at = `genåbnede denne pull-anmodning %[2]s` -pulls.closed_at = `lukkede denne pull-anmodning %[2]s` +pulls.reopened_at = `genåbnede denne pull-anmodning %s` +pulls.closed_at = `lukkede denne pull-anmodning %s` pulls.cmd_instruction_checkout_desc = Fra dit projektdepot, tjek en ny gren og test ændringerne. pulls.editable = Redigerbar pulls.made_using_agit = AGit diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 829912b3cc..f8bfc9258a 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -1577,7 +1577,7 @@ issues.remove_ref_at=`hat die Referenz %s %s entfernt` issues.add_ref_at=`hat die Referenz %s %s hinzugefügt` issues.delete_branch_at=`löschte den Branch %s %s` issues.filter_label=Label -issues.filter_label_exclude=`Alt + Klick/Enter verwenden, um Labels auszuschließen` +issues.filter_label_exclude=`Verwende Alt + Klick/Enter, um Labels auszuschließen` issues.filter_label_no_select=Alle Labels issues.filter_label_select_no_label=Kein Label issues.filter_milestone=Meilenstein @@ -1651,13 +1651,13 @@ issues.close_comment_issue=Mit Kommentar schließen issues.reopen_issue=Wieder öffnen issues.reopen_comment_issue=Mit Kommentar wieder öffnen issues.create_comment=Kommentieren -issues.closed_at=`hat diesen Issue %[2]s geschlossen` -issues.reopened_at=`hat dieses Issue %[2]s wieder geöffnet` -issues.commit_ref_at=`hat dieses Issue %[2]s aus einem Commit referenziert` -issues.ref_issue_from=`hat %[2]s auf dieses Issue verwiesen %[4]s` -issues.ref_pull_from=`hat %[2]s auf diesen Pull-Request verwiesen %[4]s` -issues.ref_closing_from=`hat %[2]s in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es schließen wird` -issues.ref_reopening_from=`hat %[2]s in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es erneut öffnen wird` +issues.closed_at=`hat dieses Issue %s geschlossen` +issues.reopened_at=`hat dieses Issue %s wieder geöffnet` +issues.commit_ref_at=`hat dieses Issue %s aus einem Commit referenziert` +issues.ref_issue_from=`hat %[1]s auf dieses Issue verwiesen %[3]s` +issues.ref_pull_from=`referenzierte diesen Pull-Request %[3]s %[1]s` +issues.ref_closing_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es schließen wird, %[1]s` +issues.ref_reopening_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es wieder öffnen wird, %[1]s` issues.ref_closed_from=`hat dieses Issue %[4]s geschlossen %[2]s` issues.ref_reopened_from=`hat dieses Issue %[4]s %[2]s wieder geöffnet` issues.ref_from=`von %[1]s` @@ -1962,8 +1962,8 @@ pulls.update_branch_success=Branch-Aktualisierung erfolgreich pulls.update_not_allowed=Du hast keine Berechtigung, den Branch zu updaten pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits des Basis-Branches pulls.close=Pull-Request schließen -pulls.closed_at=`hat diesen Pull-Request %[2]s geschlossen` -pulls.reopened_at=`hat diesen Pull-Request %[2]s wieder geöffnet` +pulls.closed_at=`hat diesen Pull-Request %s geschlossen` +pulls.reopened_at=`hat diesen Pull-Request %s wieder geöffnet` pulls.clear_merge_message=Merge-Nachricht löschen pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie „Co-Authored-By …“ erhalten. @@ -2767,7 +2767,7 @@ settings.wiki_globally_editable = Allen erlauben, das Wiki zu bearbeiten settings.protect_branch_name_pattern_desc = Geschützte Branch-Namens-Patterns. Siehe die Dokumentation für Pattern-Syntax. Beispiele: main, release/** settings.ignore_stale_approvals = Abgestandene Genehmigungen ignorieren settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Reviews), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Reviews bereits verworfen werden. -pulls.commit_ref_at = `hat sich auf diesen Pull-Request von einem Commit %[2]s bezogen` +pulls.commit_ref_at = `referenzierte diesen Pull-Request aus einem Commit %s` pulls.fast_forward_only_merge_pull_request = Nur Fast-forward pulls.cmd_instruction_checkout_desc = Checke einen neuen Branch aus deinem Projekt-Repository aus und teste die Änderungen. pulls.cmd_instruction_merge_title = Zusammenführen @@ -3061,8 +3061,8 @@ teams.invite.by=Von %s eingeladen teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten. follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat. open_dashboard = Übersicht öffnen -settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Abkühldauer von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. -settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Abkühldauer von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. +settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Schutzzeit von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen. +settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Schutzzeit von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen. [admin] dashboard=Übersicht diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 29085aebf1..398a0d9ce4 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -1626,13 +1626,13 @@ issues.close_comment_issue=Αποστολή σχολίου και κλείσιμ issues.reopen_issue=Ανοίξτε ξανά issues.reopen_comment_issue=Αποστολή σχολίου και επανάνοιγμα ζητήματος issues.create_comment=Προσθήκη Σχολίου -issues.closed_at=`αυτό το ζήτημα έκλεισε %[2]s` -issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα %[2]s` -issues.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή %[2]s` -issues.ref_issue_from=`αναφέρθηκε σε αυτό το ζήτημα %[4]s %[2]s` -issues.ref_pull_from=`αναφέρθηκε σε αυτό το pull request %[4]s %[2]s` -issues.ref_closing_from=`ανέφερε αυτό το ζήτημα σε ένα pull request %[4]s που στοχεύει να κλείσει το ζήτημα %[2]s` -issues.ref_reopening_from=`αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[4]s που θα ξαναανοίξει αυτό το ζήτημα %[2]s` +issues.closed_at=`αυτό το ζήτημα έκλεισε %s` +issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα %s` +issues.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή %s` +issues.ref_issue_from=`αναφέρθηκε σε αυτό το ζήτημα %[3]s %[1]s` +issues.ref_pull_from=`αναφέρθηκε σε αυτό το pull request %[3]s %[1]s` +issues.ref_closing_from=`ανέφερε αυτό το ζήτημα σε ένα pull request %[3]s που στοχεύει να κλείσει το ζήτημα %[1]s` +issues.ref_reopening_from=`αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[3]s που θα ξαναανοίξει αυτό το ζήτημα %[1]s` issues.ref_closed_from=`έκλεισε αυτό το ζήτημα %[4]s %[2]s` issues.ref_reopened_from=`άνοιξε ξανά αυτό το ζήτημα %[4]s %[2]s` issues.ref_from=`από %[1]s` @@ -1939,8 +1939,8 @@ pulls.update_branch_success=Η ενημέρωση του κλάδου ήταν pulls.update_not_allowed=Δεν επιτρέπεται να ενημερώσετε τον κλάδο pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ενημερωμένος με τον βασικό κλάδο pulls.close=Κλείσιμο pull request -pulls.closed_at=`έκλεισε αυτό το pull request %[2]s` -pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %[2]s` +pulls.closed_at=`έκλεισε αυτό το pull request %s` +pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %s` pulls.cmd_instruction_hint=Προβολή οδηγιών γραμμής εντολών pulls.cmd_instruction_checkout_title=Έλεγχος pulls.cmd_instruction_checkout_desc=Από το repository του έργου σας, ελέγξτε έναν νέο κλάδο και δοκιμάστε τις αλλαγές. @@ -2720,7 +2720,7 @@ settings.new_owner_blocked_doer = Ο νέος κάτοχος του αποθετ settings.enter_repo_name = Γράψτε το όνομα του κατόχου και του αποθετηρίου ακριβώς όπως το βλέπετε: settings.confirmation_string = Κείμενο επιβεβαίωσης settings.units.overview = Επισκόπηση -pulls.commit_ref_at = `ανέφερε το pull request στο commit %[2]s` +pulls.commit_ref_at = `ανέφερε το pull request στο commit %s` contributors.contribution_type.filter_label = Είδος συνεισφοράς: settings.wiki_rename_branch_main_notices_1 = Αυτή η ενέργεια ΔΕΝ αναιρείται. activity.navbar.contributors = Συνεισφέροντες diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index f912409cc9..bdafba93b4 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1648,13 +1648,13 @@ issues.close_comment_issue=Cerrar con comentario issues.reopen_issue=Reabrir issues.reopen_comment_issue=Reabrir con comentario issues.create_comment=Comentar -issues.closed_at=`cerró esta incidencia %[2]s` -issues.reopened_at=`reabrió esta incidencia %[2]s` -issues.commit_ref_at=`referenció esta incidencia en un commit %[2]s` -issues.ref_issue_from=`referenció esta incidencia %[4]s %[2]s` -issues.ref_pull_from=`referenció este pull request %[4]s %[2]s` -issues.ref_closing_from=`hizo referencia a esta incidencia desde un pull request %[4]s que lo cerrará , %[2]s` -issues.ref_reopening_from=`hizo referencia a esta incidencia desde un pull request %[4]s que lo reabrirá, %[2]s` +issues.closed_at=`cerró esta incidencia %s` +issues.reopened_at=`reabrió esta incidencia %s` +issues.commit_ref_at=`referenció esta incidencia en un commit %s` +issues.ref_issue_from=`referenció esta incidencia %[3]s %[1]s` +issues.ref_pull_from=`referenció este pull request %[3]s %[1]s` +issues.ref_closing_from=`hizo referencia a esta incidencia desde un pull request %[3]s que lo cerrará , %[1]s` +issues.ref_reopening_from=`hizo referencia a esta incidencia desde un pull request %[3]s que lo reabrirá, %[1]s` issues.ref_closed_from=`cerró esta incidencia %[4]s %[2]s` issues.ref_reopened_from=`reabrió esta incidencia %[4]s %[2]s` issues.ref_from=`de %[1]s` @@ -1959,8 +1959,8 @@ pulls.update_branch_success=La actualización de la rama ha finalizado correctam pulls.update_not_allowed=No tiene permisos para actualizar esta rama pulls.outdated_with_base_branch=Esta rama está desactualizada con la rama base pulls.close=Cerrar pull request -pulls.closed_at=`cerró este pull request %[2]s` -pulls.reopened_at=`reabrió este pull request %[2]s` +pulls.closed_at=`cerró este pull request %s` +pulls.reopened_at=`reabrió este pull request %s` pulls.clear_merge_message=Borrar mensaje de fusión pulls.clear_merge_message_hint=Limpiar el mensaje de fusión solo eliminará el contenido del mensaje de commit y mantendrá frases generadas como "Co-Autorizado por …". @@ -2789,7 +2789,7 @@ pulls.status_checks_hide_all = Ocultar todas las verificaciones settings.federation_not_enabled = La federación no está habilitada en tu instancia. wiki.search = Buscar en wiki pulls.status_checks_show_all = Mostrar todas las verificaciones -pulls.commit_ref_at = `hizo referencia a este pull request desde un commit %[2]s` +pulls.commit_ref_at = `hizo referencia a este pull request desde un commit %s` pulls.cmd_instruction_merge_title = Fusionar contributors.contribution_type.deletions = Eliminaciones contributors.contribution_type.filter_label = Tipo de contribución: diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 804b48b2b2..dae0695495 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -1250,13 +1250,13 @@ issues.close_comment_issue=ثبت دیدگاه و بستن issues.reopen_issue=بازگشایی issues.reopen_comment_issue=ثبت دیدگاه و بازگشایی issues.create_comment=دیدگاه -issues.closed_at=`%[2]s این موضوع را بست` -issues.reopened_at=`%[2]s این موضوع را دوباره باز کرد` -issues.commit_ref_at=`ارجاع این مسئله به کامیت %[2]s` -issues.ref_issue_from=` ارجاعات این مسائله %[4] %[2]s` -issues.ref_pull_from=` ارجاعات این تقاضای ادغام %[4] %[2]s` -issues.ref_closing_from=` ارجاعات این تقاضای واکشی %[4] %[2]s` -issues.ref_reopening_from=` تقاضای واکشی ارجاع شده %[4] که مسائله بازگشایی خواهد کرد %[2] ` +issues.closed_at=`%s این موضوع را بست` +issues.reopened_at=`%s این موضوع را دوباره باز کرد` +issues.commit_ref_at=`ارجاع این مسئله به کامیت %s` +issues.ref_issue_from=` ارجاعات این مسائله %[3] %[1]s` +issues.ref_pull_from=` ارجاعات این تقاضای ادغام %[4] %[1]s` +issues.ref_closing_from=` ارجاعات این تقاضای واکشی %[4] %[1]s` +issues.ref_reopening_from=` تقاضای واکشی ارجاع شده %[3]sکه مسائله بازگشایی خواهد کرد %[2] ` issues.ref_closed_from=` بسته شده این مسائله %[4] %[2]s` issues.ref_reopened_from=` بازگشایی این مسائله %[4] %[2]s` issues.ref_from=`از %[1]` @@ -1493,8 +1493,8 @@ pulls.update_branch_rebase=بروزآوری شاخه با بازسازی مجد pulls.update_branch_success=شاخه به موفقیت بروز شد pulls.update_not_allowed=شما اجازه بروزرسانی شاخه را ندارید pulls.outdated_with_base_branch=این شاخه با شاخه پایه منسوخ شده است -pulls.closed_at=`این درخواست pull بسته شده %[2]s` -pulls.reopened_at=`این درخواست pull را بازگشایی کرد %[2]s` +pulls.closed_at=`این درخواست pull بسته شده %s` +pulls.reopened_at=`این درخواست pull را بازگشایی کرد %s` diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index cff940a05a..164a60cc8d 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -1293,9 +1293,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 %[2]s` -issues.reopened_at=`uudelleenavasi tämän ongelman %[2]s` -issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa %[2]s` +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.author=Tekijä issues.role.owner=Omistaja issues.role.member=Jäsen @@ -2185,7 +2185,7 @@ settings.confirmation_string = Vahvistusteksti settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi tietovaraston %s mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset. issues.filter_assginee_no_select = Kaikki käsittelijät issues.new.assign_to_me = Osoita itselle -pulls.closed_at = `sulki tämän vetopyynnön %[2]s` +pulls.closed_at = `sulki tämän vetopyynnön %s` tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa. generate_repo = Luo tietovarasto @@ -2199,8 +2199,8 @@ issues.new.no_reviewers = Ei katselmoijia 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 = `viittasi tähän vetopyyntöön %[4]s %[2]s` -pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %[2]s` +issues.ref_pull_from = `viittasi tähän vetopyyntöön %[3]s %[1]s` +pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %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 @@ -2381,7 +2381,7 @@ 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 %[2]s` +pulls.reopened_at = `avasi uudelleen tämän vetopyynnön %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.twofa = Sinulla tulee olla kaksivaiheinen todennus käytössä, jotta kommitit voi allekirjoittaa. diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 7d1405f633..8c9badb04b 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -365,7 +365,7 @@ table_modal.label.columns = Mga Column link_modal.header = Magdagdag ng link link_modal.url = Url link_modal.description = Deskripsyon -link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maari mong direktang i-paste sa editor para gumawa ng link. +link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maaari mong direktang i-paste sa editor para gumawa ng link. [filter] string.asc = A - Z @@ -432,7 +432,7 @@ openid_connect_desc = Ang piniling OpenID URI ay hindi alam. Iugnay iyan sa bago invalid_code = Ang iyong confirmation code ay hindi wasto o nag-expire na. oauth_signin_title = Mag-sign in para pahintulutan ang naka-link na account invalid_code_forgot_password = Ang iyong confirmation code ay hindi wasto o nag-expire na. Mag-click dito para magsimula ng bagong session. -confirmation_mail_sent_prompt = Ang isang bagong email na pang-kumpirma ay ipinadala sa %s. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address. +confirmation_mail_sent_prompt = Ang isang bagong email na pang-kumpirma ay ipinadala sa %s. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maaari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address. invalid_password = Ang iyong password ay hindi tugma sa password na ginamit para gawin ang account. twofa_scratch_used = Ginamit mo na ang scratch code. Na-redirect ka sa two-factor settings page para tanggalin ang device enrollment o mag-generate ng bagong scratch code. manual_activation_only = Makipag-ugnayan sa tagapangangasiwa ng site para kumpletuhin ang pagrehistro. @@ -484,7 +484,7 @@ admin.new_user.text = Mangyaring mag-click dito para ipamahala register_notify = Maligayang Pagdating sa %s register_notify.title = %[1]s, maligayang pagdating sa %[2]s register_notify.text_1 = ito ang iyong registration confirmation email para sa %s! -register_notify.text_2 = Maari kang mag-sign in sa iyong account gamit ng iyong username: %s +register_notify.text_2 = Maaari kang mag-sign in sa iyong account gamit ng iyong username: %s reset_password = I-recover ang iyong account reset_password.title = %s, nagkaroon kami ng hiling para i-recover ang iyong account reset_password.text = Kung ikaw ito, paki-click ang sumusunod na link para i-recover ang iyong account sa loob ng %s: @@ -535,7 +535,7 @@ totp_disabled.text_1 = Ngayon lang na-disable ang Time-based one-time password ( totp_disabled.no_2fa = Wala nang mga ibang paraan ng 2FA ang naka-configure, nangangahulugan na hindi na kailangang mag-log in sa iyong account gamit ang 2FA. removed_security_key.subject = May tinanggal na security key removed_security_key.text_1 = Tinanggal ngayon lang ang security key na "%[1]s" sa iyong account. -account_security_caution.text_1 = Kung ikaw ito, maari mong ligtas na huwag pansinin ang mail na ito. +account_security_caution.text_1 = Kung ikaw ito, maaari mong ligtas na huwag pansinin ang mail na ito. account_security_caution.text_2 = Kung hindi ito ikaw, nakompromiso ang iyong account. Mangyaring makipag-ugnayan sa mga tagapangasiwa ng site na ito. totp_enrolled.subject = Nag-activate ka ng TOTP bilang paraan ng 2FA totp_enrolled.text_1.has_webauthn = Na-enable mo lang ang TOTP para sa iyong account. Nangangahulugan ito na para sa lahat ng mga hinaharap na pag-login sa iyong account, kailangan mong gumamit ng TOTP bilang paraan ng 2FA o gamitin ang iyong mga security key. @@ -644,7 +644,7 @@ AccessToken = Token ng pag-access Biography = Byograpya Location = Lokasyon visit_rate_limit = Natugunan ang limitasyon sa rate ng malayuang pagbisita. -username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maari itong i-claim sa %[1]s. +username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maaari itong i-claim sa %[1]s. email_domain_is_not_allowed = Sumasalungat ang domain ng email address ng user %s sa EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Siguraduhing natakda mo ang email address nang tama. [user] @@ -685,7 +685,7 @@ followers.title.few = Mga tagasunod following.title.one = Sinusundan followers.title.one = Tagasunod public_activity.visibility_hint.self_public = Nakikita ng lahat ang iyong aktibidad, maliban sa mga interaksyon sa pribadong espasyo. I-configure. -public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maari mo ring makita ang mga interaksyon sa mga pribadong espasyo. +public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maaari mo ring makita ang mga interaksyon sa mga pribadong espasyo. public_activity.visibility_hint.self_private = Nakikita mo lang at mga tagapangasiwa ng instansya ang iyong aktibidad. I-configure. public_activity.visibility_hint.admin_private = Nakikita mo ang aktibidad na ito dahil isa kang tagapangasiwa, ngunit gusto ng user na panatilihin itong pribado. public_activity.visibility_hint.self_private_profile = Ikaw lang at ang mga tagapangasiwa ng instansya ang makakakita ng iyong aktibidad dahil pribado ang iyong profile. I-configure. @@ -842,7 +842,7 @@ gpg_key_verify = I-verify gpg_invalid_token_signature = Ang ibinigay na GPG key, signature, at token ay hindi tumutugma o luma. gpg_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba gpg_token = Token -gpg_token_help = Maari kang mag-generate ng signature gamit ng: +gpg_token_help = Maaari kang mag-generate ng signature gamit ng: gpg_token_signature = Naka-armor na GPG signature key_signature_gpg_placeholder = Nagsisimula sa "-----BEGIN PGP SIGNATURE-----" verify_gpg_key_success = Na-verify na ang GPG key na "%s". @@ -851,7 +851,7 @@ ssh_key_verify = I-verify ssh_invalid_token_signature = Ang ibinigay na SSH key, signature, o token ay hindi tumutugma o luma. ssh_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba ssh_token = Token -ssh_token_help = Maari kang mag-generate ng signature gamit ng: +ssh_token_help = Maaari kang mag-generate ng signature gamit ng: ssh_token_signature = Naka-armor na SSH signature key_signature_ssh_placeholder = Nagsisimula sa "-----BEGIN SSH SIGNATURE-----" verify_ssh_key_success = Na-verify na ang SSH key na "%s". @@ -912,10 +912,10 @@ create_oauth2_application_success = Matagumpay kang gumawa ang bagong OAuth2 app oauth2_confidential_client = Kumpidensyal na kliyente. Piliin para sa mga app na pinapatilihing kumpidensyal ang sikreto, tulad ng mga web app. Huwag piliin para sa mga web app kasama ang mga desktop at mobile app. twofa_desc = Para protektahin ang iyong account laban sa pagnanakaw ng password, pwede mo gamitin ang iyong smartphone o ibang device para sa pagtanggap ng time-based one-time password ("TOTP"). twofa_scratch_token_regenerated = Ang iyong isang-beses na paggamit na recovery key ngayon ay %s. Ilagay ito sa ligtas na lugar, dahil hindi na ito ipapakita muli. -regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maari mong i-reset dito. +regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maaari mong i-reset dito. twofa_disable_desc = Ang pag-disable ng authentikasyong two-factor ay gagawing hindi gaanong ligtas ang iyong account. Magpatuloy? twofa_enrolled = Matagumpay na na-enroll ang iyong account. Ilagay ang iyong isang-beses na paggamit na recovery key (%s) sa isang ligtas na lugar, dahil hindi na ito ipapakita muli. -webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang WebAuthn Authenticator na standard. +webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maaari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang WebAuthn Authenticator na standard. remove_oauth2_application = Tanggalin ang OAuth2 Application remove_oauth2_application_desc = Ang pagtanggal ng OAuth2 application ay babawiin ang access sa lahat ng mga naka-sign na access token. Magpatuloy? remove_oauth2_application_success = Binura na ang application. @@ -931,13 +931,13 @@ oauth2_regenerate_secret = I-regenerate ang sikreto oauth2_regenerate_secret_hint = Nawala mo ang iyong sikreto? oauth2_client_secret_hint = Ang sikreto ay hindi ipapakita muli pagkatapos umalis ka o i-refresh ang page na ito. Mangyaring siguraduhin na na-save mo iyan. oauth2_application_edit = I-edit -twofa_recovery_tip = Kapag mawala mo ang iyong device, maari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account. +twofa_recovery_tip = Kapag mawala mo ang iyong device, maaari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account. twofa_is_enrolled = Ang iyong account ay kasalukuyang naka-enroll sa autentikasyong two-factor. twofa_not_enrolled = Kasalukuyang hindi naka-enroll ang iyong account sa authentikasyong two-factor. twofa_disable = I-disable ang authentikasyong two-factor twofa_scratch_token_regenerate = I-regenerate ang isang-beses na paggamit na recovery key twofa_enroll = Mag-enroll sa authentikasyong two-factor -twofa_disable_note = Maari mong i-disable ang authentikasyong two-factor kapag kinakailangan. +twofa_disable_note = Maaari mong i-disable ang authentikasyong two-factor kapag kinakailangan. twofa_disabled = Na-disable na ang authentikasyong two-factor. scan_this_image = I-scan ang image na ito gamit ng iyong aplikasyong pang-authentikasyon: or_enter_secret = O ilagay ang sikreto: %s @@ -1005,8 +1005,8 @@ language.description = Mase-save ang wika sa iyong account at gagamitin bilang d language.localization_project = Tulungan kaming isalin ang Forgejo sa iyong wika! Matuto pa. pronouns_custom_label = Mga pasadyang pronoun user_block_yourself = Hindi mo maaaring harangan ang sarili mo. -change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. -change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. +change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. +change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. keep_pronouns_private = Ipakita lang ang mga panghalip sa mga naka-authenticate na user keep_pronouns_private.description = Itatago nito ang iyong mga panghalip mula sa mga bisita na hindi naka-log in. quota.applies_to_user = Nag-aapply ang mga sumusunod na panuntunan ng quota sa iyong account @@ -1071,7 +1071,7 @@ readme_helper_desc = Ito ang lugar kung saan makakasulat ka ng kumpletong deskri trust_model_helper_collaborator_committer = Katulong+Committer: I-trust ang mga signature batay sa mga katulong na tumutugma sa committer mirror_interval = Interval ng mirror (ang mga wastong unit ng oras ay "h", "m", "s"). 0 para i-disable ang periodic sync. (Pinakamababang interval: %s) transfer.reject_desc = Kanselahin ang pag-transfer mula sa "%s" -mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang matukoy ang LFS server. Maari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar. +mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang matukoy ang LFS server. Maaari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar. adopt_search = Ilagay ang username para maghanap ng mga unadopted na repositoryo… (iwanang walang laman para hanapin lahat) object_format = Format ng object readme_helper = Pumili ng README file template @@ -1164,8 +1164,8 @@ tree_path_not_found_commit = Hindi umiiral ang path na %[1]s sa commit %[2]s tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa branch %[2]s migrate_items_pullrequests = Mga hiling sa paghila archive.pull.nocomment = Naka-archive ang repositoryong ito. Hindi ka makakakomento sa mga pull request. -archive.title = Naka-archive ang repositoryong ito. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento. -archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento. +archive.title = Naka-archive ang repositoryong ito. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento. +archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento. pulls = Mga hiling sa paghila activity.merged_prs_count_n = Mga naisamang hiling sa paghila wiki.last_updated = Huling binago %s @@ -1183,7 +1183,7 @@ issues.action_open = Buksan issues.closed_title = Sarado issues.reopen_issue = Buksang muli pulls.merged = Naisama na -pulls.merged_info_text = Maari nang burahin ang branch %s. +pulls.merged_info_text = Maaari nang burahin ang branch %s. milestones.update_ago = Binago %s activity.closed_issue_label = Sarado activity.merged_prs_label = Naisama @@ -1205,7 +1205,7 @@ migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na reposit need_auth = Awtorisasyon migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account. template.invalid = Kailangang pumili ng kahit isang template na repositoryo -migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. +migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maaari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa .git-blame-ignore-revs. tree_path_not_found_tag = Hindi umiiral ang path na %[1]s sa tag %[2]s form.reach_limit_of_creation_n = Naabot na ng may-ari ang limitasyon na %d mga repositoryo. @@ -1471,10 +1471,10 @@ activity.new_issue_label = Nabuksan activity.merged_prs_count_1 = Naisamang hiling sa paghila activity.opened_prs_count_1 = Inimungkahing hiling sa paghila activity.opened_prs_label = Inimungkahi -pulls.reopened_at = `nabuksang muli ang hiling sa paghatak na %[2]s` +pulls.reopened_at = `binuksan muli ang hiling sa paghila %s` issues.opened_by_fake = binuksan ang %[1]s ni/ng %[2]s pulls.reopen_failed.base_branch = Hindi mabuksang muli ang hiling sa paghatak na ito dahil hindi na umiiral ang base branch. -issues.reopened_at = `binuksang muli ang isyung ito %[2]s` +issues.reopened_at = `binuksang muli ang isyung ito %s` pulls.reopen_failed.head_branch = Hindi mabubuksan muli ang hiling sa paghila, dahil hindi na umiiral ang head branch. settings.event_pull_request_desc = Binuksan, sinara, muling binuksan, o binago ang hiling sa paghatak. activity.opened_prs_count_n = Mga inimungkahing hiling sa paghila @@ -1500,7 +1500,7 @@ issues.content_history.created = ginawa editor.patching = Pina-patch: editor.fail_to_apply_patch = Hindi malapat ang patch na "%s" settings.danger_zone = Mapanganib na lugar -issues.closed_at = `isinara ang isyung ito %[2]s` +issues.closed_at = `isinara ang isyung ito %s` settings.collaboration.admin = Tagapangasiwa settings.admin_settings = Mga setting ng tagapangasiwa issues.start_tracking_history = `sinimulan ang trabaho %s` @@ -1627,7 +1627,7 @@ projects.column.edit_title = Pangalan projects.column.new_title = Pangalan projects.card_type.desc = Mga preview ng card commits.desc = I-browse ang history ng pagbabago ng source code. -commits.search.tooltip = Maari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09". +commits.search.tooltip = Maaari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09". issues.force_push_codes = `puwersahang itinulak ang %[1]s mula %[2]s sa %[4]s %[6]s` issues.push_commit_1 = idinagdag ang %d commit %s issues.push_commits_n = idinagdag ang %d mga commit %s @@ -1707,7 +1707,7 @@ issues.action_milestone = Milestone issues.action_milestone_no_select = Walang milestone issues.delete_branch_at = `binura ang branch na %s %s` issues.filter_label = Label -issues.filter_label_exclude = `Gamitin ang alt + click/enter para hindi isama ang mga label` +issues.filter_label_exclude = `Gamitin ang Alt + Click para hindi isama ang mga label` issues.filter_label_no_select = Lahat ng mga label issues.filter_milestone_closed = Mga nakasarang milestone issues.filter_assignee = Mangangasiwa @@ -1771,7 +1771,7 @@ issues.lock = I-lock ang usapan issues.unlock = I-unlock ang usapan issues.unlock_comment = na-unlock ang usapang ito %s issues.unlock.notice_1 = - Makakakomento muli ang lahat ng mga tao sa isyung ito. -issues.unlock.notice_2 = - Maari mong i-lock muli ang isyung ito sa hinaharap. +issues.unlock.notice_2 = - Maaari mong i-lock muli ang isyung ito sa hinaharap. issues.comment_on_locked = Hindi ka makakakomento sa naka-lock na isyu. issues.closed_by_fake = ni/ng %[2]s ay isinara %[1]s issues.comment_manually_pull_merged_at = manwal na isinama ang commit %[1]s sa %[2]s %[3]s @@ -1787,10 +1787,10 @@ issues.label_archive_tooltip = Ang mga naka-archive na label ay hindi isasama bi issues.is_stale = May mga pagbabago sa PR na ito mula sa pagsuri na ito issues.role.first_time_contributor = Unang-beses na contributor issues.lock.notice_1 = - Hindi makakadagdag ng mga bagong komento ang mga ibang user sa isyu na ito. -issues.lock.notice_3 = - Maari mong i-unlock muli ang isyung ito sa hinaharap. +issues.lock.notice_3 = - Maaari mong i-unlock muli ang isyung ito sa hinaharap. issues.label_deletion_desc = Ang pagbura ng label ay tatanggalin ito sa lahat ng mga isyu. Magpatuloy? -issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit %[2]s` -issues.ref_issue_from = `isinangguni ang isyu na ito sa %[4]s %[2]s` +issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit %s` +issues.ref_issue_from = `isinangguni ang isyu na ito sa %[3]s %[1]s` issues.num_participants_one = %d kasali issues.attachment.download = `I-click para i-download ang "%s" ` issues.num_participants_few = %d mga kasali @@ -1815,10 +1815,10 @@ issues.sign_in_require_desc = Mag-sign in upang sumali sa usapa issues.num_comments = %d mga komento issues.role.contributor_helper = Nakaraang nag-commit ang user na ito sa repositoryo na ito. issues.comment_pull_merged_at = isinama ang commit %[1]s sa %[2]s %[3]s -pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit %[2]s` +pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit %s` wiki.last_commit_info = Binago ni %s ang pahinang ito %s issues.content_history.edited = binago -issues.ref_pull_from = `isinangguni ang hiling sa paghila na ito %[4]s %[2]s` +issues.ref_pull_from = `isinangguni ang hiling sa paghila na ito %[3]s %[1]s` pulls.merged_title_desc_few = isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s settings.org_not_allowed_to_be_collaborator = Hindi maaaring idagdag ang mga organisasyon bilang tagatulong. settings.add_collaborator_success = Naidagdag ang tagatulong. @@ -1828,7 +1828,7 @@ pulls.create = Gumawa ng hiling sa paghila issues.dependency.pr_close_blocked = Kailangan mong isara ang lahat ng mga isyu na humaharang sa hiling sa paghila na ito bago mo ito isama. pulls.delete.title = Burahin ang hiling sa paghila na ito? issues.dependency.pr_closing_blockedby = Hinarang ng mga sumusunod na isyu mula sa pagsara ng hiling sa paghila na ito -pulls.closed_at = `isinara ang hiling sa paghila na %[2]s` +pulls.closed_at = `isinara ang hiling sa paghila na ito %s` pulls.close = Isara ang hiling sa paghila pulls.cmd_instruction_hint = Tingnan ang mga panuto para sa command line project = Mga proyekto @@ -1836,8 +1836,8 @@ issues.content_history.deleted = binura pulls.no_results = Walang mga nahanap na resulta. pulls.closed = Sarado ang hiling sa paghila pulls.is_closed = Naisara na ang hiling sa paghila. -issues.ref_closing_from = `nagsangguni ang isyu mula sa hiling sa paghila %[4]s na magsasara sa isyu, %[2]s` -issues.ref_reopening_from = `nagsangguni ang isyu na ito mula sa hiling sa paghila %[4]s na muling bubukas, %[2]s` +issues.ref_closing_from = `nagsangguni ang isyu mula sa hiling sa paghila %[3]s na magsasara sa isyu, %[1]s` +issues.ref_reopening_from = `nagsangguni ang isyu na ito mula sa hiling sa paghila %[3]s na muling bubukas nito, %[1]s` issues.ref_closed_from = `isinara ang isyung ito %[4]s%[2]s` issues.review.wait = hiniling sa pagsuri %s issues.review.reject = hinihiling ang mga pagbago %s @@ -2015,14 +2015,14 @@ wiki.cancel = Kanselahin settings.collaboration.undefined = Hindi Natukoy settings.federation_settings = Mga Setting ng Federation settings = Mga Setting -settings.desc = Ang mga setting ang lugar kung saan maari mong ipamahala ang mga setting para sa repositoryo +settings.desc = Ang mga setting ang lugar kung saan maaari mong ipamahala ang mga setting para sa repositoryo pulls.collapse_files = I-collapse ang lahat ng mga file pulls.add_prefix = Magdagdag ng %s na prefix pulls.still_in_progress = Ginagawa pa? activity.title.prs_1 = %d hiling sa paghila activity.active_issues_count_n = %d mga aktibong isyu pulls.required_status_check_missing = Nawawala ang ilang mga kinakailangang pagsusuri. -pulls.required_status_check_administrator = Bilang tagapangasiwa, maari mo pa ring isama ang hiling sa paghila na ito. +pulls.required_status_check_administrator = Bilang tagapangasiwa, maaari mo pa ring isama ang hiling sa paghila na ito. pulls.blocked_by_approvals = Wala pang sapat na pag-apruba ang hiling sa paghila na ito. %d ng %d na pag-apruba ang ibinigay. settings.options = Repositoryo wiki.back_to_wiki = Bumalik sa pahina ng wiki @@ -2110,7 +2110,7 @@ settings.actions_desc = I-enable ang mga kasamang CI/CD pipeline gamit ang Forge settings.admin_indexer_commit_sha = Huling na-index na commit settings.admin_indexer_unindexed = Hindi naka-index settings.transfer_notices_3 = - Kung pribado ang repositoryo at ilipat sa isang indibidwal na user, ang aksyon na ito ay sinisigurado na ang user ay may pahintulot na basahin (at palitan ang mga pahintulot kung kailangan). -settings.convert_desc = Maari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi. +settings.convert_desc = Maaari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi. settings.transfer.button = Ilipat ang pagmamay-ari settings.signing_settings = Mga setting sa pagpapatunay ng pag-sign settings.admin_enable_close_issues_via_commit_in_any_branch = Isara ang isyu sa pamamagitan ng commit na ginawa sa hindi default na branch @@ -2137,7 +2137,7 @@ settings.deploy_key_deletion = Tanggalin ang deploy key settings.protect_enable_push = I-enable ang pagtulak settings.discord_icon_url.exceeds_max_length = Kailangang bababa o equal sa 2048 characters ang URL ng icon settings.protected_branch.save_rule = I-save ang rule -settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maari mo pa rin gamitin ang iyong umiiral na mirror. +settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maaari mo pa rin gamitin ang iyong umiiral na mirror. settings.slack_color = Kulay settings.discord_icon_url = URL ng icon settings.convert_fork_confirm = I-convert ang repositoryo @@ -2254,7 +2254,7 @@ settings.pulls.allow_rebase_update = I-enable ang pag-update ng hiling sa paghil settings.admin_enable_health_check = I-enable ang pagsusuri ng kalusugan ng repositoryo (git fsck) settings.new_owner_has_same_repo = Ang bagong may-ari ay may repositoryo na may katulad na pangalan. Mangyaring pumili ng ibang pangalan. settings.convert = I-convert sa regular na repositoryo -settings.convert_fork_desc = Maari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi. +settings.convert_fork_desc = Maaari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi. settings.convert_fork_notices_1 = Ang operasyon na ito ay ico-convert ang fork bilang regular na repositoryo at hindi mababawi. settings.transfer_abort_invalid = Hindi mo makakansela ang isang hindi umiiral na paglipat ng repositoryo. settings.transfer_quota_exceeded = Ang bagong may-ari (%s) ay lumalagpas sa quota. Hindi nailipat ang repositoryo. @@ -2290,8 +2290,8 @@ settings.webhook.headers = Mga header settings.webhook.payload = Nilalaman settings.webhook.body = Katawan settings.webhook.replay.description = I-replay ang webhook na ito. -settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maari magtagal ng ilang segundo bago makita sa delivery history. -settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon. +settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maaari magtagal ng ilang segundo bago makita sa delivery history. +settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maaari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon. settings.githook_name = Pangalan ng hook settings.githook_content = Nilalaman ng hook settings.update_githook = I-update ang hook @@ -2362,7 +2362,7 @@ settings.mirror_settings.docs.pull_mirror_instructions = Para mag-set up ng pull milestones.invalid_due_date_format = Kailangang "yyyy-mm-dd" na format ang takdang petsa. signing.wont_sign.nokey = Walang key ang instansya na ito para i-sign ang commit na ito. activity.title.releases_1 = %d paglabas -settings.mirror_settings.docs.more_information_if_disabled = Maari kang matuto pa tungkol sa mga push at pull na mirror dito: +settings.mirror_settings.docs.more_information_if_disabled = Maaari kang matuto pa tungkol sa mga push at pull na mirror dito: settings.branches.switch_default_branch = Magpalit ng default branch settings.convert_notices_1 = Ang operasyon na ito ay ico-covert ang mirror sa regular na repositoryo at hindi mababawi. settings.convert_fork_succeed = Na-convert na ang fork sa regular na repositoryo. @@ -2732,7 +2732,7 @@ settings.protect_protected_file_patterns = Mga pattern ng nakaprotektang file (h settings.update_protect_branch_success = Binago na ang branch protection rule na "%s". settings.remove_protected_branch_success = Tinanggal ang branch protection rule na "%s". settings.tags.protection.pattern = Pattern ng tag -settings.tags.protection.pattern.description = Maari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa guide ng mga nakaprotektang tag. +settings.tags.protection.pattern.description = Maaari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa guide ng mga nakaprotektang tag. settings.thread_id = ID ng thread settings.matrix.room_id = ID ng room diff.has_escaped = May mga nakatagong Unicode character ang linya na ito @@ -2746,7 +2746,7 @@ diff.bin = BIN settings.default_update_style_desc = Ang default na istilio na gagamitin sa pag-update ng mga hiling sa paghila na nalilipas sa base branch. pulls.sign_in_require = Mag-sign in para gumawa ng bagong hiling sa paghila. new_from_template = Gumamit ng template -new_from_template_description = Maari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito. +new_from_template_description = Maaari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito. new_advanced = Mga advanced na setting new_advanced_expand = I-click para i-expand auto_init_description = Simulan ang kasaysayan ng Git gamit ang README at opsyonal na magdagdag ng mga lisensya at .gitignore na file. @@ -2780,6 +2780,7 @@ settings.event_action_recover = I-recover settings.event_action_success = Matagumpay settings.event_action_success_desc = Matagumpay na natapos ang Action Run. settings.event_action_recover_desc = Matagumpay na natapos ang Action Run pagkatapos na nabigo ang huling Action Run sa katulad na workflow. +issues.filter_type.all_pull_requests = Lahat ng mga hiling sa paghila [search] commit_kind = Maghanap ng mga commit… @@ -3205,7 +3206,7 @@ self_check.database_collation_mismatch = Inaasahan ang database na gamitin ang c auths.oauth2_admin_group = Group claim value para sa mga tagapangasiwa. (Opsyonal - kinakailangan ang claim name sa itaas) auths.tip.facebook = Magrehistro ng bagong application sa %s at idagdag ang produktong "Facebook Login" users.restricted.description = Payagan lamang ang interaksyon sa mga repositoryo at organisasyon kung saan ang user ay dinagdag bilang tagatulong. Iniiwasan nito ang pag-access sa publikong repositoryo sa instansya na ito. -users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maari itong maging isyu sa seguridad. +users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maaari itong maging isyu sa seguridad. emails.delete = Burahin ang Email emails.deletion_success = Binura na ang email address. auths.oauth2_required_claim_value = Kinakailangan na claim value @@ -3450,8 +3451,8 @@ teams.owners_permission_desc = Ang mga owner ay may punong access sa lah teams.add_nonexistent_repo = Hindi pa umiiral ang repositoryo na sinusubukan mong idagdag. Mangyaring gawin iyan muna. teams.all_repositories = Lahat ng mga repositoryo teams.all_repositories_helper = Ang koponan ay may access sa lahat ng mga repositoryo. Ang pagpili nito ay idadagdag ang lahat ng mga umiiral na repositoryo sa koponan. -settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown. -settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown. +settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown. +settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown. [packages] diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 3fcfda18bd..1cb7103bc0 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1062,8 +1062,8 @@ language.localization_project = Aidez-nous à traduire Forgejo dans votre langue language.description = Cette langue sera enregistrée dans votre compte et utilisée comme langue par défaut après votre connexion. user_block_yourself = Vous ne pouvez pas vous bloquer vous même. pronouns_custom_label = Pronoms personnalisés -change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. -change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. +change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. +change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. quota.rule.exceeded = Dépassé regenerate_token = Régénérer access_token_regeneration = Régénérer le token d'accès @@ -1653,13 +1653,13 @@ issues.close_comment_issue=Fermer avec le commentaire issues.reopen_issue=Rouvrir issues.reopen_comment_issue=Réouvrir avec le commentaire issues.create_comment=Commenter -issues.closed_at=`a fermé ce ticket %[2]s.` -issues.reopened_at=`a rouvert ce ticket %[2]s.` -issues.commit_ref_at=`a référencé ce ticket depuis une révision %[2]s.` -issues.ref_issue_from=`a fait référence à %[4]s ce ticket %[2]s.` -issues.ref_pull_from=`a fait référence à cette demande d'ajout %[4]s %[2]s.` -issues.ref_closing_from=`a fait référence à une demande d'ajout %[4]s qui clora ce ticket, %[2]s.` -issues.ref_reopening_from=`a référencé une pull request %[4]s qui va ré-ouvrir ce ticket %[2]s` +issues.closed_at=`a fermé ce ticket %s` +issues.reopened_at=`a rouvert ce ticket %s` +issues.commit_ref_at=`a référencé ce ticket depuis une révision %s` +issues.ref_issue_from=`a fait référence à ce ticket %[3]s %[1]s` +issues.ref_pull_from=`a fait référence à cette demande d'ajout %[3]s %[1]s` +issues.ref_closing_from=`a fait référence à une demande d'ajout %[3]s qui clora ce ticket, %[1]s` +issues.ref_reopening_from=`a référencé ce ticket dans une pull request %[3]s qui va ré-ouvrir ce ticket, %[1]s` issues.ref_closed_from=`a fermé ce ticket %[4]s %[2]s` issues.ref_reopened_from=`a rouvert ce ticket %[4]s %[2]s.` issues.ref_from=`de %[1]s` @@ -1967,8 +1967,8 @@ pulls.update_branch_success=La mise à jour de la branche a réussi pulls.update_not_allowed=Vous n'êtes pas autorisé à mettre à jour la branche pulls.outdated_with_base_branch=Cette branche est désynchronisée avec la branche de base pulls.close=Fermer la demande d’ajout -pulls.closed_at=`a fermé cette demande d'ajout %[2]s.` -pulls.reopened_at=`a rouvert cette demande d'ajout %[2]s.` +pulls.closed_at=`a fermé cette demande d'ajout %s` +pulls.reopened_at=`a rouvert cette demande d'ajout %s` pulls.cmd_instruction_hint=Voir les instructions en ligne de commande pulls.cmd_instruction_checkout_title=Basculer pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications. @@ -2762,7 +2762,7 @@ issues.blocked_by_user = Vous ne pouvez pas créer de tickets sur ce dépôt car pulls.blocked_by_user = Vous ne pouvez pas créer une pull request sur ce dépôt car vous êtes bloqué par son propriétaire. wiki.cancel = Annuler settings.wiki_globally_editable = Permettre l'édition du wiki a tout le monde -pulls.commit_ref_at = `a référencé cette pull request depuis le commit %[2]s` +pulls.commit_ref_at = `a référencé cette pull request depuis un commit %s` settings.new_owner_blocked_doer = Le nouveau propriétaire vous a bloqué. settings.enter_repo_name = Confirmez en entrant le propriétaire et le nom du dépôt exactement comme affiché : settings.wiki_rename_branch_main = Normalise le nom de la branche du Wiki @@ -3058,8 +3058,8 @@ teams.invite.by=Invité par %s teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindre l’équipe. follow_blocked_user = Vous ne pouvez pas suivre cette organisation car elle vous a bloqué. open_dashboard = Ouvrir le tableau de bord -settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. -settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. +settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. +settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. [admin] dashboard=Tableau de bord diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index d2d960b627..3bb06e8c21 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -1219,11 +1219,11 @@ issues.close_comment_issue = Dún le trácht issues.reopen_issue = Athoscail issues.reopen_comment_issue = Athoscail le trácht issues.create_comment = Trácht -issues.closed_at = `dhún an cheist seo %[2]s` -issues.reopened_at = `athoscail an t-eagrán seo %[2]s` -issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas %[2]s` -issues.ref_issue_from = `rinne dagairt don cheist seo %[4]s %[2]s` -issues.ref_pull_from = `rinne dagairt don iarratas tarraingthe seo %[4]s %[ 2]s` +issues.closed_at = `dhún an cheist seo %s` +issues.reopened_at = `athoscail an t-eagrán seo %s` +issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas %s` +issues.ref_issue_from = `rinne dagairt don cheist seo %[3]s %[1]s` +issues.ref_pull_from = `rinne dagairt don iarratas tarraingthe seo %[3]s %[1]s` issues.ref_closed_from = `dhún an cheist seo %[4]s %[2]s` issues.ref_reopened_from = `d'athoscail an eagrán seo %[4]s %[2]s` issues.ref_from = `ó %[1]s` @@ -1456,8 +1456,8 @@ pulls.update_branch_success = Bhí nuashonrú brainse rathúil pulls.update_not_allowed = Ní cheadaítear duit brainse a nuashonrú pulls.outdated_with_base_branch = Tá an brainse seo as dáta leis an mbunbhrainse pulls.close = Dún Iarratas Tarraing -pulls.closed_at = `dhún an t-iarratas tarraingthe seo %[2]s` -pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo %[2]s` +pulls.closed_at = `dhún an t-iarratas tarraingthe seo %s` +pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo %s` pulls.cmd_instruction_checkout_title = Seiceáil pulls.cmd_instruction_checkout_desc = Ó stór tionscadail, seiceáil brainse nua agus déan tástáil ar na hathruithe. pulls.cmd_instruction_merge_title = Cumaisc diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 411bad835a..3e93ee8ba9 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -932,7 +932,7 @@ issues.close_comment_issue=Hozzászólás és lezárás issues.reopen_issue=Újranyitás issues.reopen_comment_issue=Hozzászólás és újranyitás issues.create_comment=Hozzászólás -issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból %[2]s` +issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból %s` issues.role.owner=Tulajdonos issues.role.member=Tag issues.re_request_review=Véleményezés újrakérése diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 673d1464b1..f1a392105e 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -796,7 +796,7 @@ issues.close_comment_issue=Komentar dan Tutup issues.reopen_issue=Buka kembali issues.reopen_comment_issue=Komentar dan Buka Kembali issues.create_comment=Komentar -issues.commit_ref_at=`merujuk masalah dari komit %[2]s` +issues.commit_ref_at=`merujuk masalah dari komit %s` issues.role.owner=Pemilik issues.role.member=Anggota issues.sign_in_require_desc=Masuk untuk bergabung dengan percakapan ini. diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index 9b1d56fed9..baf8286923 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -805,8 +805,8 @@ issues.close_comment_issue=Senda ummæli og Loka issues.reopen_issue=Enduropna issues.reopen_comment_issue=Senda ummæli og Enduropna issues.create_comment=Senda Ummæli -issues.closed_at=`lokaði þessu vandamáli %[2]s` -issues.reopened_at=`enduropnaði þetta vandamál %[2]s` +issues.closed_at=`lokaði þessu vandamáli %s` +issues.reopened_at=`enduropnaði þetta vandamál %s` issues.ref_reopened_from=`enduropnaði þetta vandamál %[4]s %[2]s` issues.author=Höfundur issues.role.owner=Eigandi diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 48995e951f..d46f709cde 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -54,7 +54,7 @@ mirror=Mirror new_repo=Nuovo repository new_migrate=Nuova migrazione new_mirror=Nuovo mirror -new_fork=Nuova derivazione +new_fork=Nuova biforcazione new_org=Nuova organizzazione new_project=Nuovo progetto manage_org=Gestisci le organizzazioni @@ -143,12 +143,12 @@ confirm_delete_selected = Confermare l'eliminazione di tutti gli elementi selezi sign_in_with_provider = Accedi con %s new_project_column = Nuova colonna toggle_menu = Mostra/Nascondi menu -filter.not_fork = Non fork +filter.not_fork = Non biforcazioni filter = Filtro filter.clear = Rimuovi filtri filter.is_archived = Archiviato filter.not_archived = Non archiviato -filter.is_fork = Da fork +filter.is_fork = Biforcazioni filter.is_mirror = Mirror filter.not_mirror = Non mirror filter.is_template = Modelli @@ -209,6 +209,7 @@ table_modal.label.columns = Colonne link_modal.header = Aggiungi collegamento link_modal.url = Url link_modal.description = Descrizione +link_modal.paste_reminder = Suggerimento: se hai già copiato un URL negli appunti, puoi incollarlo direttamente nell’editor per creare un collegamento. [filter] string.asc = A - Z @@ -232,6 +233,7 @@ lightweight_desc=Forgejo ha requisiti minimi bassi e può funzionare su un econo license=Open Source license_desc=Ottieni Forgejo! Partecipa per contribuire a rendere questo progetto ancora più bello. Non aver paura di diventare collaborante! install_desc = Semplicemente avvia l'eseguibile per la tua piattaforma, distribuiscilo con Docker, oppure scarica il pacchetto. +platform_desc = È stato verificato che Forgejo è pienamente compatibile con sistemi operativi liberi, come Linux e FreeBSD, nonché con diverse architetture CPU. Scegli liberamente la piattaforma che preferisci! [install] install=Installazione @@ -396,12 +398,12 @@ go_to = Vai a search.type.tooltip = Tipo di ricerca search.fuzzy.tooltip = Includi anche i risultati che corrispondono parzialmente ai termini di ricerca code_search_results = Risultati di ricerca per "%s" -relevant_repositories_tooltip = I repositori derivati o che non hanno argomento, icona, né descrizione sono nascosti. +relevant_repositories_tooltip = I repositori che sono biforcazioni o che non hanno argomento, icona, né descrizione sono nascosti. relevant_repositories = Sono visibili solo i repositori pertinenti, mostra risultati non filtrati. search.match.tooltip = Includi solo risultati che combaciano perfettamente con i termini di ricerca stars_few = %d stelle -forks_one = %d fork -forks_few = %d fork +forks_one = %d biforcazioni +forks_few = %d biforcazioni stars_one = %d stella [auth] @@ -485,6 +487,8 @@ sign_in_openid = Procedi con OpenID hint_login = Hai già un'utenza? Accedi! hint_register = Non hai un'utenza? Registrati ora. sign_up_button = Registrati ora. +unauthorized_credentials = Le credenziali non sono corrette o sono scadute. Controlla il comando o vedi %s per maggiori informazioni +use_onetime_code = Usa un codice monouso [mail] view_it_on=Visualizza su %s @@ -680,6 +684,8 @@ Location = Posizione AccessToken = Token di accesso FullName = Nome e cognome To = Nome del ramo +email_domain_is_not_allowed = Il dominio dell'indirizzo email dell'utente %s è in conflitto con EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Assicurati di aver inserito correttamente l'indirizzo email. +username_claiming_cooldown = Il nome utente non può essere assegnato, poiché il periodo di attesa non è ancora terminato. Sarà disponibile il %[1]s. [user] @@ -723,6 +729,7 @@ followers.title.one = Seguace followers.title.few = Seguaci following.title.one = Seguito following.title.few = Osservato +public_activity.visibility_hint.self_private_profile = Poiché il tuo profilo è privato, la tua attività è visibile solo a te e agli amministratori dell'istanza. Configura. [settings] @@ -1045,7 +1052,7 @@ added_on = Aggiunto su %s additional_repo_units_hint = Suggerisci l'attivazione di unità aggiuntive nel repositorio update_hints = Aggiorna suggerimenti update_hints_success = I suggerimenti sono stati aggiornati. -additional_repo_units_hint_description = Mostra un pulsante "Aggiungi più sezioni..." per i repositori che non hanno tutte le sezioni disponibili aggiunte. +additional_repo_units_hint_description = Visualizza un suggerimento “Abilita altro” per i repositori che non hanno tutte le unità disponibili abilitate. hints = Suggerimenti pronouns = Pronomi pronouns_custom = Personalizzato @@ -1053,6 +1060,34 @@ pronouns_unspecified = Non specificato language.title = Lingua predefinita language.description = Questa lingua verrà salvata nella tua utenza e verrà usata come predefinita ogni volta che farai l'accesso. language.localization_project = Aiutaci a tradurre Forgejo nella tua lingua! Più informazioni. +quota.sizes.assets.attachments.all = Allegati +quota.rule.no_limit = Illimitato +quota.sizes.assets.attachments.releases = Allegati del rilascio +quota.rule.exceeded = Superato +regenerate_token = Rigenera +access_token_regeneration = Rigenera il token d'accesso +access_token_regeneration_desc = Rigenerare un token comporterà la revoca dell'accesso al tuo account per tutte le applicazioni che lo utilizzano. Questa operazione è irreversibile. Vuoi procedere? +regenerate_token_success = Il token è stato rigenerato. Le applicazioni che lo utilizzano non hanno più accesso alla tua utenza e devono essere aggiornate con il nuovo token. +user_block_yourself = Non puoi bloccare te stesso. +quota.applies_to_user = Le seguenti regole di quota si applicano al tuo account +quota.applies_to_org = Le seguenti regole di quota si applicano a questa organizzazione +quota.rule.exceeded.helper = La dimensione totale degli oggetti per questa regola ha superato la quota. +quota.sizes.all = Tutti +quota.sizes.repos.all = Repositori +quota.sizes.repos.public = Repositori pubblici +quota.sizes.repos.private = Repositori privati +quota.sizes.git.all = Contenuto git +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = Risorse +quota.sizes.assets.attachments.issues = Allegati della segnalazione +quota.sizes.assets.artifacts = Artefatti +quota.sizes.assets.packages.all = Pacchetti +quota.sizes.wiki = Wiki +keep_pronouns_private = Mostra i pronomi solo agli utenti che hanno effettuato il login +keep_pronouns_private.description = Questa impostazione nasconderà i tuoi pronomi agli utenti non ancora autenticati. +storage_overview = Panoramica spazio di archiviazione +quota = Quota +change_username_redirect_prompt.with_cooldown.one = Il vecchio nome utente sarà disponibile per tutti dopo un periodo di protezione di %\[1]d giorni. Durante questo periodo di attesa potrai comunque tornare al vecchio nome utente. [repo] owner=Proprietario @@ -1067,10 +1102,10 @@ template_description=I modelli di repositori consentono allɜ utenti di generare visibility=Visibilità visibility_description=Solo il proprietario o i membri dell'organizzazione se hanno diritti, saranno in grado di vederlo. visibility_helper_forced=L'amministratorə del sito impone che i nuovi repositori siano privati. -visibility_fork_helper=(Questa modifica influenzerà la visibilità di tutti i fork.) +visibility_fork_helper=(Questa modifica influenzerà la visibilità di tutte le biforcazioni.) clone_helper=Hai bisogno di aiuto per la clonazione? Visita Help. fork_repo=Deriva repositorio -fork_from=Deriva da +fork_from=Biforcazione di already_forked=Hai già fatto il fork di %s fork_to_different_account=Fai Fork a un account diverso fork_visibility_helper=La visibilità di un repositorio derivato non può essere modificata. @@ -1514,13 +1549,13 @@ issues.close_comment_issue=Commenta e chiudi issues.reopen_issue=Riapri issues.reopen_comment_issue=Commenta e riapri issues.create_comment=Commento -issues.closed_at=`ha chiuso questa segnalazione %[2]s` -issues.reopened_at=`ha riaperto questa segnalazione %[2]s` -issues.commit_ref_at=`ha fatto riferimento a questa segnalazione dal commit %[2]s` -issues.ref_issue_from=`ha fatto riferimento a questa segnalazione %[4]s %[2]s` -issues.ref_pull_from=`ha fatto riferimento a questa richiesta di modifica %[4]s %[2]s` -issues.ref_closing_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[4]s che la chiuderà, %[2]s` -issues.ref_reopening_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[4]s che la riaprirà, %[2]s` +issues.closed_at=`ha chiuso questa segnalazione %s` +issues.reopened_at=`ha riaperto questa segnalazione %s` +issues.commit_ref_at=`ha fatto riferimento a questa segnalazione dal commit %s` +issues.ref_issue_from=`ha fatto riferimento a questa segnalazione %[3]s %[1]s` +issues.ref_pull_from=`ha fatto riferimento a questa richiesta di modifica %[3]s %[1]s` +issues.ref_closing_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[3]s che la chiuderà, %[1]s` +issues.ref_reopening_from=`ha fatto riferimento a questa segnalazione da una richiesta di modifica %[3]s che la riaprirà, %[1]s` issues.ref_closed_from=`chiuso questa segnalazione %[4]s %[2]s` issues.ref_reopened_from=`ha riaperto questa segnalazione %[4]s %[2]s` issues.ref_from=`da %[1]s` @@ -1718,7 +1753,7 @@ pulls.cannot_merge_work_in_progress=Questa richiesta di modifica è contrassegna pulls.still_in_progress=Ancora in corso? pulls.add_prefix=Aggiungi prefisso %s pulls.remove_prefix=Rimuovi il prefisso %s -pulls.data_broken=Questa richiesta di modifica è rovinata a causa di informazioni mancanti riguardo la derivazione. +pulls.data_broken=Questa richiesta di modifica non è valida a causa di informazioni mancanti sulla biforcazione. pulls.files_conflicted=Questa richiesta di modifica va in conflitto con il ramo di destinazione. pulls.is_checking=Verifica dei conflitti di fusione in corso. Riprova tra qualche istante. pulls.is_ancestor=Questo ramo è già incluso nel ramo di destinazione. Non c'è nulla da fondere. @@ -1776,8 +1811,8 @@ pulls.update_branch_rebase=Aggiorna il ramo per cambio base pulls.update_branch_success=Ramo aggiornato con successo pulls.update_not_allowed=Non ti è permesso aggiornare il ramo pulls.outdated_with_base_branch=Questo ramo non è aggiornato con il ramo di base -pulls.closed_at=`ha chiuso questa richiesta di modifica %[2]s` -pulls.reopened_at=`ha riaperto questa richiesta di modifica %[2]s` +pulls.closed_at=`ha chiuso questa richiesta di modifica %s` +pulls.reopened_at=`ha riaperto questa richiesta di modifica %s` pulls.auto_merge_button_when_succeed=(Quando i controlli sono superati) pulls.auto_merge_when_succeed=Unione automatica quando tutti i controlli sono superati @@ -2100,7 +2135,7 @@ settings.event_create_desc=Ramo o etichetta creati. settings.event_delete=Elimina settings.event_delete_desc=Ramo o etichetta eliminati. settings.event_fork=Deriva -settings.event_fork_desc=Repository derivato. +settings.event_fork_desc=Creata una biforcazione del repositorio. settings.event_wiki=Wiki settings.event_release=Release settings.event_release_desc=Release pubblicata, aggiornata o rimossa in una repository. @@ -2137,7 +2172,7 @@ settings.event_pull_request_sync_desc=Pull request sincronizzata. settings.event_package=Pacchetto settings.event_package_desc=Pacchetto creato o eliminato in un repository. settings.branch_filter=Filtro rami -settings.branch_filter_desc=Whitelist dei rami per gli eventi di spinta, creazione dei rami e cancellazione dei rami, specificati come modello globo. Se vuoto o *, gli eventi per tutti i rami sono segnalati. Vedi la documentazione %[2]s per la sintassi. Esempi: master, {master,release*}. +settings.branch_filter_desc=Filtro, scritto come pattern glob, da applicare ai rami per gli eventi di tipo immissione, creazione di rami e rimozione di rami. Se vuoto o *, vengono considerati tutti gli eventi di tutti i rami. Maggiori dettagli sulla sintassi presso %[2]s. Esempi: master, {master,release*}. settings.active=Attivo settings.active_helper=Le informazioni sugli eventi innescati saranno inviate a questo URL del webhook. settings.add_hook_success=Il webhook è stato aggiunto. @@ -2167,8 +2202,8 @@ settings.web_hook_name_packagist=Packagist settings.packagist_username=Nome utente Packagist settings.packagist_api_token=API token settings.packagist_package_url=Url pacchetto pacchetti -settings.deploy_keys=Dispiega chiavi -settings.add_deploy_key=Aggiungi chiave di dispiego +settings.deploy_keys=Chiavi di distribuzione +settings.add_deploy_key=Aggiungi chiave di distribuzione settings.deploy_key_desc=Le deploy key possiedono l'accesso solamente alla lettura di un repository. settings.is_writable=Abilita accesso scrittura settings.is_writable_info=Permetti a questa deploy key di pushare nella repository. @@ -2177,7 +2212,7 @@ settings.title=Titolo settings.deploy_key_content=Contenuto settings.key_been_used=Una deploy key con contenuto identico è già in uso. settings.key_name_used=Esiste già una deploy key con questo nome. -settings.deploy_key_deletion=Rimuovi chiave di dispiego +settings.deploy_key_deletion=Rimuovi chiave di distribuzione settings.deploy_key_deletion_desc=Rimuovere una chiave di distribuzione ne revocherà l'accesso a questo repository. Continuare? settings.deploy_key_deletion_success=La chiave di distribuzione è stata rimossa. settings.branches=Rami @@ -2620,7 +2655,7 @@ issues.filter_type.reviewed_by_you = Revisionati da te projects.edit_success = Il progetto "%s" è stato aggiornato. issues.keyword_search_unavailable = La ricerca per parola chiave non è attualmente disponibile. Contatta l'amministratore del sito. issues.role.collaborator_helper = Quest*utente è statə invitatə a collaborare al progetto. -pulls.commit_ref_at = `ha fatto riferimento a questa richiesta di modifica da un commit %[2]s` +pulls.commit_ref_at = `ha fatto riferimento a questa richiesta di modifica da un commit %s` settings.thread_id = ID della discussione release.title = Titolo del rilascio visibility_helper = Rendi il repositorio privato @@ -2660,7 +2695,7 @@ wiki.page_title = Titolo della pagina wiki.page_content = Contenuto della pagina settings.mirror_settings.pushed_repository = Repositorio immesso settings.mirror_settings.push_mirror.edit_sync_time = Modifica intervallo di sincronizzazione degli specchi -settings.units.units = Unità della repository +settings.units.units = Sezioni del repositorio settings.units.add_more = Aggiungi ancora... settings.wiki_globally_editable = Consenti a tutti di modificare la wiki settings.pull_mirror_sync_in_progress = Prelevando cambiamenti dal progetto remoto %s. @@ -2732,7 +2767,7 @@ pulls.merged_title_desc_one = ha fuso %[1]d commit da %[2]s in Accedi per creare una richiesta di modifica. +settings.mirror_settings.push_mirror.none_ssh = Nessuno +sync_fork.branch_behind_one = Questo ramo è indietro di %[1]d commit rispetto a %[2]s +sync_fork.branch_behind_few = Questo ramo è indietro di %[1]d commit rispetto a %[2]s +no_eol.text = Nessun fine linea +no_eol.tooltip = Questo file non contiene un carattere di fine linea finale. +milestones.filter_sort.name = Nome +settings.protect_new_rule = Crea una nuova regola di protezione dei rami +editor.commit_email = E-mail di commit +mirror_public_key = Chiave SSH pubblica +mirror_denied_combination = Non è possibile utilizzare contemporaneamente l'autenticazione tramite chiave pubblica e password. +release.type_attachment = Allegato +release.invalid_external_url = URL esterno invalido: "%s" +new_from_template = Utilizza un modello +new_from_template_description = Puoi selezionare un modello di repositorio esistente su questa istanza e applicare le sue impostazioni. +new_advanced = Impostazioni avanzate +new_advanced_expand = Clicca per espandere +summary_card_alt = Scheda riepilogativa del repository %s +issues.filter_sort.relevance = Rilevanza +issues.num_reviews_one = %d revisioni +issues.num_reviews_few = %d revisioni +issues.reaction.add = Aggiungi reazione +issues.reaction.alt_many = %[1] e altri %[2]d hanno reagito %[3]s. +issues.reaction.alt_remove = Rimuovi la reazione %[1]s dal commento. +issues.reaction.alt_add = Aggiungi la reazione %[1]s al commento. +issues.review.remove_review_requests = rimosso richieste di revisione per %\[1]s %\[2]s +comment.blocked_by_user = Non è possibile commentare perché sei stato bloccato dal proprietario del repositorio o dall'autore. +issues.summary_card_alt = Scheda riepilogativa di una segnalazione intitolata "%s" nel repositorio %s +pulls.delete_after_merge.head_branch.is_default = Il ramo head che desideri eliminare è il ramo predefinito e non può essere eliminato. +settings.event_action_success = Successo +settings.event_action_success_desc = L'esecuzione dell'azione è andata a buon fine. +diff.git-notes.remove-header = Rimuovi nota +diff.git-notes.remove-body = Questa nota verrà rimossa. +activity.commit = Attività di commit [graphs] contributors.what = contribuzioni @@ -2839,7 +2947,7 @@ team_name_helper=I nomi dei team devono essere brevi e semplici da ricordare. team_desc_helper=Descrivi lo scopo o il ruolo del team. team_access_desc=Accesso al repository team_permission_desc=Autorizzazione -team_unit_desc=Consenti l'accesso a sezioni di progetto +team_unit_desc=Consenti l'accesso alle sezioni del repositorio team_unit_disabled=(Disabilitato) form.create_org_not_allowed=Non disponi dell'autorizzazione per creare un organizzazione. @@ -3493,6 +3601,12 @@ config.cache_test_slow = Successo nel controllo della cache, ma la risposta è l config.app_slogan = Slogan dell'istanza auths.default_domain_name = Nome di dominio predefinito utilizzato per l'indirizzo e-mail users.restricted.description = Permetti di interagire solo con i repositori e le organizzazioni in cui l'utente è aggiuntə come collaborante. Ciò evita l'accesso ai repositori pubblici di quest'istanza. +emails.deletion_success = L'indirizzo e-mail è stato eliminato. +monitor.duration = Durata (s) +emails.delete_desc = Confermare l’eliminazione di questo indirizzo email? +emails.delete_primary_email_error = Non puoi eliminare la e-mail primaria. +emails.delete = Elimina e-mail +users.organization_creation.description = Abilita la creazione di nuove organizzazioni. [action] @@ -3737,6 +3851,31 @@ owner.settings.cargo.initialize.success = L'indice di Cargo è stato creato corr owner.settings.cargo.rebuild.no_index = Impossibile ricostruire, nessun indice è inizializzato. owner.settings.cargo.rebuild.description = La ricostruzione può essere utile se l'indice non è sincronizzato con i pacchetti Cargo conservati. npm.dependencies.bundle = Dipendenze raggruppate +arch.version.groups = Gruppo +arch.version.conflicts = Va in conflitto con +arch.version.depends = Dipende da +arch.version.makedepends = Dipendenze di build +arch.version.checkdepends = Dipendenze di controllo +arch.version.replaces = Sostituisce +arch.version.optdepends = Dipende opzionalmente da +arch.version.backup = Backup +search_in_external_registry = Cerca in %s +arch.version.provides = Fornisce +arch.pacman.conf = Aggiungi il server con la relativa distribuzione e architettura a /etc/pacman.conf: +alt.setup = Aggiungi il repositorio alla lista dei repositori in rete (seleziona l'architettura necessaria al posto di "_arch_"): +container.images.title = Immagini +arch.version.properties = Proprietà della versione +alt.registry.install = Per installare il pacchetto, esegui il comando seguente: +alt.install = Installa pacchetto +alt.registry = Configura questo registro dalla riga di comando: +arch.pacman.helper.gpg = Aggiungi il certificato a pacman: +arch.pacman.repo.multi = %s ha la stessa versione in diverse distribuzioni. +arch.pacman.repo.multi.item = Configurazione per %s +arch.pacman.sync = Sincronizza il paccketto con pacman: +arch.version.description = Descrizione +alt.repository = Informazioni del repositorio +alt.repository.architectures = Architetture +alt.repository.multiple_groups = Questo pacchetto è disponibile per più gruppi. [secrets] secrets = Segreti @@ -3834,7 +3973,7 @@ runs.empty_commit_message = (messaggio di commit vuoto) runs.no_runs = Il flusso di lavoro non è stato ancora eseguito. variables.creation.success = La variabile "%s" è stata aggiunta. variables.description = Le variabili saranno passate a determinate azioni e non possono essere lette altrimenti. -need_approval_desc = È necessaria l'approvazione per eseguire flussi di lavoro per richieste di modifica da derivazioni. +need_approval_desc = È necessaria l'approvazione per eseguire flussi di lavoro per richieste di modifica da biforcazioni. runs.no_workflows.documentation = Per ulteriori informazioni sulle Forgejo Actions vedi la documentazione. runs.no_workflows.quick_start = Non sai come iniziare con le Forgejo Actions? Vedi la guida rapida. runners.delete_runner_notice = Se un'attività è in esecuzione su questo esecutore sarà terminata ed etichettata fallito. Potrebbe rompere flussi di lavoro di costruzione. @@ -3848,6 +3987,8 @@ workflow.dispatch.invalid_input_type = Tipo ingresso "%s" non valido. workflow.dispatch.warn_input_limit = Visualizzati solo i primi %d ingressi. runs.no_job = Il flusso di lavoro deve contenere almeno un incarico workflow.dispatch.use_from = Usa flusso di lavoro da +variables.not_found = Non è stato possibile trovare la variabile. +runs.expire_log_message = I log sono stati eliminati in quanto troppo vecchi. @@ -3856,6 +3997,7 @@ workflow.dispatch.use_from = Usa flusso di lavoro da type-3.display_name = Progetto dell'organizzazione type-1.display_name = Progetto individuale type-2.display_name = Progetto +deleted.display_name = Progetto eliminato [git.filemode] symbolic_link=Link Simbolico @@ -3896,6 +4038,7 @@ milestone_kind = Ricerca tappe... regexp_tooltip = Interpreta i termini di ricerca come un'espressione regolare regexp = Espressione Regolare union_tooltip = Include i risultati che combaciano con una qualsiasi delle parole chiave separata da spazi +union = Parole chiavi [munits.data] gib = GiB @@ -3914,4 +4057,16 @@ filepreview.line = Linea %[1]d in %[2]s [repo.permissions] issues.write = Scrittura: Chiudere segnalazioni e gestire metadati come etichette, traguardi, assegnatarɜ, scadenze e dipendenze. -pulls.write = Scrittura: Chiudere richieste di modifica e gestire metadati come etichette, traguardi, assegnatarɜ, scadenze e dipendenze. \ No newline at end of file +pulls.write = Scrittura: Chiudere richieste di modifica e gestire metadati come etichette, traguardi, assegnatarɜ, scadenze e dipendenze. +releases.write = Scrittura: Può pubblicare, modificare ed eliminare rilasci e le risorse ad essi allegate. +code.write = Scrittura: Può aggiungere commit al repositorio, creare rami ed etichette. +wiki.read = Lettura: Può leggere la wiki integrata e la sua cronologia. +releases.read = Lettura: Può visualizzare e scaricare i rilasci. +projects.read = Lettura: Può accedere alle board di progetto del repositorio. +code.read = Lettura: Può accedere e clonare il codice del repositorio. +wiki.write = Scrittura: Può creare, aggiornare ed eliminare pagine nella wiki integrata. +issues.read = Lettura: Può leggere e creare segnalazioni e commenti. +pulls.read = Lettura: Può leggere e creare richieste di modifica. + +[translation_meta] +test = daje Roma \ No newline at end of file diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index d4d7024f5d..555f5c6a75 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -1610,13 +1610,13 @@ issues.close_comment_issue=コメントしてクローズ issues.reopen_issue=再オープンする issues.reopen_comment_issue=コメントして再オープン issues.create_comment=コメントする -issues.closed_at=`がイシューをクローズ %[2]s` -issues.reopened_at=`がイシューを再オープン %[2]s` -issues.commit_ref_at=`がコミットでこのイシューを参照 %[2]s` -issues.ref_issue_from=`が%[4]s、このイシューを参照 %[2]s` -issues.ref_pull_from=`が%[4]s、このプルリクエストを参照 %[2]s` -issues.ref_closing_from=`が%[4]s、プルリクエストがこのイシューをクローズするよう参照 %[2]s` -issues.ref_reopening_from=`が%[4]s、プルリクエストがこのイシューを再オープンするよう参照 %[2]s` +issues.closed_at=`がイシューをクローズ %s` +issues.reopened_at=`がイシューを再オープン %s` +issues.commit_ref_at=`がコミットでこのイシューを参照 %s` +issues.ref_issue_from=`が%[3]s、このイシューを参照 %[1]s` +issues.ref_pull_from=`が%[3]s、このプルリクエストを参照 %[1]s` +issues.ref_closing_from=`が%[3]s、プルリクエストがこのイシューをクローズするよう参照 %[1]s` +issues.ref_reopening_from=`が%[3]s、プルリクエストがこのイシューを再オープンするよう参照 %[1]s` issues.ref_closed_from=`が%[4]s、このイシューをクローズ %[2]s` issues.ref_reopened_from=`が%[4]s、このイシューを再オープン %[2]s` issues.ref_from=` %[1]s にて` @@ -1923,8 +1923,8 @@ pulls.update_branch_success=ブランチの更新が成功しました pulls.update_not_allowed=ブランチを更新する権限がありません pulls.outdated_with_base_branch=このブランチはベースブランチに対して最新ではありません pulls.close=プルリクエストをクローズ -pulls.closed_at=`がプルリクエストをクローズ %[2]s` -pulls.reopened_at=`がプルリクエストを再オープン %[2]s` +pulls.closed_at=`がプルリクエストをクローズ %s` +pulls.reopened_at=`がプルリクエストを再オープン %s` pulls.cmd_instruction_hint=コマンドラインの手順を表示 pulls.cmd_instruction_checkout_title=チェックアウト pulls.cmd_instruction_checkout_desc=プロジェクトリポジトリから新しいブランチをチェックアウトし、変更内容をテストします。 @@ -2721,7 +2721,7 @@ settings.wiki_rename_branch_main = wikiのブランチ名を正規化する settings.wiki_rename_branch_main_desc = wikiによって内部的に使われているブランチ名を "%s" に変更します。これは恒久的で元に戻すことはできません。 contributors.contribution_type.additions = 追加 vendored = vendor済み -pulls.commit_ref_at = `このプルリクエストを言及するコミット %[2]s` +pulls.commit_ref_at = `このプルリクエストを言及するコミット %s` pulls.fast_forward_only_merge_pull_request = Fast-forwardのみ admin.manage_flags = フラグ管理 admin.update_flags = フラグを更新 diff --git a/options/locale/locale_jbo.ini b/options/locale/locale_jbo.ini index 6124dc4d22..947bb298de 100644 --- a/options/locale/locale_jbo.ini +++ b/options/locale/locale_jbo.ini @@ -2,4 +2,12 @@ [common] -home = zdani \ No newline at end of file +home = zdani +dashboard = jitypalna +explore = sisku +help = se sidju +logo = se'isni +sign_in = co'a nerkla +sign_in_with_provider = co'a nerka sepi'o la .%s. +sign_out = co'a cliva +sign_up = co'a gumri \ No newline at end of file diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 433ec01828..be0400bea4 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -943,7 +943,7 @@ issues.close_comment_issue=클로즈 및 코멘트 issues.reopen_issue=다시 열기 issues.reopen_comment_issue=다시 오픈 및 코멘트 issues.create_comment=코멘트 -issues.commit_ref_at=` 커밋 %[2]s에서 이 이슈 언급` +issues.commit_ref_at=` 커밋 %s에서 이 이슈 언급` issues.role.owner=소유자 issues.role.member=멤버 issues.sign_in_require_desc=로그인하여 이 대화에 참여하세요. @@ -1378,7 +1378,7 @@ issues.closed_by_fake = %[2]s님이 %[1]s에 닫음 issues.new.closed_projects = 닫힌 프로젝트 pulls.merged_by_fake = %[2]s님이 %[1]s 병합함 issues.closed_by = %[3]s님이 %[1]s에 닫음 -issues.closed_at = `%[2]s`에 이 이슈를 닫음 +issues.closed_at = `%s`에 이 이슈를 닫음 issues.filter_milestone_closed = 닫힌 마일스톤 issues.opened_by_fake = %[2]s님이 %[1]s에 열음 issues.filter_project_none = 프로젝트 없음 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 789871f3c3..98baff217b 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1651,13 +1651,13 @@ issues.close_comment_issue=Aizvērt ar piebildi issues.reopen_issue=Atvērt atkārtoti issues.reopen_comment_issue=Atkārtoti atvērt ar piebildi issues.create_comment=Pievienot piebildi -issues.closed_at=`aizvēra šo pieteikumu %[2]s` -issues.reopened_at=`atkārtoti atvēra šo pieteikumu %[2]s` -issues.commit_ref_at=`atsaucās uz šo pieteikumu iesūtījumā %[2]s` -issues.ref_issue_from=`atsaucās uz šo pieteikumu %[4]s %[2]s` -issues.ref_pull_from=`atsaucās uz šo izmaiņu pieprasījumu %[4]s %[2]s` -issues.ref_closing_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[4]s, kas aizvērs to, %[2]s` -issues.ref_reopening_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[4]s, kas atkārtoti atvērs to, %[2]s` +issues.closed_at=`aizvēra šo pieteikumu %s` +issues.reopened_at=`atkārtoti atvēra šo pieteikumu %s` +issues.commit_ref_at=`atsaucās uz šo pieteikumu iesūtījumā %s` +issues.ref_issue_from=`atsaucās uz šo pieteikumu %[3]s %[1]s` +issues.ref_pull_from=`atsaucās uz šo izmaiņu pieprasījumu %[3]s %[1]s` +issues.ref_closing_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[3]s, kas aizvērs to, %[1]s` +issues.ref_reopening_from=`atsaucās uz šo pieteikumu izmaiņu pieprasījumā %[3]s, kas atkārtoti atvērs to, %[1]s` issues.ref_closed_from=`aizvēra pieteikumu %[4]s %[2]s` issues.ref_reopened_from=`atkārtoti atvēra pieteikumu %[4]s %[2]s` issues.ref_from=`no %[1]s` @@ -1964,8 +1964,8 @@ pulls.update_branch_success=Zara atjaunināšana bija sekmīga pulls.update_not_allowed=Nav ļauts atjaunināt zaru pulls.outdated_with_base_branch=Šis zars ir novecojis salīdzinājumā ar pamata zaru pulls.close=Aizvērt izmaiņu pieprasījumu -pulls.closed_at=`aizvēra šo izmaiņu pieprasījumu %[2]s` -pulls.reopened_at=`atkārtoti atvēra šo izmaiņu pieprasījumu %[2]s` +pulls.closed_at=`aizvēra šo izmaiņu pieprasījumu %s` +pulls.reopened_at=`atkārtoti atvēra šo izmaiņu pieprasījumu %s` pulls.cmd_instruction_hint=Apskatīt komandrindas izmantošanas norādes pulls.cmd_instruction_checkout_title=Paņemt pulls.cmd_instruction_checkout_desc=Projekta glabātavā jāizveido jauns zars un jāpārbauda izmaiņas. @@ -2826,7 +2826,7 @@ issues.author.tooltip.pr = Šis lietotājs ir šī izmaiņu pieprasījuma izveid pulls.edit.already_changed = Neizdevās saglabāt izmaiņu pieprasījuma izmaiņas. Izskatās, ka saturu jau ir mainījis kāds cits lietotājs. Lūgums atsvaidzināt lapu un mēģināt labot vēlreiz, lai izvairītos no izmaiņu pārrakstīšanas pulls.blocked_by_user = Tu nevari izveidot izmaiņu pieprasījumu šajā glabātavā, jo tās īpašnieks ir Tevi liedzis. issues.all_title = Visi -pulls.commit_ref_at = ` atsaucāš uz šo izmaiņu pieprasījumu iesūtījumā %[2]s` +pulls.commit_ref_at = ` atsaucās uz šo izmaiņu pieprasījumu iesūtījumā %s` issues.num_participants_one = %d dalībnieks pulls.title_desc_one = vēlas iekļaut %[1]d iesūtījumu no %[2]s %[3]s issues.archived_label_description = (Arhivēts) %s diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index 57985942ed..68fe899d6e 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -1347,7 +1347,7 @@ issues.change_title_at = `hett %[3]s de Titel vun %[1]s issues.change_ref_at = `hett %[3]s de Nömen vun %[1]s to %[2]s ännert` issues.delete_branch_at = `hett %[2]s de Twieg %[1]s lösket` issues.filter_label = Vermark -issues.filter_label_exclude = `Bruuk Alt+Klick/Enter, um Vermarkens uttosluten` +issues.filter_label_exclude = Bruuk Alt + Klick, um Vermarkens uttosluten issues.filter_label_no_select = All Vermarkens issues.filter_label_select_no_label = Keen Vermark issues.filter_milestone = Marksteen @@ -1434,12 +1434,12 @@ issues.comment_pull_merged_at = hett Kommitteren %[1]s in %[2]s %[3]s tosamenfö issues.close_comment_issue = Mit Kommentaar dichtmaken issues.reopen_comment_issue = Mit Kommentaar weer opmaken issues.create_comment = Kommenteren -issues.reopened_at = `hett deeses Gefall %[2]s weer opmaakt` +issues.reopened_at = `hett deeses Gefall %s weer opmaakt` issues.comment_manually_pull_merged_at = hett Kommitteren %[1]s in %[2]s %[3]s vun Hand tosamenföhrt issues.reopen_issue = Weer opmaken -issues.closed_at = `hett deeses Gefall %[2]s dichtmaakt` -issues.commit_ref_at = `hett deeses Gefall %[2]s vun eenem Kommitteren benöömt` -issues.ref_closing_from = `hett deeses Gefall %[2]s vun eenem Haalvörslag, wat ’t %[4]s dichtmaken word, benöömt` +issues.closed_at = `hett deeses Gefall %s dichtmaakt` +issues.commit_ref_at = `hett deeses Gefall %s vun eenem Kommitteren benöömt` +issues.ref_closing_from = `hett deeses Gefall %[1]s vun eenem Haalvörslag, wat ’t %[3]s dichtmaken word, benöömt` issues.ref_closed_from = `hett deeses Gefall %[4]s %[2]s dichtmaakt` issues.ref_reopened_from = `hett deeses Gefall %[4]s %[2]s weer opmaakt` issues.ref_from = `vun %[1]s` @@ -1477,12 +1477,12 @@ issues.label.filter_sort.reverse_alphabetically = Umdreiht na de Alphabeet issues.label.filter_sort.by_size = Lüttste Grött issues.num_participants_one = %d Mitmaker issues.num_participants_few = %d Mitmakers -issues.ref_pull_from = `hett deesen Haalvörslag %[4]s %[2]s benöömt` +issues.ref_pull_from = `hett deesen Haalvörslag %[3]s %[1]s benöömt` issues.label_title = Naam issues.label_archived_filter = Archiveert Vermarkens wiesen issues.archived_label_description = (Archiveert) %s -issues.ref_issue_from = `hett deeses Gefall %[4]s %[2]s benöömt` -issues.ref_reopening_from = `hett deeses Gefall vun eenem Haalvörslag, wat ’t %[4]s weer opmaken word, %[2]s benöömt` +issues.ref_issue_from = `hett deeses Gefall %[3]s %[1]s benöömt` +issues.ref_reopening_from = `hett deeses Gefall vun eenem Haalvörslag, wat ’t %[3]s weer opmaken word, %[1]s benöömt` issues.author.tooltip.issue = Deeser Bruker is de Autor vun deesem Gefall. issues.role.member_helper = Deeser Bruker is een Liddmaat vun de Vereenigung, wat de Eegner vun deesem Repositorium is. issues.role.collaborator_helper = Deeser Bruuker is inladen worden, in deesem Repositorium mittoarbeiden. @@ -1740,8 +1740,8 @@ pulls.status_checks_show_all = All Överprüfens wiesen pulls.update_branch_rebase = Twieg mit Umbaseren vernejen pulls.outdated_with_base_branch = De Twieg is tegen de Grund-Twieg verollt pulls.close = Haalvörslag dichtmaken -pulls.closed_at = `hett deesen Haalvörslag %[2]s dichtmaakt` -pulls.reopened_at = `hett deesen Haalvörslag %[2]s weer opmaakt` +pulls.closed_at = `hett deesen Haalvörslag %s dichtmaakt` +pulls.reopened_at = `hett deesen Haalvörslag %s weer opmaakt` pulls.cmd_instruction_hint = Wies Oorderreeg-Instruksjes pulls.cmd_instruction_checkout_title = Utchecken pulls.cmd_instruction_merge_title = Tosamenföhren @@ -1771,7 +1771,7 @@ milestones.deletion = Marksteen lösken pulls.has_merged = Fehlslagen: De Haalvörslag is tosamenföhrt worden, du kannst nich noch eenmaal tosamenföhren of de Enn-Twieg ännern. pulls.unrelated_histories = Tosamenföhren fehlslagen: De Tosamenföhrens-Kopp un -Grund hebben keene gemeensame Histoorje. Wenk: Versöök eene anner Tosamenföhrens-Aard pulls.update_not_allowed = Du düürst deesen Twieg nich vernejen -pulls.commit_ref_at = `hett deesen Haalvörslag %[2]s vun eenem Kommitteren benöömt` +pulls.commit_ref_at = `hett deesen Haalvörslag %s vun eenem Kommitteren benöömt` pulls.auto_merge_newly_scheduled = De Haalvörslag weer sett, sik tosamentoföhren, wenn all Överprüfens kumpleet sünd. milestones.clear = Leeg maken pulls.push_rejected_no_message = Schuven fehlslagen: Dat Schuven is sünner feerne Naricht oflehnt worden. Bidde överprüüf de Git-Hakens för deeses Repositorium diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 549718ce23..48442bc39f 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1554,13 +1554,13 @@ issues.close_comment_issue=Sluit met commentaar issues.reopen_issue=Heropen issues.reopen_comment_issue=Heropen met commentaar issues.create_comment=Reageer -issues.closed_at=`heeft dit probleem gesloten %[2]s` -issues.reopened_at=`heropende dit probleem %[2]s` -issues.commit_ref_at=`verwees naar dit probleem vanuit commit %[2]s'` -issues.ref_issue_from=`refereerde aan dit issue %[4]s %[2]s` -issues.ref_pull_from=`refereerde aan deze pull request %[4]s %[2]s` -issues.ref_closing_from=`verwees naar deze issue van een pull request %[4]s dat het zal sluiten, %[2]s` -issues.ref_reopening_from=`verwees naar een pull request %[4]s dat dit issue heropent %[2]s ` +issues.closed_at=`heeft dit probleem gesloten %s` +issues.reopened_at=`heropende dit probleem %s` +issues.commit_ref_at=`verwees naar dit probleem vanuit commit %s` +issues.ref_issue_from=`refereerde aan dit issue %[3]s %[1]s` +issues.ref_pull_from=`refereerde aan deze pull request %[3]s %[1]s` +issues.ref_closing_from=`verwees naar deze issue van een pull request %[3]s dat het zal sluiten, %[1]s` +issues.ref_reopening_from=`verwees naar een pull request %[3]s dat dit issue heropent %[1]s ` issues.ref_closed_from=`sloot dit issue %[4]s %[2]s` issues.ref_reopened_from=`heropende dit issue %[4]s %[2]s` issues.ref_from=`van %[1]s` @@ -1815,8 +1815,8 @@ pulls.update_branch_rebase=Update branch via herbaseren pulls.update_branch_success=Branch update is geslaagd pulls.update_not_allowed=Je hebt geen toestemming om branch bij te werken pulls.outdated_with_base_branch=Deze branch is verouderd met de basis branch -pulls.closed_at=`heeft deze pull request gesloten %[2]s` -pulls.reopened_at=`heropende deze pull request %[2]s` +pulls.closed_at=`heeft deze pull request gesloten %s` +pulls.reopened_at=`heropende deze pull request %s` pulls.auto_merge_button_when_succeed=(Bij geslaagde controles) pulls.auto_merge_when_succeed=Automatisch samenvoegen wanneer alle controles gelukt zijn @@ -2627,7 +2627,7 @@ projects.column.set_default_desc = Stel deze kolom in als standaard voor ongecat issues.action_check = Aanvinken/uitvinken issues.dependency.issue_batch_close_blocked = Het is niet mogelijk om de issues die u gekozen heeft in bulk te sluiten, omdat issue #%d nog open afhankelijkheden heeft pulls.review_only_possible_for_full_diff = Beoordeling is alleen mogelijk bij het bekijken van de volledige diff -pulls.commit_ref_at = `heeft naar deze pull request verwezen vanuit een commit %[2]s` +pulls.commit_ref_at = `heeft naar deze pull request verwezen vanuit een commit %s` pulls.cmd_instruction_hint = Bekijk opdrachtregelinstructies pulls.cmd_instruction_checkout_desc = Vanuit uw project repository, schakel over naar een nieuwe branch en test de veranderingen. pulls.showing_specified_commit_range = Alleen veranderingen weergeven tussen %[1]s..%[2]s diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 86a333a886..189e663618 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1460,13 +1460,13 @@ issues.close_comment_issue=Zamknij z komentarzem issues.reopen_issue=Otwórz ponownie issues.reopen_comment_issue=Otwórz ponownie z komentarzem issues.create_comment=Skomentuj -issues.closed_at=`zamknął(-ęła) to zgłoszenie %[2]s` -issues.reopened_at=`otworzył(-a) ponownie to zgłoszenie %[2]s` -issues.commit_ref_at=`wspomniał(-a) to zgłoszenie z commita %[2]s` -issues.ref_issue_from=`odwołał(-a) się do tego zgłoszenia %[4]s %[2]s` -issues.ref_pull_from=`odwołał(-a) się do tego Pull Requesta %[4]s %[2]s` -issues.ref_closing_from=`odwołał(-a) się do pull requesta %[4]s, który zamknie to zgłoszenie %[2]s` -issues.ref_reopening_from=`odwołał(-a) się z pull requesta %[4]s, który otworzy na nowo to zgłoszenie %[2]s` +issues.closed_at=`zamknął(-ęła) to zgłoszenie %s` +issues.reopened_at=`otworzył(-a) ponownie to zgłoszenie %s` +issues.commit_ref_at=`wspomniał(-a) to zgłoszenie z commita %s` +issues.ref_issue_from=`odwołał(-a) się do tego zgłoszenia %[3]s %[1]s` +issues.ref_pull_from=`odwołał(-a) się do tego Pull Requesta %[3]s %[1]s` +issues.ref_closing_from=`odwołał(-a) się do pull requesta %[3]s, który zamknie to zgłoszenie %[1]s` +issues.ref_reopening_from=`odwołał(-a) się z pull requesta %[3]s, który otworzy na nowo to zgłoszenie %[1]s` issues.ref_closed_from=`zamknął(-ęła) to zgłoszenie %[4]s %[2]s` issues.ref_reopened_from=`ponownie otworzył(-a) to zgłoszenie %[4]s %[2]s` issues.ref_from=`z %[1]s` @@ -1679,8 +1679,8 @@ pulls.update_branch_rebase=Aktualizuj branch przez rebase pulls.update_branch_success=Aktualizacja gałęzi powiodła się pulls.update_not_allowed=Nie masz uprawnień do aktualizacji gałęzi pulls.outdated_with_base_branch=Ta gałąź jest przestarzała w stosunku do gałęzi bazowej -pulls.closed_at=`zamknął(-ęła) ten pull request %[2]s` -pulls.reopened_at=`otworzył(-a) ponownie ten Pull Request %[2]s` +pulls.closed_at=`zamknął(-ęła) ten pull request %s` +pulls.reopened_at=`otworzył(-a) ponownie ten Pull Request %s` @@ -2643,7 +2643,7 @@ pulls.closed = Pull request zamknięty pulls.blocked_by_outdated_branch = Ten pull request jest zablokowany ponieważ jest przedawniony. pulls.blocked_by_changed_protected_files_1 = Ten pull request jest zablokowany ponieważ wprowadza zmiany do chronionego pliku: pulls.push_rejected_no_message = Wypchnięcie nie powiodło się: Wypchnięcie zostało odrzucone, ale nie otrzymano zdalnej wiadomości. Sprawdź hooki Git dla tego repozytorium.= -pulls.commit_ref_at = `odniósł się do tego pull requesta z commita %[2]s` +pulls.commit_ref_at = `odniósł się do tego pull requesta z commita %s` pulls.cmd_instruction_checkout_desc = Ze swojego repozytorium projektu, utwórz nową gałąź i przetestuj zmiany. pulls.clear_merge_message_hint = Wyczyszczenie wiadomości scalenia usunie tylko treść wiadomości commitu pozostawiając wygenerowane przez git dopiski takie jak "Co-Authored-By ...". pulls.delete_after_merge.head_branch.insufficient_branch = Nie masz uprawnień by usunąć head gałęzi. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 26bdd35420..8de0374eb2 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1063,8 +1063,8 @@ language.localization_project = Ajude-nos a traduzir Forgejo para o seu idioma! language.description = Essa língua será salva em sua conta e será usada como padrão após você iniciar a sessão. user_block_yourself = Você não pode se bloquear. pronouns_custom_label = Pronomes personalizados -change_username_redirect_prompt.with_cooldown.one = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dia, você ainda pode recuperar o nome de usuário antigo durante este período de espera. -change_username_redirect_prompt.with_cooldown.few = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dias, você ainda pode recuperar o nome de usuário antigo durante este período de espera. +change_username_redirect_prompt.with_cooldown.one = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia. Você ainda pode recuperar o nome de usuário antigo durante este período de proteção. +change_username_redirect_prompt.with_cooldown.few = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dias. Você ainda pode recuperar o nome de usuário antigo durante este período de proteção. quota.applies_to_user = As seguintes regras de cota se aplicam à sua conta quota.rule.exceeded.helper = O tamanho total de objetos para esta regra excedeu a cota. keep_pronouns_private = Mostrar pronomes apenas para usuários autenticados @@ -1568,7 +1568,7 @@ issues.remove_ref_at=`removeu a referência %s %s` issues.add_ref_at=`adicionou a referência %s %s` issues.delete_branch_at=`excluiu branch %s %s` issues.filter_label=Etiqueta -issues.filter_label_exclude=`Use alt + clique/enter para excluir etiquetas` +issues.filter_label_exclude=Use Alt + Clique para excluir etiquetas issues.filter_label_no_select=Todas as etiquetas issues.filter_label_select_no_label=Sem etiqueta issues.filter_milestone=Marco @@ -1642,13 +1642,13 @@ issues.close_comment_issue=Comentar e fechar issues.reopen_issue=Reabrir issues.reopen_comment_issue=Comentar e reabrir issues.create_comment=Comentar -issues.closed_at=`fechou esta issue %[2]s` -issues.reopened_at=`reabriu esta issue %[2]s` -issues.commit_ref_at=`citou esta issue em um commit %[2]s` -issues.ref_issue_from=`referenciado esta issue %[4]s %[2]s` -issues.ref_pull_from=`referenciado este pull request %[4]s %[2]s` -issues.ref_closing_from=`referenciado esta issue de um pull request %[4]s que a fechará %[2]s` -issues.ref_reopening_from=`referenciado esta issue de um pull request %[4]s que a reabrirá %[2]s` +issues.closed_at=`fechou esta issue %s` +issues.reopened_at=`reabriu esta issue %s` +issues.commit_ref_at=`citou esta issue de um commit %s` +issues.ref_issue_from=`citou esta issue %[3]s %[1]s` +issues.ref_pull_from=`citou este pull request %[3]s %[1]s` +issues.ref_closing_from=`citou esta issue de um pull request %[3]s que a fechará %[1]s` +issues.ref_reopening_from=`citou esta issue de um pull request %[3]s que a reabrirá, %[1]s` issues.ref_closed_from=`fechou esta issue %[4]s %[2]s` issues.ref_reopened_from=`reabriu esta issue %[4]s %[2]s` issues.ref_from=`de %[1]s` @@ -1942,8 +1942,8 @@ pulls.update_branch_success=Atualização do branch foi bem-sucedida pulls.update_not_allowed=Você não tem permissão para atualizar o branch pulls.outdated_with_base_branch=Este branch está desatualizado com o branch base pulls.close=Fechar pull request -pulls.closed_at=`fechou este pull request %[2]s` -pulls.reopened_at=`reabriu este pull request %[2]s` +pulls.closed_at=`fechou este pull request %s` +pulls.reopened_at=`reabriu este pull request %s` pulls.clear_merge_message=Limpar mensagem do merge pulls.clear_merge_message_hint=Limpar a mensagem de merge só irá remover o conteúdo da mensagem de commit e manter trailers git gerados, como "Co-Authored-By …". @@ -2719,7 +2719,7 @@ issues.label_archive_tooltip = Etiquetas arquivadas não serão exibidas nas sug activity.navbar.pulse = Recente settings.units.overview = Geral settings.units.add_more = Habilitar mais -pulls.commit_ref_at = `referenciou este pedido de mesclagem no commit %[2]s` +pulls.commit_ref_at = `citou este pull request de um commit %s` pulls.cmd_instruction_merge_title = Mesclar settings.units.units = Unidades vendored = Externo @@ -2920,6 +2920,7 @@ settings.event_action_recover = Recuperar settings.event_action_recover_desc = A execução da Action teve sucesso após a última execução no mesmo workflow ter falhado. settings.event_action_success = Sucesso settings.event_action_success_desc = A execução da Action foi bem sucedida. +issues.filter_type.all_pull_requests = Todos os pull requests [graphs] component_loading = Carregando %s… @@ -3056,8 +3057,8 @@ open_dashboard = Abrir painel settings.change_orgname_prompt = Obs.: Alterar o nome de uma organização resultará na alteração do URL dela e disponibilizará o nome antigo para uso. follow_blocked_user = Não foi possível seguir esta organização porque ela bloqueou-o(a). form.name_pattern_not_allowed = O padrão "%s" não é permitido no nome de uma organização. -settings.change_orgname_redirect_prompt.with_cooldown.one = O nome de organização antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia, você ainda pode recuperar o nome antigo durante este período de proteção. -settings.change_orgname_redirect_prompt.with_cooldown.few = O nome de organização antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dia, você ainda pode recuperar o nome antigo durante este período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.one = O nome de organização antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia. Você ainda pode recuperar o nome antigo durante este período de proteção. +settings.change_orgname_redirect_prompt.with_cooldown.few = O nome de organização antigo ficará disponível para qualquer pessoa após um período de proteção de %[1]d dia. Você ainda pode recuperar o nome antigo durante este período de proteção. [admin] dashboard=Painel diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 7f36d164b3..0e8f2d485e 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1657,13 +1657,13 @@ issues.close_comment_issue=Fechar com comentário issues.reopen_issue=Reabrir issues.reopen_comment_issue=Reabrir com comentário issues.create_comment=Comentar -issues.closed_at=`encerrou esta questão %[2]s` -issues.reopened_at=`reabriu esta questão %[2]s` -issues.commit_ref_at=`referenciou esta questão num cometimento %[2]s` -issues.ref_issue_from=`referiu esta questão %[4]s %[2]s` -issues.ref_pull_from=`referiu este pedido de integração %[4]s %[2]s` -issues.ref_closing_from=`referiu esta questão a partir de um pedido de integração %[4]s que a fechará %[2]s` -issues.ref_reopening_from=`referiu esta questão a partir de um pedido de integração %[4]s que a reabrirá %[2]s` +issues.closed_at=`encerrou esta questão %s` +issues.reopened_at=`reabriu esta questão %s` +issues.commit_ref_at=`referenciou esta questão num cometimento %s` +issues.ref_issue_from=`referiu esta questão %[3]s %[1]s` +issues.ref_pull_from=`referiu este pedido de integração %[3]s %[1]s` +issues.ref_closing_from=`referiu esta questão a partir de um pedido de integração %[3]s que a fechará %[1]s` +issues.ref_reopening_from=`referiu esta questão a partir de um pedido de integração %[3]s que a reabrirá %[1]s` issues.ref_closed_from=`encerrou esta questão %[4]s %[2]s` issues.ref_reopened_from=`reabriu esta questão %[4]s %[2]s` issues.ref_from=`de %[1]s` @@ -1972,8 +1972,8 @@ pulls.update_branch_success=A sincronização do ramo foi bem sucedida pulls.update_not_allowed=Não tem autorização para sincronizar o ramo pulls.outdated_with_base_branch=Este ramo é obsoleto em relação ao ramo base pulls.close=Encerrar pedido de integração -pulls.closed_at=`fechou este pedido de integração %[2]s` -pulls.reopened_at=`reabriu este pedido de integração %[2]s` +pulls.closed_at=`fechou este pedido de integração %s` +pulls.reopened_at=`reabriu este pedido de integração %s` pulls.cmd_instruction_hint=Ver instruções para a linha de comandos pulls.cmd_instruction_checkout_title=Conferir pulls.cmd_instruction_checkout_desc=No seu repositório, irá criar um novo ramo para que possa testar as modificações. @@ -2785,7 +2785,7 @@ settings.wiki_rename_branch_main_desc = Renomear o ramo usado internamente pelo settings.add_collaborator_blocked_our = Não foi possível adicionar o/a colaborador/a porque o/a proprietário/a do repositório bloqueou-os. settings.add_webhook.invalid_path = A localização não pode conter "." ou ".." ou ficar em branco. Não pode começar ou terminar com uma barra. settings.graphql_url = URL do GraphQL -pulls.commit_ref_at = `referiu este pedido de integração a partir de um cometimento %[2]s` +pulls.commit_ref_at = `referiu este pedido de integração a partir de um cometimento %s` settings.confirm_wiki_branch_rename = Renomear o ramo do wiki settings.wiki_branch_rename_success = O nome do ramo do wiki do repositório foi normalizado com sucesso. settings.wiki_branch_rename_failure = Falhou a normalização do nome do ramo do wiki do repositório. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 9158329978..2ef1b868d4 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1563,7 +1563,7 @@ issues.remove_ref_at=`убрана ссылка на %s %s` issues.add_ref_at=`добавлена ссылка на %s %s` issues.delete_branch_at=`удалена ветвь %s %s` issues.filter_label=Метки -issues.filter_label_exclude=`Исключайте метки с помощью alt + лкм/enter` +issues.filter_label_exclude=Исключайте метки с помощью Alt + ЛКМ issues.filter_label_no_select=Любые метки issues.filter_label_select_no_label=Без меток issues.filter_milestone=Этап @@ -1637,13 +1637,13 @@ issues.close_comment_issue=Закрыть комментарием issues.reopen_issue=Открыть снова issues.reopen_comment_issue=Открыть снова комментарием issues.create_comment=Комментировать -issues.closed_at=`задача была закрыта %[2]s` -issues.reopened_at=`задача была открыта снова %[2]s` -issues.commit_ref_at=`упоминание этой задачи в коммите %[2]s` -issues.ref_issue_from=`упоминание этой задачи %[4]s %[2]s` -issues.ref_pull_from=`упоминание этого запроса слияния %[4]s %[2]s` -issues.ref_closing_from=`упоминание из запроса на слияние %[4]s, который закроет эту задачу %[2]s` -issues.ref_reopening_from=`упоминание из запроса на слияние %[4]s, который повторно откроет эту задачу %[2]s` +issues.closed_at=`задача была закрыта %s` +issues.reopened_at=`задача была открыта снова %s` +issues.commit_ref_at=`упоминание этой задачи в коммите %s` +issues.ref_issue_from=`упоминание этой задачи %[3]s %[1]s` +issues.ref_pull_from=`упоминание этого запроса слияния %[3]s %[1]s` +issues.ref_closing_from=`упоминание из запроса на слияние %[3]s, который закроет эту задачу %[1]s` +issues.ref_reopening_from=`упоминание из запроса на слияние %[3]s, который повторно откроет эту задачу %[1]s` issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s` issues.ref_reopened_from=`задача была открыта снова %[4]s %[2]s` issues.ref_from=`из %[1]s` @@ -1943,8 +1943,8 @@ pulls.update_branch_success=Ветвь успешно обновлена pulls.update_not_allowed=Недостаточно прав для обновления ветви pulls.outdated_with_base_branch=Эта ветвь отстает от базовой ветви pulls.close=Закрыть запрос слияния -pulls.closed_at=`закрыл этот запрос на слияние %[2]s` -pulls.reopened_at=`переоткрыл этот запрос на слияние %[2]s` +pulls.closed_at=`закрыл этот запрос на слияние %s` +pulls.reopened_at=`переоткрыл этот запрос на слияние %s` pulls.cmd_instruction_hint=Показать инструкции для командной строки pulls.cmd_instruction_merge_title=Слейте изменения pulls.cmd_instruction_merge_desc=Слейте изменения и отправьте их обратно. @@ -2772,7 +2772,7 @@ settings.ignore_stale_approvals = Игнорировать устаревшие contributors.contribution_type.additions = Добавления contributors.contribution_type.deletions = Удаления contributors.contribution_type.filter_label = Вид деятельности: -pulls.commit_ref_at = `упоминание этого запроса слияния в коммите %[2]s` +pulls.commit_ref_at = `сослался на этот запрос слияния в коммите %s` settings.thread_id = ИД обсуждения pulls.made_using_agit = AGit activity.navbar.contributors = Соавторы diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index d55b238b1c..54b0b246db 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -1100,12 +1100,12 @@ issues.close_comment_issue=අදහස් දක්වා වසන්න issues.reopen_issue=නැවත විවෘත කරන්න issues.reopen_comment_issue=අදහස් දක්වා විවෘත කරන්න issues.create_comment=අදහස -issues.closed_at=`මෙම ගැටළුව වසා %[2]s` -issues.reopened_at=`මෙම ගැටළුව නැවත විවෘත කරන ලදි %[2]s` -issues.ref_issue_from=`මෙම නිකුතුව %[4]s හි %[2]s` -issues.ref_pull_from=`මෙම අදින්න ඉල්ලීම%[4]s %[2]s` -issues.ref_closing_from=`මෙම ගැටළුව වසා දමනු ඇත%[4]s මෙම ගැටළුව %[2]s` -issues.ref_reopening_from=`මෙම ගැටළුව නැවත විවෘත කරනු ඇත%[4]s මෙම ගැටළුව %[2]s` +issues.closed_at=`මෙම ගැටළුව වසා %s` +issues.reopened_at=`මෙම ගැටළුව නැවත විවෘත කරන ලදි %s` +issues.ref_issue_from=`මෙම නිකුතුව %[3]s හි %[1]s` +issues.ref_pull_from=`මෙම අදින්න ඉල්ලීම%[3]s %[1]s` +issues.ref_closing_from=`මෙම ගැටළුව වසා දමනු ඇත%[3]s මෙම ගැටළුව %[1]s` +issues.ref_reopening_from=`මෙම ගැටළුව නැවත විවෘත කරනු ඇත%[3]s මෙම ගැටළුව %[1]s` issues.ref_closed_from=`මෙම නිකුතුව%[4]s %[2]s` issues.ref_reopened_from=`මෙම නිකුතුව%[4]s %[2]sනැවත විවෘත කරන ලදි` issues.ref_from=`හිම%[1]s` @@ -1342,8 +1342,8 @@ pulls.update_branch_rebase=රිබේස් මගින් ශාඛාව pulls.update_branch_success=ශාඛා යාවත්කාලීන කිරීම සාර්ථක විය pulls.update_not_allowed=ශාඛාව යාවත්කාලීන කිරීමට ඔබට අවසර නැත pulls.outdated_with_base_branch=මෙම ශාඛාව මූලික ශාඛාව සමඟ දිවයයි -pulls.closed_at=`මෙම අදින්න ඉල්ලීම වසා %[2]s` -pulls.reopened_at=`මෙම අදින්න ඉල්ලීම නැවත විවෘත කරන ලදි %[2]s` +pulls.closed_at=`මෙම අදින්න ඉල්ලීම වසා %s` +pulls.reopened_at=`මෙම අදින්න ඉල්ලීම නැවත විවෘත කරන ලදි %s` diff --git a/options/locale/locale_sr-SP.ini b/options/locale/locale_sr-SP.ini index 56c1a7e650..b14fdc1a35 100644 --- a/options/locale/locale_sr-SP.ini +++ b/options/locale/locale_sr-SP.ini @@ -326,7 +326,7 @@ issues.no_content=Још нема садржаја. issues.close_issue=Затвори issues.reopen_issue=Поново отвори issues.create_comment=Коментирај -issues.commit_ref_at=`поменуо овај задатак у комит %[2]s` +issues.commit_ref_at=`поменуо овај задатак у комит %s` issues.poster=Аутор issues.collaborator=Коаутор issues.owner=Власник diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 541acbf408..8b43cb29b8 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -1157,13 +1157,13 @@ issues.close_comment_issue=Stäng med kommentar issues.reopen_issue=Återöppna issues.reopen_comment_issue=Öppna igen med kommentar issues.create_comment=Kommentera -issues.closed_at=`stängde ärendet %[2]s` -issues.reopened_at=`återöppnade detta ärende %[2]s` -issues.commit_ref_at=`refererade till detta ärende från en incheckning %[2]s` -issues.ref_issue_from=`refererade till detta ärende %[4]s %[2]s` -issues.ref_pull_from=`refererade till denna pull-förfrågan %[4]s %[2]s` -issues.ref_closing_from=`hänvisade till detta ärende från en pull-förfrågan %[4]s som kommer att stänga det %[2]s` -issues.ref_reopening_from=`hänvisade till detta ärende från en pull-förfrågan %[4]s som kommer att öppna ärendet på nytt %[2]s` +issues.closed_at=`stängde ärendet %s` +issues.reopened_at=`återöppnade detta ärende %s` +issues.commit_ref_at=`refererade till detta ärende från en incheckning %s` +issues.ref_issue_from=`refererade till detta ärende %[3]s %[1]s` +issues.ref_pull_from=`refererade till denna pull-förfrågan %[3]s %[1]s` +issues.ref_closing_from=`hänvisade till detta ärende från en pull-förfrågan %[3]s som kommer att stänga det %[1]s` +issues.ref_reopening_from=`hänvisade till detta ärende från en pull-förfrågan %[3]s som kommer att öppna ärendet på nytt %[1]s` issues.ref_closed_from=`stängde detta ärende %[4]s %[2]s` issues.ref_reopened_from=`öpnnade detta ärende igen %[4]s %[2]s` issues.ref_from=`från %[1]s` diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 4f51ddcc7e..c07cefdab9 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -1594,13 +1594,13 @@ issues.close_comment_issue=Yorum Yap ve Kapat issues.reopen_issue=Yeniden aç issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç issues.create_comment=Yorum yap -issues.closed_at=`%[2]s konusunu kapattı` -issues.reopened_at=`%[2]s konusunu yeniden açtı` -issues.commit_ref_at=`%[2]s işlemesinde bu konuyu işaret etti` -issues.ref_issue_from=`bu konuya referansta bulundu %[4]s %[2]s` -issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[4]s %[2]s` -issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu kapatılacak %[2]s` -issues.ref_reopening_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu yeniden açılacak %[2]s` +issues.closed_at=`%s konusunu kapattı` +issues.reopened_at=`%s konusunu yeniden açtı` +issues.commit_ref_at=`%s işlemesinde bu konuyu işaret etti` +issues.ref_issue_from=`bu konuya referansta bulundu %[3]s %[1]s` +issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[3]s %[1]s` +issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[3]s bu konu kapatılacak %[1]s` +issues.ref_reopening_from=`bir değişiklik isteğine referansta bulundu %[3]s bu konu yeniden açılacak %[1]s` issues.ref_closed_from=`bu konuyu kapat%[4]s %[2]s` issues.ref_reopened_from=`konuyu yeniden aç%[4]s %[2]s` issues.ref_from=`%[1]s'den` @@ -1907,8 +1907,8 @@ pulls.update_branch_success=Dal güncellemesi başarıyla gerçekleştirildi pulls.update_not_allowed=Dalı güncelleme izniniz yok pulls.outdated_with_base_branch=Bu dal, temel dal ile güncel değil pulls.close=Değişiklik İsteğini Kapat -pulls.closed_at=`%[2]s değişiklik isteğini kapattı` -pulls.reopened_at=`%[2]s değişiklik isteğini yeniden açtı` +pulls.closed_at=`%s değişiklik isteğini kapattı` +pulls.reopened_at=`%s değişiklik isteğini yeniden açtı` pulls.cmd_instruction_hint=`Komut satırı talimatlarını görüntüleyin.` pulls.cmd_instruction_checkout_title=Çekme pulls.cmd_instruction_checkout_desc=Proje deponuzdan yeni bir dalı çekin ve değişiklikleri test edin. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 2e536c3d1a..faa3f2a56e 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -694,7 +694,7 @@ disabled_public_activity=Цей користувач вимкнув публіч joined_on = Реєстрація %s email_visibility.private = Ваш email видно лише вам і адміністраторам email_visibility.limited = Вашу е-пошту видно всім авторизованим -settings = Користувацькі параметри +settings = Користувацькі налаштування block_user.detail_3 = Ви не зможете додати один одного в якості співавтора репозиторію. show_on_map = Показати це місце на мапі block_user.detail_2 = Цей користувач не зможе взаємодіяти з репозиторіями, власником яких є ви, а також із задачами та коментарями, які ви створили. @@ -1447,7 +1447,7 @@ issues.remove_ref_at=`видалив посилання %s %s` issues.add_ref_at=`додав посилання %s %s` issues.delete_branch_at=`видалена гілка %s %s` issues.filter_label=Мітка -issues.filter_label_exclude=`Використовуйте Alt + клік/Enter для виключення міток` +issues.filter_label_exclude=Використовуйте Alt + клік для виключення міток issues.filter_label_no_select=Всі мітки issues.filter_milestone=Етап issues.filter_project=Проєкт @@ -1496,17 +1496,17 @@ issues.context.quote_reply=Цитувати відповідь issues.context.reference_issue=Послатися в новій задачі issues.context.edit=Редагувати issues.context.delete=Видалити -issues.close_comment_issue=Прокоментувати і закрити +issues.close_comment_issue=Закрити з коментарем issues.reopen_issue=Відкрити знову -issues.reopen_comment_issue=Прокоментувати та відкрити знову +issues.reopen_comment_issue=Відкрити знову з коментарем issues.create_comment=Коментар -issues.closed_at=`закрив цю задачу %[2]s` -issues.reopened_at=`повторно відкрив цю задачу %[2]s` -issues.commit_ref_at=`згадано цю задачу в коміті %[2]s` -issues.ref_issue_from=`посилається на цю задачу %[4]s %[2]s` -issues.ref_pull_from=`послався на цей запит злиття %[4]s %[2]s` -issues.ref_closing_from=`згадав запит на злиття %[4]s, які закриють цю задачу %[2]s` -issues.ref_reopening_from=`згадав запит на злиття %[4]s, які повторно відкриють цю задачу %[2]s` +issues.closed_at=`закриває цю задачу %s` +issues.reopened_at=`повторно відкриває цю задачу %s` +issues.commit_ref_at=`посилається на цю задачу в коміті %s` +issues.ref_issue_from=`посилається на цю задачу %[3]s %[1]s` +issues.ref_pull_from=`посилається на цей запит злиття %[3]s %[1]s` +issues.ref_closing_from=`посилається в запиті на злиття %[3]s, який закриє цю задачу, %[1]s` +issues.ref_reopening_from=`посилається в запиті на злиття %[3]s, який повторно відкриє цю задачу, %[1]s` issues.ref_closed_from=`закрив цю задачу %[4]s %[2]s` issues.ref_reopened_from=`повторно відкрито цю задачу %[4]s %[2]s` issues.ref_from=`із %[1]s` @@ -1743,8 +1743,8 @@ pulls.update_branch_rebase=Оновити гілку перебазування pulls.update_branch_success=Оновлення гілки пройшло успішно pulls.update_not_allowed=Ви не можете оновити гілку pulls.outdated_with_base_branch=Ця гілка застаріла відносно базової гілки -pulls.closed_at=`закрив цей запит на злиття %[2]s` -pulls.reopened_at=`повторно відкрив цей запит на злиття %[2]s` +pulls.closed_at=`закриває цей запит на злиття %s` +pulls.reopened_at=`повторно відкриває цей запит на злиття %s` @@ -1887,7 +1887,7 @@ settings.collaboration.owner=Власник settings.collaboration.undefined=Не визначено settings.hooks=Веб-хуки settings.githooks=Git хуки -settings.basic_settings=Базові налаштування +settings.basic_settings=Основні налаштування settings.mirror_settings=Налаштування дзеркала settings.mirror_settings.mirrored_repository=Віддзеркалений репозиторій settings.mirror_settings.direction=Напрямок @@ -2055,12 +2055,12 @@ settings.event_issue_assign=Призначення settings.event_issue_assign_desc=Задачу призначено або скасовано. settings.event_issue_label=Мітки settings.event_issue_label_desc=Додавання або видалення міток задач. -settings.event_issue_milestone=Задача з етапом +settings.event_issue_milestone=Етапи settings.event_issue_milestone_desc=Етап призначено, видалено або змінено. settings.event_issue_comment=Коментарі settings.event_issue_comment_desc=Коментар задачі створено, видалено чи відредаговано. settings.event_header_pull_request=Події запиту на злиття -settings.event_pull_request=Запити до злиття +settings.event_pull_request=Зміна settings.event_pull_request_desc=Запит до злиття відкрито, закрито, перевідкрито або відредаговано. settings.event_pull_request_assign=Призначення settings.event_pull_request_assign_desc=Запит про злиття призначено або скасовано. @@ -2485,7 +2485,7 @@ signing.will_sign = Коміт буде підписано ключем «%s». signing.wont_sign.error = Під час перевірки можливості підписати коміт сталася помилка. commits.search_branch = У цій гілці ext_wiki = Зовнішня вікі -pulls.commit_ref_at = `посилається на цей запит на злиття в коміті %[2]s` +pulls.commit_ref_at = `посилається на цей запит на злиття в коміті %s` pulls.cmd_instruction_hint = Переглянути інструкції для командного рядка issues.max_pinned = Неможливо закріпити більше задач issues.unpin_comment = відкріпив %s @@ -2847,7 +2847,7 @@ dashboard.update_migration_poster_id=Оновити мігровані ID авт dashboard.git_gc_repos=Виконати очистку сміття для всіх репозиторіїв dashboard.resync_all_sshkeys=Оновити файл «.ssh/authorized_keys» з SSH-ключами Forgejo. dashboard.resync_all_sshprincipals=Оновити файл «.ssh/authorized_principals» з SSH даними користувача Forgejo. -dashboard.resync_all_hooks=Пересинхронізувати перед-прийнятні, оновлюючі та пост-прийнятні хуки в усіх репозиторіях +dashboard.resync_all_hooks=Пересинхронізувати хуки pre-receive, update та post-receive в усіх репозиторіях dashboard.reinit_missing_repos=Переініціалізувати усі репозитрії git-файли яких втрачено dashboard.sync_external_users=Синхронізувати дані зовнішніх користувачів dashboard.cleanup_hook_task_table=Очистити hook_task таблицю @@ -2906,7 +2906,7 @@ users.edit_account=Редагувати обліковий запис users.max_repo_creation=Максимальна кількість репозиторіїв users.max_repo_creation_desc=(Введіть -1, щоб використовувати глобальний ліміт за замовчуванням.) users.is_activated=Обліковий запис користувача увімкнено -users.prohibit_login=Вимкнути вхід +users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора users.is_restricted=Обмежений users.allow_git_hook=Може створювати Git хуки @@ -3305,6 +3305,7 @@ dashboard.cron.cancelled = Cron: %[1]s скасовано: %[3]s defaulthooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки є типовими і будуть скопійовані до всіх нових репозиторіїв. Докладніше — в посібнику з вебхуків. assets = Ресурси коду auths.invalid_openIdConnectAutoDiscoveryURL = Неправильна URL-адреса автоматичного виявлення (повинна бути дійсна URL-адреса, що починається з http:// або https://) +settings = Налаштування адміністратора [action] diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index acdd4c0ced..c6c534df9f 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1062,8 +1062,8 @@ language.description = 此语言将保存到您的账号中,并在您登录后 language.localization_project = 帮助我们将 Forgejo 翻译成您的语言!了解更多。 user_block_yourself = 您不能屏蔽自己。 pronouns_custom_label = 自定义代词 -change_username_redirect_prompt.with_cooldown.one = 旧的用户名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 -change_username_redirect_prompt.with_cooldown.few = 旧的用户名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧的用户名。 +change_username_redirect_prompt.with_cooldown.one = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 +change_username_redirect_prompt.with_cooldown.few = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 keep_pronouns_private = 仅向已认证用户显示代词 keep_pronouns_private.description = 这将对未登录的访问者隐藏您的代词。 quota = 配额 @@ -1581,7 +1581,7 @@ issues.remove_ref_at=`删除了引用 %s %s` issues.add_ref_at=`添加了引用 %s %s` issues.delete_branch_at=`于 %[2]s 删除了分支 %[1]s` issues.filter_label=标签筛选 -issues.filter_label_exclude=`使用 alt + 鼠标左键 / 回车 排除标签` +issues.filter_label_exclude=使用 Alt + 单击 排除标签 issues.filter_label_no_select=所有标签 issues.filter_label_select_no_label=无标签 issues.filter_milestone=里程碑筛选 @@ -1655,13 +1655,13 @@ issues.close_comment_issue=评论并关闭 issues.reopen_issue=重新开放 issues.reopen_comment_issue=重新打开并评论 issues.create_comment=评论 -issues.closed_at=`于%[2]s关闭此议题` -issues.reopened_at=`重新打开此问题 %[2]s` -issues.commit_ref_at=`于%[2]s在代码提交中引用了该议题` -issues.ref_issue_from=`引用了议题 %[4]s %[2]s` -issues.ref_pull_from=`引用了合并请求 %[4]s %[2]s` -issues.ref_closing_from=`于 %[2]s 从合并请求 %[4]s引用了此议题,将关闭此议题` -issues.ref_reopening_from=`于 %[2]s 引用了合并请求 %[4]s 将重新讨论此议题 ` +issues.closed_at=`于 %s 关闭了此议题` +issues.reopened_at=`于 %s 重新打开了此议题` +issues.commit_ref_at=`于 %s 从提交中引用了此议题` +issues.ref_issue_from=`引用了此议题 %[3]s %[1]s` +issues.ref_pull_from=`引用了此合并请求 %[3]s %[1]s` +issues.ref_closing_from=`于 %[1]s 从合并请求 %[3]s 引用了此议题,将关闭此议题` +issues.ref_reopening_from=`于 %[1]s 从合并请求 %[3]s 引用了此议题,将重新打开此议题 ` issues.ref_closed_from=`关闭了这个议题 %[4]s %[2]s` issues.ref_reopened_from=`重新打开这个议题 %[4]s %[2]s` issues.ref_from=`来自 %[1]s` @@ -1969,8 +1969,8 @@ pulls.update_branch_success=分支更新成功 pulls.update_not_allowed=您无权更新分支 pulls.outdated_with_base_branch=此分支相比基础分支已过期 pulls.close=关闭 -pulls.closed_at=`于%[2]s关闭此合并请求 ` -pulls.reopened_at=`重新打开此合并请求 %[2]s` +pulls.closed_at=`于 %s 关闭了此合并请求 ` +pulls.reopened_at=`于 %s 重新打开了此合并请求` pulls.cmd_instruction_hint=查看命令行说明 pulls.cmd_instruction_checkout_title=检出 pulls.cmd_instruction_checkout_desc=从你的仓库中检出一个新的分支并测试变更。 @@ -2770,7 +2770,7 @@ settings.wiki_rename_branch_main = 标准化百科分支名称 settings.wiki_rename_branch_main_notices_1 = 此操作无法撤消。 settings.wiki_branch_rename_success = 百科仓库的分支名称已成功规范化。 settings.confirm_wiki_branch_rename = 重命名百科分支 -pulls.commit_ref_at = `在提交 %[2]s 中引用了此合并请求` +pulls.commit_ref_at = `于 %s 从提交中引用了此合并请求` settings.wiki_rename_branch_main_notices_2 = 这将永久重命名 %s 的仓库百科的内部分支。现存的检出方式需要更新。 settings.wiki_branch_rename_failure = 无法标准化仓库百科的分支名称。 settings.add_collaborator_blocked_our = 因仓库所有者已将其拉黑,不能添加该用户为协作者。 @@ -2922,6 +2922,7 @@ settings.event_action_success = 成功 settings.event_action_success_desc = Action运行以成功结束。 settings.event_action_failure_desc = Action运行以失败结束。 settings.event_header_action = Action运行事件 +issues.filter_type.all_pull_requests = 所有合并请求 [graphs] component_loading=正在加载 %s… @@ -3057,8 +3058,8 @@ teams.invite.by=邀请人 %s teams.invite.description=请点击下面的按钮加入团队。 follow_blocked_user = 你无法关注此组织,因为此组织已屏蔽你。 open_dashboard = 打开仪表盘 -settings.change_orgname_redirect_prompt.with_cooldown.one = 旧的组织名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧的名字。 -settings.change_orgname_redirect_prompt.with_cooldown.few = 旧的组织名将在%[1]d天的保护期后对所有人可用,您仍可以在此期间重新认领旧名字。 +settings.change_orgname_redirect_prompt.with_cooldown.one = 旧组织名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧名称。 +settings.change_orgname_redirect_prompt.with_cooldown.few = 旧组织名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧名称。 [admin] dashboard=管理面板 diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini index e2cb0d8b2c..45534801de 100644 --- a/options/locale/locale_zh-HK.ini +++ b/options/locale/locale_zh-HK.ini @@ -574,7 +574,7 @@ issues.delete_comment_confirm=您確定要刪除該條評論嗎? issues.context.edit=編輯 issues.reopen_issue=重新開啟 issues.create_comment=評論 -issues.commit_ref_at=`在代碼提交 %[2]s 中引用了該問題` +issues.commit_ref_at=`在代碼提交 %s 中引用了該問題` issues.role.owner=管理員 issues.role.member=普通成員 issues.sign_in_require_desc= 登入 才能加入這對話。 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index b21e4c8f79..fba51a391e 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1604,13 +1604,13 @@ issues.close_comment_issue=留言並關閉 issues.reopen_issue=重新開放 issues.reopen_comment_issue=留言並重新開放 issues.create_comment=留言 -issues.closed_at=`關閉了這個問題 %[2]s` -issues.reopened_at=`重新開放了這個問題 %[2]s` -issues.commit_ref_at=`在提交中關聯了這個問題 %[2]s` -issues.ref_issue_from=`關聯了這個問題 %[4]s %[2]s` -issues.ref_pull_from=`關聯了這個合併請求 %[4]s %[2]s` -issues.ref_closing_from=從將關閉此問題的拉取請求 %[4]s 中提及了此問題%[2]s -issues.ref_reopening_from=從將重新開啟此問題的拉取請求 %[4]s 中提及了此問題%[2]s +issues.closed_at=`關閉了這個問題 %s` +issues.reopened_at=`重新開放了這個問題 %s` +issues.commit_ref_at=`在提交中關聯了這個問題 %s` +issues.ref_issue_from=`關聯了這個問題 %[3]s %[1]s` +issues.ref_pull_from=`關聯了這個合併請求 %[3]s %[1]s` +issues.ref_closing_from=從將關閉此問題的拉取請求 %[3]s 中提及了此問題,%[1]s +issues.ref_reopening_from=從將重新開啟此問題的拉取請求 %[3]s 中提及了此問題,%[1]s issues.ref_closed_from=`關閉了這個問題 %[4]s %[2]s` issues.ref_reopened_from=`重新開放了這個問題 %[4]s %[2]s` issues.ref_from=`自 %[1]s` @@ -1879,8 +1879,8 @@ pulls.update_branch_success=分支更新成功 pulls.update_not_allowed=您無權更新分支 pulls.outdated_with_base_branch=相對於基底分支,此分支已過時 pulls.close=關閉合併請求 -pulls.closed_at=`關閉了這個合併請求 %[2]s` -pulls.reopened_at=`重新開放了這個合併請求 %[2]s` +pulls.closed_at=`關閉了這個合併請求 %s` +pulls.reopened_at=`重新開放了這個合併請求 %s` pulls.clear_merge_message=清除合併訊息 pulls.clear_merge_message_hint=清除合併訊息將僅移除提交訊息內容,留下產生的 git 結尾,如「Co-Authored-By …」。 @@ -2634,7 +2634,7 @@ commits.search_branch = 此分支 commits.browse_further = 進一步瀏覽 commits.renamed_from = 自 %s 重新命名 issues.filter_milestone_none = 沒有里程碑 -issues.num_comments_1 = %s 則留言 +issues.num_comments_1 = %d 則留言 issues.no_content = 沒有提供敘述。 settings.new_owner_blocked_doer = 新的所有者已封鎖您。 new_repo_helper = 一個儲存庫包含專案的所有檔案和它們的修訂歷史。在別處已經有儲存庫了嗎?遷移儲存庫。 @@ -2693,7 +2693,7 @@ signing.wont_sign.never = 永不簽署提交。 editor.push_out_of_date = 該推送似乎過期了。 issues.cancel_tracking_history = `已取消時間追蹤 %s` issues.due_date_not_writer = 您需要有寫入這個儲存庫的權限才能更新其問題的到期日。 -pulls.commit_ref_at = `在提交 %[2]s 引用了這個合併請求` +pulls.commit_ref_at = `在提交 %s 引用了這個合併請求` pulls.cmd_instruction_checkout_desc = 從您的專案儲存庫中,建立並切換到一個新分支以測試這些變更。 pulls.cmd_instruction_merge_title = 合併 pulls.ready_for_review = 可以開始審閱了嗎? diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 94ab12f180..3847de2b43 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -97,5 +97,10 @@ "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr", "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]dx%[3]d Pixel sein", "repo.diff.commit.next-short": "Nächste", - "repo.diff.commit.previous-short": "Vorherige" + "repo.diff.commit.previous-short": "Vorherige", + "profile.edit.link": "Profil bearbeiten", + "feed.atom.link": "Atom-Feed", + "keys.ssh.link": "SSH-Schlüssel", + "keys.gpg.link": "GPG-Schlüssel", + "profile.actions.tooltip": "Mehr Aktionen" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 1f1e535dad..884a7b44eb 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -21,7 +21,7 @@ "alert.asset_load_failed": "Nabigong i-load ang mga asset file mula sa {path}. Siguraduhin na maa-access ang mga asset file.", "install.invalid_lfs_path": "Nabigong gawin ang LFS root sa tinakdang path: %[1]s", "alert.range_error": " dapat ay numero sa pagitan ng %[1]s at %[2]s.", - "meta.last_line": "Sayori... I love you. — MC from Doki Doki Literature Club", + "meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices. What will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings. What good are words when a smile says it all? And if this world won't write me an ending... What will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat What do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.", "mail.actions.successful_run_after_failure": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.run_info_previous_status": "Nakaraang Status ng Run: %[1]s", @@ -94,5 +94,13 @@ "editor.textarea.tab_hint": "Naka-indent na ang linya. Pindutin ulit ang Tab o Escape para umalis sa editor.", "editor.textarea.shift_tab_hint": "Walang indentation sa linyang ito. Pindutin ang Shift + Tab ulit o Escape para umalis sa editor.", "admin.dashboard.cleanup_offline_runners": "Linisin ang mga offline na runner", - "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa" + "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa", + "avatar.constraints_hint": "Hindi maaaring lumagpas sa laking %[1]s o mas malaki sa %[2]dx%[3]d pixel ang custom na avatar", + "repo.diff.commit.next-short": "Susunod", + "repo.diff.commit.previous-short": "Nakaraan", + "profile.edit.link": "I-edit ang profile", + "feed.atom.link": "Atom feed", + "keys.ssh.link": "Mga SSH key", + "keys.gpg.link": "Mga GPG key", + "profile.actions.tooltip": "Higit pang mga aksyon" } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index a9035f0848..da26d56107 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -6,7 +6,7 @@ }, "repo.pulls.title_desc": { "one": "veut fusionner %[1]d commit depuis %[2]s vers %[3]s", - "many": "souhaite fusionner %[1]d révision(s) depuis %[2]s vers %[3]s", + "many": "veut fusionner %[1]d commits depuis %[2]s vers %[3]s", "other": "" }, "search.milestone_kind": "Recherche dans les jalons…", diff --git a/options/locale_next/locale_it-IT.json b/options/locale_next/locale_it-IT.json index 8bd6d811a0..8464d6244e 100644 --- a/options/locale_next/locale_it-IT.json +++ b/options/locale_next/locale_it-IT.json @@ -15,5 +15,83 @@ "home.welcome.no_activity": "Nessun'attività", "home.explore_repos": "Esplora i repositori", "home.explore_users": "Esplora l'utenza", - "home.explore_orgs": "Esplora le organizzazioni" + "home.explore_orgs": "Esplora le organizzazioni", + "mail.actions.successful_run_after_failure_subject": "Il flusso di lavoro %[1] ha ripreso a funzionare nel repositorio %[2]s", + "mail.actions.not_successful_run_subject": "Il flusso di lavoro %[1]s è fallito nel repositorio %[2]s", + "relativetime.future": "nel futuro", + "relativetime.days": { + "one": "ieri", + "many": "%d giorni fa", + "other": "%d giorni fa" + }, + "relativetime.1day": "ieri", + "repo.form.cannot_create": "Tutti gli spazi in cui puoi creare repositori hanno raggiunto il limite di repositori.", + "discussion.locked": "Questa discussione è stata bloccata. Solo i contributori possono commentare.", + "relativetime.hours": { + "one": "un'ora fa", + "many": "%d ore fa", + "other": "%d ore fa" + }, + "relativetime.2years": "due anni fa", + "relativetime.now": "adesso", + "relativetime.weeks": { + "one": "una settimana fa", + "many": "%d settimane fa", + "other": "%d settimane fa" + }, + "relativetime.months": { + "one": "un mese fa", + "many": "%d mesi fa", + "other": "%d mesi fa" + }, + "relativetime.years": { + "one": "un anno fa", + "many": "%d anni fa", + "other": "%d anni fa" + }, + "repo.issue_indexer.title": "Indicizzatore delle segnalazioni", + "admin.config.moderation_config": "Impostazioni di moderazione", + "moderation.report_abuse": "Segnala abuso", + "moderation.report_content": "Segnala contenuto", + "moderation.report_abuse_form.already_reported": "Hai già segnalato questo contenuto", + "moderation.abuse_category": "Categoria", + "moderation.abuse_category.placeholder": "Seleziona una categoria", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Contenuti illegali", + "moderation.abuse_category.other_violations": "Altre violazioni delle regole della piattaforma", + "moderation.report_remarks": "Note aggiuntive", + "moderation.report_remarks.placeholder": "Aggiungi dettagli riguardanti l'abuso che stai segnalando.", + "moderation.submit_report": "Invia segnalazione", + "error.not_found.title": "Pagina non trovata", + "themes.names.forgejo-auto": "Forgejo (segui le impostazioni di sistema)", + "stars.list.none": "Nessuno ha messo una stella a questo repo.", + "watch.list.none": "Nessuno sta osservando questo repo.", + "followers.incoming.list.self.none": "Nessuno sta seguendo il tuo profilo.", + "followers.incoming.list.none": "Nessuno sta seguendo questo utente.", + "followers.outgoing.list.self.none": "Non segui nessuno.", + "followers.outgoing.list.none": "%s non sta seguendo nessuno.", + "relativetime.2days": "due giorni fa", + "relativetime.2weeks": "due settimane fa", + "relativetime.1week": "la settimana scorsa", + "relativetime.1month": "il mese scorso", + "relativetime.2months": "due mesi fa", + "relativetime.1year": "l'anno scorso", + "moderation.report_abuse_form.header": "Segnala abuso all'amministratore", + "moderation.report_abuse_form.details": "Questo modulo dovrebbe essere utilizzato per segnalare utenti che creano profili, repositori, segnalazioni o commenti spam o che si comportano in modo non adeguato.", + "moderation.report_abuse_form.invalid": "Argomenti non validi", + "moderation.reporting_failed": "Impossibile inviare segnalazione: %v", + "moderation.reported_thank_you": "Grazie per la segnalazione. L'amministratore è stato avvertito.", + "mail.actions.run_info_ref": "Ramo: %[1]s (%[2]s)", + "alert.asset_load_failed": "Impossibile caricare i file di risorsa da {path}. Controlla che i file di risorsa siano accessibili.", + "install.invalid_lfs_path": "Non è possibile creare una root LFS nel percorso specificato: %[1]s", + "home.welcome.activity_hint": "Non c'è nulla nel tuo feed. Le tue azioni e le attività dei repositori che segui verranno mostrate qui.", + "relativetime.mins": { + "one": "un minuto fa", + "many": "%d minuti fa", + "other": "%d minuti fa" + }, + "editor.textarea.tab_hint": "Linea già indentata. Premi di nuovo Tab o Esc per uscire dall'editor.", + "repo.diff.commit.previous-short": "Precedente", + "meta.last_line": "Ambaraba cicci cocco." } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 9a3884a87f..24268e2082 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -97,5 +97,10 @@ "settings.visibility.description": "De Profil-Sichtbaarkeid maakt daar wat an, of un wo anner Lüü diene nich-privaaten Repositoriums ankieken könen. Mehr unnerhören", "avatar.constraints_hint": "Dat eegene Kontobill düür nich groter as %[1]s wesen of groter as %[2]d×%[3]d Billtüttels wesen", "repo.diff.commit.next-short": "Anner", - "repo.diff.commit.previous-short": "Vörig" + "repo.diff.commit.previous-short": "Vörig", + "feed.atom.link": "Atom-Schuuv", + "keys.ssh.link": "SSH-Slötels", + "keys.gpg.link": "GPG-Slötels", + "profile.actions.tooltip": "Mehr Aktioonen", + "profile.edit.link": "Profil bewarken" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 92e140878e..1a5eca6d34 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -103,5 +103,7 @@ "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Esc para sair do editor.", "admin.dashboard.cleanup_offline_runners": "Limpar runners desconectados", "avatar.constraints_hint": "Imagem de perfil personalizada não pode exceder %[1]s em tamanho ou ser maior que %[2]dx%[3]d pixels", - "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais" + "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais", + "repo.diff.commit.next-short": "Próximo", + "repo.diff.commit.previous-short": "Anterior" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 8992cc6abd..922e2612af 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -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": "Unskip.", + "meta.last_line": "Unskip..", "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_ref": "Ветвь: %[1]s (%[2]s)", @@ -104,6 +104,11 @@ "admin.dashboard.cleanup_offline_runners": "Удалить недоступных исполнителей", "avatar.constraints_hint": "Изображение профиля не может быть более %[1]s и крупнее %[2]dx%[3]d пикселей", "settings.visibility.description": "Видимость профиля влияет на доступ других до ваших не частных репозиториев. Подробнее", - "repo.diff.commit.previous-short": "Предыдущий", - "repo.diff.commit.next-short": "Далее" + "repo.diff.commit.previous-short": "Пред.", + "repo.diff.commit.next-short": "След.", + "profile.actions.tooltip": "Показать действия", + "feed.atom.link": "Atom-лента", + "keys.ssh.link": "Ключи SSH", + "keys.gpg.link": "Ключи GPG", + "profile.edit.link": "Изменить профиль" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index c3bcf5397c..33cb5a41a3 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -105,5 +105,10 @@ "settings.visibility.description": "Видимість профілю впливає на можливість інших користувачів отримати доступ до ваших неприватних репозиторіїв. Дізнатися більше", "avatar.constraints_hint": "Розмір користувацького аватара не може перевищувати %[1]s або бути більшим за %[2]d×%[3]d пікселів", "repo.diff.commit.next-short": "Наступний", - "repo.diff.commit.previous-short": "Попередній" + "repo.diff.commit.previous-short": "Попередній", + "keys.ssh.link": "Ключі SSH", + "keys.gpg.link": "Ключі GPG", + "profile.edit.link": "Редагувати профіль", + "feed.atom.link": "Стрічка Atom", + "profile.actions.tooltip": "Більше дій" } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index e7c1bad81e..0f408997bf 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -71,5 +71,12 @@ "editor.textarea.shift_tab_hint": "此行无缩进。再次按 Shift + Tab 或按 Escape 退出编辑器。", "admin.dashboard.cleanup_offline_runners": "清理离线运行器", "settings.visibility.description": "个人资料可见性设置会影响他人对您的非私有仓库的访问。了解更多", - "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,或大于 %[2]d×%[3]d 像素" + "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,或大于 %[2]d×%[3]d 像素", + "keys.ssh.link": "SSH 密钥", + "keys.gpg.link": "GPG 密钥", + "profile.actions.tooltip": "更多操作", + "repo.diff.commit.next-short": "下个", + "repo.diff.commit.previous-short": "上个", + "feed.atom.link": "Atom 订阅源", + "profile.edit.link": "编辑个人资料" } diff --git a/options/locale_next/locale_zh-TW.json b/options/locale_next/locale_zh-TW.json index 5e3e43f66e..3ae0b00d2b 100644 --- a/options/locale_next/locale_zh-TW.json +++ b/options/locale_next/locale_zh-TW.json @@ -68,5 +68,10 @@ "moderation.report_abuse_form.details": "這個表單是用來檢舉用戶建立垃圾帳號、儲存庫、問題、留言,或其他不當行為。", "moderation.report_abuse_form.invalid": "無效參數", "moderation.report_abuse_form.already_reported": "您已檢舉此內容", - "meta.last_line": "Rubi-chan? Hai! Nani ga suki? Choko minto yori mo a・na・ta♡ Ayumu-chan? Hai! Nani ga suki? Sutoroberii fureibaa yori mo a・na・ta♡ Shiki-chan! Hai! Nani ga suki? Kukkii and kuriimu yori mo a・na・ta♡ Minna? Hai! Nani ga suki? Mochiron daisuki AiScReam." + "meta.last_line": "Rubi-chan? Hai! Nani ga suki? Choko minto yori mo a・na・ta♡ Ayumu-chan? Hai! Nani ga suki? Sutoroberii fureibaa yori mo a・na・ta♡ Shiki-chan! Hai! Nani ga suki? Kukkii and kuriimu yori mo a・na・ta♡ Minna? Hai! Nani ga suki? Mochiron daisuki AiScReam.", + "admin.dashboard.cleanup_offline_runners": "清理離線 runners", + "settings.visibility.description": "個人資料的可見度會影響他人存取您非私人儲存庫的能力。了解更多", + "avatar.constraints_hint": "自定義大頭貼的大小不得超過 %[1]s,且解析度不得大於 %[2]d×%[3]d 像素", + "repo.diff.commit.next-short": "下一個", + "repo.diff.commit.previous-short": "上一個" } From 184e068f376ce8c5f5bfe74ec17f3188d8ba9189 Mon Sep 17 00:00:00 2001 From: Danko Aleksejevs Date: Thu, 26 Jun 2025 20:06:21 +0200 Subject: [PATCH 013/297] feat: show more relevant results for 'dependencies' dropdown (#8003) - Fix issue dropdown breaking when currently selected issue is included in results. - Add `sort` parameter to `/issues/search` API. - Sort dropdown by relevance. - Make priority_repo_id work again. - Added E2E test. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8003 Reviewed-by: Shiny Nematoda Reviewed-by: Gusted Co-authored-by: Danko Aleksejevs Co-committed-by: Danko Aleksejevs --- models/fixtures/repository.yml | 2 +- models/issues/issue_search.go | 17 ++-- models/issues/pull_list.go | 2 +- modules/indexer/issues/bleve/bleve.go | 11 ++- modules/indexer/issues/db/db.go | 3 + modules/indexer/issues/db/options.go | 5 ++ .../issues/elasticsearch/elasticsearch.go | 6 +- modules/indexer/issues/internal/model.go | 5 +- .../indexer/issues/internal/tests/tests.go | 19 ++++ routers/api/v1/repo/issue.go | 15 +++- routers/web/repo/issue.go | 11 +-- templates/swagger/v1_json.tmpl | 18 ++++ tests/e2e/declare_repos_test.go | 81 ++++++++++++++--- tests/e2e/issue-sidebar.test.e2e.ts | 88 +++++++++++++++++++ tests/e2e/utils_e2e_test.go | 1 + tests/test_utils.go | 7 ++ web_src/js/features/repo-issue.js | 19 ++-- 17 files changed, 269 insertions(+), 41 deletions(-) diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index c383fa43ac..2f104eed65 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -32,7 +32,7 @@ created_unix: 1731254961 updated_unix: 1731254961 topics: '[]' - + - id: 2 owner_id: 2 diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 91a69c26a7..529f0c15d4 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -48,7 +48,9 @@ type IssuesOptions struct { //nolint UpdatedBeforeUnix int64 // prioritize issues from this repo PriorityRepoID int64 - IsArchived optional.Option[bool] + // if this issue index (not ID) exists and matches the filters, *and* priorityrepo sort is used, show it first + PriorityIssueIndex int64 + IsArchived optional.Option[bool] // If combined with AllPublic, then private as well as public issues // that matches the criteria will be returned, if AllPublic is false @@ -60,7 +62,7 @@ type IssuesOptions struct { //nolint // applySorts sort an issues-related session based on the provided // sortType string -func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { +func applySorts(sess *xorm.Session, sortType string, priorityRepoID, priorityIssueIndex int64) { switch sortType { case "oldest": sess.Asc("issue.created_unix").Asc("issue.id") @@ -97,8 +99,11 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { case "priorityrepo": sess.OrderBy("CASE "+ "WHEN issue.repo_id = ? THEN 1 "+ - "ELSE 2 END ASC", priorityRepoID). - Desc("issue.created_unix"). + "ELSE 2 END ASC", priorityRepoID) + if priorityIssueIndex != 0 { + sess.OrderBy("issue.index = ? DESC", priorityIssueIndex) + } + sess.Desc("issue.created_unix"). Desc("issue.id") case "project-column-sorting": sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") @@ -470,7 +475,7 @@ func Issues(ctx context.Context, opts *IssuesOptions) (IssueList, error) { Join("INNER", "repository", "`issue`.repo_id = `repository`.id") applyLimit(sess, opts) applyConditions(sess, opts) - applySorts(sess, opts.SortType, opts.PriorityRepoID) + applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex) issues := IssueList{} if err := sess.Find(&issues); err != nil { @@ -494,7 +499,7 @@ func IssueIDs(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Co } applyLimit(sess, opts) - applySorts(sess, opts.SortType, opts.PriorityRepoID) + applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex) var res []int64 total, err := sess.Select("`issue`.id").Table(&Issue{}).FindAndCount(&res) diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 8fc0491026..ddb813cf44 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -149,7 +149,7 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio } findSession := listPullRequestStatement(ctx, baseRepoID, opts) - applySorts(findSession, opts.SortType, 0) + applySorts(findSession, opts.SortType, 0, 0) findSession = db.SetSessionPagination(findSession, opts) prs := make([]*PullRequest, 0, opts.PageSize) found := findSession.Find(&prs) diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 573d63a446..8549ba8dfc 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -170,7 +170,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if issueID, err := token.ParseIssueReference(); err == nil { idQuery := inner_bleve.NumericEqualityQuery(issueID, "index") - idQuery.SetBoost(5.0) + idQuery.SetBoost(20.0) innerQ.AddQuery(idQuery) } @@ -197,6 +197,15 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( queries = append(queries, bleve.NewDisjunctionQuery(repoQueries...)) } + if options.PriorityRepoID.Has() { + eq := inner_bleve.NumericEqualityQuery(options.PriorityRepoID.Value(), "repo_id") + eq.SetBoost(10.0) + meh := bleve.NewMatchAllQuery() + meh.SetBoost(0) + should := bleve.NewDisjunctionQuery(eq, meh) + queries = append(queries, should) + } + if options.IsPull.Has() { queries = append(queries, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull")) } diff --git a/modules/indexer/issues/db/db.go b/modules/indexer/issues/db/db.go index 397daa3265..5f42bce9a1 100644 --- a/modules/indexer/issues/db/db.go +++ b/modules/indexer/issues/db/db.go @@ -53,6 +53,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( cond := builder.NewCond() + var priorityIssueIndex int64 if options.Keyword != "" { repoCond := builder.In("repo_id", options.RepoIDs) if len(options.RepoIDs) == 1 { @@ -82,6 +83,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( builder.Eq{"`index`": issueID}, cond, ) + priorityIssueIndex = issueID } } @@ -89,6 +91,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if err != nil { return nil, err } + opt.PriorityIssueIndex = priorityIssueIndex // If pagesize == 0, return total count only. It's a special case for search count. if options.Paginator != nil && options.Paginator.PageSize == 0 { diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 4411cc1c37..55a471fc8e 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -78,6 +78,11 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m User: nil, } + if options.PriorityRepoID.Has() { + opts.SortType = "priorityrepo" + opts.PriorityRepoID = options.PriorityRepoID.Value() + } + if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 { opts.MilestoneIDs = []int64{db.NoConditionID} } else { diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 9d2786e101..d632a22b2a 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -165,7 +165,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( } var eitherQ elastic.Query = innerQ if issueID, err := token.ParseIssueReference(); err == nil { - indexQ := elastic.NewTermQuery("index", issueID).Boost(15.0) + indexQ := elastic.NewTermQuery("index", issueID).Boost(20) eitherQ = elastic.NewDisMaxQuery().Query(indexQ).Query(innerQ).TieBreaker(0.5) } switch token.Kind { @@ -188,6 +188,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( } query.Must(q) } + if options.PriorityRepoID.Has() { + q := elastic.NewTermQuery("repo_id", options.PriorityRepoID.Value()).Boost(10) + query.Should(q) + } if options.IsPull.Has() { query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value())) diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 6c55405179..cdd113212d 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -75,8 +75,9 @@ type SearchResult struct { type SearchOptions struct { Keyword string // keyword to search - RepoIDs []int64 // repository IDs which the issues belong to - AllPublic bool // if include all public repositories + RepoIDs []int64 // repository IDs which the issues belong to + AllPublic bool // if include all public repositories + PriorityRepoID optional.Option[int64] // issues from this repository will be prioritized when SortByScore IsPull optional.Option[bool] // if the issues is a pull request IsClosed optional.Option[bool] // if the issues is closed diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index ef75955a14..b63957ff84 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -742,6 +742,25 @@ var cases = []*testIndexerCase{ } }, }, + { + Name: "PriorityRepoID", + SearchOptions: &internal.SearchOptions{ + IsPull: optional.Some(false), + IsClosed: optional.Some(false), + PriorityRepoID: optional.Some(int64(3)), + Paginator: &db.ListOptionsAll, + SortBy: internal.SortByScore, + }, + Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { + for i, v := range result.Hits { + if i < 7 { + assert.Equal(t, int64(3), data[v.ID].RepoID) + } else { + assert.NotEqual(t, int64(3), data[v.ID].RepoID) + } + } + }, + }, } type testIndexerCase struct { diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 5495c4a6ba..442e109843 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -121,6 +121,12 @@ func SearchIssues(ctx *context.APIContext) { // description: Number of items per page // type: integer // minimum: 0 + // - name: sort + // in: query + // description: Type of sort + // type: string + // enum: [relevance, latest, oldest, recentupdate, leastupdate, mostcomment, leastcomment, nearduedate, farduedate] + // default: latest // responses: // "200": // "$ref": "#/responses/IssueList" @@ -276,7 +282,7 @@ func SearchIssues(ctx *context.APIContext) { IsClosed: isClosed, IncludedAnyLabelIDs: includedAnyLabels, MilestoneIDs: includedMilestones, - SortBy: issue_indexer.SortByCreatedDesc, + SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc), } if since != 0 { @@ -305,9 +311,10 @@ func SearchIssues(ctx *context.APIContext) { } } - // FIXME: It's unsupported to sort by priority repo when searching by indexer, - // it's indeed an regression, but I think it is worth to support filtering by indexer first. - _ = ctx.FormInt64("priority_repo_id") + priorityRepoID := ctx.FormInt64("priority_repo_id") + if priorityRepoID > 0 { + searchOpt.PriorityRepoID = optional.Some(priorityRepoID) + } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) if err != nil { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 5e228507c0..a34e3b7c78 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2775,7 +2775,7 @@ func SearchIssues(ctx *context.Context) { IncludedAnyLabelIDs: includedAnyLabels, MilestoneIDs: includedMilestones, ProjectID: projectID, - SortBy: issue_indexer.SortByCreatedDesc, + SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc), } if since != 0 { @@ -2804,9 +2804,10 @@ func SearchIssues(ctx *context.Context) { } } - // FIXME: It's unsupported to sort by priority repo when searching by indexer, - // it's indeed an regression, but I think it is worth to support filtering by indexer first. - _ = ctx.FormInt64("priority_repo_id") + priorityRepoID := ctx.FormInt64("priority_repo_id") + if priorityRepoID > 0 { + searchOpt.PriorityRepoID = optional.Some(priorityRepoID) + } ids, total, err := issue_indexer.SearchIssues(ctx, searchOpt) if err != nil { @@ -2944,7 +2945,7 @@ func ListIssues(ctx *context.Context) { IsPull: isPull, IsClosed: isClosed, ProjectID: projectID, - SortBy: issue_indexer.SortByCreatedDesc, + SortBy: issue_indexer.ParseSortBy(ctx.FormString("sort"), issue_indexer.SortByCreatedDesc), } if since != 0 { searchOpt.UpdatedAfterUnix = optional.Some(since) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 59c13cd9e6..0e8382b8ab 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -4524,6 +4524,24 @@ "description": "Number of items per page", "name": "limit", "in": "query" + }, + { + "enum": [ + "relevance", + "latest", + "oldest", + "recentupdate", + "leastupdate", + "mostcomment", + "leastcomment", + "nearduedate", + "farduedate" + ], + "type": "string", + "default": "latest", + "description": "Type of sort", + "name": "sort", + "in": "query" } ], "responses": { diff --git a/tests/e2e/declare_repos_test.go b/tests/e2e/declare_repos_test.go index 351f7821eb..93f69faf4c 100644 --- a/tests/e2e/declare_repos_test.go +++ b/tests/e2e/declare_repos_test.go @@ -9,16 +9,23 @@ import ( "testing" "time" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/git" "forgejo.org/modules/indexer/stats" + "forgejo.org/modules/optional" + "forgejo.org/modules/timeutil" + issue_service "forgejo.org/services/issue" files_service "forgejo.org/services/repository/files" "forgejo.org/tests" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "xorm.io/xorm/convert" ) // first entry represents filename @@ -29,19 +36,34 @@ type FileChanges struct { Versions []string } +// performs additional repo setup as needed +type SetupRepo func(*user_model.User, *repo_model.Repository) + // put your Git repo declarations in here // feel free to amend the helper function below or use the raw variant directly func DeclareGitRepos(t *testing.T) func() { + now := timeutil.TimeStampNow() + postIssue := func(repo *repo_model.Repository, user *user_model.User, age int64, title, content string) { + issue := &issues_model.Issue{ + RepoID: repo.ID, + PosterID: user.ID, + Title: title, + Content: content, + CreatedUnix: now.Add(-age), + } + require.NoError(t, issue_service.NewIssue(db.DefaultContext, repo, issue, nil, nil, nil)) + } + cleanupFunctions := []func(){ - newRepo(t, 2, "diff-test", []FileChanges{{ + newRepo(t, 2, "diff-test", nil, []FileChanges{{ Filename: "testfile", Versions: []string{"hello", "hallo", "hola", "native", "ubuntu-latest", "- runs-on: ubuntu-latest", "- runs-on: debian-latest"}, - }}), - newRepo(t, 2, "language-stats-test", []FileChanges{{ + }}, nil), + newRepo(t, 2, "language-stats-test", nil, []FileChanges{{ Filename: "main.rs", Versions: []string{"fn main() {", "println!(\"Hello World!\");", "}"}, - }}), - newRepo(t, 2, "mentions-highlighted", []FileChanges{ + }}, nil), + newRepo(t, 2, "mentions-highlighted", nil, []FileChanges{ { Filename: "history1.md", Versions: []string{""}, @@ -52,11 +74,34 @@ func DeclareGitRepos(t *testing.T) func() { Versions: []string{""}, CommitMsg: "Another commit which mentions @user1 in the title\nand @user2 in the text", }, - }), - newRepo(t, 2, "unicode-escaping", []FileChanges{{ + }, nil), + newRepo(t, 2, "unicode-escaping", nil, []FileChanges{{ Filename: "a-file", Versions: []string{"{a}{а}"}, - }}), + }}, nil), + newRepo(t, 11, "dependency-test", &tests.DeclarativeRepoOptions{ + UnitConfig: optional.Some(map[unit_model.Type]convert.Conversion{ + unit_model.TypeIssues: &repo_model.IssuesConfig{ + EnableDependencies: true, + }, + }), + }, []FileChanges{}, func(user *user_model.User, repo *repo_model.Repository) { + postIssue(repo, user, 500, "first issue here", "an issue created earlier") + postIssue(repo, user, 400, "second issue here (not 1)", "not the right issue, but in the right repo") + postIssue(repo, user, 300, "third issue here", "depends on things") + postIssue(repo, user, 200, "unrelated issue", "shrug emoji") + postIssue(repo, user, 100, "newest issue", "very new") + }), + newRepo(t, 11, "dependency-test-2", &tests.DeclarativeRepoOptions{ + UnitConfig: optional.Some(map[unit_model.Type]convert.Conversion{ + unit_model.TypeIssues: &repo_model.IssuesConfig{ + EnableDependencies: true, + }, + }), + }, []FileChanges{}, func(user *user_model.User, repo *repo_model.Repository) { + postIssue(repo, user, 450, "right issue", "an issue containing word right") + postIssue(repo, user, 150, "left issue", "an issue containing word left") + }), // add your repo declarations here } @@ -67,12 +112,18 @@ func DeclareGitRepos(t *testing.T) func() { } } -func newRepo(t *testing.T, userID int64, repoName string, fileChanges []FileChanges) func() { +func newRepo(t *testing.T, userID int64, repoName string, initOpts *tests.DeclarativeRepoOptions, fileChanges []FileChanges, setup SetupRepo) func() { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}) - somerepo, _, cleanupFunc := tests.CreateDeclarativeRepo(t, user, repoName, - []unit_model.Type{unit_model.TypeCode, unit_model.TypeIssues}, nil, - nil, - ) + + opts := tests.DeclarativeRepoOptions{} + if initOpts != nil { + opts = *initOpts + } + opts.Name = optional.Some(repoName) + if !opts.EnabledUnits.Has() { + opts.EnabledUnits = optional.Some([]unit_model.Type{unit_model.TypeCode, unit_model.TypeIssues}) + } + somerepo, _, cleanupFunc := tests.CreateDeclarativeRepoWithOptions(t, user, opts) var lastCommitID string for _, file := range fileChanges { @@ -118,6 +169,10 @@ func newRepo(t *testing.T, userID int64, repoName string, fileChanges []FileChan } } + if setup != nil { + setup(user, somerepo) + } + err := stats.UpdateRepoIndexer(somerepo) require.NoError(t, err) diff --git a/tests/e2e/issue-sidebar.test.e2e.ts b/tests/e2e/issue-sidebar.test.e2e.ts index bc65b0842c..34885d0d5d 100644 --- a/tests/e2e/issue-sidebar.test.e2e.ts +++ b/tests/e2e/issue-sidebar.test.e2e.ts @@ -262,3 +262,91 @@ test('New Issue: Milestone', async ({page}, workerInfo) => { await expect(selectedMilestone).toContainText('No milestone'); await save_visual(page); }); + +test.describe('Dependency dropdown', () => { + test.use({user: 'user11'}); + test('Issue: Dependencies', async ({page}) => { + const response = await page.goto('/user11/dependency-test/issues/3'); + expect(response?.status()).toBe(200); + + const depsBlock = page.locator('.issue-content-right .depending'); + const deleteDepBtn = page.locator('.issue-content-right .depending .delete-dependency-button'); + + const input = page.locator('#new-dependency-drop-list .search'); + const current = page.locator('#new-dependency-drop-list .text').first(); + const menu = page.locator('#new-dependency-drop-list .menu'); + const items = page.locator('#new-dependency-drop-list .menu .item'); + + const confirmDelete = async () => { + const modal = page.locator('.modal.remove-dependency'); + await expect(modal).toBeVisible(); + await expect(modal).toContainText('This will remove the dependency from this issue'); + await modal.locator('button.ok').click(); + }; + + // A kludge to set the dropdown to the *wrong* value so it lets us select the correct one next. + const resetDropdown = async () => { + if (await current.textContent().then((s) => s.includes('#4'))) return; + await input.click(); + await input.fill('unrelated'); + await expect(items.first()).toContainText('unrelated'); + await items.first().click(); + await expect(current).toContainText('#4'); + await input.click(); + }; + + await expect(depsBlock).toBeVisible(); + while (await deleteDepBtn.first().isVisible()) { + await deleteDepBtn.first().click(); // wipe added dependencies from any previously failed tests + await confirmDelete(); + } + await expect(depsBlock).toContainText('No dependencies set'); + + await input.scrollIntoViewIfNeeded(); + await input.click(); + + const first = 'first issue here'; + const second = 'second issue here'; + const newest = 'newest issue'; + + // Without query, it should show issues in the same repo, sorted by date, except current one. + await expect(menu).toBeVisible(); + await expect(items).toHaveCount(4); // 5 issues in this repo, minus current one + await expect(items.first()).toContainText(newest); + await expect(items.last()).toContainText(first); + await resetDropdown(); + + // With query, it should search all repos, but show current repo issues first. + await input.fill('right'); + await expect(items.first()).toContainText(second); + await expect.poll(() => items.count()).toBeGreaterThan(1); // there is an issue in user11/dependency-test-2 containing the word "right" + await resetDropdown(); + + // When entering an issue number, it should always show that one first, then all text matches. + await input.fill('1'); + await expect(items.first()).toContainText(first); + await expect(items.nth(1)).toBeVisible(); + await resetDropdown(); + + // Should behave the same with a prefix + await input.fill('#1'); + await expect(items.first()).toContainText(first); + + // Selecting an issue + await items.first().click(); + await expect(current).toContainText(first); + + // Add dependency + const link = page.locator('.issue-content-right .depending .dependency a.title'); + await page.locator('.issue-content-right .depending button').click(); + await expect(link).toHaveAttribute('href', '/user11/dependency-test/issues/1'); + + // Remove dependency + await expect(deleteDepBtn).toBeVisible(); + await deleteDepBtn.click(); + + await confirmDelete(); + + await expect(depsBlock).toContainText('No dependencies set'); + }); +}); diff --git a/tests/e2e/utils_e2e_test.go b/tests/e2e/utils_e2e_test.go index e121c604c3..efa1657cee 100644 --- a/tests/e2e/utils_e2e_test.go +++ b/tests/e2e/utils_e2e_test.go @@ -93,6 +93,7 @@ func createSessions(t testing.TB) { users := []string{ "user1", "user2", + "user11", "user12", "user18", "user29", diff --git a/tests/test_utils.go b/tests/test_utils.go index 75d1f98914..b53159ae2c 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -42,6 +42,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "xorm.io/xorm/convert" ) func exitf(format string, args ...any) { @@ -342,6 +343,7 @@ type DeclarativeRepoOptions struct { Name optional.Option[string] EnabledUnits optional.Option[[]unit_model.Type] DisabledUnits optional.Option[[]unit_model.Type] + UnitConfig optional.Option[map[unit_model.Type]convert.Conversion] Files optional.Option[[]*files_service.ChangeRepoFile] WikiBranch optional.Option[string] AutoInit optional.Option[bool] @@ -390,9 +392,14 @@ func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts enabledUnits = make([]repo_model.RepoUnit, len(units)) for i, unitType := range units { + var config convert.Conversion + if cfg, ok := opts.UnitConfig.Value()[unitType]; ok { + config = cfg + } enabledUnits[i] = repo_model.RepoUnit{ RepoID: repo.ID, Type: unitType, + Config: config, } } } diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 297329d816..bf76453428 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -125,16 +125,21 @@ function excludeLabel(item) { export function initRepoIssueSidebarList() { const repolink = $('#repolink').val(); const repoId = $('#repoId').val(); - const crossRepoSearch = $('#crossRepoSearch').val(); + const crossRepoSearch = $('#crossRepoSearch').val() === 'true'; const tp = $('#type').val(); - let issueSearchUrl = `${appSubUrl}/${repolink}/issues/search?q={query}&type=${tp}`; - if (crossRepoSearch === 'true') { - issueSearchUrl = `${appSubUrl}/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`; - } $('#new-dependency-drop-list') .dropdown({ apiSettings: { - url: issueSearchUrl, + beforeSend(settings) { + if (!settings.urlData.query.trim()) { + settings.url = `${appSubUrl}/${repolink}/issues/search?q={query}&type=${tp}&sort=updated`; + } else if (crossRepoSearch) { + settings.url = `${appSubUrl}/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}&sort=relevance`; + } else { + settings.url = `${appSubUrl}/${repolink}/issues/search?q={query}&type=${tp}&sort=relevance`; + } + return settings; + }, onResponse(response) { const filteredResponse = {success: true, results: []}; const currIssueId = $('#new-dependency-drop-list').data('issue-id'); @@ -142,7 +147,7 @@ export function initRepoIssueSidebarList() { for (const [_, issue] of Object.entries(response)) { // Don't list current issue in the dependency list. if (issue.id === currIssueId) { - return; + continue; } filteredResponse.results.push({ name: `#${issue.number} ${issueTitleHTML(htmlEscape(issue.title)) From 7ad20a2730be25112e51f79d982ec5d34fa386bc Mon Sep 17 00:00:00 2001 From: oliverpool Date: Fri, 27 Jun 2025 11:22:10 +0200 Subject: [PATCH 014/297] git/blob: GetContentBase64 with fewer allocations and no goroutine (#8297) See #8222 for context.i `GetBlobContentBase64` was using a pipe and a goroutine to read the blob content as base64. This can be replace by a pre-allocated buffer and a direct copy. Note that although similar to `GetBlobContent`, it does not truncate the content if the blob size is over the limit (but returns an error). I think that `GetBlobContent` should adopt the same behavior at some point (error instead of truncating). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [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. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8297 Reviewed-by: Earl Warren Co-authored-by: oliverpool Co-committed-by: oliverpool --- modules/git/blob.go | 51 +++++++++++++++++----------- modules/git/blob_test.go | 18 ++++++++++ routers/api/v1/repo/wiki.go | 8 ++--- services/repository/files/content.go | 11 +++--- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/modules/git/blob.go b/modules/git/blob.go index 30615afe32..14ca2b1445 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "encoding/base64" + "fmt" "io" "forgejo.org/modules/log" @@ -172,33 +173,43 @@ func (b *Blob) GetBlobContent(limit int64) (string, error) { return string(buf), err } -// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string -func (b *Blob) GetBlobContentBase64() (string, error) { - dataRc, err := b.DataAsync() - if err != nil { - return "", err - } - defer dataRc.Close() +type BlobTooLargeError struct { + Size, Limit int64 +} - pr, pw := io.Pipe() - encoder := base64.NewEncoder(base64.StdEncoding, pw) +func (b BlobTooLargeError) Error() string { + return fmt.Sprintf("blob: content larger than limit (%d > %d)", b.Size, b.Limit) +} - go func() { - _, err := io.Copy(encoder, dataRc) - _ = encoder.Close() - - if err != nil { - _ = pw.CloseWithError(err) - } else { - _ = pw.Close() +// GetContentBase64 Reads the content of the blob and returns it as base64 encoded string. +// Returns [BlobTooLargeError] if the (unencoded) content is larger than the limit. +func (b *Blob) GetContentBase64(limit int64) (string, error) { + if b.Size() > limit { + return "", BlobTooLargeError{ + Size: b.Size(), + Limit: limit, } - }() + } - out, err := io.ReadAll(pr) + rc, size, err := b.NewTruncatedReader(limit) if err != nil { return "", err } - return string(out), nil + defer rc.Close() + + encoding := base64.StdEncoding + buf := bytes.NewBuffer(make([]byte, 0, encoding.EncodedLen(int(size)))) + + encoder := base64.NewEncoder(encoding, buf) + + if _, err := io.Copy(encoder, rc); err != nil { + return "", err + } + if err := encoder.Close(); err != nil { + return "", err + } + + return buf.String(), nil } // GuessContentType guesses the content type of the blob. diff --git a/modules/git/blob_test.go b/modules/git/blob_test.go index 54115013d3..a4b8033941 100644 --- a/modules/git/blob_test.go +++ b/modules/git/blob_test.go @@ -63,6 +63,24 @@ func TestBlob(t *testing.T) { require.Equal(t, "file2\n", r) }) + t.Run("GetContentBase64", func(t *testing.T) { + r, err := testBlob.GetContentBase64(100) + require.NoError(t, err) + require.Equal(t, "ZmlsZTIK", r) + + r, err = testBlob.GetContentBase64(-1) + require.ErrorAs(t, err, &BlobTooLargeError{}) + require.Empty(t, r) + + r, err = testBlob.GetContentBase64(4) + require.ErrorAs(t, err, &BlobTooLargeError{}) + require.Empty(t, r) + + r, err = testBlob.GetContentBase64(6) + require.NoError(t, err) + require.Equal(t, "ZmlsZTIK", r) + }) + t.Run("NewTruncatedReader", func(t *testing.T) { // read fewer than available rc, size, err := testBlob.NewTruncatedReader(100) diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index bb4cf0f211..7b6a00408a 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -5,6 +5,7 @@ package repo import ( "encoding/base64" + "errors" "fmt" "net/http" "net/url" @@ -506,11 +507,8 @@ func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) // given tree entry, encoded with base64. Writes to ctx if an error occurs. func wikiContentsByEntry(ctx *context.APIContext, entry *git.TreeEntry) string { blob := entry.Blob() - if blob.Size() > setting.API.DefaultMaxBlobSize { - return "" - } - content, err := blob.GetBlobContentBase64() - if err != nil { + content, err := blob.GetContentBase64(setting.API.DefaultMaxBlobSize) + if err != nil && !errors.As(err, &git.BlobTooLargeError{}) { ctx.Error(http.StatusInternalServerError, "GetBlobContentBase64", err) return "" } diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 3d2217df18..dfdee1d1df 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -5,6 +5,7 @@ package files import ( "context" + "errors" "fmt" "net/url" "path" @@ -273,13 +274,11 @@ func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git if err != nil { return nil, err } - content := "" - if gitBlob.Size() <= setting.API.DefaultMaxBlobSize { - content, err = gitBlob.GetBlobContentBase64() - if err != nil { - return nil, err - } + content, err := gitBlob.GetContentBase64(setting.API.DefaultMaxBlobSize) + if err != nil && !errors.As(err, &git.BlobTooLargeError{}) { + return nil, err } + return &api.GitBlob{ SHA: gitBlob.ID.String(), URL: repo.APIURL() + "/git/blobs/" + url.PathEscape(gitBlob.ID.String()), From a2e7446fe7785b2299118b6baf4b89a5821001b8 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 27 Jun 2025 11:44:59 +0200 Subject: [PATCH 015/297] chore: use eventually for mysql collation test (#8301) - Regression of removing `time.Sleep(5 * time.Second)` in forgejo/forgejo#7917. - Ref: https://codeberg.org/forgejo/forgejo/issues/8221#issuecomment-5532035 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8301 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- tests/integration/db_collation_test.go | 29 +++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/integration/db_collation_test.go b/tests/integration/db_collation_test.go index bf55bdd8ee..c41209f1fe 100644 --- a/tests/integration/db_collation_test.go +++ b/tests/integration/db_collation_test.go @@ -7,6 +7,7 @@ package integration import ( "net/http" "testing" + "time" "forgejo.org/models/db" "forgejo.org/modules/setting" @@ -97,9 +98,13 @@ func TestDatabaseCollation(t *testing.T) { defer test.MockVariableValue(&setting.Database.CharsetCollation, "utf8mb4_bin")() require.NoError(t, db.ConvertDatabaseTable()) - r, err := db.CheckCollations(x) - require.NoError(t, err) - assert.Equal(t, "utf8mb4_bin", r.DatabaseCollation) + var r *db.CheckCollationsResult + assert.Eventually(t, func() bool { + r, err = db.CheckCollations(x) + require.NoError(t, err) + + return r.DatabaseCollation == "utf8mb4_bin" + }, time.Second*30, time.Second) assert.True(t, r.CollationEquals(r.ExpectedCollation, r.DatabaseCollation)) assert.Empty(t, r.InconsistentCollationColumns) @@ -117,9 +122,13 @@ func TestDatabaseCollation(t *testing.T) { defer test.MockVariableValue(&setting.Database.CharsetCollation, "utf8mb4_general_ci")() require.NoError(t, db.ConvertDatabaseTable()) - r, err := db.CheckCollations(x) - require.NoError(t, err) - assert.Equal(t, "utf8mb4_general_ci", r.DatabaseCollation) + var r *db.CheckCollationsResult + assert.Eventually(t, func() bool { + r, err = db.CheckCollations(x) + require.NoError(t, err) + + return r.DatabaseCollation == "utf8mb4_general_ci" + }, time.Second*30, time.Second) assert.True(t, r.CollationEquals(r.ExpectedCollation, r.DatabaseCollation)) assert.Empty(t, r.InconsistentCollationColumns) @@ -137,9 +146,15 @@ func TestDatabaseCollation(t *testing.T) { defer test.MockVariableValue(&setting.Database.CharsetCollation, "")() require.NoError(t, db.ConvertDatabaseTable()) + var r *db.CheckCollationsResult r, err := db.CheckCollations(x) require.NoError(t, err) - assert.True(t, r.IsCollationCaseSensitive(r.DatabaseCollation)) + assert.Eventually(t, func() bool { + r, err = db.CheckCollations(x) + require.NoError(t, err) + + return r.IsCollationCaseSensitive(r.DatabaseCollation) + }, time.Second*30, time.Second) assert.True(t, r.CollationEquals(r.ExpectedCollation, r.DatabaseCollation)) assert.Empty(t, r.InconsistentCollationColumns) }) From 1b9ac275785e67148305a731b0cee6e33c7bb007 Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Mon, 23 Jun 2025 23:00:32 +0200 Subject: [PATCH 016/297] feat(ui): add issue comment assignment doer links --- modules/templates/helper.go | 1 + modules/templates/util_render.go | 28 +++++++++++++------ modules/templates/util_render_test.go | 26 +++++++++++++++++ .../repo/issue/view_content/comments.tmpl | 22 ++++++--------- tests/integration/issue_comment_test.go | 6 ++-- 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 02b175e6f6..42b4bad83c 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -188,6 +188,7 @@ func NewFuncMap() template.FuncMap { "RenderMarkdownToHtml": RenderMarkdownToHtml, "RenderLabel": RenderLabel, "RenderLabels": RenderLabels, + "RenderUser": RenderUser, "RenderReviewRequest": RenderReviewRequest, // ----------------------------------------------------------------- diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index a4d7a82eea..48f4eb04a3 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -7,6 +7,7 @@ import ( "context" "encoding/hex" "fmt" + "html" "html/template" "math" "net/url" @@ -15,6 +16,7 @@ import ( "unicode" issues_model "forgejo.org/models/issues" + user_model "forgejo.org/models/user" "forgejo.org/modules/emoji" "forgejo.org/modules/log" "forgejo.org/modules/markup" @@ -26,7 +28,7 @@ import ( // RenderCommitMessage renders commit message with XSS-safe and special links. func RenderCommitMessage(ctx context.Context, msg string, metas map[string]string) template.HTML { - cleanMsg := template.HTMLEscapeString(msg) + cleanMsg := html.EscapeString(msg) // we can safely assume that it will not return any error, since there // shouldn't be any special HTML. fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ @@ -63,7 +65,7 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlDefault string, Ctx: ctx, DefaultLink: urlDefault, Metas: metas, - }, template.HTMLEscapeString(msgLine)) + }, html.EscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessageSubject: %v", err) return template.HTML("") @@ -88,7 +90,7 @@ func RenderCommitBody(ctx context.Context, msg string, metas map[string]string) renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ Ctx: ctx, Metas: metas, - }, template.HTMLEscapeString(msgLine)) + }, html.EscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessage: %v", err) return "" @@ -122,7 +124,7 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{ Ctx: ctx, Metas: metas, - }, template.HTMLEscapeString(text)) + }, html.EscapeString(text)) if err != nil { log.Error("RenderIssueTitle: %v", err) return template.HTML("") @@ -132,7 +134,7 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) // RenderRefIssueTitle renders referenced issue/pull title with defined post processors func RenderRefIssueTitle(ctx context.Context, text string) template.HTML { - renderedText, err := markup.RenderRefIssueTitle(&markup.RenderContext{Ctx: ctx}, template.HTMLEscapeString(text)) + renderedText, err := markup.RenderRefIssueTitle(&markup.RenderContext{Ctx: ctx}, html.EscapeString(text)) if err != nil { log.Error("RenderRefIssueTitle: %v", err) return "" @@ -150,7 +152,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m labelScope = label.ExclusiveScope() ) - description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) + description := emoji.ReplaceAliases(html.EscapeString(label.Description)) if label.IsArchived() { archivedCSSClass = "archived-label" @@ -212,7 +214,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m // RenderEmoji renders html text with emoji post processors func RenderEmoji(ctx context.Context, text string) template.HTML { renderedText, err := markup.RenderEmoji(&markup.RenderContext{Ctx: ctx}, - template.HTMLEscapeString(text)) + html.EscapeString(text)) if err != nil { log.Error("RenderEmoji: %v", err) return template.HTML("") @@ -263,10 +265,20 @@ func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issu return template.HTML(htmlCode) } +func RenderUser(ctx context.Context, user user_model.User) template.HTML { + if user.ID > 0 { + return template.HTML(fmt.Sprintf( + "%s", + user.HomeLink(), html.EscapeString(user.GetDisplayName()))) + } + return template.HTML(fmt.Sprintf("%s", + html.EscapeString(user.GetDisplayName()))) +} + func RenderReviewRequest(users []issues_model.RequestReviewTarget) template.HTML { usernames := make([]string, 0, len(users)) for _, user := range users { - usernames = append(usernames, template.HTMLEscapeString(user.Name())) + usernames = append(usernames, html.EscapeString(user.Name())) } htmlCode := `` diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index b75b061218..00543a1b33 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -11,6 +11,9 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" "forgejo.org/modules/translation" "github.com/stretchr/testify/assert" @@ -221,3 +224,26 @@ func TestRenderLabels(t *testing.T) { assert.Contains(t, RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", true), "user2/repo1/pulls?labels=1") } + +func TestRenderUser(t *testing.T) { + unittest.PrepareTestEnv(t) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) + ghost := user_model.NewGhostUser() + + assert.Contains(t, RenderUser(db.DefaultContext, *user), + "user2") + assert.Contains(t, RenderUser(db.DefaultContext, *org), + "org3") + assert.Contains(t, RenderUser(db.DefaultContext, *ghost), + "Ghost") + + defer test.MockVariableValue(&setting.UI.DefaultShowFullName, true)() + assert.Contains(t, RenderUser(db.DefaultContext, *user), + "< U<se>r Tw<o > ><") + assert.Contains(t, RenderUser(db.DefaultContext, *org), + "<<<< >> >> > >> > >>> >>") + assert.Contains(t, RenderUser(db.DefaultContext, *ghost), + "Ghost") +} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index aca77a92e5..53a64b9b41 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -197,27 +197,23 @@ {{else if and (eq .Type 9) (gt .AssigneeID 0)}}
{{svg "octicon-person"}} - {{if .RemovedAssignee}} - {{template "shared/user/avatarlink" dict "user" .Assignee}} - - {{template "shared/user/authorlink" .Assignee}} + {{template "shared/user/avatarlink" dict "user" .Assignee}} + + {{template "shared/user/authorlink" .Assignee}} + {{if .RemovedAssignee}} {{if eq .Poster.ID .Assignee.ID}} {{ctx.Locale.Tr "repo.issues.remove_self_assignment" $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.remove_assignee_at" .Poster.GetDisplayName $createdStr}} + {{ctx.Locale.Tr "repo.issues.remove_assignee_at" (RenderUser $.Context .Poster) $createdStr}} {{end}} - - {{else}} - {{template "shared/user/avatarlink" dict "user" .Assignee}} - - {{template "shared/user/authorlink" .Assignee}} + {{else}} {{if eq .Poster.ID .AssigneeID}} {{ctx.Locale.Tr "repo.issues.self_assign_at" $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_assignee_at" .Poster.GetDisplayName $createdStr}} + {{ctx.Locale.Tr "repo.issues.add_assignee_at" (RenderUser $.Context .Poster) $createdStr}} {{end}} - - {{end}} + {{end}} +
{{else if eq .Type 10}}
diff --git a/tests/integration/issue_comment_test.go b/tests/integration/issue_comment_test.go index ee62b418f4..70a60cd402 100644 --- a/tests/integration/issue_comment_test.go +++ b/tests/integration/issue_comment_test.go @@ -205,15 +205,13 @@ func TestIssueCommentChangeAssignee(t *testing.T) { testIssueCommentChangeEvent(t, htmlDoc, "2041", "octicon-person", "User One", "/user1", []string{"user1 was unassigned by user2"}, - []string{"/user1"}) - // []string{"/user1", "/user2"}) + []string{"/user1", "/user2"}) // Add other testIssueCommentChangeEvent(t, htmlDoc, "2042", "octicon-person", "< Ur Tw ><", "/user2", []string{"user2 was assigned by user1"}, - []string{"/user2"}) - // []string{"/user2", "/user1"}) + []string{"/user2", "/user1"}) // Self-remove testIssueCommentChangeEvent(t, htmlDoc, "2043", From 76b3f4cd6ad1a692199eec90db7c8b7985708d9d Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Fri, 27 Jun 2025 13:27:06 +0200 Subject: [PATCH 017/297] test: prevent XSS for label rendering --- models/fixtures/label.yml | 24 ++++++++++++++++++++++++ modules/templates/util_render_test.go | 27 +++++++++++++++++++++++---- services/convert/issue_test.go | 9 +++++---- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/models/fixtures/label.yml b/models/fixtures/label.yml index acfac74968..84c2a7f418 100644 --- a/models/fixtures/label.yml +++ b/models/fixtures/label.yml @@ -3,6 +3,7 @@ repo_id: 1 org_id: 0 name: label1 + description: 'First label' color: '#abcdef' exclusive: false num_issues: 2 @@ -107,3 +108,26 @@ num_issues: 0 num_closed_issues: 0 archived_unix: 0 + +- + id: 11 + repo_id: 3 + org_id: 0 + name: " /'?&" + description: "Malicious label ' " + color: '#000000' + exclusive: true + num_issues: 0 + num_closed_issues: 0 + archived_unix: 0 + +- + id: 12 + repo_id: 3 + org_id: 0 + name: 'archived label<>' + color: '#000000' + exclusive: false + num_issues: 0 + num_closed_issues: 0 + archived_unix: 2991092130 diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 00543a1b33..5974c34073 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -218,11 +218,30 @@ func TestRenderLabels(t *testing.T) { tr := &translation.MockLocale{} label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) + labelScoped := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 7}) + labelMalicious := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 11}) + labelArchived := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 12}) - assert.Contains(t, RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", false), - "user2/repo1/issues?labels=1") - assert.Contains(t, RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", true), - "user2/repo1/pulls?labels=1") + rendered := RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=1") + assert.Contains(t, rendered, ">label1<") + assert.Contains(t, rendered, "title='First label'") + rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", true) + assert.Contains(t, rendered, "user2/repo1/pulls?labels=1") + assert.Contains(t, rendered, ">label1<") + rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{labelScoped}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=7") + assert.Contains(t, rendered, ">scope<") + assert.Contains(t, rendered, ">label1<") + rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{labelMalicious}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=11") + assert.Contains(t, rendered, "> <script>malicious</script> <") + assert.Contains(t, rendered, ">'?&<") + assert.Contains(t, rendered, "title='Malicious label ' <script>malicious</script>'") + rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{labelArchived}, "user2/repo1", false) + assert.Contains(t, rendered, "user2/repo1/issues?labels=12") + assert.Contains(t, rendered, ">archived label<><") + assert.Contains(t, rendered, "title='repo.issues.archived_label_description'") } func TestRenderUser(t *testing.T) { diff --git a/services/convert/issue_test.go b/services/convert/issue_test.go index 97bacfb229..ea8ad9b7ef 100644 --- a/services/convert/issue_test.go +++ b/services/convert/issue_test.go @@ -24,10 +24,11 @@ func TestLabel_ToLabel(t *testing.T) { label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID}) assert.Equal(t, &api.Label{ - ID: label.ID, - Name: label.Name, - Color: "abcdef", - URL: fmt.Sprintf("%sapi/v1/repos/user2/repo1/labels/%d", setting.AppURL, label.ID), + ID: label.ID, + Name: label.Name, + Color: "abcdef", + Description: label.Description, + URL: fmt.Sprintf("%sapi/v1/repos/user2/repo1/labels/%d", setting.AppURL, label.ID), }, ToLabel(label, repo, nil)) } From aee161ff255f29ce57155fddca944360779c8a36 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 27 Jun 2025 13:59:07 +0200 Subject: [PATCH 018/297] [gitea] week 2025-19 cherry pick (gitea/main -> forgejo) (#7909) ## Checklist - [x] go to the last cherry-pick PR (forgejo/forgejo#7804) to figure out how far it went: [gitea@a2024953c5](https://github.com/go-gitea/gitea/commit/a2024953c5914c5a7d59d236262f9bd94b65b996) - [x] cherry-pick and open PR (forgejo/forgejo#7909) - [ ] have the PR pass the CI - end-to-end (specially important if there are actions related changes) - [ ] add `run-end-to-end` label - [ ] check the result - [ ] write release notes - [ ] assign reviewers - [ ] 48h later, last call - merge 1 hour after the last call ## Legend - :question: - No decision about the commit has been made. - :cherries: - The commit has been cherry picked. - :fast_forward: - The commit has been skipped. - :bulb: - The commit has been skipped, but should be ported to Forgejo. - :writing_hand: - The commit has been skipped, and a port to Forgejo already exists. ## Commits - :cherries: [`gitea`](https://github.com/go-gitea/gitea/commit/e92c4f18083ed312b69591ebb77e0f504ee77025) -> [`forgejo`](https://codeberg.org/forgejo/forgejo/commit/56fa2caef32c4b0e5017f4b09188ad1dfc8d3603) Add missing setting load in dump-repo command ([gitea#34479](https://github.com/go-gitea/gitea/pull/34479)) - :cherries: [`gitea`](https://github.com/go-gitea/gitea/commit/7b518bc6c79035a53c0b752680d833fce5e1a2fe) -> [`forgejo`](https://codeberg.org/forgejo/forgejo/commit/6e5299606a1bd42cb45ed472a84ba797cf2fa790) Change "rejected" to "changes requested" in 3rd party PR review notification ([gitea#34481](https://github.com/go-gitea/gitea/pull/34481)) ## TODO - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/972381097c6b3488bf9d5c3c4fde04775b2e3a3c) Fix url validation in webhook add/edit API ([gitea#34492](https://github.com/go-gitea/gitea/pull/34492)) Relevant input validation but test needs more backport. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/59df03b5542f05a1a927996fbf6483f7da363e03) Fix get / delete runner to use consistent http 404 and 500 status ([gitea#34480](https://github.com/go-gitea/gitea/pull/34480)) It may be relevant to Forgejo as well ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/1e2f3514b9afd903e19a0413e5d925515e13abf8) Add endpoint deleting workflow run ([gitea#34337](https://github.com/go-gitea/gitea/pull/34337)) Actions, it would be worth having in Forgejo as well. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/5cb4cbf044e2f0483afc92516bb4b9aff6ea2b9a) Fix repo broken check ([gitea#34444](https://github.com/go-gitea/gitea/pull/34444)) Check wether this is relevant to us, port if yes. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/355e9a9d544aa2d3f3a17b06cdb2bf1ceb290fd7) Add a webhook push test for dev branch ([gitea#34421](https://github.com/go-gitea/gitea/pull/34421)) Enhances webhook integration tests. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/34281bc198a5ad9a1faa5285d4648b05d7218aaa) Fix bug webhook milestone is not right. ([gitea#34419](https://github.com/go-gitea/gitea/pull/34419)) Testcode diverged, port required. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/780e92ea99646dfefbe11734a4845fbf304be83c) Only git operations should update `last changed` of a repository ([gitea#34388](https://github.com/go-gitea/gitea/pull/34388)) Port required, would benefit from additional tests. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/b07e03956af8f29464067b19cb5cacee358b592f) When updating comment, if the content is the same, just return and not update the databse ([gitea#34422](https://github.com/go-gitea/gitea/pull/34422)) Codebase diverged, port required. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/71a11872091634f1370374ef123d32798ec0447d) Fix incorrect divergence cache after switching default branch ([gitea#34370](https://github.com/go-gitea/gitea/pull/34370)) Depends on previous gitea changes, port needed. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/4c611bf280c501c22c6a58e94d9e3ce6a73214df) Add a button editing action secret ([gitea#34348](https://github.com/go-gitea/gitea/pull/34348)) This is an interesting feature and it has tests as well. Feature request covering this: https://codeberg.org/forgejo/forgejo/issues/7882 ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/2fbc8f9e87fc37f21997bf32d9b29fc16e92780c) Fix LFS file not stored in LFS when uploaded/edited via API or web UI ([gitea#34367](https://github.com/go-gitea/gitea/pull/34367)) Our code diverged - pls. check relevance & maybe port. ------ - :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/020e774b915512815637aac743ddbe595c5eede5) feat: add label 'state' to metric 'gitea_users' ([gitea#34326](https://github.com/go-gitea/gitea/pull/34326)) Adjust our existing tests while porting this. ------ ## Skipped - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/ec10c6ba5a6c4c3a5ab9bdf47616d6058c6fb59c) [skip ci] Updated translations via Crowdin ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/d89eed998f9e4dc84d0ef02451703590d7a8ef51) Fix edithook api can not update package, status and workflow_job events ([gitea#34495](https://github.com/go-gitea/gitea/pull/34495)) - gitea actions specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/b6c066747400927407a6b4807165d2de40c7495b) Add R-HNF to the TRANSLATORS file ([gitea#34494](https://github.com/go-gitea/gitea/pull/34494)) - gitea translators update specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/6fbf0e67383dd2970d8e6c309ebc5c634732866c) nix flake update ([gitea#34476](https://github.com/go-gitea/gitea/pull/34476)) - gitea dependency update specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/c24f4b3d2912224164ee3a75850a6a31bdd041f1) Add migrations tests ([gitea#34456](https://github.com/go-gitea/gitea/pull/34456)) ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/bf338bb9e231a8f9ccef7de2e13a0fdf871fc680) Fix project board view ([gitea#34470](https://github.com/go-gitea/gitea/pull/34470)) - gitea ui specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/319d03fbc049de34a6fa0bc3019107ec5b724ff2) [skip ci] Updated translations via Crowdin ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/dd500ce5598848e4f50999b627155ec8520932d3) Fix Workflow run Not Found page ([gitea#34459](https://github.com/go-gitea/gitea/pull/34459)) - gitea actions specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/b6bf128f1e1a943df85c1ce276d0317c8689ffca) [skip ci] Updated translations via Crowdin ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/a0595add72db4a5fb421579b9c6bb7dae1392c86) Fix remove org user failure on mssql ([gitea#34449](https://github.com/go-gitea/gitea/pull/34449)) ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/b5fd3e7210cfbcb87015f3a5b8c3f25eab2a5715) Fix comment textarea scroll issue in Firefox ([gitea#34438](https://github.com/go-gitea/gitea/pull/34438)) - gitea ui specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/4011e2245bcd96f53077f73b7a33b1a754f7151f) Fix releases sidebar navigation link ([gitea#34436](https://github.com/go-gitea/gitea/pull/34436)) - gitea ui specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/0902d42fc753cd5f266046f003307285fe9507d5) [skip ci] Updated translations via Crowdin ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/4a98ab05403ef1900937487d434bc075812b0303) Remove legacy template helper functions ([gitea#34426](https://github.com/go-gitea/gitea/pull/34426)) - gitea specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/9b8609e017aef8376eb59d9fd3e428e35f9caeda) Fix GetUsersByEmails ([gitea#34423](https://github.com/go-gitea/gitea/pull/34423)) - gitea specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/0f63a5ef48b23c6ab26a4b13cfd26edbe4efbfa3) [skip ci] Updated translations via Crowdin ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/ad271444e912ddf44591451292b39b0d6b859955) Fix a bug when uploading file via lfs ssh command ([gitea#34408](https://github.com/go-gitea/gitea/pull/34408)) :skiP: present with PR #7752 ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/8b16ab719cab24805beb2189af7ee960ca94d524) Merge and tweak markup editor expander CSS ([gitea#34409](https://github.com/go-gitea/gitea/pull/34409)) - gitea ui specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/2ecd73d2e586cd4ff2001246d54c4affe0e1ccec) Bump `@github/relative-time-element` to v4.4.8 ([gitea#34413](https://github.com/go-gitea/gitea/pull/34413)) - gitea dependency update specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/179068fddbb463f3a34162730649d82acec522d3) Refactor commit message rendering and fix bugs ([gitea#34412](https://github.com/go-gitea/gitea/pull/34412)) - gitea ui specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/44aadc37c9c0810f3a41189929ae21c613b6bc98) [skip ci] Updated translations via Crowdin ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/f63822fe64b0759dc7e38b467eaa7c41b71d8c5d) Fix autofocus behavior ([gitea#34397](https://github.com/go-gitea/gitea/pull/34397)) - gitea ui specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/82071ee7300d478f56519ec30be0213b18a7882c) [skip ci] Updated translations via Crowdin ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/bbfc21e74f69a9b1ac83271923210c731edd2873) Fix "The sidebar of the repository file list does not have a fixed height #34298" ([gitea#34321](https://github.com/go-gitea/gitea/pull/34321)) - gitea ui specific specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/dd886d729f6206ad878aa5e0f0f3d4d20ea9a208) Update JS and PY dependencies ([gitea#34391](https://github.com/go-gitea/gitea/pull/34391)) - gitea dependency update specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/2a660b4a1b6913f80981d8840b3dc129a4eb7f26) Upgrade go-github v61 -> v71 ([gitea#34385](https://github.com/go-gitea/gitea/pull/34385)) - gitea dependency update specific ------ - :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/6bd8fe53537172fb4df976962115f1b928bf2993) Bump `@github/relative-time-element` to v4.4.7 ([gitea#34384](https://github.com/go-gitea/gitea/pull/34384)) - gitea dependency update specific ------

Stats


Between [`gitea@a2024953c5`](https://github.com/go-gitea/gitea/commit/a2024953c5914c5a7d59d236262f9bd94b65b996) and [`gitea@ec10c6ba5a`](https://github.com/go-gitea/gitea/commit/ec10c6ba5a6c4c3a5ab9bdf47616d6058c6fb59c), **41** commits have been reviewed. We picked **2**, skipped **27**, and decided to port **12**.
Co-authored-by: Sebastian Weigand Co-authored-by: Lunny Xiao Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7909 Reviewed-by: Earl Warren Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- cmd/dump_repo.go | 5 +++++ services/webhook/discord.go | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go index eb89273e7f..7159d55e99 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -82,6 +82,11 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme } func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error { + setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr) + + // setting.DisableLoggerInit() + setting.LoadSettings() // cannot access skip_tls_verify settings otherwise + stdCtx, cancel := installSignals(stdCtx) defer cancel() diff --git a/services/webhook/discord.go b/services/webhook/discord.go index db98d40583..7259c4a995 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -350,7 +350,7 @@ func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, case webhook_module.HookEventPullRequestReviewApproved: return "approved", nil case webhook_module.HookEventPullRequestReviewRejected: - return "rejected", nil + return "requested changes", nil case webhook_module.HookEventPullRequestReviewComment: return "comment", nil default: From 3fb6e171051c4946b93a38bbb5212d3245d485ef Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 27 Jun 2025 15:29:44 +0200 Subject: [PATCH 019/297] fix: add missing trust status to pull review commits (#8296) Closes: #8293 ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8296 Reviewed-by: Earl Warren Co-authored-by: Lucas Schwiderski Co-committed-by: Lucas Schwiderski --- routers/web/repo/pull.go | 7 ++ tests/integration/pull_commit_test.go | 93 +++++++++++++++++++++++++++ tests/integration/repo_test.go | 78 ++++++++++++++++++++++ 3 files changed, 178 insertions(+) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index fd18646211..4e365f24ea 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -996,6 +996,13 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi ctx.Data["Verification"] = verification ctx.Data["Author"] = user_model.ValidateCommitWithEmail(ctx, curCommit) + if err := asymkey_model.CalculateTrustStatus(verification, ctx.Repo.Repository.GetTrustModel(), func(user *user_model.User) (bool, error) { + return repo_model.IsOwnerMemberCollaborator(ctx, ctx.Repo.Repository, user.ID) + }, nil); err != nil { + ctx.ServerError("CalculateTrustStatus", err) + return + } + note := &git.Note{} err = git.GetNote(ctx, ctx.Repo.GitRepo, specifiedEndCommit, note) if err == nil { diff --git a/tests/integration/pull_commit_test.go b/tests/integration/pull_commit_test.go index 8ca78f8147..1de437ef46 100644 --- a/tests/integration/pull_commit_test.go +++ b/tests/integration/pull_commit_test.go @@ -4,13 +4,26 @@ package integration import ( + "context" + "encoding/base64" + "fmt" "net/http" + "net/url" + "os" "testing" + auth_model "forgejo.org/models/auth" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + "forgejo.org/modules/setting" + api "forgejo.org/modules/structs" + "forgejo.org/modules/test" pull_service "forgejo.org/services/pull" "forgejo.org/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestListPullCommits(t *testing.T) { @@ -48,3 +61,83 @@ func TestPullCommitLinks(t *testing.T) { commitLinkHref, _ := commitLink.Attr("href") assert.Equal(t, "/user2/repo1/pulls/3/commits/5f22f7d0d95d614d25a5b68592adb345a4b5c7fd", commitLinkHref) } + +func TestPullCommitSignature(t *testing.T) { + t.Cleanup(func() { + // Cannot use t.Context(), it is in the done state. + require.NoError(t, git.InitFull(context.Background())) //nolint:usetesting + }) + + defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "UwU")() + defer test.MockVariableValue(&setting.Repository.Signing.SigningEmail, "fox@example.com")() + defer test.MockVariableValue(&setting.Repository.Signing.CRUDActions, []string{"always"})() + defer test.MockVariableValue(&setting.Repository.Signing.InitialCommit, []string{"always"})() + + filePath := "signed.txt" + fromBranch := "master" + toBranch := "branch-signed" + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + // Use a new GNUPGPHOME to avoid messing with the existing GPG keyring. + tmpDir := t.TempDir() + require.NoError(t, os.Chmod(tmpDir, 0o700)) + t.Setenv("GNUPGHOME", tmpDir) + + rootKeyPair, err := importTestingKey() + require.NoError(t, err) + defer test.MockVariableValue(&setting.Repository.Signing.SigningKey, rootKeyPair.PrimaryKey.KeyIdShortString())() + defer test.MockVariableValue(&setting.Repository.Signing.Format, "openpgp")() + + // Ensure the git config is updated with the new signing format. + require.NoError(t, git.InitFull(t.Context())) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + testCtx := NewAPITestContext(t, user.Name, "pull-request-commit-header-signed", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + u.Path = testCtx.GitPath() + + t.Run("Create repository", doAPICreateRepository(testCtx, false, git.Sha1ObjectFormat)) + + t.Run("Create commit", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + options := &api.CreateFileOptions{ + FileOptions: api.FileOptions{ + BranchName: fromBranch, + NewBranchName: toBranch, + Message: fmt.Sprintf("from:%s to:%s path:%s", fromBranch, toBranch, filePath), + Author: api.Identity{ + Name: user.FullName, + Email: user.Email, + }, + Committer: api.Identity{ + Name: user.FullName, + Email: user.Email, + }, + }, + ContentBase64: base64.StdEncoding.EncodeToString(fmt.Appendf(nil, "This is new text for %s", filePath)), + } + + req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", testCtx.Username, testCtx.Reponame, filePath), &options). + AddTokenAuth(testCtx.Token) + resp := testCtx.Session.MakeRequest(t, req, http.StatusCreated) + + var contents api.FileResponse + DecodeJSON(t, resp, &contents) + + assert.True(t, contents.Verification.Verified) + }) + + t.Run("Create pull request", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + pr, err := doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, fromBranch, toBranch)(t) + require.NoError(t, err) + + req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/pulls/%d/commits/%s", testCtx.Username, testCtx.Reponame, pr.Index, pr.Head.Sha)) + resp := testCtx.Session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, "#diff-commit-header .commit-header-row.message.isSigned.isVerified", true) + }) + }) +} diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index b66726a3e6..329a31ace8 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -5,15 +5,19 @@ package integration import ( + "context" + "encoding/base64" "fmt" "net/http" "net/url" + "os" "path" "regexp" "strings" "testing" "time" + auth_model "forgejo.org/models/auth" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" unit_model "forgejo.org/models/unit" @@ -22,6 +26,7 @@ import ( "forgejo.org/modules/git" "forgejo.org/modules/optional" "forgejo.org/modules/setting" + api "forgejo.org/modules/structs" "forgejo.org/modules/test" "forgejo.org/modules/translation" repo_service "forgejo.org/services/repository" @@ -682,6 +687,79 @@ func TestViewCommit(t *testing.T) { assert.True(t, test.IsNormalPageCompleted(resp.Body.String()), "non-existing commit should render 404 page") } +func TestViewCommitSignature(t *testing.T) { + t.Cleanup(func() { + // Cannot use t.Context(), it is in the done state. + require.NoError(t, git.InitFull(context.Background())) //nolint:usetesting + }) + + defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "UwU")() + defer test.MockVariableValue(&setting.Repository.Signing.SigningEmail, "fox@example.com")() + defer test.MockVariableValue(&setting.Repository.Signing.CRUDActions, []string{"always"})() + defer test.MockVariableValue(&setting.Repository.Signing.InitialCommit, []string{"always"})() + + filePath := "signed.txt" + fromBranch := "master" + toBranch := "branch-signed" + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + // Use a new GNUPGPHOME to avoid messing with the existing GPG keyring. + tmpDir := t.TempDir() + require.NoError(t, os.Chmod(tmpDir, 0o700)) + t.Setenv("GNUPGHOME", tmpDir) + + rootKeyPair, err := importTestingKey() + require.NoError(t, err) + defer test.MockVariableValue(&setting.Repository.Signing.SigningKey, rootKeyPair.PrimaryKey.KeyIdShortString())() + defer test.MockVariableValue(&setting.Repository.Signing.Format, "openpgp")() + + // Ensure the git config is updated with the new signing format. + require.NoError(t, git.InitFull(t.Context())) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + testCtx := NewAPITestContext(t, user.Name, "commit-header-signed", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + u.Path = testCtx.GitPath() + + t.Run("Create repository", doAPICreateRepository(testCtx, false, git.Sha1ObjectFormat)) + + t.Run("Create commit", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + options := &api.CreateFileOptions{ + FileOptions: api.FileOptions{ + BranchName: fromBranch, + NewBranchName: toBranch, + Message: fmt.Sprintf("from:%s to:%s path:%s", fromBranch, toBranch, filePath), + Author: api.Identity{ + Name: user.FullName, + Email: user.Email, + }, + Committer: api.Identity{ + Name: user.FullName, + Email: user.Email, + }, + }, + ContentBase64: base64.StdEncoding.EncodeToString(fmt.Appendf(nil, "This is new text for %s", filePath)), + } + + req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", testCtx.Username, testCtx.Reponame, filePath), &options). + AddTokenAuth(testCtx.Token) + resp := testCtx.Session.MakeRequest(t, req, http.StatusCreated) + + var contents api.FileResponse + DecodeJSON(t, resp, &contents) + + assert.True(t, contents.Verification.Verified) + + req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/commit/%s", testCtx.Username, testCtx.Reponame, contents.Commit.SHA)) + resp = testCtx.Session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, ".commit-header-row.message.isSigned.isVerified", true) + }) + }) +} + func TestCommitView(t *testing.T) { defer tests.PrepareTestEnv(t)() From c085d6c9ac7afe822a59eccba589c67eb1ac9b1c Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 27 Jun 2025 15:43:31 +0200 Subject: [PATCH 020/297] fix: pass doer's ID for CRUD instance signing (#8304) - When doing CRUD actions, the commiter and author are reconstructed and do not contain the doer's ID. Make sure to pass this ID along so it can be used to verify the rules of instance signing for CRUD actions. - Regression of forgejo/forgejo#7693. It seems that previously this didn't work correctly as it would not care about a empty ID. - Resolves forgejo/forgejo#8278 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8304 Reviewed-by: Michael Kriese Reviewed-by: Beowulf Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- services/repository/files/file.go | 51 +++++++++++----------- tests/integration/signing_git_test.go | 61 ++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 27 deletions(-) diff --git a/services/repository/files/file.go b/services/repository/files/file.go index ef9a87dbcf..5b93258840 100644 --- a/services/repository/files/file.go +++ b/services/repository/files/file.go @@ -104,36 +104,35 @@ func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *user_m // then we use bogus User objects for them to store their FullName and Email. // If only one of the two are provided, we set both of them to it. // If neither are provided, both are the doer. - if committer != nil && committer.Email != "" { - if doer != nil && strings.EqualFold(doer.Email, committer.Email) { - committerUser = doer // the committer is the doer, so will use their user object - if committer.Name != "" { - committerUser.FullName = committer.Name + getUser := func(identity *IdentityOptions) *user_model.User { + if identity == nil || identity.Email == "" { + return nil + } + + if doer != nil && strings.EqualFold(doer.Email, identity.Email) { + user := doer // the committer is the doer, so will use their user object + if identity.Name != "" { + user.FullName = identity.Name } // Use the provided email and not revert to placeholder mail. - committerUser.KeepEmailPrivate = false - } else { - committerUser = &user_model.User{ - FullName: committer.Name, - Email: committer.Email, - } - } - } - if author != nil && author.Email != "" { - if doer != nil && strings.EqualFold(doer.Email, author.Email) { - authorUser = doer // the author is the doer, so will use their user object - if authorUser.Name != "" { - authorUser.FullName = author.Name - } - // Use the provided email and not revert to placeholder mail. - authorUser.KeepEmailPrivate = false - } else { - authorUser = &user_model.User{ - FullName: author.Name, - Email: author.Email, - } + user.KeepEmailPrivate = false + return user + } + + var id int64 + if doer != nil { + id = doer.ID + } + return &user_model.User{ + ID: id, // Needed to ensure the doer is checked to pass rules for instance signing of CRUD actions. + FullName: identity.Name, + Email: identity.Email, } } + + committerUser = getUser(committer) + authorUser = getUser(author) + if authorUser == nil { if committerUser != nil { authorUser = committerUser // No valid author was given so use the committer diff --git a/tests/integration/signing_git_test.go b/tests/integration/signing_git_test.go index 9d69306e0a..c4759aaddb 100644 --- a/tests/integration/signing_git_test.go +++ b/tests/integration/signing_git_test.go @@ -235,7 +235,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O })) }) - t.Run("No publickey", func(t *testing.T) { + t.Run("No 2fa", func(t *testing.T) { defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, "user4", "initial-no-2fa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) @@ -287,6 +287,65 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O })) }) + t.Run("AlwaysSign-Initial-CRUD-Pubkey", func(t *testing.T) { + setting.Repository.Signing.CRUDActions = []string{"pubkey"} + + t.Run("Has publickey", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + testCtx := NewAPITestContext(t, username, "initial-always-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile( + t, testCtx, user, "master", "pubkey", "signed-pubkey.txt", func(t *testing.T, response api.FileResponse) { + assert.True(t, response.Verification.Verified) + assert.Equal(t, "fox@example.com", response.Verification.Signer.Email) + })) + }) + + t.Run("No publickey", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + testCtx := NewAPITestContext(t, "user4", "initial-always-no-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile( + t, testCtx, user, "master", "pubkey", "unsigned-pubkey.txt", func(t *testing.T, response api.FileResponse) { + assert.False(t, response.Verification.Verified) + })) + }) + }) + + t.Run("AlwaysSign-Initial-CRUD-Twofa", func(t *testing.T) { + setting.Repository.Signing.CRUDActions = []string{"twofa"} + + t.Run("Has 2fa", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + t.Cleanup(func() { + unittest.AssertSuccessfulDelete(t, &auth_model.WebAuthnCredential{UserID: user.ID}) + }) + + testCtx := NewAPITestContext(t, username, "initial-always-twofa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + unittest.AssertSuccessfulInsert(t, &auth_model.WebAuthnCredential{UserID: user.ID}) + t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateCRUDFile-Twofa", crudActionCreateFile( + t, testCtx, user, "master", "twofa", "signed-twofa.txt", func(t *testing.T, response api.FileResponse) { + assert.True(t, response.Verification.Verified) + assert.Equal(t, "fox@example.com", response.Verification.Signer.Email) + })) + }) + + t.Run("No 2fa", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + testCtx := NewAPITestContext(t, "user4", "initial-always-no-twofa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile( + t, testCtx, user, "master", "twofa", "unsigned-twofa.txt", func(t *testing.T, response api.FileResponse) { + assert.False(t, response.Verification.Verified) + })) + }) + }) + t.Run("AlwaysSign-Initial-CRUD-Always", func(t *testing.T) { defer tests.PrintCurrentTest(t)() setting.Repository.Signing.CRUDActions = []string{"always"} From 69d374435b0aa9e4c6891e0072ba8e6241696a8e Mon Sep 17 00:00:00 2001 From: floss4good Date: Fri, 27 Jun 2025 15:47:58 +0200 Subject: [PATCH 021/297] fix: abuse reports string data types (#8267) Follow-up of !6977 I was fooled by the fact that for SQLite the columns corresponding to `string` fields were created as `TEXT`; but this is not the case for PostgreSQL and MariaDB/MySQL. According to XORM default mapping rules[^1] _String is corresponding to varchar(255)_. Therefore `abuse_report`.`remarks` should be of type `VARCHAR(500)` and `abuse_report_shadow_copy`.`raw_value` of type `LONGTEXT`. ### Testing I have dropped the affected columns (or the entire tables) and checked that for PostgreSQL and MariaDB they are created with the correct type and also manually tested the abusive content reporting functionality in order to make sure that no DB error will be returned if for 'Remarks' a text longer than 255 characters is submitted or when a (big) shadow copy is created. [^1]: https://xorm.io/docs/chapter-02/4.columns/ Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8267 Reviewed-by: Gusted Co-authored-by: floss4good Co-committed-by: floss4good --- models/moderation/abuse_report.go | 2 +- models/moderation/shadow_copy.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go index dadd61a95e..3a6244ef4c 100644 --- a/models/moderation/abuse_report.go +++ b/models/moderation/abuse_report.go @@ -100,7 +100,7 @@ type AbuseReport struct { // The abuse category selected by the reporter. Category AbuseCategoryType `xorm:"INDEX NOT NULL"` // Remarks provided by the reporter. - Remarks string + Remarks string `xorm:"VARCHAR(500)"` // The ID of the corresponding shadow-copied content when exists; otherwise null. ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` diff --git a/models/moderation/shadow_copy.go b/models/moderation/shadow_copy.go index cdd8f69c52..d363610a48 100644 --- a/models/moderation/shadow_copy.go +++ b/models/moderation/shadow_copy.go @@ -17,7 +17,7 @@ import ( type AbuseReportShadowCopy struct { ID int64 `xorm:"pk autoincr"` - RawValue string `xorm:"NOT NULL"` + RawValue string `xorm:"LONGTEXT NOT NULL"` // A JSON with relevant fields from user, repository, issue or comment table. CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } From 6b27fa66b9876390323169089e2136d1cdd8dd6e Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 27 Jun 2025 17:37:29 +0200 Subject: [PATCH 022/297] chore: sort blocked users list for determistic results (#8320) - Ref https://codeberg.org/forgejo/forgejo/issues/8221#issuecomment-5515478 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8320 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- tests/integration/api_block_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/api_block_test.go b/tests/integration/api_block_test.go index 8b25ce9283..0e2cf7ee25 100644 --- a/tests/integration/api_block_test.go +++ b/tests/integration/api_block_test.go @@ -4,8 +4,10 @@ package integration import ( + "cmp" "fmt" "net/http" + "slices" "testing" auth_model "forgejo.org/models/auth" @@ -46,6 +48,7 @@ func TestAPIUserBlock(t *testing.T) { var blockedUsers []api.BlockedUser DecodeJSON(t, resp, &blockedUsers) assert.Len(t, blockedUsers, 2) + slices.SortFunc(blockedUsers, func(a, b api.BlockedUser) int { return cmp.Compare(a.BlockID, b.BlockID) }) assert.EqualValues(t, 1, blockedUsers[0].BlockID) assert.EqualValues(t, 2, blockedUsers[1].BlockID) }) From 225a0f7026e885cec6605ddcf27aeacfb1a90221 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Fri, 27 Jun 2025 23:10:09 +0200 Subject: [PATCH 023/297] git/TreeEntry: LinkTarget simplification (#8323) See #8222 for context. This PR removes a call to `Blob.GetBlobContent` and `Blob.DataAsync` and unifies symlink resolution: - length was unlimited in one case - length was truncated to 1024 chars in the other case Now it is hard-limited to 4096 chars (ie error if larger), which is a length which seems appropriate according to https://stackoverflow.com/a/22575737. ### Tests - Tests are already present in `tests/integration/repo_test.go:972`: `TestRepoFollowSymlink` (it caught a cap/len stupid mistake). - [x] I did not document these changes and I do not expect someone else to do it. - [x] I do not want this change to show in the release notes. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8323 Reviewed-by: Earl Warren Co-authored-by: oliverpool Co-committed-by: oliverpool --- modules/git/tree_entry.go | 43 ++++++++++++++++------------ services/repository/files/content.go | 2 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go index d51b7992fe..ec5c632ca0 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -116,32 +116,37 @@ func (te *TreeEntry) Type() string { } } +// LinkTarget returns the target of the symlink as string. +func (te *TreeEntry) LinkTarget() (string, error) { + if !te.IsLink() { + return "", ErrBadLink{te.Name(), "not a symlink"} + } + + const symlinkLimit = 4096 // according to git config core.longpaths https://stackoverflow.com/a/22575737 + blob := te.Blob() + if blob.Size() > symlinkLimit { + return "", ErrBadLink{te.Name(), "symlink too large"} + } + + rc, size, err := blob.NewTruncatedReader(symlinkLimit) + if err != nil { + return "", err + } + defer rc.Close() + + buf := make([]byte, int(size)) + _, err = io.ReadFull(rc, buf) + return string(buf), err +} + // FollowLink returns the entry pointed to by a symlink func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) { - if !te.IsLink() { - return nil, "", ErrBadLink{te.Name(), "not a symlink"} - } - // read the link - r, err := te.Blob().DataAsync() + lnk, err := te.LinkTarget() if err != nil { return nil, "", err } - closed := false - defer func() { - if !closed { - _ = r.Close() - } - }() - buf := make([]byte, te.Size()) - _, err = io.ReadFull(r, buf) - if err != nil { - return nil, "", err - } - _ = r.Close() - closed = true - lnk := string(buf) t := te.ptree // traverse up directories diff --git a/services/repository/files/content.go b/services/repository/files/content.go index dfdee1d1df..5a6006e9f2 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -206,7 +206,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref } else if entry.IsLink() { contentsResponse.Type = string(ContentTypeLink) // The target of a symlink file is the content of the file - targetFromContent, err := entry.Blob().GetBlobContent(1024) + targetFromContent, err := entry.LinkTarget() if err != nil { return nil, err } From d6e4342353a19d8579687743f020f9bd9bc99435 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 28 Jun 2025 23:28:12 +0200 Subject: [PATCH 024/297] fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks (#8326) - fix: API must use headGitRepo instead of ctx.Repo.GitRepo for comparing - fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks - add test coverage for both fixes and the underlying function `parseCompareInfo` - refactor and improve part of the helpers from `tests/integration/api_helper_for_declarative_test.go` - remove a few wrong or misleading comments Refs forgejo/forgejo#7978 ## Note on the focus of the PR It was initially created to address a regression introduced in v12. But the tests that verify it is fixed discovered a v11.0 bug. They cannot conveniently be separated because they both relate to the same area of code that was previously not covered by any test. ## Note on v11.0 backport It must be manually done by cherry-picking all commits up to and not including `fix: API must use headGitRepo instead of ctx.Repo.GitRepo for comparing` because it is v12 specific. ## 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 the `tests/integration` directory if it involves interactions with a live Forgejo server. ### Documentation - [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. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8326 Reviewed-by: Otto Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- routers/api/v1/repo/compare.go | 4 +- routers/api/v1/repo/pull.go | 9 +- tests/integration/api_branch_test.go | 2 +- .../api_helper_for_declarative_test.go | 67 ++++-- tests/integration/api_repo_compare_test.go | 224 ++++++++++++++---- .../integration/api_repo_file_create_test.go | 9 +- tests/integration/api_repo_file_get_test.go | 2 +- tests/integration/api_repo_lfs_test.go | 2 +- tests/integration/api_repo_test.go | 4 +- tests/integration/git_test.go | 37 +-- tests/integration/pull_commit_test.go | 2 +- tests/integration/repo_test.go | 2 +- tests/integration/signing_git_test.go | 71 +++--- tests/integration/ssh_key_test.go | 14 +- 14 files changed, 311 insertions(+), 138 deletions(-) diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go index 9c941ea07f..7fc59ea171 100644 --- a/routers/api/v1/repo/compare.go +++ b/routers/api/v1/repo/compare.go @@ -64,7 +64,7 @@ func CompareDiff(ctx *context.APIContext) { } } - _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{ + headRepository, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{ Base: infos[0], Head: infos[1], }) @@ -80,7 +80,7 @@ func CompareDiff(ctx *context.APIContext) { apiFiles := []*api.CommitAffectedFiles{} userCache := make(map[string]*user_model.User) for i := 0; i < len(ci.Commits); i++ { - apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache, + apiCommit, err := convert.ToCommit(ctx, headRepository, headGitRepo, ci.Commits[i], userCache, convert.ToCommitOptions{ Stat: true, Verification: verification, diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index c9dda124de..9360ff1335 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1125,7 +1125,6 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } } - // Check if current user has fork of repository or in the same repository. headRepo := repo_model.GetForkedRepo(ctx, headUser.ID, baseRepo.ID) if headRepo == nil && !isSameRepo { err := baseRepo.GetBaseRepo(ctx) @@ -1134,13 +1133,11 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) return nil, nil, nil, "", "" } - // Check if baseRepo's base repository is the same as headUser's repository. if baseRepo.BaseRepo == nil || baseRepo.BaseRepo.OwnerID != headUser.ID { log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID) ctx.NotFound("GetBaseRepo") return nil, nil, nil, "", "" } - // Assign headRepo so it can be used below. headRepo = baseRepo.BaseRepo } @@ -1202,9 +1199,9 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } // Check if head branch is valid. - headIsCommit := ctx.Repo.GitRepo.IsCommitExist(headBranch) - headIsBranch := ctx.Repo.GitRepo.IsBranchExist(headBranch) - headIsTag := ctx.Repo.GitRepo.IsTagExist(headBranch) + headIsCommit := headGitRepo.IsCommitExist(headBranch) + headIsBranch := headGitRepo.IsBranchExist(headBranch) + headIsTag := headGitRepo.IsTagExist(headBranch) if !headIsCommit && !headIsBranch && !headIsTag { // Check if headBranch is short sha commit hash if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil { diff --git a/tests/integration/api_branch_test.go b/tests/integration/api_branch_test.go index 8e88501596..d8800217d3 100644 --- a/tests/integration/api_branch_test.go +++ b/tests/integration/api_branch_test.go @@ -116,7 +116,7 @@ func testAPICreateBranches(t *testing.T, giteaURL *url.URL) { ctx := NewAPITestContext(t, "user2", "my-noo-repo-"+objectFormat.Name(), auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) giteaURL.Path = ctx.GitPath() - t.Run("CreateRepo", doAPICreateRepository(ctx, false, objectFormat)) + t.Run("CreateRepo", doAPICreateRepository(ctx, nil, objectFormat)) testCases := []struct { OldBranch string NewBranch string diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index c9b6f84f4f..ada6a2c311 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -48,20 +48,22 @@ func (ctx APITestContext) GitPath() string { return fmt.Sprintf("%s/%s.git", ctx.Username, ctx.Reponame) } -func doAPICreateRepository(ctx APITestContext, empty bool, objectFormat git.ObjectFormat, callback ...func(*testing.T, api.Repository)) func(*testing.T) { +func doAPICreateRepository(ctx APITestContext, opts *api.CreateRepoOption, objectFormat git.ObjectFormat, callback ...func(*testing.T, api.Repository)) func(*testing.T) { return func(t *testing.T) { - createRepoOption := &api.CreateRepoOption{ - AutoInit: !empty, - Description: "Temporary repo", - Name: ctx.Reponame, - Private: true, - Template: true, - Gitignores: "", - License: "WTFPL", - Readme: "Default", - ObjectFormatName: objectFormat.Name(), + if opts == nil { + opts = &api.CreateRepoOption{ + AutoInit: true, + Description: "Temporary repo", + Name: ctx.Reponame, + Private: true, + Template: true, + Gitignores: "", + License: "WTFPL", + Readme: "Default", + } } - req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", createRepoOption). + opts.ObjectFormatName = objectFormat.Name() + req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", opts). AddTokenAuth(ctx.Token) if ctx.ExpectedCode != 0 { ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) @@ -237,8 +239,8 @@ func doAPICreatePullRequest(ctx APITestContext, owner, repo, baseBranch, headBra } } -func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) (api.PullRequest, error) { - return func(t *testing.T) (api.PullRequest, error) { +func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) func(*testing.T) api.PullRequest { + return func(t *testing.T) api.PullRequest { req := NewRequest(t, http.MethodGet, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner, repo, index)). AddTokenAuth(ctx.Token) @@ -248,10 +250,9 @@ func doAPIGetPullRequest(ctx APITestContext, owner, repo string, index int64) fu } resp := ctx.Session.MakeRequest(t, req, expected) - decoder := json.NewDecoder(resp.Body) pr := api.PullRequest{} - err := decoder.Decode(&pr) - return pr, err + DecodeJSON(t, resp, &pr) + return pr } } @@ -347,20 +348,40 @@ func doAPICancelAutoMergePullRequest(ctx APITestContext, owner, repo string, ind } } -func doAPIGetBranch(ctx APITestContext, branch string, callback ...func(*testing.T, api.Branch)) func(*testing.T) { - return func(t *testing.T) { +func doAPIGetBranch(ctx APITestContext, branch string) func(*testing.T) api.Branch { + return func(t *testing.T) api.Branch { req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/branches/%s", ctx.Username, ctx.Reponame, branch). AddTokenAuth(ctx.Token) + expected := http.StatusOK + if ctx.ExpectedCode != 0 { + expected = ctx.ExpectedCode + } + resp := ctx.Session.MakeRequest(t, req, expected) + + branch := api.Branch{} + DecodeJSON(t, resp, &branch) + return branch + } +} + +func doAPICreateTag(ctx APITestContext, tag, target, message string, callback ...func(*testing.T, api.Tag)) func(*testing.T) { + return func(t *testing.T) { + req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/tags", ctx.Username, ctx.Reponame), &api.CreateTagOption{ + TagName: tag, + Message: message, + Target: target, + }). + AddTokenAuth(ctx.Token) if ctx.ExpectedCode != 0 { ctx.Session.MakeRequest(t, req, ctx.ExpectedCode) return } - resp := ctx.Session.MakeRequest(t, req, http.StatusOK) + resp := ctx.Session.MakeRequest(t, req, http.StatusCreated) - var branch api.Branch - DecodeJSON(t, resp, &branch) + var tag api.Tag + DecodeJSON(t, resp, &tag) if len(callback) > 0 { - callback[0](t, branch) + callback[0](t, tag) } } } diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go index 35f0a21d82..e4e85fc742 100644 --- a/tests/integration/api_repo_compare_test.go +++ b/tests/integration/api_repo_compare_test.go @@ -4,56 +4,200 @@ package integration import ( + "encoding/base64" + "fmt" "net/http" + "net/url" "testing" + "time" auth_model "forgejo.org/models/auth" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" + "forgejo.org/modules/git" api "forgejo.org/modules/structs" - "forgejo.org/tests" "github.com/stretchr/testify/assert" ) -func TestAPICompareBranches(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - // Login as User2. - session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - repoName := "repo20" - - req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/add-csv...remove-files-b", repoName). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - - var apiResp *api.Compare - DecodeJSON(t, resp, &apiResp) - - assert.Equal(t, 2, apiResp.TotalCommits) - assert.Len(t, apiResp.Commits, 2) - assert.Len(t, apiResp.Files, 3) -} - func TestAPICompareCommits(t *testing.T) { - defer tests.PrepareTestEnv(t)() - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - // Login as User2. - session := loginUser(t, user.Name) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - - req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo20/compare/c8e31bc...8babce9"). - AddTokenAuth(token) - resp := MakeRequest(t, req, http.StatusOK) - - var apiResp *api.Compare - DecodeJSON(t, resp, &apiResp) - - assert.Equal(t, 2, apiResp.TotalCommits) - assert.Len(t, apiResp.Commits, 2) - assert.Len(t, apiResp.Files, 3) + forEachObjectFormat(t, testAPICompareCommits) +} + +func testAPICompareCommits(t *testing.T, objectFormat git.ObjectFormat) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + newBranchAndFile := func(ctx APITestContext, user *user_model.User, branch, filename string) func(*testing.T) { + return func(t *testing.T) { + doAPICreateFile(ctx, filename, &api.CreateFileOptions{ + FileOptions: api.FileOptions{ + NewBranchName: branch, + Message: "create " + filename, + Author: api.Identity{ + Name: user.Name, + Email: user.Email, + }, + Committer: api.Identity{ + Name: user.Name, + Email: user.Email, + }, + Dates: api.CommitDateOptions{ + Author: time.Now(), + Committer: time.Now(), + }, + }, + ContentBase64: base64.StdEncoding.EncodeToString([]byte("content " + filename)), + })(t) + } + } + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + user2repo := "repoA" + user2Ctx := NewAPITestContext(t, user2.Name, user2repo, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + t.Run("CreateUser2Repository", doAPICreateRepository(user2Ctx, &api.CreateRepoOption{ + AutoInit: true, + Description: "Temporary repo", + Name: user2Ctx.Reponame, + }, objectFormat)) + user2branchName := "user2branch" + t.Run("CreateUser2RepositoryBranch", newBranchAndFile(user2Ctx, user2, user2branchName, "user2branchfilename.txt")) + user2branch := doAPIGetBranch(user2Ctx, user2branchName)(t) + user2master := doAPIGetBranch(user2Ctx, "master")(t) + user2tag1 := "tag1" + t.Run("CreateUser2RepositoryTag1", doAPICreateTag(user2Ctx, user2tag1, "master", "user2branchtag1")) + user2tag2 := "tag2" + t.Run("CreateUser2RepositoryTag1", doAPICreateTag(user2Ctx, user2tag2, user2branchName, "user2branchtag2")) + + shortCommitLength := 7 + + for _, testCase := range []struct { + name string + a string + b string + }{ + { + name: "Commits", + a: user2master.Commit.ID, + b: user2branch.Commit.ID, + }, + { + name: "ShortCommits", + a: user2master.Commit.ID[:shortCommitLength], + b: user2branch.Commit.ID[:shortCommitLength], + }, + { + name: "Branches", + a: "master", + b: user2branchName, + }, + { + name: "Tags", + a: user2tag1, + b: user2tag2, + }, + } { + t.Run("SameRepo"+testCase.name, func(t *testing.T) { + // a...b + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/%s...%s", user2.Name, user2repo, testCase.a, testCase.b). + AddTokenAuth(user2Ctx.Token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiResp *api.Compare + DecodeJSON(t, resp, &apiResp) + + assert.Equal(t, 1, apiResp.TotalCommits) + assert.Len(t, apiResp.Commits, 1) + assert.Len(t, apiResp.Files, 1) + + // b...a + req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/%s...%s", user2.Name, user2repo, testCase.b, testCase.a). + AddTokenAuth(user2Ctx.Token) + resp = MakeRequest(t, req, http.StatusOK) + + DecodeJSON(t, resp, &apiResp) + + assert.Equal(t, 0, apiResp.TotalCommits) + assert.Empty(t, apiResp.Commits) + assert.Empty(t, apiResp.Files) + }) + } + + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + user4Ctx := NewAPITestContext(t, user4.Name, user2repo, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + t.Run("User4ForksUser2Repository", doAPIForkRepository(user4Ctx, user2.Name)) + user4branchName := "user4branch" + t.Run("CreateUser4RepositoryBranch", newBranchAndFile(user4Ctx, user4, user4branchName, "user4branchfilename.txt")) + user4branch := doAPIGetBranch(user4Ctx, user4branchName)(t) + user4tag4 := "tag4" + t.Run("CreateUser4RepositoryTag4", doAPICreateTag(user4Ctx, user4tag4, user4branchName, "user4branchtag4")) + + t.Run("FromTheForkedRepo", func(t *testing.T) { + // user4/repoA is a fork of user2/repoA and when evaluating + // + // user4/repoA/compare/master...user2:user2branch + // + // user2/repoA is not explicitly specified, it is implicitly the repository + // from which user4/repoA was forked + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/%s...%s:%s", user4.Name, user2repo, "master", user2.Name, user2branchName). + AddTokenAuth(user4Ctx.Token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiResp *api.Compare + DecodeJSON(t, resp, &apiResp) + + assert.Equal(t, 1, apiResp.TotalCommits) + assert.Len(t, apiResp.Commits, 1) + assert.Len(t, apiResp.Files, 1) + }) + + for _, testCase := range []struct { + name string + a string + b string + }{ + { + name: "Commits", + a: user2master.Commit.ID, + b: fmt.Sprintf("%s:%s", user4.Name, user4branch.Commit.ID), + }, + { + name: "ShortCommits", + a: user2master.Commit.ID[:shortCommitLength], + b: fmt.Sprintf("%s:%s", user4.Name, user4branch.Commit.ID[:shortCommitLength]), + }, + { + name: "Branches", + a: "master", + b: fmt.Sprintf("%s:%s", user4.Name, user4branchName), + }, + { + name: "Tags", + a: user2tag1, + b: fmt.Sprintf("%s:%s", user4.Name, user4tag4), + }, + { + name: "SameRepo", + a: "master", + b: fmt.Sprintf("%s:%s", user2.Name, user2branchName), + }, + } { + t.Run("ForkedRepo"+testCase.name, func(t *testing.T) { + // user2/repoA is forked into user4/repoA and when evaluating + // + // user2/repoA/compare/a...user4:b + // + // user4/repoA is not explicitly specified, it is implicitly the repository + // owned by user4 which is a fork of repoA + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/%s...%s", user2.Name, user2repo, testCase.a, testCase.b). + AddTokenAuth(user2Ctx.Token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiResp *api.Compare + DecodeJSON(t, resp, &apiResp) + + assert.Equal(t, 1, apiResp.TotalCommits) + assert.Len(t, apiResp.Commits, 1) + assert.Len(t, apiResp.Files, 1) + }) + } + }) } diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index c112653e11..4916ef97ef 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -287,7 +287,14 @@ func TestAPICreateFile(t *testing.T) { // Test creating a file in an empty repository forEachObjectFormat(t, func(t *testing.T, objectFormat git.ObjectFormat) { reponame := "empty-repo-" + objectFormat.Name() - doAPICreateRepository(NewAPITestContext(t, "user2", reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser), true, objectFormat)(t) + ctx := NewAPITestContext(t, "user2", reponame, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + opts := &api.CreateRepoOption{ + Description: "Temporary repo", + Name: ctx.Reponame, + Private: true, + Template: true, + } + doAPICreateRepository(ctx, opts, objectFormat)(t) createFileOptions = getCreateFileOptions() fileID++ treePath = fmt.Sprintf("new/file%d.txt", fileID) diff --git a/tests/integration/api_repo_file_get_test.go b/tests/integration/api_repo_file_get_test.go index 7bd7393b01..408c630a1c 100644 --- a/tests/integration/api_repo_file_get_test.go +++ b/tests/integration/api_repo_file_get_test.go @@ -27,7 +27,7 @@ func TestAPIGetRawFileOrLFS(t *testing.T) { // Test with LFS onGiteaRun(t, func(t *testing.T, u *url.URL) { httpContext := NewAPITestContext(t, "user2", "repo-lfs-test", auth_model.AccessTokenScopeWriteRepository) - doAPICreateRepository(httpContext, false, git.Sha1ObjectFormat, func(t *testing.T, repository api.Repository) { // FIXME: use forEachObjectFormat + doAPICreateRepository(httpContext, nil, git.Sha1ObjectFormat, func(t *testing.T, repository api.Repository) { // FIXME: use forEachObjectFormat u.Path = httpContext.GitPath() dstPath := t.TempDir() diff --git a/tests/integration/api_repo_lfs_test.go b/tests/integration/api_repo_lfs_test.go index b8ba54f876..b0edf7b854 100644 --- a/tests/integration/api_repo_lfs_test.go +++ b/tests/integration/api_repo_lfs_test.go @@ -64,7 +64,7 @@ func TestAPILFSMediaType(t *testing.T) { func createLFSTestRepository(t *testing.T, name string) *repo_model.Repository { ctx := NewAPITestContext(t, "user2", "lfs-"+name+"-repo", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepo", doAPICreateRepository(ctx, false, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat + t.Run("CreateRepo", doAPICreateRepository(ctx, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs-"+name+"-repo") require.NoError(t, err) diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index fd62670eb3..e81f4307ee 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -415,7 +415,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) { httpContext := baseAPITestContext httpContext.Reponame = "repo-tmp-17" - t.Run("CreateRepo", doAPICreateRepository(httpContext, false, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat + t.Run("CreateRepo", doAPICreateRepository(httpContext, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat user, err := user_model.GetUserByName(db.DefaultContext, httpContext.Username) require.NoError(t, err) @@ -498,7 +498,7 @@ func testAPIRepoCreateConflict(t *testing.T, u *url.URL) { httpContext := baseAPITestContext httpContext.Reponame = "repo-tmp-17" - t.Run("CreateRepo", doAPICreateRepository(httpContext, false, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat + t.Run("CreateRepo", doAPICreateRepository(httpContext, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{ diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index e79f6fe802..9a66781024 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -69,7 +69,7 @@ func testGit(t *testing.T, u *url.URL) { dstPath := t.TempDir() - t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false, objectFormat)) + t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, nil, objectFormat)) t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, httpContext.Username, perm.AccessModeRead)) t.Run("ForkFromDifferentUser", doAPIForkRepository(httpContext, forkedUserCtx.Username)) @@ -110,7 +110,7 @@ func testGit(t *testing.T, u *url.URL) { sshContext.Reponame = "repo-tmp-18-" + objectFormat.Name() keyname := "my-testing-key" forkedUserCtx.Reponame = sshContext.Reponame - t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, false, objectFormat)) + t.Run("CreateRepoInDifferentUser", doAPICreateRepository(forkedUserCtx, nil, objectFormat)) t.Run("AddUserAsCollaborator", doAPIAddCollaborator(forkedUserCtx, sshContext.Username, perm.AccessModeRead)) t.Run("ForkFromDifferentUser", doAPIForkRepository(sshContext, forkedUserCtx.Username)) @@ -529,8 +529,7 @@ func doMergeFork(ctx, baseCtx APITestContext, baseBranch, headBranch string) fun t.Run("EnsureCanSeePull", doEnsureCanSeePull(headCtx, pr, false)) t.Run("CheckPR", func(t *testing.T) { oldMergeBase := pr.MergeBase - pr2, err := doAPIGetPullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) - require.NoError(t, err) + pr2 := doAPIGetPullRequest(baseCtx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) assert.Equal(t, oldMergeBase, pr2.MergeBase) }) t.Run("EnsurDiffNoChange", doEnsureDiffNoChange(baseCtx, pr, diffHash, diffLength)) @@ -730,24 +729,21 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) { // Check pr status ctx.ExpectedCode = 0 - pr, err = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) - require.NoError(t, err) + pr = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) assert.False(t, pr.HasMerged) // Call API to add Failure status for commit t.Run("CreateStatus", addCommitStatus(api.CommitStatusFailure)) // Check pr status - pr, err = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) - require.NoError(t, err) + pr = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) assert.False(t, pr.HasMerged) // Call API to add Success status for commit t.Run("CreateStatus", addCommitStatus(api.CommitStatusSuccess)) // test pr status - pr, err = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) - require.NoError(t, err) + pr = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t) assert.True(t, pr.HasMerged) } } @@ -836,8 +832,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string assert.Equal(t, 1, pr1.CommitsAhead) assert.Equal(t, 0, pr1.CommitsBehind) - prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) - require.NoError(t, err) + prMsg := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) assert.Equal(t, "user2/"+headBranch, pr1.HeadBranch) assert.False(t, prMsg.HasMerged) @@ -858,8 +853,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string } assert.Equal(t, 1, pr2.CommitsAhead) assert.Equal(t, 0, pr2.CommitsBehind) - prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) - require.NoError(t, err) + prMsg = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) assert.Equal(t, "user2/test/"+headBranch, pr2.HeadBranch) assert.False(t, prMsg.HasMerged) @@ -910,8 +904,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string require.NoError(t, err) unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2) - prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) - require.NoError(t, err) + prMsg := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) assert.False(t, prMsg.HasMerged) assert.Equal(t, commit, prMsg.Head.Sha) @@ -928,8 +921,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string require.NoError(t, err) unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2) - prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) - require.NoError(t, err) + prMsg = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) assert.False(t, prMsg.HasMerged) assert.Equal(t, commit, prMsg.Head.Sha) @@ -953,8 +945,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string err := pr3.LoadIssue(db.DefaultContext) require.NoError(t, err) - _, err2 := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr3.Index)(t) - require.NoError(t, err2) + doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr3.Index)(t) assert.Equal(t, "Testing commit 2", pr3.Issue.Title) assert.Contains(t, pr3.Issue.Content, "Longer description.") @@ -975,8 +966,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string err := pr.LoadIssue(db.DefaultContext) require.NoError(t, err) - _, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr.Index)(t) - require.NoError(t, err) + doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr.Index)(t) assert.Equal(t, "my-shiny-title", pr.Issue.Title) assert.Contains(t, pr.Issue.Content, "Longer description.") @@ -998,8 +988,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string err := pr.LoadIssue(db.DefaultContext) require.NoError(t, err) - _, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr.Index)(t) - require.NoError(t, err) + doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr.Index)(t) assert.Equal(t, "Testing commit 2", pr.Issue.Title) assert.Contains(t, pr.Issue.Content, "custom") diff --git a/tests/integration/pull_commit_test.go b/tests/integration/pull_commit_test.go index 1de437ef46..f82fc08df4 100644 --- a/tests/integration/pull_commit_test.go +++ b/tests/integration/pull_commit_test.go @@ -95,7 +95,7 @@ func TestPullCommitSignature(t *testing.T) { testCtx := NewAPITestContext(t, user.Name, "pull-request-commit-header-signed", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) u.Path = testCtx.GitPath() - t.Run("Create repository", doAPICreateRepository(testCtx, false, git.Sha1ObjectFormat)) + t.Run("Create repository", doAPICreateRepository(testCtx, nil, git.Sha1ObjectFormat)) t.Run("Create commit", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index 329a31ace8..7370b63dcd 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -720,7 +720,7 @@ func TestViewCommitSignature(t *testing.T) { testCtx := NewAPITestContext(t, user.Name, "commit-header-signed", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) u.Path = testCtx.GitPath() - t.Run("Create repository", doAPICreateRepository(testCtx, false, git.Sha1ObjectFormat)) + t.Run("Create repository", doAPICreateRepository(testCtx, nil, git.Sha1ObjectFormat)) t.Run("Create commit", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/signing_git_test.go b/tests/integration/signing_git_test.go index c4759aaddb..e4c0d6049b 100644 --- a/tests/integration/signing_git_test.go +++ b/tests/integration/signing_git_test.go @@ -109,13 +109,14 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, username, "initial-unsigned"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) - t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) + t.Run("CheckMasterBranchUnsigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) assert.NotNil(t, branch.Commit) assert.NotNil(t, branch.Commit.Verification) assert.False(t, branch.Commit.Verification.Verified) assert.Empty(t, branch.Commit.Verification.Signature) - })) + }) t.Run("CreateCRUDFile-Never", crudActionCreateFile( t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -191,25 +192,27 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, username, "initial-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) - t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) + t.Run("CheckMasterBranchSigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.True(t, branch.Commit.Verification.Verified) assert.Equal(t, "fox@example.com", branch.Commit.Verification.Signer.Email) - })) + }) }) t.Run("No publickey", func(t *testing.T) { defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, "user4", "initial-no-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) - t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) + t.Run("CheckMasterBranchSigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.False(t, branch.Commit.Verification.Verified) - })) + }) }) }) @@ -226,25 +229,27 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O testCtx := NewAPITestContext(t, username, "initial-2fa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) unittest.AssertSuccessfulInsert(t, &auth_model.WebAuthnCredential{UserID: user.ID}) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) - t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) + t.Run("CheckMasterBranchSigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.True(t, branch.Commit.Verification.Verified) assert.Equal(t, "fox@example.com", branch.Commit.Verification.Signer.Email) - })) + }) }) t.Run("No 2fa", func(t *testing.T) { defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, "user4", "initial-no-2fa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) - t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) + t.Run("CheckMasterBranchSigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.False(t, branch.Commit.Verification.Verified) - })) + }) }) }) @@ -253,13 +258,14 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O setting.Repository.Signing.InitialCommit = []string{"always"} testCtx := NewAPITestContext(t, username, "initial-always"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) - t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) + t.Run("CheckMasterBranchSigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.True(t, branch.Commit.Verification.Verified) assert.Equal(t, "fox@example.com", branch.Commit.Verification.Signer.Email) - })) + }) }) t.Run("AlwaysSign-Initial-CRUD-Never", func(t *testing.T) { @@ -267,7 +273,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O setting.Repository.Signing.CRUDActions = []string{"never"} testCtx := NewAPITestContext(t, username, "initial-always-never"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) t.Run("CreateCRUDFile-Never", crudActionCreateFile( t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -279,7 +285,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O setting.Repository.Signing.CRUDActions = []string{"parentsigned"} testCtx := NewAPITestContext(t, username, "initial-always-parent"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile( t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) { assert.True(t, response.Verification.Verified) @@ -294,7 +300,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, username, "initial-always-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile( t, testCtx, user, "master", "pubkey", "signed-pubkey.txt", func(t *testing.T, response api.FileResponse) { assert.True(t, response.Verification.Verified) @@ -306,7 +312,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, "user4", "initial-always-no-pubkey"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile( t, testCtx, user, "master", "pubkey", "unsigned-pubkey.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -326,7 +332,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O testCtx := NewAPITestContext(t, username, "initial-always-twofa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) unittest.AssertSuccessfulInsert(t, &auth_model.WebAuthnCredential{UserID: user.ID}) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) t.Run("CreateCRUDFile-Twofa", crudActionCreateFile( t, testCtx, user, "master", "twofa", "signed-twofa.txt", func(t *testing.T, response api.FileResponse) { assert.True(t, response.Verification.Verified) @@ -338,7 +344,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O defer tests.PrintCurrentTest(t)() testCtx := NewAPITestContext(t, "user4", "initial-always-no-twofa"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) t.Run("CreateCRUDFile-Pubkey", crudActionCreateFile( t, testCtx, user, "master", "twofa", "unsigned-twofa.txt", func(t *testing.T, response api.FileResponse) { assert.False(t, response.Verification.Verified) @@ -351,7 +357,7 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O setting.Repository.Signing.CRUDActions = []string{"always"} testCtx := NewAPITestContext(t, username, "initial-always-always"+suffix, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) - t.Run("CreateRepository", doAPICreateRepository(testCtx, false, objectFormat)) + t.Run("CreateRepository", doAPICreateRepository(testCtx, nil, objectFormat)) t.Run("CreateCRUDFile-Always", crudActionCreateFile( t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) { assert.True(t, response.Verification.Verified) @@ -369,12 +375,13 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O require.NoError(t, err) t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index)) }) - t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CheckMasterBranchUnsigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.False(t, branch.Commit.Verification.Verified) assert.Empty(t, branch.Commit.Verification.Signature) - })) + }) }) t.Run("BaseSignedMerging", func(t *testing.T) { @@ -387,12 +394,13 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O require.NoError(t, err) t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index)) }) - t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CheckMasterBranchUnsigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.False(t, branch.Commit.Verification.Verified) assert.Empty(t, branch.Commit.Verification.Signature) - })) + }) }) t.Run("CommitsSignedMerging", func(t *testing.T) { @@ -405,11 +413,12 @@ func testCRUD(t *testing.T, u *url.URL, signingFormat string, objectFormat git.O require.NoError(t, err) t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index)) }) - t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) { + t.Run("CheckMasterBranchUnsigned", func(t *testing.T) { + branch := doAPIGetBranch(testCtx, "master")(t) require.NotNil(t, branch.Commit) require.NotNil(t, branch.Commit.Verification) assert.True(t, branch.Commit.Verification.Verified) - })) + }) }) } diff --git a/tests/integration/ssh_key_test.go b/tests/integration/ssh_key_test.go index aece9c3fd9..a92694d2fa 100644 --- a/tests/integration/ssh_key_test.go +++ b/tests/integration/ssh_key_test.go @@ -55,7 +55,13 @@ func testPushDeployKeyOnEmptyRepo(t *testing.T, u *url.URL) { keyname := fmt.Sprintf("%s-push", ctx.Reponame) u.Path = ctx.GitPath() - t.Run("CreateEmptyRepository", doAPICreateRepository(ctx, true, objectFormat)) + opts := &api.CreateRepoOption{ + Description: "Temporary repo", + Name: ctx.Reponame, + Private: true, + Template: true, + } + t.Run("CreateEmptyRepository", doAPICreateRepository(ctx, opts, objectFormat)) t.Run("CheckIsEmpty", doCheckRepositoryEmptyStatus(ctx, true)) @@ -105,8 +111,8 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { failCtx := ctx failCtx.ExpectedCode = http.StatusUnprocessableEntity - t.Run("CreateRepository", doAPICreateRepository(ctx, false, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat - t.Run("CreateOtherRepository", doAPICreateRepository(otherCtx, false, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat + t.Run("CreateRepository", doAPICreateRepository(ctx, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat + t.Run("CreateOtherRepository", doAPICreateRepository(otherCtx, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat withKeyFile(t, keyname, func(keyFile string) { var userKeyPublicKeyID int64 @@ -180,7 +186,7 @@ func testKeyOnlyOneType(t *testing.T, u *url.URL) { t.Run("DeleteOtherRepository", doAPIDeleteRepository(otherCtxWithDeleteRepo)) - t.Run("RecreateRepository", doAPICreateRepository(ctxWithDeleteRepo, false, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat + t.Run("RecreateRepository", doAPICreateRepository(ctxWithDeleteRepo, nil, git.Sha1ObjectFormat)) // FIXME: use forEachObjectFormat t.Run("CreateUserKey", doAPICreateUserKey(ctx, keyname, keyFile, func(t *testing.T, publicKey api.PublicKey) { userKeyPublicKeyID = publicKey.ID From a300c0b9fd23e9c116c571a4eab17ab6c99376f9 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 28 Jun 2025 18:43:06 +0200 Subject: [PATCH 025/297] chore(cleanup): GitRepo.GetCommit is equivalent to GitRepo.IsBranchExist Since go-git was dropped in a21128a734636c2a18431cfc1742e8a7cf165f58, IsBranchExist relies on git-cat-file to figure out if a commit ID exists. There is no need to fallback to GetCommit if IsBranchExist determines the commit does not exist and it will come to the same conclusion. --- routers/api/v1/repo/pull.go | 19 ++++--------------- routers/web/repo/compare.go | 21 ++++----------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 9360ff1335..cb66b4d09d 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1116,13 +1116,8 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch) baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch) if !baseIsCommit && !baseIsBranch && !baseIsTag { - // Check for short SHA usage - if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil { - baseBranch = baseCommit.ID.String() - } else { - ctx.NotFound("BaseNotExist") - return nil, nil, nil, "", "" - } + ctx.NotFound("BaseNotExist") + return nil, nil, nil, "", "" } headRepo := repo_model.GetForkedRepo(ctx, headUser.ID, baseRepo.ID) @@ -1203,14 +1198,8 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) headIsBranch := headGitRepo.IsBranchExist(headBranch) headIsTag := headGitRepo.IsTagExist(headBranch) if !headIsCommit && !headIsBranch && !headIsTag { - // Check if headBranch is short sha commit hash - if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil { - headBranch = headCommit.ID.String() - } else { - headGitRepo.Close() - ctx.NotFound("IsRefExist", nil) - return nil, nil, nil, "", "" - } + ctx.NotFound("IsRefExist", nil) + return nil, nil, nil, "", "" } headBranchRef := headBranch diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index de2e29ab9f..59538d8a0e 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -312,22 +312,16 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch) if !baseIsCommit && !baseIsBranch && !baseIsTag { - // Check if baseBranch is short sha commit hash - if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil { - ci.BaseBranch = baseCommit.ID.String() - ctx.Data["BaseBranch"] = ci.BaseBranch - baseIsCommit = true - } else if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() { + if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() { if isSameRepo { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadRepo.FullName()) + ":" + util.PathEscapeSegments(ci.HeadBranch)) } - return nil } else { ctx.NotFound("IsRefExist", nil) - return nil } + return nil } ctx.Data["BaseIsCommit"] = baseIsCommit ctx.Data["BaseIsBranch"] = baseIsBranch @@ -514,15 +508,8 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { headIsBranch := ci.HeadGitRepo.IsBranchExist(ci.HeadBranch) headIsTag := ci.HeadGitRepo.IsTagExist(ci.HeadBranch) if !headIsCommit && !headIsBranch && !headIsTag { - // Check if headBranch is short sha commit hash - if headCommit, _ := ci.HeadGitRepo.GetCommit(ci.HeadBranch); headCommit != nil { - ci.HeadBranch = headCommit.ID.String() - ctx.Data["HeadBranch"] = ci.HeadBranch - headIsCommit = true - } else { - ctx.NotFound("IsRefExist", nil) - return nil - } + ctx.NotFound("IsRefExist", nil) + return nil } ctx.Data["HeadIsCommit"] = headIsCommit ctx.Data["HeadIsBranch"] = headIsBranch From b8e66a5552a10dd13ef91d6806111bf0d946a93a Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 28 Jun 2025 18:48:37 +0200 Subject: [PATCH 026/297] feat: improve API /repos/{owner}/{repo}/compare/{basehead} error messages --- routers/api/v1/repo/pull.go | 11 ++-- tests/integration/api_repo_compare_test.go | 68 ++++++++++++++++++++++ 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index cb66b4d09d..20f718d6c2 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1084,7 +1084,6 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) err error ) - // If there is no head repository, it means pull request between same repository. headInfos := strings.Split(form.Head, ":") if len(headInfos) == 1 { isSameRepo = true @@ -1094,7 +1093,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) headUser, err = user_model.GetUserByName(ctx, headInfos[0]) if err != nil { if user_model.IsErrUserNotExist(err) { - ctx.NotFound("GetUserByName") + ctx.NotFound(fmt.Errorf("the owner %s does not exist", headInfos[0])) } else { ctx.Error(http.StatusInternalServerError, "GetUserByName", err) } @@ -1104,7 +1103,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) // The head repository can also point to the same repo isSameRepo = ctx.Repo.Owner.ID == headUser.ID } else { - ctx.NotFound() + ctx.NotFound(fmt.Errorf("the head part of {basehead} %s must contain zero or one colon (:) but contains %d", form.Head, len(headInfos)-1)) return nil, nil, nil, "", "" } @@ -1116,7 +1115,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch) baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch) if !baseIsCommit && !baseIsBranch && !baseIsTag { - ctx.NotFound("BaseNotExist") + ctx.NotFound(fmt.Errorf("could not find '%s' to be a commit, branch or tag in the base repository %s/%s", baseBranch, baseRepo.Owner.Name, baseRepo.Name)) return nil, nil, nil, "", "" } @@ -1130,7 +1129,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) if baseRepo.BaseRepo == nil || baseRepo.BaseRepo.OwnerID != headUser.ID { log.Trace("parseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID) - ctx.NotFound("GetBaseRepo") + ctx.NotFound(fmt.Errorf("%[1]s does not have a fork of %[2]s/%[3]s and %[2]s/%[3]s is not a fork of a repository from %[1]s", headUser.Name, baseRepo.Owner.Name, baseRepo.Name)) return nil, nil, nil, "", "" } headRepo = baseRepo.BaseRepo @@ -1198,7 +1197,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) headIsBranch := headGitRepo.IsBranchExist(headBranch) headIsTag := headGitRepo.IsTagExist(headBranch) if !headIsCommit && !headIsBranch && !headIsTag { - ctx.NotFound("IsRefExist", nil) + ctx.NotFound(fmt.Errorf("could not find '%s' to be a commit, branch or tag in the head repository %s/%s", headBranch, headRepo.Owner.Name, headRepo.Name)) return nil, nil, nil, "", "" } diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go index e4e85fc742..1724924fdc 100644 --- a/tests/integration/api_repo_compare_test.go +++ b/tests/integration/api_repo_compare_test.go @@ -7,7 +7,9 @@ import ( "encoding/base64" "fmt" "net/http" + "net/http/httptest" "net/url" + "strings" "testing" "time" @@ -50,6 +52,28 @@ func testAPICompareCommits(t *testing.T, objectFormat git.ObjectFormat) { } } + requireErrorContains := func(t *testing.T, resp *httptest.ResponseRecorder, expected string) { + t.Helper() + + type response struct { + Message string `json:"message"` + Errors []string `json:"errors"` + } + var bodyResp response + DecodeJSON(t, resp, &bodyResp) + + if strings.Contains(bodyResp.Message, expected) { + return + } + for _, error := range bodyResp.Errors { + if strings.Contains(error, expected) { + return + } + } + t.Log(fmt.Sprintf("expected %s in %+v", expected, bodyResp)) + t.Fail() + } + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) user2repo := "repoA" user2Ctx := NewAPITestContext(t, user2.Name, user2repo, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) @@ -123,6 +147,14 @@ func testAPICompareCommits(t *testing.T, objectFormat git.ObjectFormat) { user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) user4Ctx := NewAPITestContext(t, user4.Name, user2repo, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + + t.Run("ForkNotFound", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/%s...%s:%s", user2.Name, user2repo, "master", user4.Name, user2branchName). + AddTokenAuth(user2Ctx.Token) + resp := MakeRequest(t, req, http.StatusNotFound) + requireErrorContains(t, resp, "user4 does not have a fork of user2/repoA and user2/repoA is not a fork of a repository from user4") + }) + t.Run("User4ForksUser2Repository", doAPIForkRepository(user4Ctx, user2.Name)) user4branchName := "user4branch" t.Run("CreateUser4RepositoryBranch", newBranchAndFile(user4Ctx, user4, user4branchName, "user4branchfilename.txt")) @@ -199,5 +231,41 @@ func testAPICompareCommits(t *testing.T, objectFormat git.ObjectFormat) { assert.Len(t, apiResp.Files, 1) }) } + + t.Run("ForkUserDoesNotExist", func(t *testing.T) { + notUser := "notauser" + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/master...%s:branchname", user2.Name, user2repo, notUser). + AddTokenAuth(user2Ctx.Token) + resp := MakeRequest(t, req, http.StatusNotFound) + requireErrorContains(t, resp, fmt.Sprintf("the owner %s does not exist", notUser)) + }) + + t.Run("HeadHasTooManyColon", func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/master...one:two:many", user2.Name, user2repo). + AddTokenAuth(user2Ctx.Token) + resp := MakeRequest(t, req, http.StatusNotFound) + requireErrorContains(t, resp, fmt.Sprintf("must contain zero or one colon (:) but contains 2")) + }) + + for _, testCase := range []struct { + what string + baseHead string + }{ + { + what: "base", + baseHead: "notexists...master", + }, + { + what: "head", + baseHead: "master...notexists", + }, + } { + t.Run("BaseHeadNotExists "+testCase.what, func(t *testing.T) { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/compare/%s", user2.Name, user2repo, testCase.baseHead). + AddTokenAuth(user2Ctx.Token) + resp := MakeRequest(t, req, http.StatusNotFound) + requireErrorContains(t, resp, fmt.Sprintf("could not find 'notexists' to be a commit, branch or tag in the %s", testCase.what)) + }) + } }) } From 66e0988a43bb81031182a83891c2d3efa4c4b2f3 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 28 Jun 2025 20:27:41 +0200 Subject: [PATCH 027/297] feat: make API pull and compare endpoint references to head more robust It is best to prefix the reference to resolve any ambiguity once it has been determined that it is a branch or a tag. It was done in forgejo/forgejo#5991 for the base reference but not for the head reference. Refs forgejo/forgejo#5991 --- routers/api/v1/repo/pull.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 20f718d6c2..e7ff533d6a 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1202,6 +1202,11 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } headBranchRef := headBranch + if headIsBranch { + headBranchRef = git.BranchPrefix + headBranch + } else if headIsTag { + headBranchRef = git.TagPrefix + headBranch + } compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranchRef, headBranchRef, false, false) if err != nil { From b5e608f3e2d42ef3991e7def7e499cf4a1f8b566 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 29 Jun 2025 00:44:18 +0200 Subject: [PATCH 028/297] feat: bump the minimum required Git version from 2.0.0 to 2.34.1 (#8328) - Resolves forgejo/discussions#324 - Remove all checks of `CheckGitVersionAtLeast` that checked for a version below 2.34.1 - The version was chosen because Debian stable supports 2.39.5 and Ubuntu 22.04 LTS supports 2.34.1 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8328 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- .forgejo/workflows/testing-integration.yml | 19 +++++-- cmd/hook.go | 44 ++++++--------- cmd/serv.go | 10 ++-- modules/git/blame.go | 2 +- modules/git/commit.go | 6 +-- modules/git/git.go | 63 +++++++--------------- modules/git/git_test.go | 12 ----- modules/git/pipeline/revlist.go | 20 ------- modules/git/remote.go | 9 +--- modules/git/repo_commit.go | 34 ++---------- modules/git/repo_commitgraph.go | 6 +-- modules/lfs/pointer_scanner.go | 11 +--- routers/private/hook_post_receive.go | 2 +- routers/private/hook_pre_receive.go | 2 +- routers/private/hook_proc_receive.go | 5 -- routers/private/serv.go | 3 +- routers/web/misc/misc.go | 5 -- routers/web/repo/githttp.go | 4 +- services/gitdiff/gitdiff.go | 2 +- services/pull/temp_repo.go | 6 +-- services/repository/files/patch.go | 6 +-- tests/integration/git_test.go | 5 -- tests/integration/repo_signed_tag_test.go | 4 -- tests/integration/signing_git_test.go | 4 -- 24 files changed, 72 insertions(+), 212 deletions(-) diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 9e5cfb92ed..630de50435 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -33,11 +33,20 @@ jobs: steps: - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - - name: install git 2.30 - uses: ./.forgejo/workflows-composite/apt-install-from - with: - packages: git/bullseye git-lfs/bullseye - release: bullseye + - name: install git 2.34.1 + run: | + export DEBIAN_FRONTEND=noninteractive + + apt-get update -qq + apt-get -q install -y -qq curl ca-certificates + + curl -sS -o git-man.deb http://archive.ubuntu.com/ubuntu/pool/main/g/git/git-man_2.34.1-1ubuntu1_all.deb + curl -sS -o git.deb https://archive.ubuntu.com/ubuntu/pool/main/g/git/git_2.34.1-1ubuntu1_amd64.deb + curl -sS -o git-lfs.deb https://archive.ubuntu.com/ubuntu/pool/universe/g/git-lfs/git-lfs_3.0.2-1_amd64.deb + + apt-get -q install -y -qq ./git-man.deb + apt-get -q install -y -qq ./git.deb + apt-get -q install -y -qq ./git-lfs.deb - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-backend test-check' diff --git a/cmd/hook.go b/cmd/hook.go index 909cdfdf84..7378dc21ad 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -231,8 +231,6 @@ Forgejo or set your environment appropriately.`, "") } } - supportProcReceive := git.CheckGitVersionAtLeast("2.29") == nil - for scanner.Scan() { // TODO: support news feeds for wiki if isWiki { @@ -250,31 +248,25 @@ Forgejo or set your environment appropriately.`, "") total++ lastline++ - // If the ref is a branch or tag, check if it's protected - // if supportProcReceive all ref should be checked because - // permission check was delayed - if supportProcReceive || refFullName.IsBranch() || refFullName.IsTag() { - oldCommitIDs[count] = oldCommitID - newCommitIDs[count] = newCommitID - refFullNames[count] = refFullName - count++ - fmt.Fprint(out, "*") + // All references should be checked because permission check was delayed. + oldCommitIDs[count] = oldCommitID + newCommitIDs[count] = newCommitID + refFullNames[count] = refFullName + count++ + fmt.Fprint(out, "*") - if count >= hookBatchSize { - fmt.Fprintf(out, " Checking %d references\n", count) + if count >= hookBatchSize { + fmt.Fprintf(out, " Checking %d references\n", count) - hookOptions.OldCommitIDs = oldCommitIDs - hookOptions.NewCommitIDs = newCommitIDs - hookOptions.RefFullNames = refFullNames - extra := private.HookPreReceive(ctx, username, reponame, hookOptions) - if extra.HasError() { - return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error) - } - count = 0 - lastline = 0 + hookOptions.OldCommitIDs = oldCommitIDs + hookOptions.NewCommitIDs = newCommitIDs + hookOptions.RefFullNames = refFullNames + extra := private.HookPreReceive(ctx, username, reponame, hookOptions) + if extra.HasError() { + return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error) } - } else { - fmt.Fprint(out, ".") + count = 0 + lastline = 0 } if lastline >= hookBatchSize { fmt.Fprint(out, "\n") @@ -513,10 +505,6 @@ Forgejo or set your environment appropriately.`, "") return nil } - if git.CheckGitVersionAtLeast("2.29") != nil { - return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.") - } - reader := bufio.NewReader(os.Stdin) repoUser := os.Getenv(repo_module.EnvRepoUsername) repoName := os.Getenv(repo_module.EnvRepoName) diff --git a/cmd/serv.go b/cmd/serv.go index 1fac2d13f5..b0571a276c 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -193,12 +193,10 @@ func runServ(ctx context.Context, c *cli.Command) error { } if len(words) < 2 { - if git.CheckGitVersionAtLeast("2.29") == nil { - // for AGit Flow - if cmd == "ssh_info" { - fmt.Print(`{"type":"agit","version":1}`) - return nil - } + // for AGit Flow + if cmd == "ssh_info" { + fmt.Print(`{"type":"agit","version":1}`) + return nil } return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd) } diff --git a/modules/git/blame.go b/modules/git/blame.go index 4ff347e31b..868edab2b8 100644 --- a/modules/git/blame.go +++ b/modules/git/blame.go @@ -132,7 +132,7 @@ func (r *BlameReader) Close() error { // CreateBlameReader creates reader for given repository, commit and file func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) { var ignoreRevsFile *string - if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore { + if !bypassBlameIgnore { ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit) } diff --git a/modules/git/commit.go b/modules/git/commit.go index 96831e3ae4..1228b4523b 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -412,11 +412,7 @@ func (c *Commit) GetSubModule(entryname string) (string, error) { // GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only') func (c *Commit) GetBranchName() (string, error) { - cmd := NewCommand(c.repo.Ctx, "name-rev") - if CheckGitVersionAtLeast("2.13.0") == nil { - cmd.AddArguments("--exclude", "refs/tags/*") - } - cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) + cmd := NewCommand(c.repo.Ctx, "name-rev", "--exclude", "refs/tags/*", "--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path}) if err != nil { // handle special case where git can not describe commit diff --git a/modules/git/git.go b/modules/git/git.go index 1dfd0b5134..851b090b53 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -23,7 +23,7 @@ import ( ) // RequiredVersion is the minimum Git version required -const RequiredVersion = "2.0.0" +const RequiredVersion = "2.34.1" var ( // GitExecutable is the command name of git @@ -33,7 +33,6 @@ var ( // DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx DefaultContext context.Context - SupportProcReceive bool // >= 2.29 SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’ InvertedGitFlushEnv bool // 2.43.1 SupportCheckAttrOnBare bool // >= 2.40 @@ -113,7 +112,7 @@ func VersionInfo() string { format := "%s" args := []any{GitVersion.Original()} // Since git wire protocol has been released from git v2.18 - if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { + if setting.Git.EnableAutoGitWireProtocol { format += ", Wire Protocol %s Enabled" args = append(args, "Version 2") // for focus color } @@ -172,16 +171,13 @@ func InitFull(ctx context.Context) (err error) { _ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg")) } - // Since git wire protocol has been released from git v2.18 - if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { + if setting.Git.EnableAutoGitWireProtocol { globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") } // Explicitly disable credential helper, otherwise Git credentials might leak - if CheckGitVersionAtLeast("2.9") == nil { - globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") - } - SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil + globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") + SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil SupportCheckAttrOnBare = CheckGitVersionAtLeast("2.40") == nil if SupportHashSha256 { @@ -195,9 +191,6 @@ func InitFull(ctx context.Context) (err error) { SupportGrepMaxCount = CheckGitVersionAtLeast("2.38") == nil if setting.LFS.StartServer { - if CheckGitVersionAtLeast("2.1.2") != nil { - return errors.New("LFS server support requires Git >= 2.1.2") - } globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=") } @@ -234,38 +227,28 @@ func syncGitConfig() (err error) { } } - // Set git some configurations - these must be set to these values for gitea to work correctly + // Set git some configurations - these must be set to these values for forgejo to work correctly if err := configSet("core.quotePath", "false"); err != nil { return err } - if CheckGitVersionAtLeast("2.10") == nil { - if err := configSet("receive.advertisePushOptions", "true"); err != nil { - return err - } + if err := configSet("receive.advertisePushOptions", "true"); err != nil { + return err } - if CheckGitVersionAtLeast("2.18") == nil { - if err := configSet("core.commitGraph", "true"); err != nil { - return err - } - if err := configSet("gc.writeCommitGraph", "true"); err != nil { - return err - } - if err := configSet("fetch.writeCommitGraph", "true"); err != nil { - return err - } + if err := configSet("core.commitGraph", "true"); err != nil { + return err + } + if err := configSet("gc.writeCommitGraph", "true"); err != nil { + return err + } + if err := configSet("fetch.writeCommitGraph", "true"); err != nil { + return err } - if SupportProcReceive { - // set support for AGit flow - if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil { - return err - } - } else { - if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil { - return err - } + // set support for AGit flow + if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil { + return err } // Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user @@ -284,11 +267,6 @@ func syncGitConfig() (err error) { switch setting.Repository.Signing.Format { case "ssh": - // First do a git version check. - if CheckGitVersionAtLeast("2.34.0") != nil { - return errors.New("ssh signing requires Git >= 2.34.0") - } - // Get the ssh-keygen binary that Git will use. // This can be overridden in app.ini in [git.config] section, so we must // query this information. @@ -325,8 +303,7 @@ func syncGitConfig() (err error) { } } - // By default partial clones are disabled, enable them from git v2.22 - if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil { + if !setting.Git.DisablePartialClone { if err = configSet("uploadpack.allowfilter", "true"); err != nil { return err } diff --git a/modules/git/git_test.go b/modules/git/git_test.go index 01200dba68..38d4db169c 100644 --- a/modules/git/git_test.go +++ b/modules/git/git_test.go @@ -14,7 +14,6 @@ import ( "forgejo.org/modules/test" "forgejo.org/modules/util" - "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -105,10 +104,6 @@ func TestSyncConfigGPGFormat(t *testing.T) { }) t.Run("SSH format", func(t *testing.T) { - if CheckGitVersionAtLeast("2.34.0") != nil { - t.SkipNow() - } - r, err := os.OpenRoot(t.TempDir()) require.NoError(t, err) f, err := r.OpenFile("ssh-keygen", os.O_CREATE|os.O_TRUNC, 0o700) @@ -121,13 +116,6 @@ func TestSyncConfigGPGFormat(t *testing.T) { assert.True(t, gitConfigContains("[gpg]")) assert.True(t, gitConfigContains("format = ssh")) - t.Run("Old version", func(t *testing.T) { - oldVersion, err := version.NewVersion("2.33.0") - require.NoError(t, err) - defer test.MockVariableValue(&GitVersion, oldVersion)() - require.ErrorContains(t, syncGitConfig(), "ssh signing requires Git >= 2.34.0") - }) - t.Run("No ssh-keygen binary", func(t *testing.T) { require.NoError(t, r.Remove("ssh-keygen")) require.ErrorContains(t, syncGitConfig(), "git signing requires a ssh-keygen binary") diff --git a/modules/git/pipeline/revlist.go b/modules/git/pipeline/revlist.go index f39b7113bb..1ee8921854 100644 --- a/modules/git/pipeline/revlist.go +++ b/modules/git/pipeline/revlist.go @@ -16,26 +16,6 @@ import ( "forgejo.org/modules/log" ) -// RevListAllObjects runs rev-list --objects --all and writes to a pipewriter -func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, basePath string, errChan chan<- error) { - defer wg.Done() - defer revListWriter.Close() - - stderr := new(bytes.Buffer) - var errbuf strings.Builder - cmd := git.NewCommand(ctx, "rev-list", "--objects", "--all") - if err := cmd.Run(&git.RunOpts{ - Dir: basePath, - Stdout: revListWriter, - Stderr: stderr, - }); err != nil { - log.Error("git rev-list --objects --all [%s]: %v - %s", basePath, err, errbuf.String()) - err = fmt.Errorf("git rev-list --objects --all [%s]: %w - %s", basePath, err, errbuf.String()) - _ = revListWriter.CloseWithError(err) - errChan <- err - } -} - // RevListObjects run rev-list --objects from headSHA to baseSHA func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, tmpBasePath, headSHA, baseSHA string, errChan chan<- error) { defer wg.Done() diff --git a/modules/git/remote.go b/modules/git/remote.go index fb66d76ff0..83a02fe2be 100644 --- a/modules/git/remote.go +++ b/modules/git/remote.go @@ -12,14 +12,7 @@ import ( // GetRemoteAddress returns remote url of git repository in the repoPath with special remote name func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) { - var cmd *Command - if CheckGitVersionAtLeast("2.7") == nil { - cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName) - } else { - cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url") - } - - result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath}) + result, _, err := NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName).RunStdString(&RunOpts{Dir: repoPath}) if err != nil { return "", err } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 4c8516f828..41ca0e39b1 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -443,42 +443,18 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit, } func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) { - if CheckGitVersionAtLeast("2.7.0") == nil { - command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix) + command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix) - if limit != -1 { - command = command.AddOptionFormat("--count=%d", limit) - } - - stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path}) - if err != nil { - return nil, err - } - - branches := strings.Fields(stdout) - return branches, nil + if limit != -1 { + command = command.AddOptionFormat("--count=%d", limit) } - stdout, _, err := NewCommand(repo.Ctx, "branch").AddOptionValues("--contains", commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path}) + stdout, _, err := command.RunStdString(&RunOpts{Dir: repo.Path}) if err != nil { return nil, err } - refs := strings.Split(stdout, "\n") - - var max int - if len(refs) > limit { - max = limit - } else { - max = len(refs) - 1 - } - - branches := make([]string, max) - for i, ref := range refs[:max] { - parts := strings.Fields(ref) - - branches[i] = parts[len(parts)-1] - } + branches := strings.Fields(stdout) return branches, nil } diff --git a/modules/git/repo_commitgraph.go b/modules/git/repo_commitgraph.go index 492438be37..c3647bd894 100644 --- a/modules/git/repo_commitgraph.go +++ b/modules/git/repo_commitgraph.go @@ -11,10 +11,8 @@ import ( // WriteCommitGraph write commit graph to speed up repo access // this requires git v2.18 to be installed func WriteCommitGraph(ctx context.Context, repoPath string) error { - if CheckGitVersionAtLeast("2.18") == nil { - if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil { - return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err) - } + if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil { + return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err) } return nil } diff --git a/modules/lfs/pointer_scanner.go b/modules/lfs/pointer_scanner.go index 632ecd19ae..80da8e5222 100644 --- a/modules/lfs/pointer_scanner.go +++ b/modules/lfs/pointer_scanner.go @@ -39,16 +39,7 @@ func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan c go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg) // 1. Run batch-check on all objects in the repository - if git.CheckGitVersionAtLeast("2.6.0") != nil { - revListReader, revListWriter := io.Pipe() - shasToCheckReader, shasToCheckWriter := io.Pipe() - wg.Add(2) - go pipeline.CatFileBatchCheck(ctx, shasToCheckReader, catFileCheckWriter, &wg, basePath) - go pipeline.BlobsFromRevListObjects(revListReader, shasToCheckWriter, &wg) - go pipeline.RevListAllObjects(ctx, revListWriter, &wg, basePath, errChan) - } else { - go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan) - } + go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan) wg.Wait() close(pointerChan) diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index c7748b01c8..a856a7a00a 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -205,7 +205,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { // post update for agit pull request // FIXME: use pr.Flow to test whether it's an Agit PR or a GH PR - if git.SupportProcReceive && refFullName.IsPull() { + if refFullName.IsPull() { if repo == nil { repo = loadRepository(ctx, ownerName, repoName) if ctx.Written() { diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 4c0e9a8551..45992e8522 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -205,7 +205,7 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { preReceiveBranch(ourCtx, oldCommitID, newCommitID, refFullName) case refFullName.IsTag(): preReceiveTag(ourCtx, oldCommitID, newCommitID, refFullName) - case git.SupportProcReceive && refFullName.IsFor(): + case refFullName.IsFor(): preReceiveFor(ourCtx, oldCommitID, newCommitID, refFullName) default: if ourCtx.isOverQuota { diff --git a/routers/private/hook_proc_receive.go b/routers/private/hook_proc_receive.go index cd45794261..9f6e23f158 100644 --- a/routers/private/hook_proc_receive.go +++ b/routers/private/hook_proc_receive.go @@ -7,7 +7,6 @@ import ( "net/http" repo_model "forgejo.org/models/repo" - "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/private" "forgejo.org/modules/web" @@ -18,10 +17,6 @@ import ( // HookProcReceive proc-receive hook - only handles agit Proc-Receive requests at present func HookProcReceive(ctx *gitea_context.PrivateContext) { opts := web.GetForm(ctx).(*private.HookOptions) - if !git.SupportProcReceive { - ctx.Status(http.StatusNotFound) - return - } results, err := agit.ProcReceive(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, opts) if err != nil { diff --git a/routers/private/serv.go b/routers/private/serv.go index 4c5b7bbccb..a4029e354c 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -14,7 +14,6 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" user_model "forgejo.org/models/user" - "forgejo.org/modules/git" "forgejo.org/modules/log" "forgejo.org/modules/private" "forgejo.org/modules/setting" @@ -303,7 +302,7 @@ func ServCommand(ctx *context.PrivateContext) { // the permission check to read. The pre-receive hook will do another // permission check which ensure for non AGit flow references the write // permission is checked. - if git.SupportProcReceive && unitType == unit.TypeCode && ctx.FormString("verb") == "git-receive-pack" { + if unitType == unit.TypeCode && ctx.FormString("verb") == "git-receive-pack" { mode = perm.AccessModeRead } diff --git a/routers/web/misc/misc.go b/routers/web/misc/misc.go index 87b5247599..22fdccf79f 100644 --- a/routers/web/misc/misc.go +++ b/routers/web/misc/misc.go @@ -7,7 +7,6 @@ import ( "net/http" "path" - "forgejo.org/modules/git" "forgejo.org/modules/httpcache" "forgejo.org/modules/log" "forgejo.org/modules/setting" @@ -15,10 +14,6 @@ import ( ) func SSHInfo(rw http.ResponseWriter, req *http.Request) { - if !git.SupportProcReceive { - rw.WriteHeader(http.StatusNotFound) - return - } rw.Header().Set("content-type", "text/json;charset=UTF-8") _, err := rw.Write([]byte(`{"type":"agit","version":1}`)) if err != nil { diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 650b1d88f4..42302d0e02 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -183,9 +183,7 @@ func httpBase(ctx *context.Context) *serviceHandler { if repoExist { // Because of special ref "refs/for" .. , need delay write permission check - if git.SupportProcReceive { - accessMode = perm.AccessModeRead - } + accessMode = perm.AccessModeRead if ctx.Data["IsActionsToken"] == true { taskID := ctx.Data["ActionsTaskID"].(int64) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 6835dfbf36..7033264f18 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1157,7 +1157,7 @@ func GetDiffSimple(ctx context.Context, gitRepo *git.Repository, opts *DiffOptio // so if we are using at least this version of git we don't have to tell ParsePatch to do // the skipping for us parsePatchSkipToFile := opts.SkipTo - if opts.SkipTo != "" && git.CheckGitVersionAtLeast("2.31") == nil { + if opts.SkipTo != "" { cmdDiff.AddOptionFormat("--skip-to=%s", opts.SkipTo) parsePatchSkipToFile = "" } diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index 1805ffc527..76ae0df018 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -103,11 +103,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) remoteRepoName := "head_repo" baseBranch := "base" - fetchArgs := git.TrustedCmdArgs{"--no-tags"} - if git.CheckGitVersionAtLeast("2.25.0") == nil { - // Writing the commit graph can be slow and is not needed here - fetchArgs = append(fetchArgs, "--no-write-commit-graph") - } + fetchArgs := git.TrustedCmdArgs{"--no-tags", "--no-write-commit-graph"} // addCacheRepo adds git alternatives for the cacheRepoPath in the repoPath addCacheRepo := func(repoPath, cacheRepoPath string) error { diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index 5b1dd65b5a..18b5226c02 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -147,11 +147,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user stdout := &strings.Builder{} stderr := &strings.Builder{} - cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary") - if git.CheckGitVersionAtLeast("2.32") == nil { - cmdApply.AddArguments("-3") - } - + cmdApply := git.NewCommand(ctx, "apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary", "-3") if err := cmdApply.Run(&git.RunOpts{ Dir: t.basePath, Stdout: stdout, diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index 9a66781024..26cddf7288 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -771,11 +771,6 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string return func(t *testing.T) { defer tests.PrintCurrentTest(t)() - // skip this test if git version is low - if git.CheckGitVersionAtLeast("2.29") != nil { - return - } - gitRepo, err := git.OpenRepository(git.DefaultContext, dstPath) require.NoError(t, err) diff --git a/tests/integration/repo_signed_tag_test.go b/tests/integration/repo_signed_tag_test.go index 686690bd19..16d8841304 100644 --- a/tests/integration/repo_signed_tag_test.go +++ b/tests/integration/repo_signed_tag_test.go @@ -25,10 +25,6 @@ import ( ) func TestRepoSSHSignedTags(t *testing.T) { - if git.CheckGitVersionAtLeast("2.34") != nil { - t.Skip("Skipping, does not support SSH signing") - return - } defer tests.PrepareTestEnv(t)() // Preparations diff --git a/tests/integration/signing_git_test.go b/tests/integration/signing_git_test.go index e4c0d6049b..8b6b30ecab 100644 --- a/tests/integration/signing_git_test.go +++ b/tests/integration/signing_git_test.go @@ -42,10 +42,6 @@ func TestInstanceSigning(t *testing.T) { defer test.MockProtect(&setting.Repository.Signing.CRUDActions)() t.Run("SSH", func(t *testing.T) { - if git.CheckGitVersionAtLeast("2.34") != nil { - t.Skip("Skipping, does not support git SSH signing") - return - } defer tests.PrintCurrentTest(t)() pubKeyContent, err := os.ReadFile("tests/integration/ssh-signing-key.pub") From 14309837d41851ab0a33be3e6ebb928e0028cd95 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 02:52:27 +0200 Subject: [PATCH 029/297] Update module github.com/niklasfasching/go-org to v1.9.0 (forgejo) (#8335) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8335 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 510ec9c3ae..33ad5dbc67 100644 --- a/go.mod +++ b/go.mod @@ -79,7 +79,7 @@ require ( github.com/minio/minio-go/v7 v7.0.94 github.com/msteinert/pam/v2 v2.1.0 github.com/nektos/act v0.2.52 - github.com/niklasfasching/go-org v1.8.0 + github.com/niklasfasching/go-org v1.9.0 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 diff --git a/go.sum b/go.sum index 53558fddd7..45497a1b49 100644 --- a/go.sum +++ b/go.sum @@ -426,8 +426,8 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY= -github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg= +github.com/niklasfasching/go-org v1.9.0 h1:4/Sr68Qx06hjC9MVDB/4etGP67JionLHGscLMOClpnk= +github.com/niklasfasching/go-org v1.9.0/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= From b6c6981c300b632c8cad1d77bae2184e68aa138a Mon Sep 17 00:00:00 2001 From: Mathieu Fenniak Date: Sun, 29 Jun 2025 05:54:07 +0200 Subject: [PATCH 030/297] feat(ui): add repository description to og:image:alt (#8325) Followup to https://codeberg.org/forgejo/forgejo/pulls/6053 Adds the repository description to the "alt" tag of the OpenGraph summary card, improving accessibility when these images are displayed. Fixes #8192. Other summary cards, for issues and releases, are not modified as they already contain the issue title or release title, which seems reasonable. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8325 Reviewed-by: Gusted Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Mathieu Fenniak Co-committed-by: Mathieu Fenniak --- options/locale_next/locale_en-US.json | 1 + services/context/repo.go | 6 +++++- tests/integration/opengraph_test.go | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index b1c98e4551..a551db87dc 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -102,5 +102,6 @@ "admin.dashboard.cleanup_offline_runners": "Cleanup offline runners", "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more", "avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels", + "og.repo.summary_card.alt_description": "Summary card of repository %[1]s, described as: %[2]s", "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." } diff --git a/services/context/repo.go b/services/context/repo.go index cce3a5fa70..c8876d7166 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -644,7 +644,11 @@ func RepoAssignment(ctx *Context) context.CancelFunc { ctx.Data["OpenGraphImageURL"] = repo.SummaryCardURL() ctx.Data["OpenGraphImageWidth"] = cardWidth ctx.Data["OpenGraphImageHeight"] = cardHeight - ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.summary_card_alt", repo.FullName()) + if util.IsEmptyString(repo.Description) { + ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.summary_card_alt", repo.FullName()) + } else { + ctx.Data["OpenGraphImageAltText"] = ctx.Tr("og.repo.summary_card.alt_description", repo.FullName(), repo.Description) + } if repo.IsFork { RetrieveBaseRepo(ctx, repo) diff --git a/tests/integration/opengraph_test.go b/tests/integration/opengraph_test.go index 56fbedd351..aa6d8daf5c 100644 --- a/tests/integration/opengraph_test.go +++ b/tests/integration/opengraph_test.go @@ -98,7 +98,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:url": setting.AppURL + "/user27/repo49/src/branch/master/test/test.txt", "og:type": "object", "og:image": setting.AppURL + "user27/repo49/-/summary-card", - "og:image:alt": "Summary card of repository user27/repo49", + "og:image:alt": "Summary card of repository user27/repo49, described as: A wonderful repository with more than just a README.md", "og:image:width": "1200", "og:image:height": "600", "og:site_name": siteName, @@ -141,7 +141,7 @@ func TestOpenGraphProperties(t *testing.T) { "og:description": "A wonderful repository with more than just a README.md", "og:type": "object", "og:image": setting.AppURL + "user27/repo49/-/summary-card", - "og:image:alt": "Summary card of repository user27/repo49", + "og:image:alt": "Summary card of repository user27/repo49, described as: A wonderful repository with more than just a README.md", "og:image:width": "1200", "og:image:height": "600", "og:site_name": siteName, From 84ed8aa740057ce9d399c9a79c36f50c33484cbe Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 29 Jun 2025 08:06:38 +0200 Subject: [PATCH 031/297] chore: use standard library function (#8334) - As mentioned in the comment, use the standard library function now its available. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8334 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/git/repo_attribute_test.go | 32 +----------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index c69382e245..3d2c845fa0 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -5,7 +5,6 @@ package git import ( "context" - "fmt" "io" "io/fs" "os" @@ -197,7 +196,7 @@ func TestGitAttributeCheckerError(t *testing.T) { path := t.TempDir() // we can't use unittest.CopyDir because of an import cycle (git.Init in unittest) - require.NoError(t, CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo")))) + require.NoError(t, os.CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo")))) gitRepo, err := openRepositoryWithDefaultContext(path) require.NoError(t, err) @@ -324,32 +323,3 @@ func TestGitAttributeCheckerError(t *testing.T) { require.ErrorIs(t, err, fs.ErrClosed) }) } - -// CopyFS is adapted from https://github.com/golang/go/issues/62484 -// which should be available with go1.23 -func CopyFS(dir string, fsys fs.FS) error { - return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, _ error) error { - targ := filepath.Join(dir, filepath.FromSlash(path)) - if d.IsDir() { - return os.MkdirAll(targ, 0o777) - } - r, err := fsys.Open(path) - if err != nil { - return err - } - defer r.Close() - info, err := r.Stat() - if err != nil { - return err - } - w, err := os.OpenFile(targ, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o666|info.Mode()&0o777) - if err != nil { - return err - } - if _, err := io.Copy(w, r); err != nil { - w.Close() - return fmt.Errorf("copying %s: %v", path, err) - } - return w.Close() - }) -} From 33217a36332f9af270aea0366707083a63f6a525 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 09:37:43 +0200 Subject: [PATCH 032/297] Update dependency @stylistic/stylelint-plugin to v3.1.3 (forgejo) (#8336) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 604ff38c18..8ddc99ff5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "@playwright/test": "1.52.0", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "4.4.1", - "@stylistic/stylelint-plugin": "3.1.2", + "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "5.2.4", "@vitest/coverage-v8": "3.2.3", "@vitest/eslint-plugin": "1.2.2", @@ -3089,9 +3089,9 @@ } }, "node_modules/@stylistic/stylelint-plugin": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.2.tgz", - "integrity": "sha512-tylFJGMQo62alGazK74MNxFjMagYOHmBZiePZFOJK2n13JZta0uVkB3Bh5qodUmOLtRH+uxH297EibK14UKm8g==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.3.tgz", + "integrity": "sha512-85fsmzgsIVmyG3/GFrjuYj6Cz8rAM7IZiPiXCMiSMfoDOC1lOrzrXPDk24WqviAghnPqGpx8b0caK2PuewWGFg==", "dev": true, "license": "MIT", "dependencies": { @@ -3099,10 +3099,10 @@ "@csstools/css-tokenizer": "^3.0.1", "@csstools/media-query-list-parser": "^3.0.1", "is-plain-object": "^5.0.0", + "postcss": "^8.4.41", "postcss-selector-parser": "^6.1.2", "postcss-value-parser": "^4.2.0", - "style-search": "^0.1.0", - "stylelint": "^16.8.2" + "style-search": "^0.1.0" }, "engines": { "node": "^18.12 || >=20.9" diff --git a/package.json b/package.json index 3d71e94cd3..90023d762e 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@playwright/test": "1.52.0", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "4.4.1", - "@stylistic/stylelint-plugin": "3.1.2", + "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "5.2.4", "@vitest/coverage-v8": "3.2.3", "@vitest/eslint-plugin": "1.2.2", From ad1adabcbb72e81e513e4a9232a74fe901e78aa8 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 09:37:44 +0200 Subject: [PATCH 033/297] Update linters (forgejo) (#8338) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 414 ++++++++++++++++++++++------------------------ package.json | 12 +- 2 files changed, 207 insertions(+), 219 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ddc99ff5d..85d4401889 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,15 +71,15 @@ "@vitest/coverage-v8": "3.2.3", "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", - "eslint": "9.28.0", - "eslint-import-resolver-typescript": "4.4.3", + "eslint": "9.30.0", + "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", - "eslint-plugin-import-x": "4.15.1", + "eslint-plugin-import-x": "4.16.1", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", "eslint-plugin-playwright": "2.2.0", "eslint-plugin-regexp": "2.9.0", - "eslint-plugin-sonarjs": "3.0.2", + "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-vitest-globals": "1.5.0", @@ -92,13 +92,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.2", - "stylelint": "16.20.0", + "stylelint": "16.21.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "typescript": "5.8.3", - "typescript-eslint": "8.34.0", + "typescript-eslint": "8.35.0", "vite-string-plugin": "1.3.4", "vitest": "3.2.3" }, @@ -1021,9 +1021,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", - "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1049,9 +1049,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", - "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1146,9 +1146,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", - "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", + "version": "9.30.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.0.tgz", + "integrity": "sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==", "dev": true, "license": "MIT", "engines": { @@ -3561,17 +3561,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", - "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.0.tgz", + "integrity": "sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.34.0", - "@typescript-eslint/type-utils": "8.34.0", - "@typescript-eslint/utils": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0", + "@typescript-eslint/scope-manager": "8.35.0", + "@typescript-eslint/type-utils": "8.35.0", + "@typescript-eslint/utils": "8.35.0", + "@typescript-eslint/visitor-keys": "8.35.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3585,7 +3585,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.34.0", + "@typescript-eslint/parser": "^8.35.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -3601,16 +3601,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", - "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.0.tgz", + "integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.34.0", - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/typescript-estree": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0", + "@typescript-eslint/scope-manager": "8.35.0", + "@typescript-eslint/types": "8.35.0", + "@typescript-eslint/typescript-estree": "8.35.0", + "@typescript-eslint/visitor-keys": "8.35.0", "debug": "^4.3.4" }, "engines": { @@ -3626,14 +3626,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", - "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.0.tgz", + "integrity": "sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.34.0", - "@typescript-eslint/types": "^8.34.0", + "@typescript-eslint/tsconfig-utils": "^8.35.0", + "@typescript-eslint/types": "^8.35.0", "debug": "^4.3.4" }, "engines": { @@ -3648,14 +3648,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", - "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.0.tgz", + "integrity": "sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0" + "@typescript-eslint/types": "8.35.0", + "@typescript-eslint/visitor-keys": "8.35.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3666,9 +3666,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", - "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.0.tgz", + "integrity": "sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==", "dev": true, "license": "MIT", "engines": { @@ -3683,14 +3683,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", - "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.0.tgz", + "integrity": "sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.34.0", - "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/typescript-estree": "8.35.0", + "@typescript-eslint/utils": "8.35.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3707,9 +3707,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", - "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.0.tgz", + "integrity": "sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==", "dev": true, "license": "MIT", "engines": { @@ -3721,16 +3721,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", - "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.0.tgz", + "integrity": "sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.34.0", - "@typescript-eslint/tsconfig-utils": "8.34.0", - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/visitor-keys": "8.34.0", + "@typescript-eslint/project-service": "8.35.0", + "@typescript-eslint/tsconfig-utils": "8.35.0", + "@typescript-eslint/types": "8.35.0", + "@typescript-eslint/visitor-keys": "8.35.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3783,16 +3783,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", - "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.0.tgz", + "integrity": "sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.34.0", - "@typescript-eslint/types": "8.34.0", - "@typescript-eslint/typescript-estree": "8.34.0" + "@typescript-eslint/scope-manager": "8.35.0", + "@typescript-eslint/types": "8.35.0", + "@typescript-eslint/typescript-estree": "8.35.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3807,14 +3807,14 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", - "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.0.tgz", + "integrity": "sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.34.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.35.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3825,9 +3825,9 @@ } }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.1.tgz", - "integrity": "sha512-dd7yIp1hfJFX9ZlVLQRrh/Re9WMUHHmF9hrKD1yIvxcyNr2BhQ3xc1upAVhy8NijadnCswAxWQu8MkkSMC1qXQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.2.tgz", + "integrity": "sha512-tS+lqTU3N0kkthU+rYp0spAYq15DU8ld9kXkaKg9sbQqJNF+WPMuNHZQGCgdxrUOEO0j22RKMwRVhF1HTl+X8A==", "cpu": [ "arm" ], @@ -3839,9 +3839,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.1.tgz", - "integrity": "sha512-EzUPcMFtDVlo5yrbzMqUsGq3HnLXw+3ZOhSd7CUaDmbTtnrzM+RO2ntw2dm2wjbbc5djWj3yX0wzbbg8pLhx8g==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.2.tgz", + "integrity": "sha512-MffGiZULa/KmkNjHeuuflLVqfhqLv1vZLm8lWIyeADvlElJ/GLSOkoUX+5jf4/EGtfwrNFcEaB8BRas03KT0/Q==", "cpu": [ "arm64" ], @@ -3853,9 +3853,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.1.tgz", - "integrity": "sha512-nB+dna3q4kOleKFcSZJ/wDXIsAd1kpMO9XrVAt8tG3RDWJ6vi+Ic6bpz4cmg5tWNeCfHEY4KuqJCB+pKejPEmQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.2.tgz", + "integrity": "sha512-dzJYK5rohS1sYl1DHdJ3mwfwClJj5BClQnQSyAgEfggbUwA9RlROQSSbKBLqrGfsiC/VyrDPtbO8hh56fnkbsQ==", "cpu": [ "arm64" ], @@ -3867,9 +3867,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.1.tgz", - "integrity": "sha512-aKWHCrOGaCGwZcekf3TnczQoBxk5w//W3RZ4EQyhux6rKDwBPgDU9Y2yGigCV1Z+8DWqZgVGQi+hdpnlSy3a1w==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.2.tgz", + "integrity": "sha512-gaIMWK+CWtXcg9gUyznkdV54LzQ90S3X3dn8zlh+QR5Xy7Y+Efqw4Rs4im61K1juy4YNb67vmJsCDAGOnIeffQ==", "cpu": [ "x64" ], @@ -3881,9 +3881,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.1.tgz", - "integrity": "sha512-4dIEMXrXt0UqDVgrsUd1I+NoIzVQWXy/CNhgpfS75rOOMK/4Abn0Mx2M2gWH4Mk9+ds/ASAiCmqoUFynmMY5hA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.2.tgz", + "integrity": "sha512-S7QpkMbVoVJb0xwHFwujnwCAEDe/596xqY603rpi/ioTn9VDgBHnCCxh+UFrr5yxuMH+dliHfjwCZJXOPJGPnw==", "cpu": [ "x64" ], @@ -3895,9 +3895,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.1.tgz", - "integrity": "sha512-vtvS13IXPs1eE8DuS/soiosqMBeyh50YLRZ+p7EaIKAPPeevRnA9G/wu/KbVt01ZD5qiGjxS+CGIdVC7I6gTOw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.2.tgz", + "integrity": "sha512-+XPUMCuCCI80I46nCDFbGum0ZODP5NWGiwS3Pj8fOgsG5/ctz+/zzuBlq/WmGa+EjWZdue6CF0aWWNv84sE1uw==", "cpu": [ "arm" ], @@ -3909,9 +3909,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.1.tgz", - "integrity": "sha512-BfdnN6aZ7NcX8djW8SR6GOJc+K+sFhWRF4vJueVE0vbUu5N1bLnBpxJg1TGlhSyo+ImC4SR0jcNiKN0jdoxt+A==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.2.tgz", + "integrity": "sha512-sqvUyAd1JUpwbz33Ce2tuTLJKM+ucSsYpPGl2vuFwZnEIg0CmdxiZ01MHQ3j6ExuRqEDUCy8yvkDKvjYFPb8Zg==", "cpu": [ "arm" ], @@ -3923,9 +3923,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.1.tgz", - "integrity": "sha512-Jhge7lFtH0QqfRz2PyJjJXWENqywPteITd+nOS0L6AhbZli+UmEyGBd2Sstt1c+l9C+j/YvKTl9wJo9PPmsFNg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.2.tgz", + "integrity": "sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==", "cpu": [ "arm64" ], @@ -3937,9 +3937,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.1.tgz", - "integrity": "sha512-ofdK/ow+ZSbSU0pRoB7uBaiRHeaAOYQFU5Spp87LdcPL/P1RhbCTMSIYVb61XWzsVEmYKjHFtoIE0wxP6AFvrA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.2.tgz", + "integrity": "sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==", "cpu": [ "arm64" ], @@ -3951,9 +3951,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.1.tgz", - "integrity": "sha512-eC8SXVn8de67HacqU7PoGdHA+9tGbqfEdD05AEFRAB81ejeQtNi5Fx7lPcxpLH79DW0BnMAHau3hi4RVkHfSCw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.2.tgz", + "integrity": "sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==", "cpu": [ "ppc64" ], @@ -3965,9 +3965,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.1.tgz", - "integrity": "sha512-fIkwvAAQ41kfoGWfzeJ33iLGShl0JEDZHrMnwTHMErUcPkaaZRJYjQjsFhMl315NEQ4mmTlC+2nfK/J2IszDOw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.2.tgz", + "integrity": "sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==", "cpu": [ "riscv64" ], @@ -3979,9 +3979,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.1.tgz", - "integrity": "sha512-RAAszxImSOFLk44aLwnSqpcOdce8sBcxASledSzuFAd8Q5ZhhVck472SisspnzHdc7THCvGXiUeZ2hOC7NUoBQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.2.tgz", + "integrity": "sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==", "cpu": [ "riscv64" ], @@ -3993,9 +3993,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.1.tgz", - "integrity": "sha512-QoP9vkY+THuQdZi05bA6s6XwFd6HIz3qlx82v9bTOgxeqin/3C12Ye7f7EOD00RQ36OtOPWnhEMMm84sv7d1XQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.2.tgz", + "integrity": "sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==", "cpu": [ "s390x" ], @@ -4007,9 +4007,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.1.tgz", - "integrity": "sha512-/p77cGN/h9zbsfCseAP5gY7tK+7+DdM8fkPfr9d1ye1fsF6bmtGbtZN6e/8j4jCZ9NEIBBkT0GhdgixSelTK9g==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.2.tgz", + "integrity": "sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==", "cpu": [ "x64" ], @@ -4021,9 +4021,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.1.tgz", - "integrity": "sha512-wInTqT3Bu9u50mDStEig1v8uxEL2Ht+K8pir/YhyyrM5ordJtxoqzsL1vR/CQzOJuDunUTrDkMM0apjW/d7/PA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.2.tgz", + "integrity": "sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==", "cpu": [ "x64" ], @@ -4035,9 +4035,9 @@ ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.1.tgz", - "integrity": "sha512-eNwqO5kUa+1k7yFIircwwiniKWA0UFHo2Cfm8LYgkh9km7uMad+0x7X7oXbQonJXlqfitBTSjhA0un+DsHIrhw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.2.tgz", + "integrity": "sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==", "cpu": [ "wasm32" ], @@ -4052,9 +4052,9 @@ } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.1.tgz", - "integrity": "sha512-Eaz1xMUnoa2mFqh20mPqSdbYl6crnk8HnIXDu6nsla9zpgZJZO8w3c1gvNN/4Eb0RXRq3K9OG6mu8vw14gIqiA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.2.tgz", + "integrity": "sha512-EdFbGn7o1SxGmN6aZw9wAkehZJetFPao0VGZ9OMBwKx6TkvDuj6cNeLimF/Psi6ts9lMOe+Dt6z19fZQ9Ye2fw==", "cpu": [ "arm64" ], @@ -4066,9 +4066,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.1.tgz", - "integrity": "sha512-H/+d+5BGlnEQif0gnwWmYbYv7HJj563PUKJfn8PlmzF8UmF+8KxdvXdwCsoOqh4HHnENnoLrav9NYBrv76x1wQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.2.tgz", + "integrity": "sha512-JY9hi1p7AG+5c/dMU8o2kWemM8I6VZxfGwn1GCtf3c5i+IKcMo2NQ8OjZ4Z3/itvY/Si3K10jOBQn7qsD/whUA==", "cpu": [ "ia32" ], @@ -4080,9 +4080,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.1.tgz", - "integrity": "sha512-rS86wI4R6cknYM3is3grCb/laE8XBEbpWAMSIPjYfmYp75KL5dT87jXF2orDa4tQYg5aajP5G8Fgh34dRyR+Rw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.2.tgz", + "integrity": "sha512-ryoo+EB19lMxAd80ln9BVf8pdOAxLb97amrQ3SFN9OCRn/5M5wvwDgAe4i8ZjhpbiHoDeP8yavcTEnpKBo7lZg==", "cpu": [ "x64" ], @@ -7262,19 +7262,19 @@ } }, "node_modules/eslint": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", - "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", + "version": "9.30.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.0.tgz", + "integrity": "sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.28.0", + "@eslint/js": "9.30.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -7286,9 +7286,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -7339,14 +7339,14 @@ } }, "node_modules/eslint-import-context": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.8.tgz", - "integrity": "sha512-bq+F7nyc65sKpZGT09dY0S0QrOnQtuDVIfyTGQ8uuvtMIF7oHp6CEP3mouN0rrnYF3Jqo6Ke0BfU/5wASZue1w==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", + "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", "dev": true, "license": "MIT", "dependencies": { "get-tsconfig": "^4.10.1", - "stable-hash-x": "^0.1.1" + "stable-hash-x": "^0.2.0" }, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" @@ -7364,9 +7364,9 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.3.tgz", - "integrity": "sha512-elVDn1eWKFrWlzxlWl9xMt8LltjKl161Ix50JFC50tHXI5/TRP32SNEqlJ/bo/HV+g7Rou/tlPQU2AcRtIhrOg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.4.tgz", + "integrity": "sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==", "dev": true, "license": "ISC", "dependencies": { @@ -7374,7 +7374,7 @@ "eslint-import-context": "^0.1.8", "get-tsconfig": "^4.10.1", "is-bun-module": "^2.0.0", - "stable-hash-x": "^0.1.1", + "stable-hash-x": "^0.2.0", "tinyglobby": "^0.2.14", "unrs-resolver": "^1.7.11" }, @@ -7412,21 +7412,21 @@ } }, "node_modules/eslint-plugin-import-x": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.15.1.tgz", - "integrity": "sha512-JfVpNg1qMkPD66iaSgmMoSYeUCGS8UFSm3GwHV0IbuV3Knar/SyK5qqCct9+AxoMIzaM+KSO7KK5pOeOkC/3GQ==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz", + "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "^8.33.1", + "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", "debug": "^4.4.1", - "eslint-import-context": "^0.1.7", + "eslint-import-context": "^0.1.9", "is-glob": "^4.0.3", "minimatch": "^9.0.3 || ^10.0.1", "semver": "^7.7.2", - "stable-hash-x": "^0.1.1", - "unrs-resolver": "^1.7.10" + "stable-hash-x": "^0.2.0", + "unrs-resolver": "^1.9.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7535,9 +7535,9 @@ } }, "node_modules/eslint-plugin-sonarjs": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.2.tgz", - "integrity": "sha512-LxjbfwI7ypENeTmGyKmDyNux3COSkMi7H/6Cal5StSLQ6edf0naP45SZR43OclaNR7WfhVTZdhOn63q3/Y6puQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-3.0.4.tgz", + "integrity": "sha512-ftQcP811kRJNXapqpQXHErEoVOdTPfYPPYd7n3AExIPwv4qWKKHf4slFvXmodiOnfgy1Tl3waPZZLD7lcvJOtw==", "dev": true, "license": "LGPL-3.0-only", "dependencies": { @@ -7546,10 +7546,11 @@ "bytes": "3.1.2", "functional-red-black-tree": "1.0.1", "jsx-ast-utils": "3.3.5", + "lodash.merge": "4.6.2", "minimatch": "9.0.5", "scslre": "0.3.0", - "semver": "7.7.1", - "typescript": "^5" + "semver": "7.7.2", + "typescript": ">=5" }, "peerDependencies": { "eslint": "^8.0.0 || ^9.0.0" @@ -7588,19 +7589,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-plugin-sonarjs/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-plugin-toml": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/eslint-plugin-toml/-/eslint-plugin-toml-0.12.0.tgz", @@ -9993,9 +9981,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.36.0.tgz", - "integrity": "sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", "dev": true, "license": "MIT" }, @@ -13590,9 +13578,9 @@ } }, "node_modules/stable-hash-x": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.1.1.tgz", - "integrity": "sha512-l0x1D6vhnsNUGPFVDx45eif0y6eedVC8nm5uACTrVFJFtl2mLRW17aWtVyxFCpn5t94VUPkjU8vSLwIuwwqtJQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", + "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", "dev": true, "license": "MIT", "engines": { @@ -13801,9 +13789,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.20.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.20.0.tgz", - "integrity": "sha512-B5Myu9WRxrgKuLs3YyUXLP2H0mrbejwNxPmyADlACWwFsrL8Bmor/nTSh4OMae5sHjOz6gkSeccQH34gM4/nAw==", + "version": "16.21.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.21.0.tgz", + "integrity": "sha512-ki3PpJGG7xhm3WtINoWGnlvqAmbqSexoRMbEMJzlwewSIOqPRKPlq452c22xAdEJISVi80r+I7KL9GPUiwFgbg==", "dev": true, "funding": [ { @@ -13817,9 +13805,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3", "@csstools/selector-specificity": "^5.0.0", "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", @@ -13830,21 +13818,21 @@ "debug": "^4.4.1", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^10.1.0", + "file-entry-cache": "^10.1.1", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^7.0.4", + "ignore": "^7.0.5", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.36.0", + "known-css-properties": "^0.37.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.5.3", + "postcss": "^8.5.5", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.0", @@ -14872,15 +14860,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.34.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.0.tgz", - "integrity": "sha512-MRpfN7uYjTrTGigFCt8sRyNqJFhjN0WwZecldaqhWm+wy0gaRt8Edb/3cuUy0zdq2opJWT6iXINKAtewnDOltQ==", + "version": "8.35.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.0.tgz", + "integrity": "sha512-uEnz70b7kBz6eg/j0Czy6K5NivaYopgxRjsnAJ2Fx5oTLo3wefTHIbL7AkQr1+7tJCRVpTs/wiM8JR/11Loq9A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.34.0", - "@typescript-eslint/parser": "8.34.0", - "@typescript-eslint/utils": "8.34.0" + "@typescript-eslint/eslint-plugin": "8.35.0", + "@typescript-eslint/parser": "8.35.0", + "@typescript-eslint/utils": "8.35.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14955,38 +14943,38 @@ } }, "node_modules/unrs-resolver": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.1.tgz", - "integrity": "sha512-4AZVxP05JGN6DwqIkSP4VKLOcwQa5l37SWHF/ahcuqBMbfxbpN1L1QKafEhWCziHhzKex9H/AR09H0OuVyU+9g==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.2.tgz", + "integrity": "sha512-VUyWiTNQD7itdiMuJy+EuLEErLj3uwX/EpHQF8EOf33Dq3Ju6VW1GXm+swk6+1h7a49uv9fKZ+dft9jU7esdLA==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.2.2" + "napi-postinstall": "^0.2.4" }, "funding": { "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.9.1", - "@unrs/resolver-binding-android-arm64": "1.9.1", - "@unrs/resolver-binding-darwin-arm64": "1.9.1", - "@unrs/resolver-binding-darwin-x64": "1.9.1", - "@unrs/resolver-binding-freebsd-x64": "1.9.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.9.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.9.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.9.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.9.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.9.1", - "@unrs/resolver-binding-linux-x64-musl": "1.9.1", - "@unrs/resolver-binding-wasm32-wasi": "1.9.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.9.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.9.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.9.1" + "@unrs/resolver-binding-android-arm-eabi": "1.9.2", + "@unrs/resolver-binding-android-arm64": "1.9.2", + "@unrs/resolver-binding-darwin-arm64": "1.9.2", + "@unrs/resolver-binding-darwin-x64": "1.9.2", + "@unrs/resolver-binding-freebsd-x64": "1.9.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.9.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.9.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.9.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-x64-musl": "1.9.2", + "@unrs/resolver-binding-wasm32-wasi": "1.9.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.9.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.9.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.9.2" } }, "node_modules/update-browserslist-db": { diff --git a/package.json b/package.json index 90023d762e..c9afea9a99 100644 --- a/package.json +++ b/package.json @@ -70,15 +70,15 @@ "@vitest/coverage-v8": "3.2.3", "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", - "eslint": "9.28.0", - "eslint-import-resolver-typescript": "4.4.3", + "eslint": "9.30.0", + "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", - "eslint-plugin-import-x": "4.15.1", + "eslint-plugin-import-x": "4.16.1", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", "eslint-plugin-playwright": "2.2.0", "eslint-plugin-regexp": "2.9.0", - "eslint-plugin-sonarjs": "3.0.2", + "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-vitest-globals": "1.5.0", @@ -91,13 +91,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.2", - "stylelint": "16.20.0", + "stylelint": "16.21.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "typescript": "5.8.3", - "typescript-eslint": "8.34.0", + "typescript-eslint": "8.35.0", "vite-string-plugin": "1.3.4", "vitest": "3.2.3" }, From 7a881e2f2648b724a905a0f61e694055ec667e24 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 10:59:35 +0200 Subject: [PATCH 034/297] Update dependency happy-dom to v18.0.1 (forgejo) (#8337) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 85d4401889..076882bb07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,7 +87,7 @@ "eslint-plugin-vue-scoped-css": "2.10.0", "eslint-plugin-wc": "3.0.1", "globals": "16.1.0", - "happy-dom": "18.0.0", + "happy-dom": "18.0.1", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", @@ -8551,9 +8551,9 @@ } }, "node_modules/happy-dom": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.0.tgz", - "integrity": "sha512-o3p2Axi1EdIfMaOUulDzO/5yXzLLV0g/54eLPVrkt3u20r3yOuOenHpyp2clAJ0eHMc+HyE139ulQxl+8pEJIw==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-18.0.1.tgz", + "integrity": "sha512-qn+rKOW7KWpVTtgIUi6RVmTBZJSe2k0Db0vh1f7CWrWclkkc7/Q+FrOfkZIb2eiErLyqu5AXEzE7XthO9JVxRA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c9afea9a99..c88721a647 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "eslint-plugin-vue-scoped-css": "2.10.0", "eslint-plugin-wc": "3.0.1", "globals": "16.1.0", - "happy-dom": "18.0.0", + "happy-dom": "18.0.1", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", From 3feceb10c79974e74bc078a7292c836dbb883c57 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 11:40:47 +0200 Subject: [PATCH 035/297] Update dependency @stylistic/eslint-plugin to v5 (forgejo) (#8340) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 15 ++++++++------- package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 076882bb07..63bb2d4cf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,7 +65,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.52.0", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "4.4.1", + "@stylistic/eslint-plugin": "5.0.0", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "5.2.4", "@vitest/coverage-v8": "3.2.3", @@ -3056,15 +3056,16 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.4.1.tgz", - "integrity": "sha512-CEigAk7eOLyHvdgmpZsKFwtiqS2wFwI1fn4j09IU9GmD4euFM4jEBAViWeCqaNLlbX2k2+A/Fq9cje4HQBXuJQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.0.0.tgz", + "integrity": "sha512-nVV2FSzeTJ3oFKw+3t9gQYQcrgbopgCASSY27QOtkhEGgSfdQQjDmzZd41NeT1myQ8Wc6l+pZllST9qIu4NKzg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.32.1", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/types": "^8.34.1", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.2" }, diff --git a/package.json b/package.json index c88721a647..258a041451 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.52.0", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "4.4.1", + "@stylistic/eslint-plugin": "5.0.0", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "5.2.4", "@vitest/coverage-v8": "3.2.3", From 216074122163ce5161be78a1ad747c0c47f357c0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 11:50:41 +0200 Subject: [PATCH 036/297] Update dependency @vitejs/plugin-vue to v6 (forgejo) (#8341) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 22 ++++++++++++++++------ package.json | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 63bb2d4cf6..b8d258e28f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,7 @@ "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.0.0", "@stylistic/stylelint-plugin": "3.1.3", - "@vitejs/plugin-vue": "5.2.4", + "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.3", "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", @@ -2221,6 +2221,13 @@ "object-assign": "^4.1.1" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", + "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/plugin-commonjs": { "version": "22.0.2", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz", @@ -4095,16 +4102,19 @@ ] }, "node_modules/@vitejs/plugin-vue": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", - "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz", + "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==", "dev": true, "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.19" + }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", "vue": "^3.2.25" } }, diff --git a/package.json b/package.json index 258a041451..2bcf441081 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.0.0", "@stylistic/stylelint-plugin": "3.1.3", - "@vitejs/plugin-vue": "5.2.4", + "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.3", "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", From 920f6d24d28d2c044eb3ed10e6a281a4523b9fbb Mon Sep 17 00:00:00 2001 From: floss4good Date: Sun, 29 Jun 2025 12:08:03 +0200 Subject: [PATCH 037/297] fix: load OldMilestone based on OldMilestoneID, not MilestoneID (#8330) Fixes #8329 ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. ### Documentation - [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. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8330 Reviewed-by: Robert Wolff Co-authored-by: floss4good Co-committed-by: floss4good --- models/fixtures/comment.yml | 38 ++++++++++++++++++++++++- models/issues/comment_list.go | 16 +++++------ tests/integration/issue_comment_test.go | 26 ++++++++++++++++- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/models/fixtures/comment.yml b/models/fixtures/comment.yml index 34407d6f81..6908d85dda 100644 --- a/models/fixtures/comment.yml +++ b/models/fixtures/comment.yml @@ -186,10 +186,46 @@ type: 8 # milestone poster_id: 1 issue_id: 1 # in repo_id 1 - milestone_id: 10 # not exsting milestone + milestone_id: 10 # not existing milestone old_milestone_id: 0 created_unix: 946685080 +- + id: 2004 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 1 + old_milestone_id: 10 # not existing (ghost) milestone + created_unix: 946685085 + +- + id: 2005 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 10 # not existing (ghost) milestone + old_milestone_id: 1 + created_unix: 946685090 + +- + id: 2006 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 11 # not existing (ghost) milestone + old_milestone_id: 10 # not existing (ghost) milestone + created_unix: 946685095 + +- + id: 2007 + type: 8 # milestone + poster_id: 1 + issue_id: 1 # in repo_id 1 + milestone_id: 0 + old_milestone_id: 11 # not existing (ghost) milestone + created_unix: 946685100 + - id: 2010 type: 30 # project diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index 7285e347b4..9b502d1c91 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -101,7 +101,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { return nil } - milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) + milestones := make(map[int64]*Milestone, len(milestoneIDs)) left := len(milestoneIDs) for left > 0 { limit := db.DefaultMaxInSize @@ -110,7 +110,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { } err := db.GetEngine(ctx). In("id", milestoneIDs[:limit]). - Find(&milestoneMaps) + Find(&milestones) if err != nil { return err } @@ -118,8 +118,8 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { milestoneIDs = milestoneIDs[limit:] } - for _, issue := range comments { - issue.Milestone = milestoneMaps[issue.MilestoneID] + for _, comment := range comments { + comment.Milestone = milestones[comment.MilestoneID] } return nil } @@ -140,7 +140,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { return nil } - milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) + milestones := make(map[int64]*Milestone, len(milestoneIDs)) left := len(milestoneIDs) for left > 0 { limit := db.DefaultMaxInSize @@ -149,7 +149,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { } err := db.GetEngine(ctx). In("id", milestoneIDs[:limit]). - Find(&milestoneMaps) + Find(&milestones) if err != nil { return err } @@ -157,8 +157,8 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { milestoneIDs = milestoneIDs[limit:] } - for _, issue := range comments { - issue.OldMilestone = milestoneMaps[issue.MilestoneID] + for _, comment := range comments { + comment.OldMilestone = milestones[comment.OldMilestoneID] } return nil } diff --git a/tests/integration/issue_comment_test.go b/tests/integration/issue_comment_test.go index f77bfaa9bd..0c53c3028b 100644 --- a/tests/integration/issue_comment_test.go +++ b/tests/integration/issue_comment_test.go @@ -102,11 +102,35 @@ func TestIssueCommentChangeMilestone(t *testing.T) { []string{"user1 removed this from the milestone2 milestone"}, []string{"/user1", "/user2/repo1/milestone/2"}) - // Deleted milestone + // Added milestone that in the meantime was deleted testIssueCommentChangeEvent(t, htmlDoc, "2003", "octicon-milestone", "User One", "/user1", []string{"user1 added this to the (deleted) milestone"}, []string{"/user1"}) + + // Modified milestone - from a meantime deleted one to a valid one + testIssueCommentChangeEvent(t, htmlDoc, "2004", + "octicon-milestone", "User One", "/user1", + []string{"user1 modified the milestone from (deleted) to milestone1"}, + []string{"/user1", "/user2/repo1/milestone/1"}) + + // Modified milestone - from a valid one to a meantime deleted one + testIssueCommentChangeEvent(t, htmlDoc, "2005", + "octicon-milestone", "User One", "/user1", + []string{"user1 modified the milestone from milestone1 to (deleted)"}, + []string{"/user1", "/user2/repo1/milestone/1"}) + + // Modified milestone - from a meantime deleted one to a meantime deleted one + testIssueCommentChangeEvent(t, htmlDoc, "2006", + "octicon-milestone", "User One", "/user1", + []string{"user1 modified the milestone from (deleted) to (deleted)"}, + []string{"/user1"}) + + // Removed milestone that in the meantime was deleted + testIssueCommentChangeEvent(t, htmlDoc, "2007", + "octicon-milestone", "User One", "/user1", + []string{"user1 removed this from the (deleted) milestone"}, + []string{"/user1"}) } func TestIssueCommentChangeProject(t *testing.T) { From 447c5789bdaf092dca6af7282bb20931999ff456 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 29 Jun 2025 13:04:28 +0200 Subject: [PATCH 038/297] fix(ci): add install-minimum-git-version helper for workflows (#8345) https://codeberg.org/forgejo-integration/forgejo/actions/runs/10592#jobstep-3-14 failed with > E: Packages were downgraded and -y was used without --allow-downgrades. Running the tests is done following the instructions in the workflow: ``` # - uncomment [on].pull_request # - swap 'forgejo-integration' and 'forgejo-coding' # - open a pull request at https://codeberg.org/forgejo/forgejo and fix things # - swap 'forgejo-integration' and 'forgejo-coding' # - comment [on].pull_request ``` The result of the test is available at https://codeberg.org/forgejo/forgejo/actions/runs/85408/jobs/0 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8345 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- .../install-minimum-git-version/action.yaml | 22 ++++++++++++++++++ .forgejo/workflows/testing-integration.yml | 23 ++++--------------- 2 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 .forgejo/workflows-composite/install-minimum-git-version/action.yaml diff --git a/.forgejo/workflows-composite/install-minimum-git-version/action.yaml b/.forgejo/workflows-composite/install-minimum-git-version/action.yaml new file mode 100644 index 0000000000..d4e6e3f2a7 --- /dev/null +++ b/.forgejo/workflows-composite/install-minimum-git-version/action.yaml @@ -0,0 +1,22 @@ +# +# Install the minimal version of Git supported by Forgejo +# +runs: + using: "composite" + steps: + - name: install git and git-lfs + run: | + set -x + + export DEBIAN_FRONTEND=noninteractive + + apt-get update -qq + apt-get -q install -y -qq curl ca-certificates + + curl -sS -o /tmp/git-man.deb http://archive.ubuntu.com/ubuntu/pool/main/g/git/git-man_2.34.1-1ubuntu1_all.deb + curl -sS -o /tmp/git.deb https://archive.ubuntu.com/ubuntu/pool/main/g/git/git_2.34.1-1ubuntu1_amd64.deb + curl -sS -o /tmp/git-lfs.deb https://archive.ubuntu.com/ubuntu/pool/universe/g/git-lfs/git-lfs_3.0.2-1_amd64.deb + + apt-get -q install --allow-downgrades -y -qq /tmp/git-man.deb + apt-get -q install --allow-downgrades -y -qq /tmp/git.deb + apt-get -q install --allow-downgrades -y -qq /tmp/git-lfs.deb diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 630de50435..102a2d9774 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -33,20 +33,8 @@ jobs: steps: - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - - name: install git 2.34.1 - run: | - export DEBIAN_FRONTEND=noninteractive - - apt-get update -qq - apt-get -q install -y -qq curl ca-certificates - - curl -sS -o git-man.deb http://archive.ubuntu.com/ubuntu/pool/main/g/git/git-man_2.34.1-1ubuntu1_all.deb - curl -sS -o git.deb https://archive.ubuntu.com/ubuntu/pool/main/g/git/git_2.34.1-1ubuntu1_amd64.deb - curl -sS -o git-lfs.deb https://archive.ubuntu.com/ubuntu/pool/universe/g/git-lfs/git-lfs_3.0.2-1_amd64.deb - - apt-get -q install -y -qq ./git-man.deb - apt-get -q install -y -qq ./git.deb - apt-get -q install -y -qq ./git-lfs.deb + - name: install git 2.34.1 and git-lfs 3.0.2 + uses: ./.forgejo/workflows-composite/install-minimum-git-version - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-backend test-check' @@ -64,11 +52,8 @@ jobs: steps: - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env - - name: install git 2.30 - uses: ./.forgejo/workflows-composite/apt-install-from - with: - packages: git/bullseye git-lfs/bullseye - release: bullseye + - name: install git 2.34.1 and git-lfs 3.0.2 + uses: ./.forgejo/workflows-composite/install-minimum-git-version - uses: ./.forgejo/workflows-composite/build-backend - run: | su forgejo -c 'make test-sqlite-migration test-sqlite' From 878ce241a40503bd716a9f711ac49fd14695da8a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 13:52:24 +0200 Subject: [PATCH 039/297] Update dependency htmx.org to v2 (forgejo) (#8342) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8342 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- web_src/js/htmx.js | 2 +- webpack.config.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8d258e28f..15b3243b33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "esbuild-loader": "4.3.0", "escape-goat": "4.0.0", "fast-glob": "3.3.3", - "htmx.org": "1.9.12", + "htmx.org": "2.0.6", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", @@ -8745,9 +8745,9 @@ } }, "node_modules/htmx.org": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz", - "integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.6.tgz", + "integrity": "sha512-7ythjYneGSk3yCHgtCnQeaoF+D+o7U2LF37WU3O0JYv3gTZSicdEFiI/Ai/NJyC5ZpYJWMpUb11OC5Lr6AfAqA==", "license": "0BSD" }, "node_modules/iconv-lite": { diff --git a/package.json b/package.json index 2bcf441081..ff4dd825f4 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "esbuild-loader": "4.3.0", "escape-goat": "4.0.0", "fast-glob": "3.3.3", - "htmx.org": "1.9.12", + "htmx.org": "2.0.6", "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", diff --git a/web_src/js/htmx.js b/web_src/js/htmx.js index 5ca3018308..c4893f7c1b 100644 --- a/web_src/js/htmx.js +++ b/web_src/js/htmx.js @@ -1,4 +1,4 @@ -import * as htmx from 'htmx.org'; +import htmx from 'htmx.org'; import {showErrorToast} from './modules/toast.js'; // https://github.com/bigskysoftware/idiomorph#htmx diff --git a/webpack.config.js b/webpack.config.js index 7729035972..8f9949d7b1 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -238,7 +238,7 @@ export default { activeModules: true, }), new webpack.ProvidePlugin({ // for htmx extensions - htmx: 'htmx.org', + htmx: ['htmx.org', 'default'], }), new DefinePlugin({ __VUE_OPTIONS_API__: true, // at the moment, many Vue components still use the Vue Options API From 31fc02332a6ad7b2dea19ff70344253d2ebc1a3a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 29 Jun 2025 13:57:15 +0200 Subject: [PATCH 040/297] Update dependency svgo to v4 (forgejo) (#8343) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8343 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 62 ++++++------------- package.json | 2 +- public/assets/img/svg/gitea-alt.svg | 2 +- public/assets/img/svg/gitea-chef.svg | 2 +- public/assets/img/svg/gitea-debian.svg | 2 +- public/assets/img/svg/gitea-gitbucket.svg | 2 +- public/assets/img/svg/gitea-gitlab.svg | 2 +- public/assets/img/svg/gitea-google.svg | 2 +- public/assets/img/svg/gitea-maven.svg | 2 +- .../assets/img/svg/gitea-microsoftonline.svg | 2 +- public/assets/img/svg/gitea-npm.svg | 2 +- public/assets/img/svg/gitea-onedev.svg | 2 +- public/assets/img/svg/gitea-openid.svg | 2 +- public/assets/img/svg/gitea-rubygems.svg | 2 +- public/assets/img/svg/gitea-swift.svg | 2 +- public/assets/img/svg/gitea-vagrant.svg | 2 +- 16 files changed, 34 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15b3243b33..3a87a54f97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,7 +96,7 @@ "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", - "svgo": "3.2.0", + "svgo": "4.0.0", "typescript": "5.8.3", "typescript-eslint": "8.35.0", "vite-string-plugin": "1.3.4", @@ -3125,16 +3125,6 @@ "integrity": "sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA==", "license": "MIT" }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -14201,25 +14191,25 @@ "dev": true }, "node_modules/svgo": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", - "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", + "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", "dev": true, "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", + "commander": "^11.1.0", "css-select": "^5.1.0", - "css-tree": "^2.3.1", + "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1", + "sax": "^1.4.1" }, "bin": { - "svgo": "bin/svgo" + "svgo": "bin/svgo.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16" }, "funding": { "type": "opencollective", @@ -14227,35 +14217,21 @@ } }, "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=16" } }, - "node_modules/svgo/node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "node_modules/svgo/node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/svgo/node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0" + "license": "ISC" }, "node_modules/swagger-ui-dist": { "version": "5.17.14", diff --git a/package.json b/package.json index ff4dd825f4..88f904c44b 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", - "svgo": "3.2.0", + "svgo": "4.0.0", "typescript": "5.8.3", "typescript-eslint": "8.35.0", "vite-string-plugin": "1.3.4", diff --git a/public/assets/img/svg/gitea-alt.svg b/public/assets/img/svg/gitea-alt.svg index 53e3f17c13..efe4830a0b 100644 --- a/public/assets/img/svg/gitea-alt.svg +++ b/public/assets/img/svg/gitea-alt.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-chef.svg b/public/assets/img/svg/gitea-chef.svg index c5e8a721cc..8fd8ed325d 100644 --- a/public/assets/img/svg/gitea-chef.svg +++ b/public/assets/img/svg/gitea-chef.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-debian.svg b/public/assets/img/svg/gitea-debian.svg index fa2f2f49dc..e92d0b6937 100644 --- a/public/assets/img/svg/gitea-debian.svg +++ b/public/assets/img/svg/gitea-debian.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-gitbucket.svg b/public/assets/img/svg/gitea-gitbucket.svg index 62f603484e..b9e99724b2 100644 --- a/public/assets/img/svg/gitea-gitbucket.svg +++ b/public/assets/img/svg/gitea-gitbucket.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-gitlab.svg b/public/assets/img/svg/gitea-gitlab.svg index 03fcb0b87e..e2d708e7be 100644 --- a/public/assets/img/svg/gitea-gitlab.svg +++ b/public/assets/img/svg/gitea-gitlab.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-google.svg b/public/assets/img/svg/gitea-google.svg index 7dd2622df6..26ee04cb64 100644 --- a/public/assets/img/svg/gitea-google.svg +++ b/public/assets/img/svg/gitea-google.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-maven.svg b/public/assets/img/svg/gitea-maven.svg index 320d01a234..f6ece7dc28 100644 --- a/public/assets/img/svg/gitea-maven.svg +++ b/public/assets/img/svg/gitea-maven.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-microsoftonline.svg b/public/assets/img/svg/gitea-microsoftonline.svg index f2ce13ac22..c143eccbb6 100644 --- a/public/assets/img/svg/gitea-microsoftonline.svg +++ b/public/assets/img/svg/gitea-microsoftonline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-npm.svg b/public/assets/img/svg/gitea-npm.svg index 7ef74e72bd..2b05c79353 100644 --- a/public/assets/img/svg/gitea-npm.svg +++ b/public/assets/img/svg/gitea-npm.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-onedev.svg b/public/assets/img/svg/gitea-onedev.svg index 94ad1bab31..7ecd18895d 100644 --- a/public/assets/img/svg/gitea-onedev.svg +++ b/public/assets/img/svg/gitea-onedev.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-openid.svg b/public/assets/img/svg/gitea-openid.svg index f4702d2cdf..10c37145a3 100644 --- a/public/assets/img/svg/gitea-openid.svg +++ b/public/assets/img/svg/gitea-openid.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-rubygems.svg b/public/assets/img/svg/gitea-rubygems.svg index 4e43bdf2f4..7cd9d34e6a 100644 --- a/public/assets/img/svg/gitea-rubygems.svg +++ b/public/assets/img/svg/gitea-rubygems.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-swift.svg b/public/assets/img/svg/gitea-swift.svg index 4182100185..891ac12b56 100644 --- a/public/assets/img/svg/gitea-swift.svg +++ b/public/assets/img/svg/gitea-swift.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-vagrant.svg b/public/assets/img/svg/gitea-vagrant.svg index ba50101d52..18b05e900d 100644 --- a/public/assets/img/svg/gitea-vagrant.svg +++ b/public/assets/img/svg/gitea-vagrant.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From c57dea336c2ad3dcff7dc4b791af69b195abe01a Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 29 Jun 2025 16:22:07 +0200 Subject: [PATCH 041/297] fix(ui): small org dashboard ui cleanup (#8327) Small UI cleanups in this area with no visual changes: https://codeberg.org/attachments/4282f225-63e0-41b7-9edb-8b64877092b2 * remove classes `ui`, `top`, `attached`: following https://codeberg.org/forgejo/forgejo/pulls/2593, it is no longer a fomantic ui segment for those classes to be relevant to it * use gap in flexbox as it is a cleaner way than setting margins Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8327 Reviewed-by: Gusted Reviewed-by: Robert Wolff Reviewed-by: Beowulf --- web_src/js/components/DashboardRepoList.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index 58c5461baa..35f1082a93 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -340,10 +340,10 @@ export default sfc; // activate the IDE's Vue plugin {{ textMyOrgs }} {{ organizationsTotalCount }}
-

-
+

+
{{ textMyRepos }} - {{ reposTotalCount }} + {{ reposTotalCount }}

-`)).IsSvgImage()) - assert.False(t, DetectContentType([]byte(``)).IsSvgImage()) - assert.False(t, DetectContentType([]byte(``)).IsSvgImage()) +`), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte(``), "").IsSvgImage()) + assert.False(t, DetectContentType([]byte(``), "").IsSvgImage()) } func TestIsPDF(t *testing.T) { pdf, _ := base64.StdEncoding.DecodeString("JVBERi0xLjYKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nF3NPwsCMQwF8D2f4s2CNYk1baF0EHRwOwg4iJt/NsFb/PpevUE4Mjwe") - assert.True(t, DetectContentType(pdf).IsPDF()) - assert.False(t, DetectContentType([]byte("plain text")).IsPDF()) + assert.True(t, DetectContentType(pdf, "").IsPDF()) + assert.False(t, DetectContentType([]byte("plain text"), "").IsPDF()) } func TestIsVideo(t *testing.T) { mp4, _ := base64.StdEncoding.DecodeString("AAAAGGZ0eXBtcDQyAAAAAGlzb21tcDQyAAEI721vb3YAAABsbXZoZAAAAADaBlwX2gZcFwAAA+gA") - assert.True(t, DetectContentType(mp4).IsVideo()) - assert.False(t, DetectContentType([]byte("plain text")).IsVideo()) + assert.True(t, DetectContentType(mp4, "").IsVideo()) + assert.False(t, DetectContentType([]byte("plain text"), "").IsVideo()) } func TestIsAudio(t *testing.T) { mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") - assert.True(t, DetectContentType(mp3).IsAudio()) - assert.False(t, DetectContentType([]byte("plain text")).IsAudio()) + assert.True(t, DetectContentType(mp3, "").IsAudio()) + assert.False(t, DetectContentType([]byte("plain text"), "").IsAudio()) - assert.True(t, DetectContentType([]byte("ID3Toy\000")).IsAudio()) - assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ...")).IsText()) // test ID3 tag for plain text - assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char + assert.True(t, DetectContentType([]byte("ID3Toy\000"), "").IsAudio()) + assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."), "").IsText()) // test ID3 tag for plain text + assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2]), "").IsText()) // test ID3 tag with incomplete UTF8 char } func TestIsGLB(t *testing.T) { glb, _ := hex.DecodeString("676c5446") - assert.True(t, DetectContentType(glb).IsGLB()) - assert.True(t, DetectContentType(glb).Is3DModel()) - assert.False(t, DetectContentType([]byte("plain text")).IsGLB()) - assert.False(t, DetectContentType([]byte("plain text")).Is3DModel()) + assert.True(t, DetectContentType(glb, "").IsGLB()) + assert.True(t, DetectContentType(glb, "").Is3DModel()) + assert.False(t, DetectContentType([]byte("plain text"), "").IsGLB()) + assert.False(t, DetectContentType([]byte("plain text"), "").Is3DModel()) } func TestDetectContentTypeFromReader(t *testing.T) { mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") - st, err := DetectContentTypeFromReader(bytes.NewReader(mp3)) + st, err := DetectContentTypeFromReader(bytes.NewReader(mp3), "") require.NoError(t, err) assert.True(t, st.IsAudio()) } func TestDetectContentTypeOgg(t *testing.T) { oggAudio, _ := hex.DecodeString("4f67675300020000000000000000352f0000000000007dc39163011e01766f72626973000000000244ac0000000000000071020000000000b8014f6767530000") - st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio)) + st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio), "") require.NoError(t, err) assert.True(t, st.IsAudio()) oggVideo, _ := hex.DecodeString("4f676753000200000000000000007d9747ef000000009b59daf3012a807468656f7261030201001e00110001e000010e00020000001e00000001000001000001") - st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo)) + st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo), "") require.NoError(t, err) assert.True(t, st.IsVideo()) } @@ -148,7 +148,7 @@ func TestDetectContentTypeAvif(t *testing.T) { avifImage, err := hex.DecodeString("000000206674797061766966") require.NoError(t, err) - st, err := DetectContentTypeFromReader(bytes.NewReader(avifImage)) + st, err := DetectContentTypeFromReader(bytes.NewReader(avifImage), "") require.NoError(t, err) assert.True(t, st.IsImage()) @@ -158,10 +158,24 @@ func TestDetectContentTypeModelGLB(t *testing.T) { glb, err := hex.DecodeString("676c5446") require.NoError(t, err) - st, err := DetectContentTypeFromReader(bytes.NewReader(glb)) + st, err := DetectContentTypeFromReader(bytes.NewReader(glb), "") require.NoError(t, err) // print st for debugging assert.Equal(t, "model/gltf-binary", st.GetMimeType()) assert.True(t, st.IsGLB()) } + +func TestDetectInterlisp(t *testing.T) { + interlisp, err := base64.StdEncoding.DecodeString("ICAKKERFRklORS1GSUxFLUlORk8gHlBBQ0tBR0UgIklOVEVSTElTUCIgHlJFQURUQUJMRSAiSU5URVJMSVNQIiAeQkFTRSAxMCkKCgYB") + require.NoError(t, err) + st, err := DetectContentTypeFromReader(bytes.NewReader(interlisp), "test") + require.NoError(t, err) + assert.True(t, st.IsText()) + st, err = DetectContentTypeFromReader(bytes.NewReader(interlisp), "") + require.NoError(t, err) + assert.False(t, st.IsText()) + st, err = DetectContentTypeFromReader(bytes.NewReader(interlisp), "test.lcom") + require.NoError(t, err) + assert.False(t, st.IsText()) +} diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 5114cc9c05..3e3cb0016d 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -189,7 +189,7 @@ func editFile(ctx *context.Context, isNewFile bool) { buf = buf[:n] // Only some file types are editable online as text. - if !typesniffer.DetectContentType(buf).IsRepresentableAsText() { + if !typesniffer.DetectContentType(buf, blob.Name()).IsRepresentableAsText() { ctx.NotFound("typesniffer.IsRepresentableAsText", nil) return } diff --git a/routers/web/repo/render.go b/routers/web/repo/render.go index b31e2e203a..05eeadc519 100644 --- a/routers/web/repo/render.go +++ b/routers/web/repo/render.go @@ -41,7 +41,7 @@ func RenderFile(ctx *context.Context) { n, _ := util.ReadAtMost(dataRc, buf) buf = buf[:n] - st := typesniffer.DetectContentType(buf) + st := typesniffer.DetectContentType(buf, blob.Name()) isTextFile := st.IsText() rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{}) diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go index 84d7cccdb8..20e211316d 100644 --- a/routers/web/repo/setting/avatar.go +++ b/routers/web/repo/setting/avatar.go @@ -45,7 +45,7 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { if err != nil { return fmt.Errorf("io.ReadAll: %w", err) } - st := typesniffer.DetectContentType(data) + st := typesniffer.DetectContentType(data, "") if !st.IsImage() || st.IsSvgImage() { return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image")) } diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index b9cb86bd08..9930d03e8e 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -291,7 +291,7 @@ func LFSFileGet(ctx *context.Context) { } buf = buf[:n] - st := typesniffer.DetectContentType(buf) + st := typesniffer.DetectContentType(buf, "") ctx.Data["IsTextFile"] = st.IsText() isRepresentableAsText := st.IsRepresentableAsText() diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index bb3e1388a8..d00f85a134 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -228,7 +228,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, n, _ := util.ReadAtMost(dataRc, buf) buf = buf[:n] - st := typesniffer.DetectContentType(buf) + st := typesniffer.DetectContentType(buf, blob.Name()) isTextFile := st.IsText() // FIXME: what happens when README file is an image? @@ -262,7 +262,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, } buf = buf[:n] - st = typesniffer.DetectContentType(buf) + st = typesniffer.DetectContentType(buf, blob.Name()) return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil } diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 400ee71f08..e0ce88b582 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -151,7 +151,7 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser * return fmt.Errorf("io.ReadAll: %w", err) } - st := typesniffer.DetectContentType(data) + st := typesniffer.DetectContentType(data, "") if !st.IsImage() || st.IsSvgImage() { return errors.New(ctx.Locale.TrString("settings.uploaded_avatar_not_a_image")) } From 0ecd9d968202d1766e5867dfbee13277e4683791 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 2 Jul 2025 13:04:22 +0200 Subject: [PATCH 054/297] fix: user activation with uppercase email address (#8367) - Right before the call to user email activation, the user is updated [^1]. This causes the email to be lowered, which in turn makes the call to activate the user activation fail (on database where collation is case sensitive, which is the recommend collation by Forgejo). - The code in `BeforeUpdate` is quite confusing, the comment has become slightly out of date and was reworded to reflect reality and its purpose. The code is also slightly reworked to only lower the email for the `AvatarEmail` field to avoid causing side-effect. - Added unit test. - Resolves forgejo/forgejo#8354 [^1]: https://codeberg.org/forgejo/forgejo/src/commit/4927d4ee3d53ad79fe88dfe862c33339c0a9cdc6/routers/web/auth/auth.go#L785 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8367 Reviewed-by: Otto Co-authored-by: Gusted Co-committed-by: Gusted --- .../TestActivateUserEmail/email_address.yml | 7 +++++++ models/fixtures/TestActivateUserEmail/user.yml | 12 ++++++++++++ models/user/email_address_test.go | 17 +++++++++++++++++ models/user/user.go | 6 +++--- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 models/fixtures/TestActivateUserEmail/email_address.yml create mode 100644 models/fixtures/TestActivateUserEmail/user.yml diff --git a/models/fixtures/TestActivateUserEmail/email_address.yml b/models/fixtures/TestActivateUserEmail/email_address.yml new file mode 100644 index 0000000000..cf41ff8241 --- /dev/null +++ b/models/fixtures/TestActivateUserEmail/email_address.yml @@ -0,0 +1,7 @@ +- + id: 1001 + uid: 1001 + email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net + lower_email: anothertestuserwithuppercaseemail@otto.splvs.net + is_activated: false + is_primary: true diff --git a/models/fixtures/TestActivateUserEmail/user.yml b/models/fixtures/TestActivateUserEmail/user.yml new file mode 100644 index 0000000000..0a68e70a4a --- /dev/null +++ b/models/fixtures/TestActivateUserEmail/user.yml @@ -0,0 +1,12 @@ +- + id: 1001 + lower_name: user1001 + name: user1001 + full_name: User That loves Upper Cases + email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net + passwd: ZogKvWdyEx:password + passwd_hash_algo: dummy + avatar: '' + avatar_email: anothertestuserwithuppercaseemail@otto.splvs.net + login_name: user1 + created_unix: 1672578000 diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go index 1801f57a23..85f5b16c65 100644 --- a/models/user/email_address_test.go +++ b/models/user/email_address_test.go @@ -181,3 +181,20 @@ func TestDeletePrimaryEmailAddressOfUser(t *testing.T) { assert.True(t, user_model.IsErrEmailAddressNotExist(err)) assert.Nil(t, email) } + +func TestActivateUserEmail(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/TestActivateUserEmail")() + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Activate email", func(t *testing.T) { + require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", true)) + + unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = true") + }) + + t.Run("Deactivate email", func(t *testing.T) { + require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", false)) + + unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = false") + }) +} diff --git a/models/user/user.go b/models/user/user.go index eedd1db80e..b124572bb6 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -182,11 +182,11 @@ func (u *User) BeforeUpdate() { u.MaxRepoCreation = -1 } - // Organization does not need email - u.Email = strings.ToLower(u.Email) + // Ensure AvatarEmail is set for non-organization users, because organization + // are not required to have a email set. if !u.IsOrganization() { if len(u.AvatarEmail) == 0 { - u.AvatarEmail = u.Email + u.AvatarEmail = strings.ToLower(u.Email) } } From abb95c8c92318c9a98910b36e78c9a60672930a7 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 2 Jul 2025 16:34:56 +0200 Subject: [PATCH 055/297] Update dependency globals to v16.3.0 (forgejo) (#8381) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8381 Reviewed-by: Michael Kriese Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 18e007e5a6..4555b299bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,7 +86,7 @@ "eslint-plugin-vue": "10.2.0", "eslint-plugin-vue-scoped-css": "2.10.0", "eslint-plugin-wc": "3.0.1", - "globals": "16.1.0", + "globals": "16.3.0", "happy-dom": "18.0.1", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", @@ -8440,9 +8440,9 @@ } }, "node_modules/globals": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", - "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index c6e96d68ec..3623c143e0 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "eslint-plugin-vue": "10.2.0", "eslint-plugin-vue-scoped-css": "2.10.0", "eslint-plugin-wc": "3.0.1", - "globals": "16.1.0", + "globals": "16.3.0", "happy-dom": "18.0.1", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", From bcde3aea4fa66e6e64ec1eebf8fd6de9b7f33b53 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 2 Jul 2025 16:40:48 +0200 Subject: [PATCH 056/297] Update module github.com/go-chi/cors to v1.2.2 (forgejo) (#8380) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8380 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 70db6ef875..1602f1ef19 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-chi/chi/v5 v5.2.2 - github.com/go-chi/cors v1.2.1 + github.com/go-chi/cors v1.2.2 github.com/go-co-op/gocron v1.37.0 github.com/go-enry/go-enry/v2 v2.9.2 github.com/go-git/go-git/v5 v5.13.2 diff --git a/go.sum b/go.sum index e9cf6413cd..1aa2380161 100644 --- a/go.sum +++ b/go.sum @@ -215,8 +215,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= -github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= +github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY= From c0eeb75322fcb62ae09130c9e7d864acd241613d Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 2 Jul 2025 19:23:07 +0200 Subject: [PATCH 057/297] fix: disable Forgejo Actions email notifications on recovery (#8374) - Make the migration to add an index a noop - do not remove it as it would break v12.next & v13.next - Keep the logic that relies on finding the last run, only always fail to find which is the same as assuming each run is one of a kind Refs forgejo/forgejo#8373 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8374 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- models/actions/run.go | 14 +-- models/actions/run_test.go | 85 ------------------- models/forgejo_migrations/migrate.go | 2 +- models/forgejo_migrations/v35.go | 12 +-- services/actions/notifier.go | 2 +- .../actions_run_now_done_notification_test.go | 39 +-------- 6 files changed, 10 insertions(+), 144 deletions(-) diff --git a/models/actions/run.go b/models/actions/run.go index 55def805ed..69592120e9 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -284,16 +284,10 @@ func GetLatestRun(ctx context.Context, repoID int64) (*ActionRun, error) { return &run, nil } -// GetRunBefore returns the last run that completed a given timestamp (not inclusive). -func GetRunBefore(ctx context.Context, repoID int64, timestamp timeutil.TimeStamp) (*ActionRun, error) { - var run ActionRun - has, err := db.GetEngine(ctx).Where("repo_id=? AND stopped IS NOT NULL AND stopped v34 NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), // v34 -> v35 - NewMigration("Add index to `stopped` column in `action_run` table", AddIndexToActionRunStopped), + NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v35.go b/models/forgejo_migrations/v35.go index 0fb3b43e2c..ca412d7951 100644 --- a/models/forgejo_migrations/v35.go +++ b/models/forgejo_migrations/v35.go @@ -4,16 +4,10 @@ package forgejo_migrations //nolint:revive import ( - "forgejo.org/modules/timeutil" - "xorm.io/xorm" ) -func AddIndexToActionRunStopped(x *xorm.Engine) error { - type ActionRun struct { - ID int64 - Stopped timeutil.TimeStamp `xorm:"index"` - } - - return x.Sync(&ActionRun{}) +// see https://codeberg.org/forgejo/forgejo/issues/8373 +func NoopAddIndexToActionRunStopped(x *xorm.Engine) error { + return nil } diff --git a/services/actions/notifier.go b/services/actions/notifier.go index f1e9a6d7e9..a5bac730be 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -788,7 +788,7 @@ func (n *actionsNotifier) MigrateRepository(ctx context.Context, doer, u *user_m // the ActionRun of the same workflow that finished before priorRun/updatedRun. func sendActionRunNowDoneNotificationIfNeeded(ctx context.Context, priorRun, updatedRun *actions_model.ActionRun) error { if !priorRun.Status.IsDone() && updatedRun.Status.IsDone() { - lastRun, err := actions_model.GetRunBefore(ctx, updatedRun.RepoID, updatedRun.Stopped) + lastRun, err := actions_model.GetRunBefore(ctx, updatedRun) if err != nil && !errors.Is(err, util.ErrNotExist) { return err } diff --git a/tests/integration/actions_run_now_done_notification_test.go b/tests/integration/actions_run_now_done_notification_test.go index d5142096c5..480d67a73d 100644 --- a/tests/integration/actions_run_now_done_notification_test.go +++ b/tests/integration/actions_run_now_done_notification_test.go @@ -49,41 +49,22 @@ func (m *mockNotifier) ActionRunNowDone(ctx context.Context, run *actions_model. assert.Equal(m.t, m.runID, run.ID) assert.Equal(m.t, actions_model.StatusFailure, run.Status) assert.Equal(m.t, actions_model.StatusRunning, priorStatus) - assert.Equal(m.t, m.lastRunID, lastRun.ID) - assert.Equal(m.t, actions_model.StatusSuccess, lastRun.Status) assert.True(m.t, run.NotifyEmail) case 2: assert.Equal(m.t, m.runID, run.ID) assert.Equal(m.t, actions_model.StatusCancelled, run.Status) assert.Equal(m.t, actions_model.StatusRunning, priorStatus) - assert.Equal(m.t, m.lastRunID, lastRun.ID) - assert.Equal(m.t, actions_model.StatusFailure, lastRun.Status) - assert.True(m.t, run.NotifyEmail) - case 3: - assert.Equal(m.t, m.runID, run.ID) - assert.Equal(m.t, actions_model.StatusSuccess, run.Status) - assert.Equal(m.t, actions_model.StatusRunning, priorStatus) - assert.Equal(m.t, m.lastRunID, lastRun.ID) - assert.Equal(m.t, actions_model.StatusCancelled, lastRun.Status) - assert.True(m.t, run.NotifyEmail) - case 4: - assert.Equal(m.t, m.runID, run.ID) - assert.Equal(m.t, actions_model.StatusSuccess, run.Status) - assert.Equal(m.t, actions_model.StatusRunning, priorStatus) - assert.Equal(m.t, m.lastRunID, lastRun.ID) - assert.Equal(m.t, actions_model.StatusSuccess, lastRun.Status) assert.True(m.t, run.NotifyEmail) default: assert.Fail(m.t, "too many notifications") } - m.lastRunID = m.runID m.runID++ m.testIdx++ } // ensure all tests have been run func (m *mockNotifier) complete() { - assert.Equal(m.t, 5, m.testIdx) + assert.Equal(m.t, 3, m.testIdx) } func TestActionNowDoneNotification(t *testing.T) { @@ -159,24 +140,6 @@ func TestActionNowDoneNotification(t *testing.T) { task = runner.fetchTask(t) require.NoError(t, actions_service.StopTask(db.DefaultContext, task.Id, actions_model.StatusCancelled)) - // we can't differentiate different runs without a delay - time.Sleep(time.Millisecond * 2000) - - // 3: successful run after failure - _, _, err = workflow.Dispatch(db.DefaultContext, inputGetter, repo, user2) - require.NoError(t, err) - task = runner.fetchTask(t) - runner.succeedAtTask(t, task) - - // we can't differentiate different runs without a delay - time.Sleep(time.Millisecond * 2000) - - // 4: successful run after success - _, _, err = workflow.Dispatch(db.DefaultContext, inputGetter, repo, user2) - require.NoError(t, err) - task = runner.fetchTask(t) - runner.succeedAtTask(t, task) - notifier.complete() }) } From 8cc20864029ff0ee705f0705da0b3790880c49d1 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Thu, 3 Jul 2025 03:51:11 +0000 Subject: [PATCH 058/297] i18n: update of translations from Codeberg Translate Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Dirk Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Juno Takano Co-authored-by: Outbreak2096 Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Xinayder Co-authored-by: artnay Co-authored-by: earl-warren Co-authored-by: jedik Co-authored-by: justbispo Co-authored-by: kwoot Co-authored-by: leandro-costa Co-authored-by: xtex Co-authored-by: yeager Co-authored-by: zub Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nb_NO/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_PT/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/sv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/zh_Hans/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/nb_NO/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/nl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/pt_PT/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/sv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_cs-CZ.ini | 6 +- options/locale/locale_fi-FI.ini | 4 +- options/locale/locale_fr-FR.ini | 4 +- options/locale/locale_lv-LV.ini | 2 +- options/locale/locale_nb_NO.ini | 118 +++++++++++++++++++++++++- options/locale/locale_nds.ini | 2 +- options/locale/locale_nl-NL.ini | 14 ++- options/locale/locale_pt-BR.ini | 18 ++-- options/locale/locale_pt-PT.ini | 11 +-- options/locale/locale_sv-SE.ini | 12 +-- options/locale/locale_uk-UA.ini | 47 ++++++++-- options/locale/locale_zh-CN.ini | 2 +- options/locale_next/locale_cs-CZ.json | 8 +- options/locale_next/locale_de-DE.json | 3 +- options/locale_next/locale_fil.json | 3 +- options/locale_next/locale_lv-LV.json | 8 +- options/locale_next/locale_nb_NO.json | 108 ++++++++++++++++++++++- options/locale_next/locale_nds.json | 3 +- options/locale_next/locale_nl-NL.json | 13 ++- options/locale_next/locale_pt-BR.json | 8 +- options/locale_next/locale_pt-PT.json | 9 +- options/locale_next/locale_sv-SE.json | 10 ++- options/locale_next/locale_uk-UA.json | 3 +- options/locale_next/locale_zh-CN.json | 3 +- 24 files changed, 366 insertions(+), 53 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 830065fb64..168965a740 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1063,7 +1063,7 @@ language.localization_project = Pomozte nám s překladem Forgejo do vašeho jaz user_block_yourself = Nemůžete zablokovat sami sebe. pronouns_custom_label = Vlastní zájmena change_username_redirect_prompt.with_cooldown.few = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnech. Do té doby budete moci své staré uživatelské jméno znovu získat. -change_username_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dni. Do té doby budete moci své staré uživatelské jméno znovu získat. +change_username_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnu. Do té doby budete moci své staré uživatelské jméno znovu získat. keep_pronouns_private = Zobrazovat zájmena pouze přihlášeným uživatelům keep_pronouns_private.description = Toto nastavení skryje vaše zájmena před návštěvníky, kteří nejsou přihlášeni. quota = Kvóta @@ -1579,7 +1579,7 @@ issues.remove_ref_at=`odstranil/a referenci %s %s` issues.add_ref_at=`přidal/a referenci %s %s` issues.delete_branch_at=`odstranil/a větev %s %s` issues.filter_label=Štítek -issues.filter_label_exclude=`Chcete-li vyloučit štítky, použijte alt + click/enter` +issues.filter_label_exclude=Chcete-li vyloučit štítky, použijte Alt + kliknutí issues.filter_label_no_select=Všechny štítky issues.filter_label_select_no_label=Bez štítku issues.filter_milestone=Milník @@ -3058,7 +3058,7 @@ teams.invite.by=Pozvání od %s teams.invite.description=Pro připojení k týmu klikněte na tlačítko níže. follow_blocked_user = Tuto organizaci nemůžete sledovat, protože jste v ní zablokováni. open_dashboard = Otevřít nástěnku -settings.change_orgname_redirect_prompt.with_cooldown.one = Starý název organizace bude dostupný ostatním po %[1]d dni. Do té doby budete moci staré jméno znovu získat. +settings.change_orgname_redirect_prompt.with_cooldown.one = Starý název organizace bude dostupný ostatním po %[1]d dnu. Do té doby budete moci staré jméno znovu získat. settings.change_orgname_redirect_prompt.with_cooldown.few = Starý název organizace bude dostupný ostatním po %[1]d dnech. Do té doby budete moci starý název znovu získat. [admin] diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 164a60cc8d..efd8a16526 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -2255,14 +2255,14 @@ pulls.cmd_instruction_merge_warning = Varoitus: Asetusta ”Tunnista manu pulls.cmd_instruction_merge_desc = Yhdistä muutokset ja päivitä Forgejossa. pulls.cannot_auto_merge_desc = Tätä vetopyyntöä ei voida yhdistää automaattisesti ristiriitojen vuoksi. adopt_preexisting_success = Omaksuttu tiedostot ja luotu tietovarasto lähteestä %s -issues.comment_manually_pull_merged_at = manuaalisesti yhdistetty kommitti %[1]s %[2]s tietovarastoon %[3]s +issues.comment_manually_pull_merged_at = manuaalisesti yhdisti kommitin %[1]s %[2]s tietovarastoon %[3]s pulls.cmd_instruction_merge_title = Yhdistä pulls.has_merged = Epäonnistui: vetopyyntö on yhdistetty, joten et voi yhdistää uudelleen tai muuttaa kohdehaaraa. pulls.cmd_instruction_checkout_title = Uloskuittaus pulls.cmd_instruction_checkout_desc = Kuittaa ulos uusi haara projektitietovarastostasi ja testaa muutokset. pulls.clear_merge_message_hint = Yhdistämisviestin tyhjentäminen poistaa vain kommittiviestin sisällön ja säilyttää luodut git-trailerit, kuten "Co-Authored-By…". settings.protect_check_status_contexts_desc = Vaadi tilatarkistusten läpäisy ennen yhdistämistä. Kun käytössä, kommitit on ensin työnnettävä toiseen haaraan ja sitten yhdistettävä tai työnnettävä suoraan tätä sääntöä vastaavaan haaraan tilantarkistuksen jälkeen. Jos konteksteja ei löydy, viimeisen kommitin on oltava onnistunut kontekstista riippumatta. -issues.comment_pull_merged_at = yhdistetty kommitti %[1]s %[2]s tietovarastoon %[3]s +issues.comment_pull_merged_at = yhdisti kommitin %[1]s %[2]s haaraan %[3]s settings.pulls.enable_autodetect_manual_merge = Ota Tunnista manuaalinen yhdistäminen automaattisesti -asetus käyttöön (Huomaa: joissakin erityistapauksissa voi esiintyä virhearviointeja) pulls.no_merge_desc = Tätä vetopyyntöä ei voida yhdistää, koska kaikki tietovaraston yhdistämisvaihtoehdot ovat poistettu käytöstä. pulls.no_merge_not_ready = Tämä vetopyyntö ei ole valmis yhdistettäväksi. Tarkista katselmoinnin tila ja tilantarkistukset. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 1cb7103bc0..e522d5ab92 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1579,7 +1579,7 @@ issues.remove_ref_at=`a supprimé la référence %s %s.` issues.add_ref_at=`a ajouté la référence %s %s.` issues.delete_branch_at=`a supprimé la branche %s %s.` issues.filter_label=Label -issues.filter_label_exclude=`Utilisez Alt + Clic/entrée pour exclure les labels.` +issues.filter_label_exclude=`Utilisez Alt + Click pour exclure les étiquettes.` issues.filter_label_no_select=Toutes les labels issues.filter_label_select_no_label=Aucun label issues.filter_milestone=Jalon @@ -4091,4 +4091,4 @@ issues.write = Écrire : Fermer des tickets et gérer les métadonnées t pulls.read = Lire : Lire et créer des demandes de tirage. [translation_meta] -test = Ceci est une chaîne de test. Elle n'est pas affichée dans l'interface de Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) +test = Ceci est une chaîne de test. Elle n'est pas affichée dans Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 98baff217b..4a98e1aa9d 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1577,7 +1577,7 @@ issues.remove_ref_at=`noņēma atsauci no %s %s` issues.add_ref_at=`pievienoja atsauci uz %s %s` issues.delete_branch_at=`izdzēsa zaru %s %s` issues.filter_label=Iezīme -issues.filter_label_exclude=`Jāizmanto alt + klikšķis/Enter, lai neiekļautu iezīmes` +issues.filter_label_exclude=Jāizmanto Alt + klikšķis, lai neiekļautu iezīmes issues.filter_label_no_select=Visas iezīmes issues.filter_label_select_no_label=Bez iezīmes issues.filter_milestone=Atskaites punkts diff --git a/options/locale/locale_nb_NO.ini b/options/locale/locale_nb_NO.ini index 2c8b5cfc64..59673fd4e3 100644 --- a/options/locale/locale_nb_NO.ini +++ b/options/locale/locale_nb_NO.ini @@ -60,7 +60,7 @@ rerun = Kjør på nytt rerun_all = Kjør alle jobber på nytt save = Lagre cancel = Avbryt -forks = Forks +forks = Kopier milestones = Milepæler ok = OK test = Test @@ -133,14 +133,42 @@ webauthn_error_duplicated = Sikkerhetsnøkkelen er ikke tillatt for denne foresp webauthn_error_timeout = Et tidsavbrudd oppsto før nøkkelen din kunne leses. Vennligst last inn siden på nytt og prøv igjen. new_fork = Ny fork av repository collaborative = Samarbeidende +error413 = Du har brukt opp kvoten din. +issues = Saker +unpin = Løsne +filter.is_fork = Forks +filter.not_fork = Ikke forks +pull_requests = Pull requests +copy_branch = Kopier branch navn +error404 = Siden du forsøker å nå eksisterer ikke, er blitt fjernet eller du har ikke tilgang til å se den. +tracked_time_summary = Oppsummering av sporet tid basert på problemfiltre [search] -search = Søk... +search = Søk… type_tooltip = Søketype fuzzy = Fuzzy union = Union regexp = RegExp exact = Nøyaktig +team_kind = Søk i teams… +code_kind = Søk i kode… +package_kind = Søk i pakker… +project_kind = Søk i prosjekter… +branch_kind = Søk i brancher… +commit_kind = Søk i commits… +regexp_tooltip = Tolk søkeordet som et regulæruttrykk +pull_kind = Søk i pulls… +keyword_search_unavailable = Søk etter nøkkelord er for øyeblikket ikke tilgjengelig. Kontakt administratoren. +exact_tooltip = Inkluder kun resultater som samsvarer nøyaktig med søkeordet +repo_kind = Søk i repositorer… +fuzzy_tooltip = Inkluder resultater som også stemmer godt overens med søketermen +org_kind = Søk i organisasjoner… +issue_kind = Søk i saker… +runner_kind = Søk i runners… +no_results = Ingen treff funnet. +union_tooltip = Inkluder resultater som samsvarer med ett eller flere av nøkkelordene adskilt med mellomrom +code_search_unavailable = Kodesøk er ikke tilgjengelig. Kontakt administratoren. +user_kind = Søk i brukere… [auth] verify = Bekreft @@ -152,4 +180,88 @@ oauth_signup_title = Fullfør ny konto oauth_signup_submit = Fullfør konto [home] -uname_holder = Brukernavn eller e-postadresse \ No newline at end of file +uname_holder = Brukernavn eller e-postadresse + +[heatmap] +contributions_zero = Ingen bidrag +number_of_contributions_in_the_last_12_months = %s bidrag de siste 12 månedene +contributions_one = bidrag +contributions_format = {contributions} den {day} {month} {year} +less = Mindre +contributions_few = bidrag +more = Mer + +[editor] +buttons.heading.tooltip = Legg til overskrift +buttons.bold.tooltip = Legg til uthevet tekst +buttons.italic.tooltip = Legg til kursiv text +buttons.list.unordered.tooltip = Legg til punktliste +buttons.list.ordered.tooltip = Legg til nummerert liste +buttons.link.tooltip = Legg til link +buttons.quote.tooltip = Siter tekst +buttons.mention.tooltip = Nevn en bruker eller team +buttons.list.task.tooltip = Legg til liste over saker +buttons.code.tooltip = Legg til kode +table_modal.header = Legg til tabell +table_modal.placeholder.header = Overskrift +table_modal.placeholder.content = Innhold +table_modal.label.columns = Kolonner +buttons.new_table.tooltip = Legg til tabell +table_modal.label.rows = Rader +buttons.switch_to_legacy.tooltip = Bruk den gamle editoren istedenfor +buttons.disable_monospace_font = Deaktiver monospace font +buttons.ref.tooltip = Referanse til en sak eller pull request +buttons.indent.tooltip = Grupper elementene med et nivå +buttons.unindent.tooltip = Pakk ut elementene med et nivå +link_modal.url = Url +link_modal.description = Beskrivelse +link_modal.header = Legg til en link +buttons.enable_monospace_font = Aktiver monospace font +link_modal.paste_reminder = Tips: Når du har en URL i utklippstavlen kan du lime den direkte inn i editoren for å lage en lenke. + +[aria] +footer = Bunntekst +navbar = Navigasjonslinje +footer.links = Linker +footer.software = Om dette programmet + +[filter] +string.asc = A - Z +string.desc = Z - A + +[error] +occurred = En feil oppstod +not_found = Kunne ikke finne målet. +report_message = Hvis du mener dette er en feil i Forgejo kan du søke på Codeberg eller åpne en ny sak. +network_error = Nettverks feil +server_internal = Intern server feil + +[install] +docker_helper = Dersom du bruker Forgejo med Docker, anbefales det å lese dokumentasjonen før du gjør endringer i konfigurasjonen. +db_title = Database innstillinger +require_db_desc = Forgejo krever MySQL, PostgreSQL, SQLite3 eller TiDB (MySQL protokoll). +db_type = Database type +password = Passord +user = Brukernavn +install = Installasjon +title = Førstegangsoppsett +host = Server +db_name = Database navn +db_schema = Skjema +db_schema_helper = La stå tomt for databasens standardverdi ("public"). +ssl_mode = SSL +path = Sti +sqlite_helper = Sti til SQLite3-databasen.
Bruk absolutt filsti dersom Forgejo kjøres som en tjeneste. +reinstall_error = Du prøver å installere i en eksisterende Forgejo-database +reinstall_confirm_message = Å installere på nytt med en eksisterende Forgejo-database kan føre til problemer. I de fleste tilfeller bør du bruke din eksisterende "app.ini" for å kjøre Forgejo. Hvis du vet hva du gjør, og vil fortsette, bekreft følgende: + +[startpage] +install = Enkel å installere +platform = Plattformuavhengig +install_desc = Du kan enkelt kjøre programfilen for din platform, bruke Docker, eller hente den som en ferdig pakke. +lightweight_desc = Forgejo krever lite ressurser og kan kjøres på en rimelig Raspberry Pi. Spar strøm og miljøet! +app_desc = En enkel Git-tjeneste du kan drifte selv +lightweight = Lettvekt +license = Åpen kildekode +platform_desc = Forgejo fungerer på frie operativsystemer som Linux og FreeBSD, og støtter flere CPU-arkitekturer. Velg den plattformen du foretrekker! +license_desc = Last ned Forgejo! Bli med ved å bidra for å gjøre prosjektet enda bedre. Ikke vær redd for å bli en bidragsyter! \ No newline at end of file diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index 68fe899d6e..cd91f7c35a 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -1552,7 +1552,7 @@ issues.dependency.pr_close_blocks = Deeser Haalvörslag blockeert dat Dichtmaken issues.dependency.issue_batch_close_blocked = Kann de utköört Gefallens nich all tosamen dichtmaken, denn Gefall #%d hett noch open Ofhangens issues.dependency.pr_close_blocked = Du muttst all Gefallens, wat deesen Haalvörslag blockeren, dichtmaken, ehr du dat hier tosamenföhren kannst. issues.dependency.blocks_short = Blockeert -issues.dependency.blocked_by_short = Hang of vun +issues.dependency.blocked_by_short = Hangt of vun issues.dependency.remove_header = Ofhangen wegdoon issues.dependency.setting = Ofhangens för Gefallens un Haalvörslagen anknipsen issues.dependency.add_error_same_issue = Du kannst een Gefall nich vun sik sülvst ofhangen laten. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 48442bc39f..7e08c8036c 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1495,7 +1495,7 @@ issues.remove_ref_at=`heeft referentie %s verwijderd %s` issues.add_ref_at=`heeft referentie %s toegevoegd %s` issues.delete_branch_at=`heeft %[2]s de branch %[1]s verwijderd.` issues.filter_label=Label -issues.filter_label_exclude=Gebruik alt + klik/voer in om labels uit te sluiten +issues.filter_label_exclude=Gebruik alt + klik om labels uit te sluiten issues.filter_label_no_select=Alle labels issues.filter_milestone=Mijlpaal issues.filter_project=Project @@ -1554,8 +1554,8 @@ issues.close_comment_issue=Sluit met commentaar issues.reopen_issue=Heropen issues.reopen_comment_issue=Heropen met commentaar issues.create_comment=Reageer -issues.closed_at=`heeft dit probleem gesloten %s` -issues.reopened_at=`heropende dit probleem %s` +issues.closed_at=`heeft deze issue gesloten %s` +issues.reopened_at=`heropende deze issue %s` issues.commit_ref_at=`verwees naar dit probleem vanuit commit %s` issues.ref_issue_from=`refereerde aan dit issue %[3]s %[1]s` issues.ref_pull_from=`refereerde aan deze pull request %[3]s %[1]s` @@ -2912,6 +2912,14 @@ comment.blocked_by_user = Commentaar geven is niet mogelijk omdat u geblokkeerd sync_fork.button = Synchroniseer sync_fork.branch_behind_one = Deze branch is %[1]d commit achter %[2]s sync_fork.branch_behind_few = Deze branch is %[1]d commits achter %[2]s +settings.event_action_failure = Mislukking +settings.event_action_failure_desc = Action run is mislukt. +settings.event_action_recover = Herstel +settings.event_action_success = Succes +settings.event_action_success_desc = Action run is geslaagd. +settings.event_header_action = Actie run evenementen +issues.filter_type.all_pull_requests = Alle pull requests +settings.event_action_recover_desc = Action run is geslaagd nadat de laatste action run in dezelfde workflow is mislukt. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 8de0374eb2..cd5cc14833 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1278,7 +1278,7 @@ star_guest_user=Entre para adicionar este repositório aos favoritos. unwatch=Deixar de observar watch=Observar unstar=Retirar dos favoritos -star=Juntar aos favoritos +star=Adicionar aos favoritos fork=Fork download_archive=Baixar repositório more_operations=Mais operações @@ -1934,7 +1934,7 @@ pulls.status_checks_success=Todas as verificações foram bem sucedidas pulls.status_checks_warning=Algumas verificações reportaram avisos pulls.status_checks_failure=Algumas verificações falharam pulls.status_checks_error=Algumas verificações reportaram erros -pulls.status_checks_requested=Obrigatário +pulls.status_checks_requested=Obrigatório pulls.status_checks_details=Detalhes pulls.update_branch=Atualizar branch por merge pulls.update_branch_rebase=Atualizar branch por rebase @@ -2803,7 +2803,7 @@ mirror_use_ssh.helper = Forgejo irá espelhar o repositório via Git através de mirror_denied_combination = Não é possível combinar o uso de chave pública e autenticação baseada em senha. mirror_public_key = Chave SSH pública mirror_use_ssh.text = Usar autenticação por SSH -mirror_use_ssh.not_available = Autenticação por SSH não está disponível. +mirror_use_ssh.not_available = A autenticação via SSH não está disponível. settings.push_mirror_sync_in_progress = Fazendo push das mudanças para o remoto %s nesse momento. settings.federation_apapiurl = URL de federação deste repositório. Copie e cole isso nas Configurações de Federação de outro repositório como uma URL de um Repositório Seguidor. pulls.agit_explanation = Criado usando o fluxo de trabalho AGit. AGit permite que contribuidores proponham mudanças usando "git push" sem criar um fork ou novo branch. @@ -2817,7 +2817,7 @@ settings.mirror_settings.pushed_repository = Repositório enviado settings.mirror_settings.docs.disabled_pull_mirror.instructions = Configure seu projeto para automaticamente fazer push de commits, tags e branches para outro repositório. Espelhos de pull foram desativados pelo administrador do seu site. settings.mirror_settings.docs.disabled_push_mirror.instructions = Configure seu projeto para automaticamente fazer pull de commits, tags e branches de outro repositório. settings.mirror_settings.docs.doc_link_pull_section = a seção "Fazendo pull de um repositório remoto" da documentação. -subscribe.pull.guest.tooltip = Entre para receber notificações deste pull request. +subscribe.pull.guest.tooltip = Inicie a sessão para receber notificações deste pull request. settings.pull_mirror_sync_quota_exceeded = Cota excedida, não será feito pull das mudanças. settings.mirror_settings.docs.more_information_if_disabled = Saiba mais sobre espelhos de push e pull aqui: settings.transfer_quota_exceeded = O novo dono (%s) excedeu a cota. O repositório não foi transferido. @@ -3900,7 +3900,7 @@ deletion=Excluir segredo deletion.description=A exclusão de um segredo é permanente e não pode ser desfeita. Continuar? deletion.success=O segredo foi excluído. deletion.failed=Falha ao excluir segredo. -management=Gerenciar segredos +management=Gerenciamento de segredos [actions] actions=Ações @@ -3909,12 +3909,12 @@ unit.desc=Gerenciar pipelines integradas de CI/CD com Forgejo Actions. status.unknown=Desconhecido status.waiting=Aguardando -status.running=Rodando +status.running=Executando status.success=Sucesso status.failure=Falha -status.cancelled=Cancelado -status.skipped=Ignorado -status.blocked=Bloqueado +status.cancelled=Cancelada +status.skipped=Ignorada +status.blocked=Bloqueada runners=Runners runners.runner_manage_panel=Gerenciar runners diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 0e8f2d485e..9e1c7f7f63 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1063,8 +1063,8 @@ language.description = Este idioma vai ser guardado na sua conta e ser usado com language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Ler mais. pronouns_custom_label = Pronomes personalizados user_block_yourself = Não se pode bloquear a si próprio. -change_username_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. -change_username_redirect_prompt.with_cooldown.few = O nome de utilizador antigo ficará disponível para todos após um período de espera de %[1]d dias, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. +change_username_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia. Pode ainda reivindicar o nome de utilizador antigo durante o período de espera. +change_username_redirect_prompt.with_cooldown.few = O nome de utilizador antigo ficará disponível para todos após um período de espera de %[1]d dias. Pode ainda reivindicar o nome de utilizador antigo durante o período de espera. quota.applies_to_user = As seguintes regras de quotas aplicam-se à sua conta quota.sizes.assets.artifacts = Artefactos quota.rule.exceeded.helper = O tamanho total dos objectos para esta regra excedeu a quota. @@ -1583,7 +1583,7 @@ issues.remove_ref_at=`removeu a referência %s %s` issues.add_ref_at=`adicionou a referência %s %s` issues.delete_branch_at=`eliminou o ramo %s %s` issues.filter_label=Rótulo -issues.filter_label_exclude=`Use alt + clique/enter para excluir rótulos` +issues.filter_label_exclude=Use Alt + Clique para excluir rótulos issues.filter_label_no_select=Todos os rótulos issues.filter_label_select_no_label=Sem rótulo issues.filter_milestone=Etapa @@ -2922,6 +2922,7 @@ settings.event_header_action = Eventos da execução de ações settings.event_action_recover_desc = A execução de ação foi bem sucedida depois da última execução de ação na mesma sequência de trabalho ter falhado. settings.event_action_success = Sucesso settings.event_action_success_desc = A Execução de ação foi bem sucedida. +issues.filter_type.all_pull_requests = Todos os pedidos de integração [graphs] component_loading=A carregar %s… @@ -3057,8 +3058,8 @@ teams.invite.by=Convidado(a) por %s teams.invite.description=Clique no botão abaixo para se juntar à equipa. follow_blocked_user = Não pode seguir esta organização porque esta organização bloqueou-o/a. open_dashboard = Abrir painel de controlo -settings.change_orgname_redirect_prompt.with_cooldown.one = O nome antigo da organização estará disponível para todos após um período de espera de %[1]d dia, podendo ainda reivindicar o nome antigo durante o período de espera. -settings.change_orgname_redirect_prompt.with_cooldown.few = O nome antigo da organização estará disponível para todos após um período de espera de %[1]d dias, podendo ainda reivindicar o nome antigo durante o período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.one = O nome antigo da organização estará disponível para todos após um período de espera de %[1]d dia. Pode ainda reivindicar o nome antigo durante o período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.few = O nome antigo da organização estará disponível para todos após um período de espera de %[1]d dias. Pode ainda reivindicar o nome antigo durante o período de espera. [admin] dashboard=Painel de controlo diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 8b43cb29b8..2e212c8c49 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -918,7 +918,7 @@ migrate.migrate_items_options=Åtkomsttoken krävs för att migrera ytterligare migrated_from=Migrerad från %[2]s migrated_from_fake=Migrerad från %[1]s migrate.migrate=Migrera från %s -migrate.migrating=Migrerar från %s ... +migrate.migrating=Migrerar från %s … migrate.migrating_failed=Migrering från %s misslyckades. migrate.migrating_issues=Migrerar ärenden @@ -2324,15 +2324,15 @@ exact = Exakt exact_tooltip = Inkludera bara resultat som exakt matchar söktermen repo_kind = Sök repon… user_kind = Sök användare… -code_kind = Sök kod... -package_kind = Sök paket... +code_kind = Sök kod… +package_kind = Sök paket… runner_kind = Sök exekutorer... -branch_kind = Sök grenar... +branch_kind = Sök grenar… commit_kind = Sök commiter... -project_kind = Sök projekt... +project_kind = Sök projekt… search = Sök… type_tooltip = Söktyp -team_kind = Sök lag... +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 diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index faa3f2a56e..331c7403eb 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -198,7 +198,7 @@ buttons.disable_monospace_font = Вимкнути моноширинний шр buttons.indent.tooltip = Вкласти предмет на один рівень buttons.unindent.tooltip = Викласти об'єкт на один рівень buttons.mention.tooltip = Згадати користувача чи команду -buttons.ref.tooltip = Послатись на задачу чи на запит на злиття +buttons.ref.tooltip = Послатися на задачу чи на запит на злиття buttons.enable_monospace_font = Увімкнути моноширинний шрифт buttons.new_table.tooltip = Додати таблицю table_modal.label.columns = Стовпці @@ -1276,7 +1276,7 @@ file_raw=Неформатований file_history=Історія file_view_source=Переглянути вихідний код file_view_rendered=Переглянути відрендерено -file_view_raw=Перегляд Raw +file_view_raw=Переглянути неформатований file_permalink=Постійне посилання file_too_large=Цей файл завеликий щоб бути показаним. @@ -2669,6 +2669,31 @@ settings.event_action_recover = Відновлено commitstatus.success = Успіх commitstatus.failure = Збій issues.filter_type.all_pull_requests = Усі запити на злиття +broken_message = Неможливо прочитати дані Git, що лежать в основі цього репозиторію. Зверніться до адміністратора цього екземпляра або видаліть репозиторій. +migrate.invalid_local_path = Локальний шлях недійсний. Він не існує або не є каталогом. +editor.filename_is_a_directory = Назва файлу «%s» уже використовується в цьому репозиторії як назва каталогу. +editor.filename_is_invalid = Хибна назва файлу: «%s». +migrate_options_lfs_endpoint.placeholder = Якщо залишити порожнім, то кінцеву точку буде визначено з URL-адреси клону +cite_this_repo = Послатися на цей репозиторій +editor.directory_is_a_file = Назва каталогу «%s» уже використовується в цьому репозиторії як назва файлу. +subscribe.issue.guest.tooltip = Увійдіть, щоб підписатися на цю задачу. +invisible_runes_header = `Цей файл містить невидимі символи Юнікоду` +invisible_runes_line = `У цьому рядку є невидимі символи Юнікоду` +subscribe.pull.guest.tooltip = Увійдіть, щоб підписатися на цей запит на злиття. +ambiguous_runes_header = `Цей файл містить неоднозначні символи Юнікоду` +ambiguous_runes_line = `У цьому рядку є неоднозначні символи Юнікоду` +issues.choose.invalid_config = У конфігурації задачі є помилки: +escape_control_characters = Escape +ambiguous_runes_description = `Цей файл містить символи Юнікоду, які легко сплутати з іншими символами. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` +unescape_control_characters = Unescape +invisible_runes_description = `Цей файл містить невидимі символи Юнікоду, які людині неможливо розрізнити, але які по-різному обробляються комп'ютером. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` +ambiguous_character = `%[1]c [U+%04[1]X] можна сплутати з %[2]c [U+%04[2]X]` +settings.sourcehut_builds.secrets_helper = Надати завданню доступ до секретів збірки (потрібен дозвіл SECRETS:RO) +no_eol.text = Без EOL +settings.remove_protected_branch_failed = Не вдалося видалити правило захисту гілок «%s». +summary_card_alt = Підсумкова картка репозиторію %s +issues.summary_card_alt = Підсумкова картка задачі «%s» в репозиторії %s +release.summary_card_alt = Підсумкова картка випуску «%s» в репозиторії %s [graphs] contributors.what = внески @@ -3193,7 +3218,7 @@ config.git_gc_timeout=Тайм-аут операції збирача смітт config.log_config=Конфігурація журналу config.disabled_logger=Вимкнено -config.access_log_mode=Режим доступу до журналу +config.access_log_mode=Режим журналювання доступу config.xorm_log_sql=Журнал SQL @@ -3250,7 +3275,7 @@ packages.package_manage_panel = Керування пакунками packages.published = Опубліковано notices.operations = Дії packages.cleanup = Очистити недійсні дані -packages.cleanup.success = Успішно очищено недійсні дані +packages.cleanup.success = Недійсні дані успішно очищено users.still_own_packages = Цей користувач досі володіє одним чи більше пакунками, спочатку видаліть ці пакунки. users.purge_help = Примусово видалити користувача і будь-які репозиторії, організації та пакунки, якими він володіє. Всі коментарі та задачі, створені цим користувачем, також будуть видалені. dashboard.cleanup_packages = Очистити непридатні пакунки @@ -3306,6 +3331,12 @@ defaulthooks.desc = Вебхуки автоматично сповіщають H assets = Ресурси коду auths.invalid_openIdConnectAutoDiscoveryURL = Неправильна URL-адреса автоматичного виявлення (повинна бути дійсна URL-адреса, що починається з http:// або https://) settings = Налаштування адміністратора +dashboard.start_schedule_tasks = Запустити заплановані завдання дій +config.logger_name_fmt = Журнал: %s +config.set_setting_failed = Не вдалося встановити параметр %s +config.access_log_template = Шаблон журналу доступу +dashboard.cancel_abandoned_jobs = Скасувати покинуті завдання дій +monitor.download_diagnosis_report = Завантажити діагностичний звіт [action] @@ -3526,6 +3557,8 @@ cran.registry = Налаштуйте цей реєстр у файлі Rpr npm.registry = Налаштуйте цей реєстр у файлі .npmrc свого проєкту: chef.registry = Налаштуйте цей реєстр у файлі ~/.chef/config.rb: owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки публічний ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. +nuget.dependency.framework = Цільовий фреймворк +owner.settings.cleanuprules.preview.overview = Заплановано видалити %d пакунків. [secrets] deletion = Видалити секрет @@ -3629,6 +3662,10 @@ runners.update_runner_success = Ранер оновлено runners.delete_runner_header = Підтвердіть видалення ранера runners.status.offline = Неактивний runners.status.idle = Простоює +runs.invalid_workflow_helper = Недійсний файл конфігурації робочого потоку. Будь ласка, перевірте файл конфігурації: %s +runs.no_job = Робочий потік повинен містити принаймні одне завдання +workflow.dispatch.use_from = Використати робочий потік із +runs.no_job_without_needs = Робочий потік повинен містити принаймні одне завдання без залежностей. @@ -3641,7 +3678,7 @@ deleted.display_name = Видалений проєкт [git.filemode] symbolic_link=Символічне посилання -directory = Тека +directory = Каталог submodule = Підмодуль normal_file = Звичайний файл executable_file = Виконуваний файл diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index c6c534df9f..d87ad53676 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -171,7 +171,7 @@ copy_path = 复制路径 [aria] navbar=导航栏 footer=页脚 -footer.software=关于软件 +footer.software=关于此软件 footer.links=链接 [heatmap] diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 97a8536d4f..24380bea1b 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -105,5 +105,11 @@ "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více", "avatar.constraints_hint": "Velikost vlastního avataru nesmí překročit %[1]s nebo být větší než %[2]dx%[3]d pixelů", "repo.diff.commit.next-short": "Další", - "repo.diff.commit.previous-short": "Předchozí" + "repo.diff.commit.previous-short": "Předchozí", + "profile.actions.tooltip": "Další akce", + "keys.gpg.link": "Klíče GPG", + "profile.edit.link": "Upravit profil", + "feed.atom.link": "Zdroj Atom", + "keys.ssh.link": "Klíče SSH", + "og.repo.summary_card.alt_description": "Karta se souhrnem repozitáře %[1]s, popsaným jako: %[2]s" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 3847de2b43..d256c54dc6 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -102,5 +102,6 @@ "feed.atom.link": "Atom-Feed", "keys.ssh.link": "SSH-Schlüssel", "keys.gpg.link": "GPG-Schlüssel", - "profile.actions.tooltip": "Mehr Aktionen" + "profile.actions.tooltip": "Mehr Aktionen", + "og.repo.summary_card.alt_description": "Zusammenfassungskarte des Repositorys %[1]s, beschrieben als %[2]s" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 884a7b44eb..84b537fbdc 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -102,5 +102,6 @@ "feed.atom.link": "Atom feed", "keys.ssh.link": "Mga SSH key", "keys.gpg.link": "Mga GPG key", - "profile.actions.tooltip": "Higit pang mga aksyon" + "profile.actions.tooltip": "Higit pang mga aksyon", + "og.repo.summary_card.alt_description": "Card ng pangkalahatang ideya ng repositoryong %[1]s, inilalarawan bilang: %[2]s" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index f71cfa227d..4deae220bf 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -105,5 +105,11 @@ "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk", "avatar.constraints_hint": "Pielāgots profila attēls nevar pārsniegt %[1]s vai būt lielāks par %[2]dx%[3]d pikseļiem", "repo.diff.commit.next-short": "Nāk.", - "repo.diff.commit.previous-short": "Iepr." + "repo.diff.commit.previous-short": "Iepr.", + "profile.actions.tooltip": "Vairāk darbību", + "profile.edit.link": "Labot profilu", + "feed.atom.link": "Atom barotne", + "keys.ssh.link": "SSH atslēgas", + "keys.gpg.link": "GPG atslēgas", + "og.repo.summary_card.alt_description": "Glabātavas %[1]s kopsavilkuma kartīte, aprakstīta kā: %[2]s" } diff --git a/options/locale_next/locale_nb_NO.json b/options/locale_next/locale_nb_NO.json index 0967ef424b..3349e136b5 100644 --- a/options/locale_next/locale_nb_NO.json +++ b/options/locale_next/locale_nb_NO.json @@ -1 +1,107 @@ -{} +{ + "relativetime.1day": "i går", + "moderation.abuse_category.other_violations": "Andre regel overtredelser", + "moderation.report_remarks": "Kommentar", + "moderation.report_remarks.placeholder": "Skriv noen detaljer rundt missbruket du rapporterer.", + "moderation.submit_report": "Send rapport", + "moderation.reporting_failed": "Missbruk rapporten kunne ikke sendes inn: %v", + "relativetime.hours": { + "one": "%d time siden", + "other": "%d timer siden" + }, + "repo.form.cannot_create": "Det maksimale antallet repositories er nådd i alle områdene du har tilgang til.", + "repo.issue_indexer.title": "Saksindekserer", + "moderation.abuse_category.illegal_content": "Ulovlig innhold", + "error.not_found.title": "Fant ikke siden", + "themes.names.forgejo-light": "Forgejo lyst", + "themes.names.forgejo-dark": "Forgejo mørk", + "stars.list.none": "Ingen har gitt stjerner til dette repoet.", + "watch.list.none": "Ingen følger dette repoet.", + "followers.incoming.list.none": "Ingen følger denne brukeren.", + "followers.outgoing.list.self.none": "Du følger ikke noen.", + "followers.outgoing.list.none": "%s følger ikke noen.", + "relativetime.2days": "to dager siden", + "relativetime.1week": "forrige uke", + "relativetime.2weeks": "to uker siden", + "relativetime.1month": "forrige måned", + "relativetime.2months": "to måneder siden", + "relativetime.1year": "i fjor", + "relativetime.2years": "to år siden", + "alert.asset_load_failed": "Kunne ikke laste inn ressursfiler fra {path}. Sørg for at ressursfilene er tilgjengelige.", + "search.milestone_kind": "Søker i milepæler…", + "home.welcome.no_activity": "Ingen aktivitet", + "home.explore_repos": "Utforsk repositorier", + "home.explore_users": "Utforsk brukere", + "home.explore_orgs": "Utforsk organisasjoner", + "relativetime.mins": { + "one": "%d minutt siden", + "other": "%d minutter siden" + }, + "relativetime.months": { + "one": "%d måned siden", + "other": "%d måneder siden" + }, + "relativetime.years": { + "one": "%d år siden", + "other": "%d år siden" + }, + "repo.pulls.title_desc": { + "one": "ønsker å slå sammen %[1]d commit fra %[2]s inn i %[3]s", + "other": "ønsker å slå sammen %[1]d commits fra %[2]s inn i %[3]s" + }, + "og.repo.summary_card.alt_description": "Sammendrag for repository %[1]s, beskrevet som: %[2]s", + "followers.incoming.list.self.none": "Ingen følger profilen din.", + "install.invalid_lfs_path": "Kan ikke opprette LFS-root på: %[1]s", + "relativetime.now": "nå", + "relativetime.future": "i fremtiden", + "repo.pulls.merged_title_desc": { + "one": "slo sammen %[1]d commit fra %[2]s inn i %[3]s %[4]s", + "other": "slo sammen %[1]d commits fra %[2]s inn i %[3]s %[4]s" + }, + "editor.textarea.tab_hint": "Linjen er allerede innrykket. Trykk Tab igjen eller trykk Escape for å gå ut av editoren.", + "editor.textarea.shift_tab_hint": "Ingen innrykk på denne linjen. Trykk Shift + Tab igjen eller Escape for å gå ut av editoren.", + "alert.range_error": " må være et nummer mellom %[1]s og %[2]s.", + "themes.names.forgejo-auto": "Forgejo (følg systemtema)", + "home.welcome.activity_hint": "Det er foreløpig ingenting i feeden din. Aktivitetene dine og handlingene dine fra repositorier du følger, vil vises her etter hvert.", + "incorrect_root_url": "Denne Forgejo instansen er konfigurert til å bruke \"%s\". Du bruker Forgejo via en annen URL, noe som kan forårsake at deler av applikasjonen ikke fungerer. Den kanoniske URL-en styres av Forgejo-administratorer via innstillingen ROOT_URL i app.ini-filen.", + "mail.actions.run_info_trigger": "Startet på grunn av: %[1]s by: %[2]s", + "admin.dashboard.cleanup_offline_runners": "Rydd opp offline runners", + "settings.visibility.description": "Profilens synlighet påvirker andres mulighet til å få tilgang til dine ikke-private repositorier. Les mer", + "profile.actions.tooltip": "Flere handlinger", + "profile.edit.link": "Rediger profil", + "relativetime.days": { + "one": "%d dag siden", + "other": "%d dager siden" + }, + "relativetime.weeks": { + "one": "%d uke siden", + "other": "%d uker siden" + }, + "feed.atom.link": "Atom feed", + "keys.ssh.link": "SSH nøkler", + "keys.gpg.link": "GPG nøkler", + "admin.config.moderation_config": "Moderasjonskonfigurasjon", + "moderation.report_abuse": "Rapporter missbruk", + "moderation.report_content": "Rapporter innhold", + "moderation.report_abuse_form.header": "Rapporter missbruk til en administrator", + "moderation.report_abuse_form.details": "Denne formen skal brukes for å rapporterer brukere som oppretter spam profiler, repositorier, saker, kommentarer eller oppfører seg upassende.", + "moderation.report_abuse_form.invalid": "Ugyldige argumenter", + "moderation.report_abuse_form.already_reported": "Du har allerede rapportert dette innholdet", + "moderation.abuse_category": "Kategori", + "moderation.abuse_category.placeholder": "Velg en kategori", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Skadelig programvare", + "moderation.reported_thank_you": "Takk for meldingen. Vi har varslet administratorene.", + "mail.actions.successful_run_after_failure_subject": "Arbeidsflyten %[1]s er gjenopprettet i repository %[2]s", + "mail.actions.not_successful_run_subject": "Arbeidsflyten %[1]s feilet i repository %[2]s", + "mail.actions.successful_run_after_failure": "Arbeidsflyten %[1]s er gjenopprettet i repository %[2]s", + "mail.actions.not_successful_run": "Arbeidsflyten %[1]s feilet i repository %[2]s", + "mail.actions.run_info_cur_status": "Status for denne kjøringen: %[1]s (oppdatert fra %[2]s)", + "mail.actions.run_info_previous_status": "Status for forrige kjøring: %[1]s", + "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", + "repo.diff.commit.next-short": "Neste", + "repo.diff.commit.previous-short": "Forrige", + "discussion.locked": "Denne diskusjonen er låst. Kommentarer kan kun gjøres av bidragsytere.", + "avatar.constraints_hint": "Egendefinert avatar kan ikke overstige %[1]s i størrelse eller være større enn %[2]d × %[3]d piksler", + "meta.last_line": "Vi gir oss ikke. Kongen har sagt nei!" +} diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 24268e2082..207a138926 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -102,5 +102,6 @@ "keys.ssh.link": "SSH-Slötels", "keys.gpg.link": "GPG-Slötels", "profile.actions.tooltip": "Mehr Aktioonen", - "profile.edit.link": "Profil bewarken" + "profile.edit.link": "Profil bewarken", + "og.repo.summary_card.alt_description": "Tosamenfatens-Kaart vun de Repositorium %[1]s, beschrieven as: %[2]s" } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 4f109825aa..5df7db2b9d 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -92,5 +92,16 @@ "watch.list.none": "Niemand houdt deze repo in de gaten.", "followers.incoming.list.self.none": "Niemand volgt uw profiel.", "followers.incoming.list.none": "Deze gebruiker wordt door niemand gevolgd.", - "followers.outgoing.list.self.none": "U volgt niemand." + "followers.outgoing.list.self.none": "U volgt niemand.", + "settings.visibility.description": "Profielzichtbaarheid beïnvloedt de mogelijkheid van anderen om toegang te krijgen tot je niet-privé repositories. Lees meer", + "repo.diff.commit.next-short": "Volgende", + "admin.dashboard.cleanup_offline_runners": "Offline runners opruimen", + "keys.ssh.link": "SSH sleutels", + "keys.gpg.link": "GPG sleutels", + "profile.actions.tooltip": "Meer acties", + "profile.edit.link": "Profiel bewerken", + "feed.atom.link": "Atom-feed", + "repo.diff.commit.previous-short": "Vorige", + "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels", + "og.repo.summary_card.alt_description": "Samenvattingsoverzicht van repositorie %[1]s, omschreven als: %[2]s" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 1a5eca6d34..336dd5a484 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -105,5 +105,11 @@ "avatar.constraints_hint": "Imagem de perfil personalizada não pode exceder %[1]s em tamanho ou ser maior que %[2]dx%[3]d pixels", "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais", "repo.diff.commit.next-short": "Próximo", - "repo.diff.commit.previous-short": "Anterior" + "repo.diff.commit.previous-short": "Anterior", + "profile.edit.link": "Editar perfil", + "feed.atom.link": "Feed Atom", + "keys.gpg.link": "Chaves GPG", + "og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s", + "profile.actions.tooltip": "Mais Actions", + "keys.ssh.link": "Chaves SSH" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 78e6dc4493..a0caa90b68 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -103,5 +103,12 @@ "stars.list.none": "Ninguém juntou este repositório aos favoritos.", "admin.dashboard.cleanup_offline_runners": "Limpeza de executores offline", "settings.visibility.description": "A visibilidade do perfil afecta a capacidade de outros acederem aos seus repositórios não privados. Ler mais", - "avatar.constraints_hint": "O avatar personalizado não pode exceder %[1]s de tamanho ou ser maior do que %[2]dx%[3]d pixéis" + "avatar.constraints_hint": "O avatar personalizado não pode exceder %[1]s de tamanho ou ser maior do que %[2]dx%[3]d pixéis", + "repo.diff.commit.next-short": "Seg.", + "profile.actions.tooltip": "Mais Actions", + "profile.edit.link": "Editar perfil", + "feed.atom.link": "Feed Atom", + "keys.ssh.link": "Chaves SSH", + "keys.gpg.link": "Chaves GPG", + "repo.diff.commit.previous-short": "Ant." } diff --git a/options/locale_next/locale_sv-SE.json b/options/locale_next/locale_sv-SE.json index 9a8762212c..3bc37deff8 100644 --- a/options/locale_next/locale_sv-SE.json +++ b/options/locale_next/locale_sv-SE.json @@ -95,5 +95,13 @@ "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. Läs mer", - "avatar.constraints_hint": "Anpassade avatarer får inte vara större än %[1] eller %[2]dx%[3] bildpunkter" + "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", + "keys.gpg.link": "GPG-nycklar", + "profile.edit.link": "Redigera profil", + "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" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 33cb5a41a3..af219b50d0 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -110,5 +110,6 @@ "keys.gpg.link": "Ключі GPG", "profile.edit.link": "Редагувати профіль", "feed.atom.link": "Стрічка Atom", - "profile.actions.tooltip": "Більше дій" + "profile.actions.tooltip": "Більше дій", + "og.repo.summary_card.alt_description": "Підсумкова картка репозиторію %[1]s з описом: %[2]s" } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 0f408997bf..62277acb95 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -78,5 +78,6 @@ "repo.diff.commit.next-short": "下个", "repo.diff.commit.previous-short": "上个", "feed.atom.link": "Atom 订阅源", - "profile.edit.link": "编辑个人资料" + "profile.edit.link": "编辑个人资料", + "og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s" } From 4935e6e1a331d60c5e4ca93e59901fe96e557b53 Mon Sep 17 00:00:00 2001 From: Danko Aleksejevs Date: Thu, 3 Jul 2025 11:29:04 +0200 Subject: [PATCH 059/297] fix: skip empty tokens in SearchOptions.Tokens() (#8261) Query string tokenizer could return a list containing empty tokens when the query string was `\` or `"` (probably in other scenarios as well). This seems undesirable and is what triggered #8260, but I'm posting this separately from that fix in case I'm wrong. Feel free to reject if so. The actual change in behavior is that now searching for `\` or `"` behaves the same as if the query were empty (the bleve/elastic code checks that the tokenizer actually returned, anything rather than just query being non-empty). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8261 Reviewed-by: Shiny Nematoda Co-authored-by: Danko Aleksejevs Co-committed-by: Danko Aleksejevs --- modules/indexer/issues/bleve/bleve.go | 11 ++- .../issues/elasticsearch/elasticsearch.go | 11 ++- modules/indexer/issues/internal/qstring.go | 15 +-- .../indexer/issues/internal/qstring_test.go | 92 +++++++++++++++++++ .../indexer/issues/internal/tests/tests.go | 36 +++++++- 5 files changed, 146 insertions(+), 19 deletions(-) diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 8549ba8dfc..cb98f722c5 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -156,11 +156,12 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error { func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) { var queries []query.Query - if options.Keyword != "" { - tokens, err := options.Tokens() - if err != nil { - return nil, err - } + tokens, err := options.Tokens() + if err != nil { + return nil, err + } + + if len(tokens) > 0 { q := bleve.NewBooleanQuery() for _, token := range tokens { innerQ := bleve.NewDisjunctionQuery( diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index d632a22b2a..311e92730e 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -149,12 +149,13 @@ func (b *Indexer) Delete(ctx context.Context, ids ...int64) error { func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (*internal.SearchResult, error) { query := elastic.NewBoolQuery() - if options.Keyword != "" { + tokens, err := options.Tokens() + if err != nil { + return nil, err + } + + if len(tokens) > 0 { q := elastic.NewBoolQuery() - tokens, err := options.Tokens() - if err != nil { - return nil, err - } for _, token := range tokens { innerQ := elastic.NewMultiMatchQuery(token.Term, "content", "comments").FieldWithBoost("title", 2.0).TieBreaker(0.5) if token.Fuzzy { diff --git a/modules/indexer/issues/internal/qstring.go b/modules/indexer/issues/internal/qstring.go index 6b60b4c5f6..348f7a564b 100644 --- a/modules/indexer/issues/internal/qstring.go +++ b/modules/indexer/issues/internal/qstring.go @@ -45,12 +45,9 @@ func (t *Tokenizer) next() (tk Token, err error) { // skip all leading white space for { - if r, _, err = t.in.ReadRune(); err == nil && r == ' ' { - //nolint:staticcheck,wastedassign // SA4006 the variable is used after the loop - r, _, err = t.in.ReadRune() - continue + if r, _, err = t.in.ReadRune(); err != nil || r != ' ' { + break } - break } if err != nil { return tk, err @@ -107,11 +104,17 @@ nextEnd: // Tokenize the keyword func (o *SearchOptions) Tokens() (tokens []Token, err error) { + if o.Keyword == "" { + return nil, nil + } + in := strings.NewReader(o.Keyword) it := Tokenizer{in: in} for token, err := it.next(); err == nil; token, err = it.next() { - tokens = append(tokens, token) + if token.Term != "" { + tokens = append(tokens, token) + } } if err != nil && err != io.EOF { return nil, err diff --git a/modules/indexer/issues/internal/qstring_test.go b/modules/indexer/issues/internal/qstring_test.go index 835491707c..eb4bdb306f 100644 --- a/modules/indexer/issues/internal/qstring_test.go +++ b/modules/indexer/issues/internal/qstring_test.go @@ -41,6 +41,36 @@ var testOpts = []testIssueQueryStringOpt{ }, }, }, + { + Keyword: "Hello World", + Results: []Token{ + { + Term: "Hello", + Fuzzy: true, + Kind: BoolOptShould, + }, + { + Term: "World", + Fuzzy: true, + Kind: BoolOptShould, + }, + }, + }, + { + Keyword: " Hello World ", + Results: []Token{ + { + Term: "Hello", + Fuzzy: true, + Kind: BoolOptShould, + }, + { + Term: "World", + Fuzzy: true, + Kind: BoolOptShould, + }, + }, + }, { Keyword: "+Hello +World", Results: []Token{ @@ -156,6 +186,68 @@ var testOpts = []testIssueQueryStringOpt{ }, }, }, + { + Keyword: "\\", + Results: nil, + }, + { + Keyword: "\"", + Results: nil, + }, + { + Keyword: "Hello \\", + Results: []Token{ + { + Term: "Hello", + Fuzzy: true, + Kind: BoolOptShould, + }, + }, + }, + { + Keyword: "\"\"", + Results: nil, + }, + { + Keyword: "\" World \"", + Results: []Token{ + { + Term: " World ", + Fuzzy: false, + Kind: BoolOptShould, + }, + }, + }, + { + Keyword: "\"\" World \"\"", + Results: []Token{ + { + Term: "World", + Fuzzy: true, + Kind: BoolOptShould, + }, + }, + }, + { + Keyword: "Best \"Hello World\" Ever", + Results: []Token{ + { + Term: "Best", + Fuzzy: true, + Kind: BoolOptShould, + }, + { + Term: "Hello World", + Fuzzy: false, + Kind: BoolOptShould, + }, + { + Term: "Ever", + Fuzzy: true, + Kind: BoolOptShould, + }, + }, + }, } func TestIssueQueryString(t *testing.T) { diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index b63957ff84..46014994a0 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -87,14 +87,44 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) { } } +func allResults(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { + assert.Len(t, result.Hits, len(data)) + assert.Equal(t, len(data), int(result.Total)) +} + var cases = []*testIndexerCase{ { Name: "default", SearchOptions: &internal.SearchOptions{}, - Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { - assert.Len(t, result.Hits, len(data)) - assert.Equal(t, len(data), int(result.Total)) + Expected: allResults, + }, + { + Name: "empty keyword", + SearchOptions: &internal.SearchOptions{ + Keyword: "", }, + Expected: allResults, + }, + { + Name: "whitespace keyword", + SearchOptions: &internal.SearchOptions{ + Keyword: " ", + }, + Expected: allResults, + }, + { + Name: "dangling slash in keyword", + SearchOptions: &internal.SearchOptions{ + Keyword: "\\", + }, + Expected: allResults, + }, + { + Name: "dangling quote in keyword", + SearchOptions: &internal.SearchOptions{ + Keyword: "\"", + }, + Expected: allResults, }, { Name: "empty", From b580c830e04125cac756c6868b570310eaa42035 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 3 Jul 2025 20:10:58 +0200 Subject: [PATCH 060/297] chore: improve reliability of webauthn e2e test (#8400) - This test is the source of many transient errors https://codeberg.org/forgejo/forgejo/issues/8359#issuecomment-5655557 and is semi-reproducible locally. Any debugging code that is added will result in the error no longer being reproducible, making it hard to say why this is failing. - It no longer seems necessary to add this `waitForURL` call as Playwright now seems to gracefully handle the case where we want to go to a specific page while playwright might still be navigating to another URL that was initiated by clicking on a button - thus removing the source of the transient error altogether. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8400 Reviewed-by: Beowulf Co-authored-by: Gusted Co-committed-by: Gusted --- tests/e2e/webauthn.test.e2e.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/webauthn.test.e2e.ts b/tests/e2e/webauthn.test.e2e.ts index 0b5a6a6c2b..d4b81621d2 100644 --- a/tests/e2e/webauthn.test.e2e.ts +++ b/tests/e2e/webauthn.test.e2e.ts @@ -42,7 +42,6 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) => await page.locator('div[aria-label="Profile and settings…"]').click(); await page.getByText('Sign out').click(); }).toPass(); - await page.waitForURL(`${workerInfo.project.use.baseURL}/`); // Login. response = await page.goto('/user/login'); From aca7e8a9afbad8b3419656c5089fb571508a68b1 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 3 Jul 2025 22:53:55 +0200 Subject: [PATCH 061/297] fix: cancelled or skipped runs are not failures for notifications (#8366) From the point of view of a notification, the only status which must be considered a fail is `StatusFailure`. All others are either success `StatusSuccess` or undetermined `StatusCancelled` and `StatusSkipped`. Those are the only four status in which a run can be when it reaches the `ActionRunNowDone` function because it is filtered on `IsDone` which is only true for those. ## 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. ### Documentation - [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. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8366 Reviewed-by: Antonin Delpeuch Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- services/mailer/mail_actions.go | 4 +- services/mailer/mail_actions_now_done_test.go | 85 +++++++++++++++++++ services/mailer/notify.go | 2 +- 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/services/mailer/mail_actions.go b/services/mailer/mail_actions.go index 09763e164e..fa0d2635f1 100644 --- a/services/mailer/mail_actions.go +++ b/services/mailer/mail_actions.go @@ -16,8 +16,8 @@ const ( tplActionNowDone base.TplName = "actions/now_done" ) -// requires !run.Status.IsSuccess() or !lastRun.Status.IsSuccess() -func MailActionRun(run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error { +var MailActionRun = mailActionRun // make it mockable +func mailActionRun(run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error { if setting.MailService == nil { // No mail service configured return nil diff --git a/services/mailer/mail_actions_now_done_test.go b/services/mailer/mail_actions_now_done_test.go index 6a01ea7631..f4c597c99c 100644 --- a/services/mailer/mail_actions_now_done_test.go +++ b/services/mailer/mail_actions_now_done_test.go @@ -57,6 +57,91 @@ func assertTranslatedLocaleMailActionsNowDone(t *testing.T, msgBody string) { AssertTranslatedLocale(t, msgBody, "mail.actions.successful_run_after_failure", "mail.actions.not_successful_run", "mail.actions.run_info_cur_status", "mail.actions.run_info_ref", "mail.actions.run_info_previous_status", "mail.actions.run_info_trigger", "mail.view_it_on") } +func TestActionRunNowDoneStatusMatrix(t *testing.T) { + successStatuses := []actions_model.Status{ + actions_model.StatusSuccess, + actions_model.StatusSkipped, + actions_model.StatusCancelled, + } + failureStatuses := []actions_model.Status{ + actions_model.StatusFailure, + } + + for _, testCase := range []struct { + name string + statuses []actions_model.Status + hasLastRun bool + lastStatuses []actions_model.Status + run bool + }{ + { + name: "FailureNoLastRun", + statuses: failureStatuses, + run: true, + }, + { + name: "SuccessNoLastRun", + statuses: successStatuses, + run: false, + }, + { + name: "FailureLastRunSuccess", + statuses: failureStatuses, + hasLastRun: true, + lastStatuses: successStatuses, + run: true, + }, + { + name: "FailureLastRunFailure", + statuses: failureStatuses, + hasLastRun: true, + lastStatuses: failureStatuses, + run: true, + }, + { + name: "SuccessLastRunFailure", + statuses: successStatuses, + hasLastRun: true, + lastStatuses: failureStatuses, + run: true, + }, + { + name: "SuccessLastRunSuccess", + statuses: successStatuses, + hasLastRun: true, + lastStatuses: successStatuses, + run: false, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + var called bool + defer test.MockVariableValue(&MailActionRun, func(run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) error { + called = true + return nil + })() + for _, status := range testCase.statuses { + for _, lastStatus := range testCase.lastStatuses { + called = false + n := NewNotifier() + var lastRun *actions_model.ActionRun + if testCase.hasLastRun { + lastRun = &actions_model.ActionRun{ + Status: lastStatus, + } + } + n.ActionRunNowDone(t.Context(), + &actions_model.ActionRun{ + Status: status, + }, + actions_model.StatusUnknown, + lastRun) + assert.Equal(t, testCase.run, called, "status = %s, lastStatus = %s", status, lastStatus) + } + } + }) + } +} + func TestActionRunNowDoneNotificationMail(t *testing.T) { ctx := t.Context() diff --git a/services/mailer/notify.go b/services/mailer/notify.go index 7461a67181..640de31fcc 100644 --- a/services/mailer/notify.go +++ b/services/mailer/notify.go @@ -212,7 +212,7 @@ func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.Us func (m *mailNotifier) ActionRunNowDone(ctx context.Context, run *actions_model.ActionRun, priorStatus actions_model.Status, lastRun *actions_model.ActionRun) { // Only send a mail on a successful run when the workflow recovered (i.e., the run before failed). - if run.Status.IsSuccess() && (lastRun == nil || lastRun.Status.IsSuccess()) { + if !run.Status.IsFailure() && (lastRun == nil || !lastRun.Status.IsFailure()) { return } if err := MailActionRun(run, priorStatus, lastRun); err != nil { From b669564f391baa98241107d183d03954486e08df Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 4 Jul 2025 00:06:28 +0200 Subject: [PATCH 062/297] Update module github.com/alecthomas/chroma/v2 to v2.19.0 (forgejo) (#8393) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8393 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1602f1ef19..de6331722b 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/ProtonMail/go-crypto v1.3.0 github.com/PuerkitoBio/goquery v1.10.3 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 - github.com/alecthomas/chroma/v2 v2.18.0 + github.com/alecthomas/chroma/v2 v2.19.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.5.2 github.com/buildkite/terminal-to-html/v3 v3.16.8 diff --git a/go.sum b/go.sum index 1aa2380161..38a9dd5708 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4= -github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +github.com/alecthomas/chroma/v2 v2.19.0 h1:Im+SLRgT8maArxv81mULDWN8oKxkzboH07CHesxElq4= +github.com/alecthomas/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= From 72620db8dfa654630603b9e8d49d994823cbe8d3 Mon Sep 17 00:00:00 2001 From: zokki Date: Fri, 4 Jul 2025 00:08:23 +0200 Subject: [PATCH 063/297] feat: add a `EXCLUSION` to the logger (#8212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This feature is intended to help reduce noisy logs generated by routine Kubernetes probes and Prometheus scraping. While logs are essential, these specific requests (e.g., to /metrics and /api/healthz) generally don't provide useful information and tend to clutter the output. The goal is to introduce functionality that effectively acts as the inverse of the existing EXPRESSION mode—allowing logging to be excluded based on a condition, rather than included. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8212 Reviewed-by: Gusted Co-authored-by: zokki Co-committed-by: zokki --- cmd/manager_logging.go | 8 +++++ custom/conf/app.example.ini | 1 + modules/log/event_writer.go | 1 + modules/log/event_writer_base.go | 15 ++++++++ modules/log/event_writer_buffer_test.go | 46 +++++++++++++++++++++++++ modules/log/logger_test.go | 16 +++++++++ modules/setting/log.go | 1 + modules/setting/log_test.go | 11 ++++++ routers/private/manager.go | 1 + 9 files changed, 100 insertions(+) diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index c543afe872..c18bfa919b 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -44,6 +44,11 @@ func defaultLoggingFlags() []cli.Flag { Aliases: []string{"e"}, Usage: "Matching expression for the logger", }, + &cli.StringFlag{ + Name: "exclusion", + Aliases: []string{"x"}, + Usage: "Exclusion for the logger", + }, &cli.StringFlag{ Name: "prefix", Aliases: []string{"p"}, @@ -286,6 +291,9 @@ func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[ if len(c.String("expression")) > 0 { vals["expression"] = c.String("expression") } + if len(c.String("exclusion")) > 0 { + vals["exclusion"] = c.String("exclusion") + } if len(c.String("prefix")) > 0 { vals["prefix"] = c.String("prefix") } diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 1b8d4c6697..37d67df5f0 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -631,6 +631,7 @@ LEVEL = Info ;LEVEL= ;FLAGS = stdflags or journald ;EXPRESSION = +;EXCLUSION = ;PREFIX = ;COLORIZE = false ;; diff --git a/modules/log/event_writer.go b/modules/log/event_writer.go index 4b77e488de..32b5b582c5 100644 --- a/modules/log/event_writer.go +++ b/modules/log/event_writer.go @@ -26,6 +26,7 @@ type WriterMode struct { Flags Flags Expression string + Exclusion string StacktraceLevel Level diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go index 9189ca4e90..4de2b953c7 100644 --- a/modules/log/event_writer_base.go +++ b/modules/log/event_writer_base.go @@ -68,6 +68,14 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) { } } + var exclusionRegexp *regexp.Regexp + if b.Mode.Exclusion != "" { + var err error + if exclusionRegexp, err = regexp.Compile(b.Mode.Exclusion); err != nil { + FallbackErrorf("unable to compile exclusion %q for writer %q: %v", b.Mode.Exclusion, b.Name, err) + } + } + handlePaused := func() { if pause := b.GetPauseChan(); pause != nil { select { @@ -95,6 +103,13 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) { continue } } + if exclusionRegexp != nil { + fileLineCaller := fmt.Sprintf("%s:%d:%s", event.Origin.Filename, event.Origin.Line, event.Origin.Caller) + matched := exclusionRegexp.MatchString(fileLineCaller) || exclusionRegexp.MatchString(event.Origin.MsgSimpleText) + if matched { + continue + } + } var err error switch msg := event.Msg.(type) { diff --git a/modules/log/event_writer_buffer_test.go b/modules/log/event_writer_buffer_test.go index ba9455ba69..d1e37c3673 100644 --- a/modules/log/event_writer_buffer_test.go +++ b/modules/log/event_writer_buffer_test.go @@ -31,3 +31,49 @@ func TestBufferLogger(t *testing.T) { logger.Close() assert.Contains(t, bufferWriter.Buffer.String(), expected) } + +func TestBufferLoggerWithExclusion(t *testing.T) { + prefix := "ExclusionPrefix " + level := log.INFO + message := "something" + + bufferWriter := log.NewEventWriterBuffer("test-buffer", log.WriterMode{ + Level: level, + Prefix: prefix, + Exclusion: message, + }) + + logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter) + + logger.SendLogEvent(&log.Event{ + Level: log.INFO, + MsgSimpleText: message, + }) + logger.Close() + assert.NotContains(t, bufferWriter.Buffer.String(), message) +} + +func TestBufferLoggerWithExpressionAndExclusion(t *testing.T) { + prefix := "BothPrefix " + level := log.INFO + expression := ".*foo.*" + exclusion := ".*bar.*" + + bufferWriter := log.NewEventWriterBuffer("test-buffer", log.WriterMode{ + Level: level, + Prefix: prefix, + Expression: expression, + Exclusion: exclusion, + }) + + logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter) + + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "foo expression"}) + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "bar exclusion"}) + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "foo bar both"}) + logger.SendLogEvent(&log.Event{Level: log.INFO, MsgSimpleText: "none"}) + logger.Close() + + assert.Contains(t, bufferWriter.Buffer.String(), "foo expression") + assert.NotContains(t, bufferWriter.Buffer.String(), "bar") +} diff --git a/modules/log/logger_test.go b/modules/log/logger_test.go index 6d6ceb69d7..99045b0f4f 100644 --- a/modules/log/logger_test.go +++ b/modules/log/logger_test.go @@ -143,3 +143,19 @@ func TestLoggerExpressionFilter(t *testing.T) { assert.Equal(t, []string{"foo\n", "foo bar\n", "by filename\n"}, w1.GetLogs()) } + +func TestLoggerExclusionFilter(t *testing.T) { + logger := NewLoggerWithWriters(t.Context(), "test") + + w1 := newDummyWriter("dummy-1", DEBUG, 0) + w1.Mode.Exclusion = "foo.*" + logger.AddWriters(w1) + + logger.Info("foo") + logger.Info("bar") + logger.Info("foo bar") + logger.SendLogEvent(&Event{Level: INFO, Filename: "foo.go", MsgSimpleText: "by filename"}) + logger.Close() + + assert.Equal(t, []string{"bar\n"}, w1.GetLogs()) +} diff --git a/modules/setting/log.go b/modules/setting/log.go index 0747ac4dac..6d069d0e9c 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -133,6 +133,7 @@ func loadLogModeByName(rootCfg ConfigProvider, loggerName, modeName string) (wri writerMode.StacktraceLevel = log.LevelFromString(ConfigInheritedKeyString(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel.String())) writerMode.Prefix = ConfigInheritedKeyString(sec, "PREFIX") writerMode.Expression = ConfigInheritedKeyString(sec, "EXPRESSION") + writerMode.Exclusion = ConfigInheritedKeyString(sec, "EXCLUSION") // flags are updated and set below switch writerType { diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go index eda6dc36af..223bd68285 100644 --- a/modules/setting/log_test.go +++ b/modules/setting/log_test.go @@ -44,6 +44,7 @@ func TestLogConfigDefault(t *testing.T) { "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -83,6 +84,7 @@ logger.xorm.MODE = "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -121,6 +123,7 @@ MODE = console "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -168,6 +171,7 @@ ACCESS = file "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -191,6 +195,7 @@ ACCESS = file "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "none", "Level": "info", "Prefix": "", @@ -257,6 +262,7 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "warn", "Prefix": "", @@ -270,6 +276,7 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "error", "Prefix": "", @@ -287,6 +294,7 @@ STDERR = true "BufferLen": 10000, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "none", "Level": "warn", "Prefix": "", @@ -323,6 +331,7 @@ MODE = file LEVEL = error STACKTRACE_LEVEL = fatal EXPRESSION = filter +EXCLUSION = not FLAGS = medfile PREFIX = "[Prefix] " FILE_NAME = file-xxx.log @@ -341,6 +350,7 @@ COMPRESSION_LEVEL = 4 "BufferLen": 10, "Colorize": false, "Expression": "", + "Exclusion": "", "Flags": "stdflags", "Level": "info", "Prefix": "", @@ -360,6 +370,7 @@ COMPRESSION_LEVEL = 4 "BufferLen": 10, "Colorize": false, "Expression": "filter", + "Exclusion": "not", "Flags": "medfile", "Level": "error", "Prefix": "[Prefix] ", diff --git a/routers/private/manager.go b/routers/private/manager.go index 7ab198f71b..90b48256df 100644 --- a/routers/private/manager.go +++ b/routers/private/manager.go @@ -145,6 +145,7 @@ func AddLogger(ctx *context.PrivateContext) { writerMode.Prefix, _ = opts.Config["prefix"].(string) writerMode.Expression, _ = opts.Config["expression"].(string) + writerMode.Exclusion, _ = opts.Config["exclusion"].(string) switch writerType { case "console": From a789dd76d5648191f2414aafd16f4c05e8ca7faf Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 4 Jul 2025 23:09:26 +0200 Subject: [PATCH 064/297] Update dependency mermaid to v11.8.0 (forgejo) (#8405) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8405 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4555b299bf..1a1c527c58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.7.0", + "mermaid": "11.8.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", @@ -2075,9 +2075,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.5.0.tgz", - "integrity": "sha512-AiaN7+VjXC+3BYE+GwNezkpjIcCI2qIMB/K4S2/vMWe0q/XJCBbx5+K7iteuz7VyltX9iAK4FmVTvGc9kjOV4w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.0.tgz", + "integrity": "sha512-7DNESgpyZ5WG1SIkrYafVBhWmImtmQuoxOO1lawI3gQYWxBX3v1FW3IyuuRfKJAO06XrZR71W0Kif5VEGGd4VA==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -10538,14 +10538,14 @@ } }, "node_modules/mermaid": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.7.0.tgz", - "integrity": "sha512-/1/5R0rt0Z1Ak0CuznAnCF3HtQgayRXUz6SguzOwN4L+DuCobz0UxnQ+ZdTSZ3AugKVVh78tiVmsHpHWV25TCw==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.8.0.tgz", + "integrity": "sha512-uAZUwnBiqREZcUrFw3G5iQ5Pj3hTYUP95EZc3ec/nGBzHddJZydzYGE09tGZDBS1VoSoDn0symZ85FmypSTo5g==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.5.0", + "@mermaid-js/parser": "^0.6.0", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", diff --git a/package.json b/package.json index 3623c143e0..c651ecfa13 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.7.0", + "mermaid": "11.8.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", From d4873965c8f9758df57d13dda2479fcb72954387 Mon Sep 17 00:00:00 2001 From: joneshf Date: Sat, 5 Jul 2025 13:53:00 +0200 Subject: [PATCH 065/297] feat: add `--attribute-ssh-pubic-key` to forgejo admin auth add-oauth and update-oauth CLI (#8383) As explained in https://codeberg.org/forgejo/forgejo/issues/8072, the CLI was missing a way to set the `AttributeSSHPublicKey` field that was added in https://codeberg.org/forgejo/forgejo/pulls/6232. We add a flag to do that, and thread it through where necessary. The checklist mentions adding tests, but the code in `cmd/admin_auth_oauth.go` seems to not have a `cmd/admin_auth_oauth_test.go`, and I'm not sure if there's something else that's testing this behavior. I can try to add tests if there's already a good spot to slot them in. If not, it seems like adding a `cmd/cmd/admin_auth_oauth_test.go` that worked similar to the current `cmd/admin_auth_ldap_test.go` might be a bit big of a change. As far as documentation, I might be wrong about this, but it seems like the CLI docs are only updated once there's a new release. I can't do that yet, so I don't think that either of the checkboxes apply to this PR. ## Manual testing There are two CLI commands that can be validated: `forgejo admin auth add-oauth` and `forgejo admin auth update-oauth`. 1. `forgejo admin auth add-oauth` requires an actual auto-discovery URL that responds appropriately. - If there is not already an OIDC provider set up that has an auto-discovery URL, the sample OIDC provider at https://openidconnect.net/ can be used with it's auto-discovery URL of https://samples.auth0.com/.well-known/openid-configuration. 1. Run the following command to create a new OAuth2 authentication source: ```Console forgejo admin auth add-oauth --attribute-ssh-public-key=ssh_public_key_field --auto-discover-url=https://samples.auth0.com/.well-known/openid-configuration --name='Delete this later' --provider=openidConnect ``` - This should create a new OAuth2 authentication source named "Delete this later" with the "Public SSH key attribute" field set to `ssh_public_key_field`.
Screenshot of newly created OAuth2 authentication source ![forgejo-admin-auth-add-oauth](/attachments/166f3f82-adc8-4ef9-9128-fb8a923fdc0d)
1. `forgejo admin auth update-oauth` requires the id of the newly created OAuth2 authentication source. - This id can be found on either the "Authentication sources" page (`/admin/auths`) or as the URL of the newly created OAuth2 authentication source (`/admins/auths/{id}`). 1. Run the following command to update the OAuth2 authentication source: ```Console forgejo admin auth update-oauth --attribute-ssh-public-key=ssh_public_key_field_new_name --id= ``` - This should change the "Public SSH key attribute" to `ssh_public_key_field_new_name`.
Screenshot of updated OAuth2 authentication source ![forgejo-admin-auth-update-oauth](/attachments/9674dba0-6ed1-4fb8-8eb8-d7f33cbf8c3a)
## 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. - [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/.md` to be be used for the release notes instead of the title. ## Release notes - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/8383): add `--attribute-ssh-pubic-key` to forgejo admin auth add-oauth and update-oauth CLI Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8383 Reviewed-by: Earl Warren Co-authored-by: joneshf Co-committed-by: joneshf --- cmd/admin_auth_oauth.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go index abdcd5d48a..a638bf0210 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -86,6 +86,11 @@ func oauthCLIFlags() []cli.Flag { Value: nil, Usage: "Scopes to request when to authenticate against this OAuth2 source", }, + &cli.StringFlag{ + Name: "attribute-ssh-public-key", + Value: "", + Usage: "Claim name providing SSH public keys for this source", + }, &cli.StringFlag{ Name: "required-claim-name", Value: "", @@ -163,6 +168,7 @@ func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source { IconURL: c.String("icon-url"), SkipLocalTwoFA: c.Bool("skip-local-2fa"), Scopes: c.StringSlice("scopes"), + AttributeSSHPublicKey: c.String("attribute-ssh-public-key"), RequiredClaimName: c.String("required-claim-name"), RequiredClaimValue: c.String("required-claim-value"), GroupClaimName: c.String("group-claim-name"), @@ -244,6 +250,10 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error { oAuth2Config.Scopes = c.StringSlice("scopes") } + if c.IsSet("attribute-ssh-public-key") { + oAuth2Config.AttributeSSHPublicKey = c.String("attribute-ssh-public-key") + } + if c.IsSet("required-claim-name") { oAuth2Config.RequiredClaimName = c.String("required-claim-name") } From d8c5083c6fbf8e02051d841a6a677591ecc65045 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 5 Jul 2025 16:16:10 +0200 Subject: [PATCH 066/297] Update linters (forgejo) (#8421) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8421 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 243 ++++++++++++++++++++++++++++------------------ package.json | 12 +-- 2 files changed, 152 insertions(+), 103 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a1c527c58..b1f0e1126c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,13 +65,13 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.53.2", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "5.0.0", + "@stylistic/eslint-plugin": "5.1.0", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", - "eslint": "9.30.0", + "eslint": "9.30.1", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", "eslint-plugin-import-x": "4.16.1", @@ -83,8 +83,8 @@ "eslint-plugin-toml": "0.12.0", "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "10.2.0", - "eslint-plugin-vue-scoped-css": "2.10.0", + "eslint-plugin-vue": "10.3.0", + "eslint-plugin-vue-scoped-css": "2.11.0", "eslint-plugin-wc": "3.0.1", "globals": "16.3.0", "happy-dom": "18.0.1", @@ -92,13 +92,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.2", - "stylelint": "16.21.0", + "stylelint": "16.21.1", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.35.0", + "typescript-eslint": "8.35.1", "vite-string-plugin": "1.3.4", "vitest": "3.2.4" }, @@ -1146,9 +1146,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.0.tgz", - "integrity": "sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", + "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", "dev": true, "license": "MIT", "engines": { @@ -3050,9 +3050,9 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.0.0.tgz", - "integrity": "sha512-nVV2FSzeTJ3oFKw+3t9gQYQcrgbopgCASSY27QOtkhEGgSfdQQjDmzZd41NeT1myQ8Wc6l+pZllST9qIu4NKzg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.1.0.tgz", + "integrity": "sha512-TJRJul4u/lmry5N/kyCU+7RWWOk0wyXN+BncRlDYBqpLFnzXkd7QGVfN7KewarFIXv0IX0jSF/Ksu7aHWEDeuw==", "dev": true, "license": "MIT", "dependencies": { @@ -3546,17 +3546,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.0.tgz", - "integrity": "sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", + "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/type-utils": "8.35.0", - "@typescript-eslint/utils": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/type-utils": "8.35.1", + "@typescript-eslint/utils": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3570,7 +3570,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.35.0", + "@typescript-eslint/parser": "^8.35.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -3586,16 +3586,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.0.tgz", - "integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz", + "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/typescript-estree": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4" }, "engines": { @@ -3611,14 +3611,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.0.tgz", - "integrity": "sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", + "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.35.0", - "@typescript-eslint/types": "^8.35.0", + "@typescript-eslint/tsconfig-utils": "^8.35.1", + "@typescript-eslint/types": "^8.35.1", "debug": "^4.3.4" }, "engines": { @@ -3633,14 +3633,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.0.tgz", - "integrity": "sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", + "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0" + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3651,9 +3651,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.0.tgz", - "integrity": "sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", + "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", "dev": true, "license": "MIT", "engines": { @@ -3668,14 +3668,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.0.tgz", - "integrity": "sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz", + "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.35.0", - "@typescript-eslint/utils": "8.35.0", + "@typescript-eslint/typescript-estree": "8.35.1", + "@typescript-eslint/utils": "8.35.1", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3692,9 +3692,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.0.tgz", - "integrity": "sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", + "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", "dev": true, "license": "MIT", "engines": { @@ -3706,16 +3706,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.0.tgz", - "integrity": "sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", + "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.35.0", - "@typescript-eslint/tsconfig-utils": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/visitor-keys": "8.35.0", + "@typescript-eslint/project-service": "8.35.1", + "@typescript-eslint/tsconfig-utils": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3768,16 +3768,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.0.tgz", - "integrity": "sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", + "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.35.0", - "@typescript-eslint/types": "8.35.0", - "@typescript-eslint/typescript-estree": "8.35.0" + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3792,13 +3792,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.0.tgz", - "integrity": "sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", + "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.0", + "@typescript-eslint/types": "8.35.1", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5245,14 +5245,14 @@ } }, "node_modules/cacheable": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.0.tgz", - "integrity": "sha512-SSgQTAnhd7WlJXnGlIi4jJJOiHzgnM5wRMEPaXAU4kECTAMpBoYKoZ9i5zHmclIEZbxcu3j7yY/CF8DTmwIsHg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.1.tgz", + "integrity": "sha512-Fa2BZY0CS9F0PFc/6aVA6tgpOdw+hmv9dkZOlHXII5v5Hw+meJBIWDcPrG9q/dXxGcNbym5t77fzmawrBQfTmQ==", "dev": true, "license": "MIT", "dependencies": { - "hookified": "^1.8.2", - "keyv": "^5.3.3" + "hookified": "^1.10.0", + "keyv": "^5.3.4" } }, "node_modules/cacheable/node_modules/keyv": { @@ -7250,9 +7250,9 @@ } }, "node_modules/eslint": { - "version": "9.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.0.tgz", - "integrity": "sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==", + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", + "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7262,7 +7262,7 @@ "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.0", + "@eslint/js": "9.30.1", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -7669,9 +7669,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-vue": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.2.0.tgz", - "integrity": "sha512-tl9s+KN3z0hN2b8fV2xSs5ytGl7Esk1oSCxULLwFcdaElhZ8btYYZFrWxvh4En+czrSDtuLCeCOGa8HhEZuBdQ==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.3.0.tgz", + "integrity": "sha512-A0u9snqjCfYaPnqqOaH6MBLVWDUIN4trXn8J3x67uDcXvR7X6Ut8p16N+nYhMCQ9Y7edg2BIRGzfyZsY0IdqoQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7686,24 +7686,30 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", "vue-eslint-parser": "^10.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/parser": { + "optional": true + } } }, "node_modules/eslint-plugin-vue-scoped-css": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.10.0.tgz", - "integrity": "sha512-oH2NY7XFHF3EGOotvuPdnhB0x4uOmjoRoWZVfMnJ2PILDKVgZgM8WZ0rhDlh+fsr9jO9P8CAXO5/9s9v/GZNhg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.11.0.tgz", + "integrity": "sha512-rrJgLY8iroTIUMSyxhyhJzFcRxABbk3gFrOLkl41F9G1VBqNNpDShyf6PmDoBEWDk07/bJlnqYlvnQ3giUrRYQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "eslint-compat-utils": "^0.6.0", + "eslint-compat-utils": "^0.6.5", "lodash": "^4.17.21", "postcss": "^8.4.31", "postcss-safe-parser": "^6.0.0", "postcss-scss": "^4.0.3", - "postcss-selector-parser": "^6.0.9", + "postcss-selector-parser": "^7.0.0", "postcss-styl": "^0.12.0" }, "engines": { @@ -7717,6 +7723,20 @@ "vue-eslint-parser": ">=7.1.0" } }, + "node_modules/eslint-plugin-vue-scoped-css/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint-plugin-wc": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-3.0.1.tgz", @@ -13777,9 +13797,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.21.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.21.0.tgz", - "integrity": "sha512-ki3PpJGG7xhm3WtINoWGnlvqAmbqSexoRMbEMJzlwewSIOqPRKPlq452c22xAdEJISVi80r+I7KL9GPUiwFgbg==", + "version": "16.21.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.21.1.tgz", + "integrity": "sha512-WCXdXnYK2tpCbebgMF0Bme3YZH/Rh/UXerj75twYo4uLULlcrLwFVdZTvTEF8idFnAcW21YUDJFyKOfaf6xJRw==", "dev": true, "funding": [ { @@ -13820,7 +13840,7 @@ "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.5.5", + "postcss": "^8.5.6", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.0", @@ -13940,15 +13960,15 @@ } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "6.1.10", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.10.tgz", - "integrity": "sha512-B6/v1f0NwjxzmeOhzfXPGWpKBVA207LS7lehaVKQnFrVktcFRfkzjZZ2gwj2i1TkEUMQht7ZMJbABUT5N+V1Nw==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.11.tgz", + "integrity": "sha512-zfOAns94mp7bHG/vCn9Ru2eDCmIxVQ5dELUHKjHfDEOJmHNzE+uGa6208kfkgmtym4a0FFjEuFksCXFacbVhSg==", "dev": true, "license": "MIT", "dependencies": { - "cacheable": "^1.10.0", + "cacheable": "^1.10.1", "flatted": "^3.3.3", - "hookified": "^1.9.1" + "hookified": "^1.10.0" } }, "node_modules/stylelint/node_modules/ignore": { @@ -13961,6 +13981,35 @@ "node": ">= 4" } }, + "node_modules/stylelint/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/stylelint/node_modules/postcss-safe-parser": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", @@ -14834,15 +14883,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.0.tgz", - "integrity": "sha512-uEnz70b7kBz6eg/j0Czy6K5NivaYopgxRjsnAJ2Fx5oTLo3wefTHIbL7AkQr1+7tJCRVpTs/wiM8JR/11Loq9A==", + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz", + "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.35.0", - "@typescript-eslint/parser": "8.35.0", - "@typescript-eslint/utils": "8.35.0" + "@typescript-eslint/eslint-plugin": "8.35.1", + "@typescript-eslint/parser": "8.35.1", + "@typescript-eslint/utils": "8.35.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index c651ecfa13..63a056ad3a 100644 --- a/package.json +++ b/package.json @@ -64,13 +64,13 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.53.2", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "5.0.0", + "@stylistic/eslint-plugin": "5.1.0", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.2.2", "@vue/test-utils": "2.4.6", - "eslint": "9.30.0", + "eslint": "9.30.1", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", "eslint-plugin-import-x": "4.16.1", @@ -82,8 +82,8 @@ "eslint-plugin-toml": "0.12.0", "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "10.2.0", - "eslint-plugin-vue-scoped-css": "2.10.0", + "eslint-plugin-vue": "10.3.0", + "eslint-plugin-vue-scoped-css": "2.11.0", "eslint-plugin-wc": "3.0.1", "globals": "16.3.0", "happy-dom": "18.0.1", @@ -91,13 +91,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.2", - "stylelint": "16.21.0", + "stylelint": "16.21.1", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.35.0", + "typescript-eslint": "8.35.1", "vite-string-plugin": "1.3.4", "vitest": "3.2.4" }, From 5f514a6e4dc5277337bfc77e28c9a116961ba2a4 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 5 Jul 2025 16:31:53 +0200 Subject: [PATCH 067/297] chore: refactor `LineBlame` (#8419) - Refactor arguments of the function to make more sense. - `path` can be inferred from `repo` receiver. - `line` can be `uint64`. - The two calls to this function check for specific errors, do this error checking in the function. - The ID of a object format is not 40 in the case of SHA256, get the object format and use the correct length. - Add test coverage for `LineBlame`, notably it checks for the errors that can legitimately happen. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8419 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/git/repo_blame.go | 38 +++++++++++++++++++++---- modules/git/repo_blame_test.go | 52 ++++++++++++++++++++++++++++++++++ services/pull/review.go | 12 +++----- 3 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 modules/git/repo_blame_test.go diff --git a/modules/git/repo_blame.go b/modules/git/repo_blame.go index 139cdd7be9..d812354af5 100644 --- a/modules/git/repo_blame.go +++ b/modules/git/repo_blame.go @@ -4,20 +4,46 @@ package git import ( + "errors" "fmt" + "regexp" +) + +var ( + ErrBlameFileDoesNotExist = errors.New("the blamed file does not exist") + ErrBlameFileNotEnoughLines = errors.New("the blamed file has not enough lines") + + notEnoughLinesRe = regexp.MustCompile(`^fatal: file .+ has only \d+ lines?\n$`) ) // LineBlame returns the latest commit at the given line -func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) { - res, _, err := NewCommand(repo.Ctx, "blame"). +func (repo *Repository) LineBlame(revision, file string, line uint64) (*Commit, error) { + res, _, gitErr := NewCommand(repo.Ctx, "blame"). AddOptionFormat("-L %d,%d", line, line). AddOptionValues("-p", revision). - AddDashesAndList(file).RunStdString(&RunOpts{Dir: path}) + AddDashesAndList(file).RunStdString(&RunOpts{Dir: repo.Path}) + if gitErr != nil { + stdErr := gitErr.Stderr() + + if stdErr == fmt.Sprintf("fatal: no such path %s in %s\n", file, revision) { + return nil, ErrBlameFileDoesNotExist + } + if notEnoughLinesRe.MatchString(stdErr) { + return nil, ErrBlameFileNotEnoughLines + } + + return nil, gitErr + } + + objectFormat, err := repo.GetObjectFormat() if err != nil { return nil, err } - if len(res) < 40 { - return nil, fmt.Errorf("invalid result of blame: %s", res) + + objectIDLen := objectFormat.FullLength() + if len(res) < objectIDLen { + return nil, fmt.Errorf("output of blame is invalid, cannot contain commit ID: %s", res) } - return repo.GetCommit(res[:40]) + + return repo.GetCommit(res[:objectIDLen]) } diff --git a/modules/git/repo_blame_test.go b/modules/git/repo_blame_test.go new file mode 100644 index 0000000000..126b95386d --- /dev/null +++ b/modules/git/repo_blame_test.go @@ -0,0 +1,52 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package git + +import ( + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLineBlame(t *testing.T) { + t.Run("SHA1", func(t *testing.T) { + repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "repo1_bare")) + require.NoError(t, err) + defer repo.Close() + + commit, err := repo.LineBlame("HEAD", "foo/link_short", 1) + require.NoError(t, err) + assert.Equal(t, "37991dec2c8e592043f47155ce4808d4580f9123", commit.ID.String()) + + commit, err = repo.LineBlame("HEAD", "foo/link_short", 512) + require.ErrorIs(t, err, ErrBlameFileNotEnoughLines) + assert.Nil(t, commit) + + commit, err = repo.LineBlame("HEAD", "non-existent/path", 512) + require.ErrorIs(t, err, ErrBlameFileDoesNotExist) + assert.Nil(t, commit) + }) + + t.Run("SHA256", func(t *testing.T) { + skipIfSHA256NotSupported(t) + + repo, err := OpenRepository(t.Context(), filepath.Join(testReposDir, "repo1_bare_sha256")) + require.NoError(t, err) + defer repo.Close() + + commit, err := repo.LineBlame("HEAD", "foo/link_short", 1) + require.NoError(t, err) + assert.Equal(t, "6aae864a3d1d0d6a5be0cc64028c1e7021e2632b031fd8eb82afc5a283d1c3d1", commit.ID.String()) + + commit, err = repo.LineBlame("HEAD", "foo/link_short", 512) + require.ErrorIs(t, err, ErrBlameFileNotEnoughLines) + assert.Nil(t, commit) + + commit, err = repo.LineBlame("HEAD", "non-existent/path", 512) + require.ErrorIs(t, err, ErrBlameFileDoesNotExist) + assert.Nil(t, commit) + }) +} diff --git a/services/pull/review.go b/services/pull/review.go index c740328e4c..7d232d6d79 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -9,8 +9,6 @@ import ( "errors" "fmt" "io" - "regexp" - "strings" "forgejo.org/models/db" issues_model "forgejo.org/models/issues" @@ -25,8 +23,6 @@ import ( notify_service "forgejo.org/services/notify" ) -var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`) - // ErrDismissRequestOnClosedPR represents an error when an user tries to dismiss a review associated to a closed or merged PR. type ErrDismissRequestOnClosedPR struct{} @@ -48,8 +44,8 @@ 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, repo.Path, c.TreePath, uint(c.UnsignedLine())) - if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { + 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) } @@ -230,10 +226,10 @@ 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, gitRepo.Path, treePath, uint(line)) + commit, err := gitRepo.LineBlame(head, treePath, uint64(line)) if err == nil { commitID = commit.ID.String() - } else if !strings.Contains(err.Error(), "exit status 128 - fatal: no such path") && !notEnoughLines.MatchString(err.Error()) { + } 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) } } From 6e239a7f65ccb9a690f2aabd66a219e61dae545e Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 5 Jul 2025 16:38:18 +0200 Subject: [PATCH 068/297] Update dependency @vitest/eslint-plugin to v1.3.4 (forgejo) (#8420) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index b1f0e1126c..bc5f65fea9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,7 +69,7 @@ "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", - "@vitest/eslint-plugin": "1.2.2", + "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", "eslint": "9.30.1", "eslint-import-resolver-typescript": "4.4.4", @@ -4140,13 +4140,13 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.2.2.tgz", - "integrity": "sha512-R8NwW+VxyKqVGcMfYsUbdThQyMbtNcoeg+jJeTgMHqWdFdcS0nrODAQXhkplvWzgd7jIJ+GQeydGqFLibsxMxg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.3.4.tgz", + "integrity": "sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.24.0" + "@typescript-eslint/utils": "^8.24.1" }, "peerDependencies": { "eslint": ">= 8.57.0", diff --git a/package.json b/package.json index 63a056ad3a..8fceed4534 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", - "@vitest/eslint-plugin": "1.2.2", + "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", "eslint": "9.30.1", "eslint-import-resolver-typescript": "4.4.4", From 212e8ac348b5ff5b92edb1d6ea702fd81496f831 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 5 Jul 2025 18:33:45 +0200 Subject: [PATCH 069/297] Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.2.1 (forgejo) (#8422) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8422 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .golangci.yml | 4 ++++ Makefile | 2 +- models/forgejo_migrations/main_test.go | 2 +- models/forgejo_migrations/migrate.go | 2 +- models/forgejo_migrations/migrate_test.go | 2 +- models/forgejo_migrations/v13.go | 2 +- models/forgejo_migrations/v14.go | 2 +- models/forgejo_migrations/v15.go | 2 +- models/forgejo_migrations/v16.go | 2 +- models/forgejo_migrations/v17.go | 2 +- models/forgejo_migrations/v18.go | 2 +- models/forgejo_migrations/v19.go | 2 +- models/forgejo_migrations/v1_20/v1.go | 2 +- models/forgejo_migrations/v1_20/v2.go | 2 +- models/forgejo_migrations/v1_20/v3.go | 2 +- models/forgejo_migrations/v1_22/main_test.go | 2 +- models/forgejo_migrations/v1_22/v10.go | 2 +- models/forgejo_migrations/v1_22/v11.go | 2 +- models/forgejo_migrations/v1_22/v12.go | 2 +- models/forgejo_migrations/v1_22/v4.go | 2 +- models/forgejo_migrations/v1_22/v5.go | 2 +- models/forgejo_migrations/v1_22/v6.go | 2 +- models/forgejo_migrations/v1_22/v7.go | 2 +- models/forgejo_migrations/v1_22/v8.go | 2 +- models/forgejo_migrations/v1_22/v8_test.go | 2 +- models/forgejo_migrations/v1_22/v9.go | 2 +- models/forgejo_migrations/v20.go | 2 +- models/forgejo_migrations/v21.go | 2 +- models/forgejo_migrations/v22.go | 2 +- models/forgejo_migrations/v23.go | 2 +- models/forgejo_migrations/v24.go | 2 +- models/forgejo_migrations/v25.go | 2 +- models/forgejo_migrations/v25_test.go | 2 +- models/forgejo_migrations/v26.go | 2 +- models/forgejo_migrations/v27.go | 2 +- models/forgejo_migrations/v28.go | 2 +- models/forgejo_migrations/v29.go | 2 +- models/forgejo_migrations/v30.go | 2 +- models/forgejo_migrations/v30_test.go | 2 +- models/forgejo_migrations/v31.go | 2 +- models/forgejo_migrations/v31_test.go | 2 +- models/forgejo_migrations/v32.go | 2 +- models/forgejo_migrations/v32_test.go | 2 +- models/forgejo_migrations/v33.go | 2 +- models/forgejo_migrations/v33_test.go | 2 +- models/forgejo_migrations/v34.go | 2 +- models/forgejo_migrations/v35.go | 2 +- models/migrations/v1_10/v100.go | 2 +- models/migrations/v1_10/v101.go | 2 +- models/migrations/v1_10/v88.go | 2 +- models/migrations/v1_10/v89.go | 2 +- models/migrations/v1_10/v90.go | 2 +- models/migrations/v1_10/v91.go | 2 +- models/migrations/v1_10/v92.go | 2 +- models/migrations/v1_10/v93.go | 2 +- models/migrations/v1_10/v94.go | 2 +- models/migrations/v1_10/v95.go | 2 +- models/migrations/v1_10/v96.go | 2 +- models/migrations/v1_10/v97.go | 2 +- models/migrations/v1_10/v98.go | 2 +- models/migrations/v1_10/v99.go | 2 +- models/migrations/v1_11/v102.go | 2 +- models/migrations/v1_11/v103.go | 2 +- models/migrations/v1_11/v104.go | 2 +- models/migrations/v1_11/v105.go | 2 +- models/migrations/v1_11/v106.go | 2 +- models/migrations/v1_11/v107.go | 2 +- models/migrations/v1_11/v108.go | 2 +- models/migrations/v1_11/v109.go | 2 +- models/migrations/v1_11/v110.go | 2 +- models/migrations/v1_11/v111.go | 2 +- models/migrations/v1_11/v112.go | 2 +- models/migrations/v1_11/v113.go | 2 +- models/migrations/v1_11/v114.go | 2 +- models/migrations/v1_11/v115.go | 2 +- models/migrations/v1_11/v116.go | 2 +- models/migrations/v1_12/v117.go | 2 +- models/migrations/v1_12/v118.go | 2 +- models/migrations/v1_12/v119.go | 2 +- models/migrations/v1_12/v120.go | 2 +- models/migrations/v1_12/v121.go | 2 +- models/migrations/v1_12/v122.go | 2 +- models/migrations/v1_12/v123.go | 2 +- models/migrations/v1_12/v124.go | 2 +- models/migrations/v1_12/v125.go | 2 +- models/migrations/v1_12/v126.go | 2 +- models/migrations/v1_12/v127.go | 2 +- models/migrations/v1_12/v128.go | 2 +- models/migrations/v1_12/v129.go | 2 +- models/migrations/v1_12/v130.go | 2 +- models/migrations/v1_12/v131.go | 2 +- models/migrations/v1_12/v132.go | 2 +- models/migrations/v1_12/v133.go | 2 +- models/migrations/v1_12/v134.go | 2 +- models/migrations/v1_12/v135.go | 2 +- models/migrations/v1_12/v136.go | 2 +- models/migrations/v1_12/v137.go | 2 +- models/migrations/v1_12/v138.go | 2 +- models/migrations/v1_12/v139.go | 2 +- models/migrations/v1_13/v140.go | 2 +- models/migrations/v1_13/v141.go | 2 +- models/migrations/v1_13/v142.go | 2 +- models/migrations/v1_13/v143.go | 2 +- models/migrations/v1_13/v144.go | 2 +- models/migrations/v1_13/v145.go | 2 +- models/migrations/v1_13/v146.go | 2 +- models/migrations/v1_13/v147.go | 2 +- models/migrations/v1_13/v148.go | 2 +- models/migrations/v1_13/v149.go | 2 +- models/migrations/v1_13/v150.go | 2 +- models/migrations/v1_13/v151.go | 2 +- models/migrations/v1_13/v152.go | 2 +- models/migrations/v1_13/v153.go | 2 +- models/migrations/v1_13/v154.go | 2 +- models/migrations/v1_14/main_test.go | 2 +- models/migrations/v1_14/v155.go | 2 +- models/migrations/v1_14/v156.go | 2 +- models/migrations/v1_14/v157.go | 2 +- models/migrations/v1_14/v158.go | 2 +- models/migrations/v1_14/v159.go | 2 +- models/migrations/v1_14/v160.go | 2 +- models/migrations/v1_14/v161.go | 2 +- models/migrations/v1_14/v162.go | 2 +- models/migrations/v1_14/v163.go | 2 +- models/migrations/v1_14/v164.go | 2 +- models/migrations/v1_14/v165.go | 2 +- models/migrations/v1_14/v166.go | 2 +- models/migrations/v1_14/v167.go | 2 +- models/migrations/v1_14/v168.go | 2 +- models/migrations/v1_14/v169.go | 2 +- models/migrations/v1_14/v170.go | 2 +- models/migrations/v1_14/v171.go | 2 +- models/migrations/v1_14/v172.go | 2 +- models/migrations/v1_14/v173.go | 2 +- models/migrations/v1_14/v174.go | 2 +- models/migrations/v1_14/v175.go | 2 +- models/migrations/v1_14/v176.go | 2 +- models/migrations/v1_14/v176_test.go | 2 +- models/migrations/v1_14/v177.go | 2 +- models/migrations/v1_14/v177_test.go | 2 +- models/migrations/v1_15/main_test.go | 2 +- models/migrations/v1_15/v178.go | 2 +- models/migrations/v1_15/v179.go | 2 +- models/migrations/v1_15/v180.go | 2 +- models/migrations/v1_15/v181.go | 2 +- models/migrations/v1_15/v181_test.go | 2 +- models/migrations/v1_15/v182.go | 2 +- models/migrations/v1_15/v182_test.go | 2 +- models/migrations/v1_15/v183.go | 2 +- models/migrations/v1_15/v184.go | 2 +- models/migrations/v1_15/v185.go | 2 +- models/migrations/v1_15/v186.go | 2 +- models/migrations/v1_15/v187.go | 2 +- models/migrations/v1_15/v188.go | 2 +- models/migrations/v1_16/main_test.go | 2 +- models/migrations/v1_16/v189.go | 2 +- models/migrations/v1_16/v189_test.go | 2 +- models/migrations/v1_16/v190.go | 2 +- models/migrations/v1_16/v191.go | 2 +- models/migrations/v1_16/v192.go | 2 +- models/migrations/v1_16/v193.go | 2 +- models/migrations/v1_16/v193_test.go | 2 +- models/migrations/v1_16/v194.go | 2 +- models/migrations/v1_16/v195.go | 2 +- models/migrations/v1_16/v195_test.go | 2 +- models/migrations/v1_16/v196.go | 2 +- models/migrations/v1_16/v197.go | 2 +- models/migrations/v1_16/v198.go | 2 +- models/migrations/v1_16/v199.go | 2 +- models/migrations/v1_16/v200.go | 2 +- models/migrations/v1_16/v201.go | 2 +- models/migrations/v1_16/v202.go | 2 +- models/migrations/v1_16/v203.go | 2 +- models/migrations/v1_16/v204.go | 2 +- models/migrations/v1_16/v205.go | 2 +- models/migrations/v1_16/v206.go | 2 +- models/migrations/v1_16/v207.go | 2 +- models/migrations/v1_16/v208.go | 2 +- models/migrations/v1_16/v209.go | 2 +- models/migrations/v1_16/v210.go | 2 +- models/migrations/v1_16/v210_test.go | 2 +- models/migrations/v1_17/main_test.go | 2 +- models/migrations/v1_17/v211.go | 2 +- models/migrations/v1_17/v212.go | 2 +- models/migrations/v1_17/v213.go | 2 +- models/migrations/v1_17/v214.go | 2 +- models/migrations/v1_17/v215.go | 2 +- models/migrations/v1_17/v216.go | 2 +- models/migrations/v1_17/v217.go | 2 +- models/migrations/v1_17/v218.go | 2 +- models/migrations/v1_17/v219.go | 2 +- models/migrations/v1_17/v220.go | 2 +- models/migrations/v1_17/v221.go | 2 +- models/migrations/v1_17/v221_test.go | 2 +- models/migrations/v1_17/v222.go | 2 +- models/migrations/v1_17/v223.go | 2 +- models/migrations/v1_18/main_test.go | 2 +- models/migrations/v1_18/v224.go | 2 +- models/migrations/v1_18/v225.go | 2 +- models/migrations/v1_18/v226.go | 2 +- models/migrations/v1_18/v227.go | 2 +- models/migrations/v1_18/v228.go | 2 +- models/migrations/v1_18/v229.go | 2 +- models/migrations/v1_18/v229_test.go | 2 +- models/migrations/v1_18/v230.go | 2 +- models/migrations/v1_18/v230_test.go | 2 +- models/migrations/v1_19/main_test.go | 2 +- models/migrations/v1_19/v231.go | 2 +- models/migrations/v1_19/v232.go | 2 +- models/migrations/v1_19/v233.go | 2 +- models/migrations/v1_19/v233_test.go | 2 +- models/migrations/v1_19/v234.go | 2 +- models/migrations/v1_19/v235.go | 2 +- models/migrations/v1_19/v236.go | 2 +- models/migrations/v1_19/v237.go | 2 +- models/migrations/v1_19/v238.go | 2 +- models/migrations/v1_19/v239.go | 2 +- models/migrations/v1_19/v240.go | 2 +- models/migrations/v1_19/v241.go | 2 +- models/migrations/v1_19/v242.go | 2 +- models/migrations/v1_19/v243.go | 2 +- models/migrations/v1_20/main_test.go | 2 +- models/migrations/v1_20/v244.go | 2 +- models/migrations/v1_20/v245.go | 2 +- models/migrations/v1_20/v246.go | 2 +- models/migrations/v1_20/v247.go | 2 +- models/migrations/v1_20/v248.go | 2 +- models/migrations/v1_20/v249.go | 2 +- models/migrations/v1_20/v250.go | 2 +- models/migrations/v1_20/v251.go | 2 +- models/migrations/v1_20/v252.go | 2 +- models/migrations/v1_20/v253.go | 2 +- models/migrations/v1_20/v254.go | 2 +- models/migrations/v1_20/v255.go | 2 +- models/migrations/v1_20/v256.go | 2 +- models/migrations/v1_20/v257.go | 2 +- models/migrations/v1_20/v258.go | 2 +- models/migrations/v1_20/v259.go | 2 +- models/migrations/v1_20/v259_test.go | 2 +- models/migrations/v1_21/main_test.go | 2 +- models/migrations/v1_21/v260.go | 2 +- models/migrations/v1_21/v261.go | 2 +- models/migrations/v1_21/v262.go | 2 +- models/migrations/v1_21/v263.go | 2 +- models/migrations/v1_21/v264.go | 2 +- models/migrations/v1_21/v265.go | 2 +- models/migrations/v1_21/v266.go | 2 +- models/migrations/v1_21/v267.go | 2 +- models/migrations/v1_21/v268.go | 2 +- models/migrations/v1_21/v269.go | 2 +- models/migrations/v1_21/v270.go | 2 +- models/migrations/v1_21/v271.go | 3 ++- models/migrations/v1_21/v272.go | 3 ++- models/migrations/v1_21/v273.go | 3 ++- models/migrations/v1_21/v274.go | 3 ++- models/migrations/v1_21/v275.go | 2 +- models/migrations/v1_21/v276.go | 2 +- models/migrations/v1_21/v277.go | 2 +- models/migrations/v1_21/v278.go | 2 +- models/migrations/v1_21/v279.go | 2 +- models/migrations/v1_22/main_test.go | 2 +- models/migrations/v1_22/v280.go | 2 +- models/migrations/v1_22/v281.go | 2 +- models/migrations/v1_22/v282.go | 2 +- models/migrations/v1_22/v283.go | 2 +- models/migrations/v1_22/v283_test.go | 2 +- models/migrations/v1_22/v284.go | 3 ++- models/migrations/v1_22/v285.go | 2 +- models/migrations/v1_22/v286.go | 2 +- models/migrations/v1_22/v286_test.go | 2 +- models/migrations/v1_22/v287.go | 2 +- models/migrations/v1_22/v288.go | 2 +- models/migrations/v1_22/v289.go | 2 +- models/migrations/v1_22/v290.go | 2 +- models/migrations/v1_22/v290_test.go | 2 +- models/migrations/v1_22/v291.go | 2 +- models/migrations/v1_22/v292.go | 2 +- models/migrations/v1_22/v293.go | 2 +- models/migrations/v1_22/v293_test.go | 2 +- models/migrations/v1_22/v294.go | 2 +- models/migrations/v1_22/v294_test.go | 5 +++-- models/migrations/v1_22/v295.go | 2 +- models/migrations/v1_22/v296.go | 2 +- models/migrations/v1_22/v298.go | 2 +- models/migrations/v1_23/main_test.go | 2 +- models/migrations/v1_23/v299.go | 2 +- models/migrations/v1_23/v300.go | 2 +- models/migrations/v1_23/v301.go | 2 +- models/migrations/v1_23/v302.go | 2 +- models/migrations/v1_23/v303.go | 2 +- models/migrations/v1_23/v303_test.go | 2 +- models/migrations/v1_6/v70.go | 2 +- models/migrations/v1_6/v71.go | 2 +- models/migrations/v1_6/v72.go | 2 +- models/migrations/v1_7/v73.go | 2 +- models/migrations/v1_7/v74.go | 2 +- models/migrations/v1_7/v75.go | 2 +- models/migrations/v1_8/v76.go | 2 +- models/migrations/v1_8/v77.go | 2 +- models/migrations/v1_8/v78.go | 2 +- models/migrations/v1_8/v79.go | 2 +- models/migrations/v1_8/v80.go | 2 +- models/migrations/v1_8/v81.go | 2 +- models/migrations/v1_9/v82.go | 2 +- models/migrations/v1_9/v83.go | 2 +- models/migrations/v1_9/v84.go | 2 +- models/migrations/v1_9/v85.go | 2 +- models/migrations/v1_9/v86.go | 2 +- models/migrations/v1_9/v87.go | 2 +- tests/integration/pull_commit_test.go | 2 +- tests/integration/repo_test.go | 2 +- tests/integration/signing_git_test.go | 2 +- 312 files changed, 322 insertions(+), 312 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 532132838d..6679a1850e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -79,6 +79,10 @@ linters: - name: unreachable-code - name: var-declaration - name: var-naming + arguments: + - [] + - [] + - - skip-package-name-checks: true - name: redefines-builtin-id disabled: true staticcheck: diff --git a/Makefile b/Makefile index db4ec2fbd5..54becdc8dc 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.2.1 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest diff --git a/models/forgejo_migrations/main_test.go b/models/forgejo_migrations/main_test.go index 031fe8090d..2246e327f0 100644 --- a/models/forgejo_migrations/main_test.go +++ b/models/forgejo_migrations/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index bc64f07013..50391ff650 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "context" diff --git a/models/forgejo_migrations/migrate_test.go b/models/forgejo_migrations/migrate_test.go index 20653929a3..9d16c9fe1c 100644 --- a/models/forgejo_migrations/migrate_test.go +++ b/models/forgejo_migrations/migrate_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v13.go b/models/forgejo_migrations/v13.go index 614f68249d..ba4183885e 100644 --- a/models/forgejo_migrations/v13.go +++ b/models/forgejo_migrations/v13.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v14.go b/models/forgejo_migrations/v14.go index 53f1ef2223..65b857d343 100644 --- a/models/forgejo_migrations/v14.go +++ b/models/forgejo_migrations/v14.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "forgejo.org/models/migrations/base" diff --git a/models/forgejo_migrations/v15.go b/models/forgejo_migrations/v15.go index 5e5588dd05..a63199ab19 100644 --- a/models/forgejo_migrations/v15.go +++ b/models/forgejo_migrations/v15.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "time" diff --git a/models/forgejo_migrations/v16.go b/models/forgejo_migrations/v16.go index f80bfc5268..a7d4d5d590 100644 --- a/models/forgejo_migrations/v16.go +++ b/models/forgejo_migrations/v16.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v17.go b/models/forgejo_migrations/v17.go index d6e2983d00..8ef6f2c681 100644 --- a/models/forgejo_migrations/v17.go +++ b/models/forgejo_migrations/v17.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v18.go b/models/forgejo_migrations/v18.go index e6c1493f0e..e39b0cbf10 100644 --- a/models/forgejo_migrations/v18.go +++ b/models/forgejo_migrations/v18.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v19.go b/models/forgejo_migrations/v19.go index 69b7746eb1..43d279dcb0 100644 --- a/models/forgejo_migrations/v19.go +++ b/models/forgejo_migrations/v19.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_20/v1.go b/models/forgejo_migrations/v1_20/v1.go index 72beaf23de..f0cb125557 100644 --- a/models/forgejo_migrations/v1_20/v1.go +++ b/models/forgejo_migrations/v1_20/v1.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_v1_20 //nolint:revive +package forgejo_v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_20/v2.go b/models/forgejo_migrations/v1_20/v2.go index 39f3b58924..3f79ac3801 100644 --- a/models/forgejo_migrations/v1_20/v2.go +++ b/models/forgejo_migrations/v1_20/v2.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -package forgejo_v1_20 //nolint:revive +package forgejo_v1_20 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_20/v3.go b/models/forgejo_migrations/v1_20/v3.go index cce227e6eb..49530df556 100644 --- a/models/forgejo_migrations/v1_20/v3.go +++ b/models/forgejo_migrations/v1_20/v3.go @@ -1,7 +1,7 @@ // Copyright 2023 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_v1_20 //nolint:revive +package forgejo_v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_22/main_test.go b/models/forgejo_migrations/v1_22/main_test.go index 03c4c5272c..d6a5bdacee 100644 --- a/models/forgejo_migrations/v1_22/main_test.go +++ b/models/forgejo_migrations/v1_22/main_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/forgejo_migrations/v1_22/v10.go b/models/forgejo_migrations/v1_22/v10.go index 819800ae71..cf45abdd24 100644 --- a/models/forgejo_migrations/v1_22/v10.go +++ b/models/forgejo_migrations/v1_22/v10.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v11.go b/models/forgejo_migrations/v1_22/v11.go index 17bb592379..f0f92bd04c 100644 --- a/models/forgejo_migrations/v1_22/v11.go +++ b/models/forgejo_migrations/v1_22/v11.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v1_22/v12.go b/models/forgejo_migrations/v1_22/v12.go index 6822524705..51354bd3c2 100644 --- a/models/forgejo_migrations/v1_22/v12.go +++ b/models/forgejo_migrations/v1_22/v12.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v4.go b/models/forgejo_migrations/v1_22/v4.go index f1195f5f66..499d377bb4 100644 --- a/models/forgejo_migrations/v1_22/v4.go +++ b/models/forgejo_migrations/v1_22/v4.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v5.go b/models/forgejo_migrations/v1_22/v5.go index 55f9fe1338..1671d3eed2 100644 --- a/models/forgejo_migrations/v1_22/v5.go +++ b/models/forgejo_migrations/v1_22/v5.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v6.go b/models/forgejo_migrations/v1_22/v6.go index 1a4874872c..072f8e6a15 100644 --- a/models/forgejo_migrations/v1_22/v6.go +++ b/models/forgejo_migrations/v1_22/v6.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v7.go b/models/forgejo_migrations/v1_22/v7.go index b42dd1af67..e7f6eb412b 100644 --- a/models/forgejo_migrations/v1_22/v7.go +++ b/models/forgejo_migrations/v1_22/v7.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v1_22/v8.go b/models/forgejo_migrations/v1_22/v8.go index 2d3c0c594b..f23b00d2ad 100644 --- a/models/forgejo_migrations/v1_22/v8.go +++ b/models/forgejo_migrations/v1_22/v8.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "strings" diff --git a/models/forgejo_migrations/v1_22/v8_test.go b/models/forgejo_migrations/v1_22/v8_test.go index baaba7290f..5117dd2dfb 100644 --- a/models/forgejo_migrations/v1_22/v8_test.go +++ b/models/forgejo_migrations/v1_22/v8_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/forgejo_migrations/v1_22/v9.go b/models/forgejo_migrations/v1_22/v9.go index 34c2844c39..e3cdea97f2 100644 --- a/models/forgejo_migrations/v1_22/v9.go +++ b/models/forgejo_migrations/v1_22/v9.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v20.go b/models/forgejo_migrations/v20.go index 8ca9e91f73..91c7b8e911 100644 --- a/models/forgejo_migrations/v20.go +++ b/models/forgejo_migrations/v20.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v21.go b/models/forgejo_migrations/v21.go index 53f141b2ab..61d7950c5a 100644 --- a/models/forgejo_migrations/v21.go +++ b/models/forgejo_migrations/v21.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v22.go b/models/forgejo_migrations/v22.go index eeb738799c..8078591da6 100644 --- a/models/forgejo_migrations/v22.go +++ b/models/forgejo_migrations/v22.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v23.go b/models/forgejo_migrations/v23.go index 20a916a716..a79a4f3d6e 100644 --- a/models/forgejo_migrations/v23.go +++ b/models/forgejo_migrations/v23.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v24.go b/models/forgejo_migrations/v24.go index ebfb5fc1c4..084a57e1ce 100644 --- a/models/forgejo_migrations/v24.go +++ b/models/forgejo_migrations/v24.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v25.go b/models/forgejo_migrations/v25.go index 8e3032a40c..56cde499a3 100644 --- a/models/forgejo_migrations/v25.go +++ b/models/forgejo_migrations/v25.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "context" diff --git a/models/forgejo_migrations/v25_test.go b/models/forgejo_migrations/v25_test.go index e7402fd021..68e71da012 100644 --- a/models/forgejo_migrations/v25_test.go +++ b/models/forgejo_migrations/v25_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v26.go b/models/forgejo_migrations/v26.go index 3292d93ffd..a0c47799c2 100644 --- a/models/forgejo_migrations/v26.go +++ b/models/forgejo_migrations/v26.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v27.go b/models/forgejo_migrations/v27.go index 2efa3485a8..9cfbc64370 100644 --- a/models/forgejo_migrations/v27.go +++ b/models/forgejo_migrations/v27.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "forgejo.org/modules/timeutil" diff --git a/models/forgejo_migrations/v28.go b/models/forgejo_migrations/v28.go index cba888d2ec..19f0dcd862 100644 --- a/models/forgejo_migrations/v28.go +++ b/models/forgejo_migrations/v28.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v29.go b/models/forgejo_migrations/v29.go index d0c2f723ae..92eb05e8b3 100644 --- a/models/forgejo_migrations/v29.go +++ b/models/forgejo_migrations/v29.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "database/sql" diff --git a/models/forgejo_migrations/v30.go b/models/forgejo_migrations/v30.go index 6c41a55316..05a1dff898 100644 --- a/models/forgejo_migrations/v30.go +++ b/models/forgejo_migrations/v30.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "time" diff --git a/models/forgejo_migrations/v30_test.go b/models/forgejo_migrations/v30_test.go index f826dab815..152fddeb47 100644 --- a/models/forgejo_migrations/v30_test.go +++ b/models/forgejo_migrations/v30_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v31.go b/models/forgejo_migrations/v31.go index fdcab21b1a..23397c7c13 100644 --- a/models/forgejo_migrations/v31.go +++ b/models/forgejo_migrations/v31.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "xorm.io/xorm" diff --git a/models/forgejo_migrations/v31_test.go b/models/forgejo_migrations/v31_test.go index 5b4aac2a60..6d1690aae0 100644 --- a/models/forgejo_migrations/v31_test.go +++ b/models/forgejo_migrations/v31_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v32.go b/models/forgejo_migrations/v32.go index bed335ab6b..81b22c585c 100644 --- a/models/forgejo_migrations/v32.go +++ b/models/forgejo_migrations/v32.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "encoding/xml" diff --git a/models/forgejo_migrations/v32_test.go b/models/forgejo_migrations/v32_test.go index cd33de2608..24cda891bc 100644 --- a/models/forgejo_migrations/v32_test.go +++ b/models/forgejo_migrations/v32_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "bytes" diff --git a/models/forgejo_migrations/v33.go b/models/forgejo_migrations/v33.go index 272035fc23..b9ea8efe47 100644 --- a/models/forgejo_migrations/v33.go +++ b/models/forgejo_migrations/v33.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "fmt" diff --git a/models/forgejo_migrations/v33_test.go b/models/forgejo_migrations/v33_test.go index 664c704bbc..1d3298da15 100644 --- a/models/forgejo_migrations/v33_test.go +++ b/models/forgejo_migrations/v33_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "testing" diff --git a/models/forgejo_migrations/v34.go b/models/forgejo_migrations/v34.go index 9e958b934f..d193d799e7 100644 --- a/models/forgejo_migrations/v34.go +++ b/models/forgejo_migrations/v34.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import "xorm.io/xorm" diff --git a/models/forgejo_migrations/v35.go b/models/forgejo_migrations/v35.go index ca412d7951..9b389fcc12 100644 --- a/models/forgejo_migrations/v35.go +++ b/models/forgejo_migrations/v35.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: GPL-3.0-or-later -package forgejo_migrations //nolint:revive +package forgejo_migrations import ( "xorm.io/xorm" diff --git a/models/migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go index 5d2fd8e244..1742bea296 100644 --- a/models/migrations/v1_10/v100.go +++ b/models/migrations/v1_10/v100.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "net/url" diff --git a/models/migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go index f023a2a0e7..6c8dfe2486 100644 --- a/models/migrations/v1_10/v101.go +++ b/models/migrations/v1_10/v101.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go index 7e86ac364f..eb8e81c19e 100644 --- a/models/migrations/v1_10/v88.go +++ b/models/migrations/v1_10/v88.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "crypto/sha1" diff --git a/models/migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go index d5f27ffdc6..0df2a6e17b 100644 --- a/models/migrations/v1_10/v89.go +++ b/models/migrations/v1_10/v89.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go index 295d4b1c1b..5521a97e32 100644 --- a/models/migrations/v1_10/v90.go +++ b/models/migrations/v1_10/v90.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go index 48cac2de70..08db6c2742 100644 --- a/models/migrations/v1_10/v91.go +++ b/models/migrations/v1_10/v91.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go index 9080108594..b6c04a9234 100644 --- a/models/migrations/v1_10/v92.go +++ b/models/migrations/v1_10/v92.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "xorm.io/builder" diff --git a/models/migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go index ee59a8db39..c131be9a8d 100644 --- a/models/migrations/v1_10/v93.go +++ b/models/migrations/v1_10/v93.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go index c131af162b..13b7d7b303 100644 --- a/models/migrations/v1_10/v94.go +++ b/models/migrations/v1_10/v94.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go index 3b1f67fd9c..86b52026bf 100644 --- a/models/migrations/v1_10/v95.go +++ b/models/migrations/v1_10/v95.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go index 3bfb770f24..bcbd618b49 100644 --- a/models/migrations/v1_10/v96.go +++ b/models/migrations/v1_10/v96.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "path/filepath" diff --git a/models/migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go index dee45b32e3..5872bb63e5 100644 --- a/models/migrations/v1_10/v97.go +++ b/models/migrations/v1_10/v97.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go index bdd9aed089..d21c326459 100644 --- a/models/migrations/v1_10/v98.go +++ b/models/migrations/v1_10/v98.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go index 7f287b77aa..addae66be9 100644 --- a/models/migrations/v1_10/v99.go +++ b/models/migrations/v1_10/v99.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go index a585d9c423..15f0c83c36 100644 --- a/models/migrations/v1_11/v102.go +++ b/models/migrations/v1_11/v102.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_11/v103.go b/models/migrations/v1_11/v103.go index 53527dac58..a515710160 100644 --- a/models/migrations/v1_11/v103.go +++ b/models/migrations/v1_11/v103.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go index af3578ca4a..7461f0cda3 100644 --- a/models/migrations/v1_11/v104.go +++ b/models/migrations/v1_11/v104.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_11/v105.go b/models/migrations/v1_11/v105.go index b91340c30a..d86973a0f6 100644 --- a/models/migrations/v1_11/v105.go +++ b/models/migrations/v1_11/v105.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go index ecb11cdd1e..edffe18683 100644 --- a/models/migrations/v1_11/v106.go +++ b/models/migrations/v1_11/v106.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go index f0bfe5862c..a158e3bb50 100644 --- a/models/migrations/v1_11/v107.go +++ b/models/migrations/v1_11/v107.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go index a85096234d..8f14504ceb 100644 --- a/models/migrations/v1_11/v108.go +++ b/models/migrations/v1_11/v108.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go index ea565ccda3..f7616aec7b 100644 --- a/models/migrations/v1_11/v109.go +++ b/models/migrations/v1_11/v109.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go index fce9be847e..e94a738f67 100644 --- a/models/migrations/v1_11/v110.go +++ b/models/migrations/v1_11/v110.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index cc3dc0d545..6f531e4858 100644 --- a/models/migrations/v1_11/v111.go +++ b/models/migrations/v1_11/v111.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go index 6112ab51a5..22054e6f68 100644 --- a/models/migrations/v1_11/v112.go +++ b/models/migrations/v1_11/v112.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go index dea344a44f..a4d54f66fb 100644 --- a/models/migrations/v1_11/v113.go +++ b/models/migrations/v1_11/v113.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go index 95adcee989..9467a8a90c 100644 --- a/models/migrations/v1_11/v114.go +++ b/models/migrations/v1_11/v114.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "net/url" diff --git a/models/migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go index 3d4b41017b..65094df93d 100644 --- a/models/migrations/v1_11/v115.go +++ b/models/migrations/v1_11/v115.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "crypto/md5" diff --git a/models/migrations/v1_11/v116.go b/models/migrations/v1_11/v116.go index 85aa76c1e0..729fbad18b 100644 --- a/models/migrations/v1_11/v116.go +++ b/models/migrations/v1_11/v116.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go index 8eadcdef2b..73b58ca34b 100644 --- a/models/migrations/v1_12/v117.go +++ b/models/migrations/v1_12/v117.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go index eb022dc5e4..e8b4249743 100644 --- a/models/migrations/v1_12/v118.go +++ b/models/migrations/v1_12/v118.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go index 60bfe6a57d..b4bf29a935 100644 --- a/models/migrations/v1_12/v119.go +++ b/models/migrations/v1_12/v119.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go index 3f7ed8d373..14d515f5a7 100644 --- a/models/migrations/v1_12/v120.go +++ b/models/migrations/v1_12/v120.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go index 175ec9164d..a28ae4e1c9 100644 --- a/models/migrations/v1_12/v121.go +++ b/models/migrations/v1_12/v121.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go index 6e31d863a1..bc1b175f6a 100644 --- a/models/migrations/v1_12/v122.go +++ b/models/migrations/v1_12/v122.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go index b0c3af07a3..52b10bb850 100644 --- a/models/migrations/v1_12/v123.go +++ b/models/migrations/v1_12/v123.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go index d2ba03ffe0..9a93f436d4 100644 --- a/models/migrations/v1_12/v124.go +++ b/models/migrations/v1_12/v124.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go index ec4ffaab25..7f582ecff5 100644 --- a/models/migrations/v1_12/v125.go +++ b/models/migrations/v1_12/v125.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go index ca9ec3aa3f..64fd7f7478 100644 --- a/models/migrations/v1_12/v126.go +++ b/models/migrations/v1_12/v126.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/builder" diff --git a/models/migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go index 11a4042973..f686fa617c 100644 --- a/models/migrations/v1_12/v127.go +++ b/models/migrations/v1_12/v127.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go index 6d7307f470..8fca974616 100644 --- a/models/migrations/v1_12/v128.go +++ b/models/migrations/v1_12/v128.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v129.go b/models/migrations/v1_12/v129.go index cf228242b9..3e4d3aca68 100644 --- a/models/migrations/v1_12/v129.go +++ b/models/migrations/v1_12/v129.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go index bfa856796a..383ef47492 100644 --- a/models/migrations/v1_12/v130.go +++ b/models/migrations/v1_12/v130.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "forgejo.org/modules/json" diff --git a/models/migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go index 5184bc3590..1266c2f185 100644 --- a/models/migrations/v1_12/v131.go +++ b/models/migrations/v1_12/v131.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go index 3b2b28f7ab..8b1ae6db93 100644 --- a/models/migrations/v1_12/v132.go +++ b/models/migrations/v1_12/v132.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go index c9087fc8c1..69e20597d8 100644 --- a/models/migrations/v1_12/v133.go +++ b/models/migrations/v1_12/v133.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go index bba996fd40..1fabdcae96 100644 --- a/models/migrations/v1_12/v134.go +++ b/models/migrations/v1_12/v134.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v135.go b/models/migrations/v1_12/v135.go index 8898011df5..5df0ad7fc4 100644 --- a/models/migrations/v1_12/v135.go +++ b/models/migrations/v1_12/v135.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go index e2557ae002..7d246a82be 100644 --- a/models/migrations/v1_12/v136.go +++ b/models/migrations/v1_12/v136.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go index 0d86b72010..9d38483488 100644 --- a/models/migrations/v1_12/v137.go +++ b/models/migrations/v1_12/v137.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go index 8c8d353f40..4485adeb2d 100644 --- a/models/migrations/v1_12/v138.go +++ b/models/migrations/v1_12/v138.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go index cd7963524e..51e57b984a 100644 --- a/models/migrations/v1_12/v139.go +++ b/models/migrations/v1_12/v139.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go index d74f808e9f..5bb612c098 100644 --- a/models/migrations/v1_13/v140.go +++ b/models/migrations/v1_13/v140.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go index ae211e0e44..b54bc1727c 100644 --- a/models/migrations/v1_13/v141.go +++ b/models/migrations/v1_13/v141.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go index 7490e0f3b4..8939f6f2f8 100644 --- a/models/migrations/v1_13/v142.go +++ b/models/migrations/v1_13/v142.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go index 1f9120e2ba..6a8da8b06d 100644 --- a/models/migrations/v1_13/v143.go +++ b/models/migrations/v1_13/v143.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go index 7e801eab8a..f138338514 100644 --- a/models/migrations/v1_13/v144.go +++ b/models/migrations/v1_13/v144.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go index a01f577ed1..f7d3895c84 100644 --- a/models/migrations/v1_13/v145.go +++ b/models/migrations/v1_13/v145.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go index a1b54ee3aa..e6a476a288 100644 --- a/models/migrations/v1_13/v146.go +++ b/models/migrations/v1_13/v146.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go index cc57504c74..831ef5842a 100644 --- a/models/migrations/v1_13/v147.go +++ b/models/migrations/v1_13/v147.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go index 7bb8ab700b..d276db3d61 100644 --- a/models/migrations/v1_13/v148.go +++ b/models/migrations/v1_13/v148.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go index 3a0c5909d5..c1bfe8b09e 100644 --- a/models/migrations/v1_13/v149.go +++ b/models/migrations/v1_13/v149.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go index be14fd130c..471a531024 100644 --- a/models/migrations/v1_13/v150.go +++ b/models/migrations/v1_13/v150.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go index ff584fff67..691b86062d 100644 --- a/models/migrations/v1_13/v151.go +++ b/models/migrations/v1_13/v151.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "context" diff --git a/models/migrations/v1_13/v152.go b/models/migrations/v1_13/v152.go index 502c82a40d..648e26446f 100644 --- a/models/migrations/v1_13/v152.go +++ b/models/migrations/v1_13/v152.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import "xorm.io/xorm" diff --git a/models/migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go index 0b2dd3eb62..e5462fc162 100644 --- a/models/migrations/v1_13/v153.go +++ b/models/migrations/v1_13/v153.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go index cf31190781..89dc7821b2 100644 --- a/models/migrations/v1_13/v154.go +++ b/models/migrations/v1_13/v154.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_14/main_test.go b/models/migrations/v1_14/main_test.go index c01faedc35..57cf995be1 100644 --- a/models/migrations/v1_14/main_test.go +++ b/models/migrations/v1_14/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go index e814f59938..505a9ae033 100644 --- a/models/migrations/v1_14/v155.go +++ b/models/migrations/v1_14/v155.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go index b6dc91a054..7bbd9f4c85 100644 --- a/models/migrations/v1_14/v156.go +++ b/models/migrations/v1_14/v156.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go index 7187278d29..ba69f71130 100644 --- a/models/migrations/v1_14/v157.go +++ b/models/migrations/v1_14/v157.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go index 3fa27cfecd..2ab3c8a1f0 100644 --- a/models/migrations/v1_14/v158.go +++ b/models/migrations/v1_14/v158.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "errors" diff --git a/models/migrations/v1_14/v159.go b/models/migrations/v1_14/v159.go index fdd7e12449..4e921ea1c6 100644 --- a/models/migrations/v1_14/v159.go +++ b/models/migrations/v1_14/v159.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v160.go b/models/migrations/v1_14/v160.go index 4dea91b514..73f3798954 100644 --- a/models/migrations/v1_14/v160.go +++ b/models/migrations/v1_14/v160.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go index 6e904cfab6..9c850ad0c2 100644 --- a/models/migrations/v1_14/v161.go +++ b/models/migrations/v1_14/v161.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "context" diff --git a/models/migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go index 5d6d7c2e3f..ead63f16f4 100644 --- a/models/migrations/v1_14/v162.go +++ b/models/migrations/v1_14/v162.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v163.go b/models/migrations/v1_14/v163.go index 60fc98c0a4..06ac36cbc7 100644 --- a/models/migrations/v1_14/v163.go +++ b/models/migrations/v1_14/v163.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v164.go b/models/migrations/v1_14/v164.go index 54f6951427..d2fd9b8464 100644 --- a/models/migrations/v1_14/v164.go +++ b/models/migrations/v1_14/v164.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go index 9315e44197..90fd2b1e46 100644 --- a/models/migrations/v1_14/v165.go +++ b/models/migrations/v1_14/v165.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go index e5731582fd..4c106bd7da 100644 --- a/models/migrations/v1_14/v166.go +++ b/models/migrations/v1_14/v166.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "crypto/sha256" diff --git a/models/migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go index 9d416f6a32..d77bbc401e 100644 --- a/models/migrations/v1_14/v167.go +++ b/models/migrations/v1_14/v167.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go index a30a8859f7..aa93eec19b 100644 --- a/models/migrations/v1_14/v168.go +++ b/models/migrations/v1_14/v168.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import "xorm.io/xorm" diff --git a/models/migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go index 5b81bb58b1..4f9df0d96f 100644 --- a/models/migrations/v1_14/v169.go +++ b/models/migrations/v1_14/v169.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go index 7b6498a3e9..a2ff4623e1 100644 --- a/models/migrations/v1_14/v170.go +++ b/models/migrations/v1_14/v170.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go index 51a35a02ad..7b200e960a 100644 --- a/models/migrations/v1_14/v171.go +++ b/models/migrations/v1_14/v171.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go index d49b70f5ad..c410d393f1 100644 --- a/models/migrations/v1_14/v172.go +++ b/models/migrations/v1_14/v172.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go index 2d9eee9197..7752fbe966 100644 --- a/models/migrations/v1_14/v173.go +++ b/models/migrations/v1_14/v173.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go index c839e15db8..4049e43070 100644 --- a/models/migrations/v1_14/v174.go +++ b/models/migrations/v1_14/v174.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go index 3cda5772a0..49fa17d046 100644 --- a/models/migrations/v1_14/v175.go +++ b/models/migrations/v1_14/v175.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go index 1ed49f75fa..ef5dce9a02 100644 --- a/models/migrations/v1_14/v176.go +++ b/models/migrations/v1_14/v176.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go index d88ff207e7..d56b3e0470 100644 --- a/models/migrations/v1_14/v176_test.go +++ b/models/migrations/v1_14/v176_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go index 6e1838f369..96676bf8d9 100644 --- a/models/migrations/v1_14/v177.go +++ b/models/migrations/v1_14/v177.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go index bffc6f92e3..0e0a67fd33 100644 --- a/models/migrations/v1_14/v177_test.go +++ b/models/migrations/v1_14/v177_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_15/main_test.go b/models/migrations/v1_15/main_test.go index 6c04d3f5ee..4cf6d6f695 100644 --- a/models/migrations/v1_15/main_test.go +++ b/models/migrations/v1_15/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "testing" diff --git a/models/migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go index 6d236eb049..ca3a5c262e 100644 --- a/models/migrations/v1_15/v178.go +++ b/models/migrations/v1_15/v178.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go index b990583303..ce514cc4a9 100644 --- a/models/migrations/v1_15/v179.go +++ b/models/migrations/v1_15/v179.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go index 02fbd57cdb..0b68c3ceb7 100644 --- a/models/migrations/v1_15/v180.go +++ b/models/migrations/v1_15/v180.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/modules/json" diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go index 2185ed0213..fb1d3d7a75 100644 --- a/models/migrations/v1_15/v181.go +++ b/models/migrations/v1_15/v181.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "strings" diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go index 4154e0b1e9..8196f751e5 100644 --- a/models/migrations/v1_15/v181_test.go +++ b/models/migrations/v1_15/v181_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "strings" diff --git a/models/migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go index 9ca500c0f9..f53ff11df9 100644 --- a/models/migrations/v1_15/v182.go +++ b/models/migrations/v1_15/v182.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go index 6865cafac4..2baf90d06a 100644 --- a/models/migrations/v1_15/v182_test.go +++ b/models/migrations/v1_15/v182_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "testing" diff --git a/models/migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go index aaad64c220..5684e35699 100644 --- a/models/migrations/v1_15/v183.go +++ b/models/migrations/v1_15/v183.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "fmt" diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go index 41b64d4743..fbe0dcd780 100644 --- a/models/migrations/v1_15/v184.go +++ b/models/migrations/v1_15/v184.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "context" diff --git a/models/migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go index e5878ec193..60af59edca 100644 --- a/models/migrations/v1_15/v185.go +++ b/models/migrations/v1_15/v185.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go index ad75822de5..55d3199335 100644 --- a/models/migrations/v1_15/v186.go +++ b/models/migrations/v1_15/v186.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go index b573fc52ef..fabef14779 100644 --- a/models/migrations/v1_15/v187.go +++ b/models/migrations/v1_15/v187.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_15/v188.go b/models/migrations/v1_15/v188.go index 71e45cab0e..4494e6ff05 100644 --- a/models/migrations/v1_15/v188.go +++ b/models/migrations/v1_15/v188.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import "xorm.io/xorm" diff --git a/models/migrations/v1_16/main_test.go b/models/migrations/v1_16/main_test.go index 6f891f3e94..8c0a043be6 100644 --- a/models/migrations/v1_16/main_test.go +++ b/models/migrations/v1_16/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go index 1ee72d9c39..19bfcb2423 100644 --- a/models/migrations/v1_16/v189.go +++ b/models/migrations/v1_16/v189.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "encoding/binary" diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go index 90b721d5f1..9d74462a92 100644 --- a/models/migrations/v1_16/v189_test.go +++ b/models/migrations/v1_16/v189_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go index 5953802849..1eb6b6ddb4 100644 --- a/models/migrations/v1_16/v190.go +++ b/models/migrations/v1_16/v190.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go index 567f88d6d1..427476b70b 100644 --- a/models/migrations/v1_16/v191.go +++ b/models/migrations/v1_16/v191.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go index 731b9fb43a..31e8c36346 100644 --- a/models/migrations/v1_16/v192.go +++ b/models/migrations/v1_16/v192.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_16/v193.go b/models/migrations/v1_16/v193.go index 8d3ce7a558..a5af2de380 100644 --- a/models/migrations/v1_16/v193.go +++ b/models/migrations/v1_16/v193.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go index 8260acf32d..bf8d8a7dc6 100644 --- a/models/migrations/v1_16/v193_test.go +++ b/models/migrations/v1_16/v193_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go index 6aa13c50cf..2e4ed8340e 100644 --- a/models/migrations/v1_16/v194.go +++ b/models/migrations/v1_16/v194.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go index 6d7e94141e..4fd42b7bd2 100644 --- a/models/migrations/v1_16/v195.go +++ b/models/migrations/v1_16/v195.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go index 71234a6fb3..1fc7b51f3c 100644 --- a/models/migrations/v1_16/v195_test.go +++ b/models/migrations/v1_16/v195_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go index 7cbafc61e5..6c9caa100f 100644 --- a/models/migrations/v1_16/v196.go +++ b/models/migrations/v1_16/v196.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go index 97888b2847..862bdfdcbd 100644 --- a/models/migrations/v1_16/v197.go +++ b/models/migrations/v1_16/v197.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go index 8b3c73addc..5d3043eb46 100644 --- a/models/migrations/v1_16/v198.go +++ b/models/migrations/v1_16/v198.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go index 6adcf890af..4020352f2b 100644 --- a/models/migrations/v1_16/v199.go +++ b/models/migrations/v1_16/v199.go @@ -1,6 +1,6 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 // We used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now. diff --git a/models/migrations/v1_16/v200.go b/models/migrations/v1_16/v200.go index c08c20e51d..de57fad8fe 100644 --- a/models/migrations/v1_16/v200.go +++ b/models/migrations/v1_16/v200.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go index 35e0c9f2fb..2c43698b0c 100644 --- a/models/migrations/v1_16/v201.go +++ b/models/migrations/v1_16/v201.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go index 6ba36152f1..d8c8fdcadc 100644 --- a/models/migrations/v1_16/v202.go +++ b/models/migrations/v1_16/v202.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go index e8e6b52453..c3241cba57 100644 --- a/models/migrations/v1_16/v203.go +++ b/models/migrations/v1_16/v203.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go index ece03e1305..4d375307e7 100644 --- a/models/migrations/v1_16/v204.go +++ b/models/migrations/v1_16/v204.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import "xorm.io/xorm" diff --git a/models/migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go index a064b9830d..cb452dfd7f 100644 --- a/models/migrations/v1_16/v205.go +++ b/models/migrations/v1_16/v205.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_16/v206.go b/models/migrations/v1_16/v206.go index 581a7d76e9..01a9c386eb 100644 --- a/models/migrations/v1_16/v206.go +++ b/models/migrations/v1_16/v206.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go index 91208f066c..19126ead1f 100644 --- a/models/migrations/v1_16/v207.go +++ b/models/migrations/v1_16/v207.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go index 1a11ef096a..fb643324f4 100644 --- a/models/migrations/v1_16/v208.go +++ b/models/migrations/v1_16/v208.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go index be3100e02a..230838647b 100644 --- a/models/migrations/v1_16/v209.go +++ b/models/migrations/v1_16/v209.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index 375a008e18..f48ab11db6 100644 --- a/models/migrations/v1_16/v210.go +++ b/models/migrations/v1_16/v210.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "crypto/ecdh" diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go index f6423a5821..8454920aa0 100644 --- a/models/migrations/v1_16/v210_test.go +++ b/models/migrations/v1_16/v210_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "encoding/hex" diff --git a/models/migrations/v1_17/main_test.go b/models/migrations/v1_17/main_test.go index 0a8e05ab5f..166860b3b1 100644 --- a/models/migrations/v1_17/main_test.go +++ b/models/migrations/v1_17/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "testing" diff --git a/models/migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go index 9b72c8610b..517cf19388 100644 --- a/models/migrations/v1_17/v211.go +++ b/models/migrations/v1_17/v211.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go index 2337adcc80..23868c0bb2 100644 --- a/models/migrations/v1_17/v212.go +++ b/models/migrations/v1_17/v212.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go index bb3f466e52..b2bbdf7279 100644 --- a/models/migrations/v1_17/v213.go +++ b/models/migrations/v1_17/v213.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go index 2268164919..1925324f0f 100644 --- a/models/migrations/v1_17/v214.go +++ b/models/migrations/v1_17/v214.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go index 5aae798562..431103c98e 100644 --- a/models/migrations/v1_17/v215.go +++ b/models/migrations/v1_17/v215.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/models/pull" diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go index 268f472a42..37aeacb6fc 100644 --- a/models/migrations/v1_17/v216.go +++ b/models/migrations/v1_17/v216.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 // This migration added non-ideal indices to the action table which on larger datasets slowed things down // it has been superseded by v218.go diff --git a/models/migrations/v1_17/v217.go b/models/migrations/v1_17/v217.go index 5f096d4824..fef48b7a5b 100644 --- a/models/migrations/v1_17/v217.go +++ b/models/migrations/v1_17/v217.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go index 5e3dcd0841..412d124286 100644 --- a/models/migrations/v1_17/v218.go +++ b/models/migrations/v1_17/v218.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go index e90656090f..7ca6a26be6 100644 --- a/models/migrations/v1_17/v219.go +++ b/models/migrations/v1_17/v219.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "time" diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go index 61bbf19725..4e010e5b76 100644 --- a/models/migrations/v1_17/v220.go +++ b/models/migrations/v1_17/v220.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( packages_model "forgejo.org/models/packages" diff --git a/models/migrations/v1_17/v221.go b/models/migrations/v1_17/v221.go index 84e9a238af..3ef34e3f06 100644 --- a/models/migrations/v1_17/v221.go +++ b/models/migrations/v1_17/v221.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "encoding/base32" diff --git a/models/migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go index 02607d6b32..a9c47136b2 100644 --- a/models/migrations/v1_17/v221_test.go +++ b/models/migrations/v1_17/v221_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "encoding/base32" diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go index ae910cbcb6..873769881e 100644 --- a/models/migrations/v1_17/v222.go +++ b/models/migrations/v1_17/v222.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "context" diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go index 7d92dcf5ae..4f5d34d841 100644 --- a/models/migrations/v1_17/v223.go +++ b/models/migrations/v1_17/v223.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "context" diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go index 33f5c51222..0c20934cea 100644 --- a/models/migrations/v1_18/main_test.go +++ b/models/migrations/v1_18/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go index f3d522b91a..6dc12020ea 100644 --- a/models/migrations/v1_18/v224.go +++ b/models/migrations/v1_18/v224.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go index 86bcb1323d..266eccfff8 100644 --- a/models/migrations/v1_18/v225.go +++ b/models/migrations/v1_18/v225.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go index f87e24b11d..8ed9761476 100644 --- a/models/migrations/v1_18/v226.go +++ b/models/migrations/v1_18/v226.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/builder" diff --git a/models/migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go index b6250fb76c..d39a010159 100644 --- a/models/migrations/v1_18/v227.go +++ b/models/migrations/v1_18/v227.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go index 1161c8a4c9..3f5b69734d 100644 --- a/models/migrations/v1_18/v228.go +++ b/models/migrations/v1_18/v228.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go index f96dde9840..00d794725f 100644 --- a/models/migrations/v1_18/v229.go +++ b/models/migrations/v1_18/v229.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "fmt" diff --git a/models/migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go index ac5e726a79..903a60c851 100644 --- a/models/migrations/v1_18/v229_test.go +++ b/models/migrations/v1_18/v229_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go index ea5b4d02e1..078fce7643 100644 --- a/models/migrations/v1_18/v230.go +++ b/models/migrations/v1_18/v230.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go index 7dd6675673..da31b0dc9b 100644 --- a/models/migrations/v1_18/v230_test.go +++ b/models/migrations/v1_18/v230_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go index 7c56926f4c..9d1c3a57ea 100644 --- a/models/migrations/v1_19/main_test.go +++ b/models/migrations/v1_19/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "testing" diff --git a/models/migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go index 79e46132f0..8ef1e4e743 100644 --- a/models/migrations/v1_19/v231.go +++ b/models/migrations/v1_19/v231.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go index 7fb4a5ac8d..2aab2cf830 100644 --- a/models/migrations/v1_19/v232.go +++ b/models/migrations/v1_19/v232.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go index 191afd4868..e62e8a9356 100644 --- a/models/migrations/v1_19/v233.go +++ b/models/migrations/v1_19/v233.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "fmt" diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go index 4dc35d1e27..3d5eac9887 100644 --- a/models/migrations/v1_19/v233_test.go +++ b/models/migrations/v1_19/v233_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "testing" diff --git a/models/migrations/v1_19/v234.go b/models/migrations/v1_19/v234.go index c610a423dd..e00b1cc2b6 100644 --- a/models/migrations/v1_19/v234.go +++ b/models/migrations/v1_19/v234.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go index 3715de3920..297d90f65a 100644 --- a/models/migrations/v1_19/v235.go +++ b/models/migrations/v1_19/v235.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go index fa01a6ab80..c453f95e04 100644 --- a/models/migrations/v1_19/v236.go +++ b/models/migrations/v1_19/v236.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go index b23c765aa5..cf30226ccd 100644 --- a/models/migrations/v1_19/v237.go +++ b/models/migrations/v1_19/v237.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go index 7c912a8341..b257315319 100644 --- a/models/migrations/v1_19/v238.go +++ b/models/migrations/v1_19/v238.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go index 10076f2401..8f4a65be95 100644 --- a/models/migrations/v1_19/v239.go +++ b/models/migrations/v1_19/v239.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go index 4ca5becede..c49ce2f49a 100644 --- a/models/migrations/v1_19/v240.go +++ b/models/migrations/v1_19/v240.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/models/db" diff --git a/models/migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go index a617d6fd2f..e35801a057 100644 --- a/models/migrations/v1_19/v241.go +++ b/models/migrations/v1_19/v241.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go index bbf227ef77..87ca9cf214 100644 --- a/models/migrations/v1_19/v242.go +++ b/models/migrations/v1_19/v242.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go index 55bbfafb2f..9c3f372594 100644 --- a/models/migrations/v1_19/v243.go +++ b/models/migrations/v1_19/v243.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go index f870dca429..ee5eec5ef6 100644 --- a/models/migrations/v1_20/main_test.go +++ b/models/migrations/v1_20/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "testing" diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go index 977566ad7d..76cdccaca5 100644 --- a/models/migrations/v1_20/v244.go +++ b/models/migrations/v1_20/v244.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go index 7e6585388b..5e034568c4 100644 --- a/models/migrations/v1_20/v245.go +++ b/models/migrations/v1_20/v245.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "context" diff --git a/models/migrations/v1_20/v246.go b/models/migrations/v1_20/v246.go index e6340ef079..22bf723404 100644 --- a/models/migrations/v1_20/v246.go +++ b/models/migrations/v1_20/v246.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go index 9ed810a623..056699d744 100644 --- a/models/migrations/v1_20/v247.go +++ b/models/migrations/v1_20/v247.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go index 40555210e7..4f2091e4bc 100644 --- a/models/migrations/v1_20/v248.go +++ b/models/migrations/v1_20/v248.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import "xorm.io/xorm" diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go index d2b096bf58..0aebb2a343 100644 --- a/models/migrations/v1_20/v249.go +++ b/models/migrations/v1_20/v249.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go index cfcde2fc9b..e12223691f 100644 --- a/models/migrations/v1_20/v250.go +++ b/models/migrations/v1_20/v250.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "strings" diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go index c8665ba7eb..7d2d259df6 100644 --- a/models/migrations/v1_20/v251.go +++ b/models/migrations/v1_20/v251.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go index bb85c78309..435cce7ebe 100644 --- a/models/migrations/v1_20/v252.go +++ b/models/migrations/v1_20/v252.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go index 5f4057e9d9..73354fd485 100644 --- a/models/migrations/v1_20/v253.go +++ b/models/migrations/v1_20/v253.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/log" diff --git a/models/migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go index 1e26979a5b..9cdbfb3916 100644 --- a/models/migrations/v1_20/v254.go +++ b/models/migrations/v1_20/v254.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go index 49b0ecf220..baa3c4b6d8 100644 --- a/models/migrations/v1_20/v255.go +++ b/models/migrations/v1_20/v255.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go index 822153b93e..7b84c1e154 100644 --- a/models/migrations/v1_20/v256.go +++ b/models/migrations/v1_20/v256.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go index 70f229d73f..8045909dba 100644 --- a/models/migrations/v1_20/v257.go +++ b/models/migrations/v1_20/v257.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go index 47174ce805..1d3faffdae 100644 --- a/models/migrations/v1_20/v258.go +++ b/models/migrations/v1_20/v258.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go index f10b94fa9c..9b2b68263e 100644 --- a/models/migrations/v1_20/v259.go +++ b/models/migrations/v1_20/v259.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "fmt" diff --git a/models/migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go index 32e4aa3050..b41b6c7995 100644 --- a/models/migrations/v1_20/v259_test.go +++ b/models/migrations/v1_20/v259_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "sort" diff --git a/models/migrations/v1_21/main_test.go b/models/migrations/v1_21/main_test.go index 7104887afb..3f10a39a94 100644 --- a/models/migrations/v1_21/main_test.go +++ b/models/migrations/v1_21/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "testing" diff --git a/models/migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go index 245f3011ab..b73b53bd61 100644 --- a/models/migrations/v1_21/v260.go +++ b/models/migrations/v1_21/v260.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_21/v261.go b/models/migrations/v1_21/v261.go index 743bef152d..83a4927704 100644 --- a/models/migrations/v1_21/v261.go +++ b/models/migrations/v1_21/v261.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go index 23e900572a..6e88e29b9d 100644 --- a/models/migrations/v1_21/v262.go +++ b/models/migrations/v1_21/v262.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go index 2c7cbadf0d..55c418bde0 100644 --- a/models/migrations/v1_21/v263.go +++ b/models/migrations/v1_21/v263.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "fmt" diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go index 5615600072..acd2c9bb48 100644 --- a/models/migrations/v1_21/v264.go +++ b/models/migrations/v1_21/v264.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "context" diff --git a/models/migrations/v1_21/v265.go b/models/migrations/v1_21/v265.go index 800eb95f72..b6892acc27 100644 --- a/models/migrations/v1_21/v265.go +++ b/models/migrations/v1_21/v265.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go index 79a5f5e14c..440549e868 100644 --- a/models/migrations/v1_21/v266.go +++ b/models/migrations/v1_21/v266.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go index f94696a22b..13992d8776 100644 --- a/models/migrations/v1_21/v267.go +++ b/models/migrations/v1_21/v267.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go index 332793ff07..b677d2383e 100644 --- a/models/migrations/v1_21/v268.go +++ b/models/migrations/v1_21/v268.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go index 475ec02380..042040927d 100644 --- a/models/migrations/v1_21/v269.go +++ b/models/migrations/v1_21/v269.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go index b9cc84d3ac..ab7c5660ba 100644 --- a/models/migrations/v1_21/v270.go +++ b/models/migrations/v1_21/v270.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go index f45c113c1f..e3ce2d4b74 100644 --- a/models/migrations/v1_21/v271.go +++ b/models/migrations/v1_21/v271.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go index a729c49f1b..14c1e0c4b0 100644 --- a/models/migrations/v1_21/v272.go +++ b/models/migrations/v1_21/v272.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go index 1ec6ade566..d6ec80d3d5 100644 --- a/models/migrations/v1_21/v273.go +++ b/models/migrations/v1_21/v273.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go index b74e5fed51..a1211d1fdd 100644 --- a/models/migrations/v1_21/v274.go +++ b/models/migrations/v1_21/v274.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "time" diff --git a/models/migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go index 78804a59d6..2bfe5c72fa 100644 --- a/models/migrations/v1_21/v275.go +++ b/models/migrations/v1_21/v275.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go index 0830c3bd92..3b0bc23da7 100644 --- a/models/migrations/v1_21/v276.go +++ b/models/migrations/v1_21/v276.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( repo_model "forgejo.org/models/repo" diff --git a/models/migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go index 12529160b7..0c102eddde 100644 --- a/models/migrations/v1_21/v277.go +++ b/models/migrations/v1_21/v277.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go index d6a462d1e7..846f228678 100644 --- a/models/migrations/v1_21/v278.go +++ b/models/migrations/v1_21/v278.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go index 2abd1bbe84..beb39effe1 100644 --- a/models/migrations/v1_21/v279.go +++ b/models/migrations/v1_21/v279.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go index dc991b78fe..7b05993e09 100644 --- a/models/migrations/v1_22/main_test.go +++ b/models/migrations/v1_22/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go index a8ee4a3bf7..2271cb6089 100644 --- a/models/migrations/v1_22/v280.go +++ b/models/migrations/v1_22/v280.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go index 5271c786be..2eeca9be82 100644 --- a/models/migrations/v1_22/v281.go +++ b/models/migrations/v1_22/v281.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go index baad9e0916..eed64c30f7 100644 --- a/models/migrations/v1_22/v282.go +++ b/models/migrations/v1_22/v282.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go index 86946d1c39..33a2513069 100644 --- a/models/migrations/v1_22/v283.go +++ b/models/migrations/v1_22/v283.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go index d8e147a131..652d96ac16 100644 --- a/models/migrations/v1_22/v283_test.go +++ b/models/migrations/v1_22/v283_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go index 2b95078980..31b38f6aed 100644 --- a/models/migrations/v1_22/v284.go +++ b/models/migrations/v1_22/v284.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 + import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go index a55cc17c04..fed89f670e 100644 --- a/models/migrations/v1_22/v285.go +++ b/models/migrations/v1_22/v285.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "time" diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go index d0489e7aeb..05247bb436 100644 --- a/models/migrations/v1_22/v286.go +++ b/models/migrations/v1_22/v286.go @@ -1,6 +1,6 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "fmt" diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go index c63deef495..5bb3334df2 100644 --- a/models/migrations/v1_22/v286_test.go +++ b/models/migrations/v1_22/v286_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go index c8b1593286..5fd901f9de 100644 --- a/models/migrations/v1_22/v287.go +++ b/models/migrations/v1_22/v287.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go index 44e4991851..78be3b6ef2 100644 --- a/models/migrations/v1_22/v288.go +++ b/models/migrations/v1_22/v288.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go index b9941aadd9..78689a4ffa 100644 --- a/models/migrations/v1_22/v289.go +++ b/models/migrations/v1_22/v289.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go index 594e417644..ebafab6567 100644 --- a/models/migrations/v1_22/v290.go +++ b/models/migrations/v1_22/v290.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_22/v290_test.go b/models/migrations/v1_22/v290_test.go index 569d77bc16..a1907cf4d6 100644 --- a/models/migrations/v1_22/v290_test.go +++ b/models/migrations/v1_22/v290_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "strconv" diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go index 74726fae96..823a644a95 100644 --- a/models/migrations/v1_22/v291.go +++ b/models/migrations/v1_22/v291.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go index beca556aee..440f48ce80 100644 --- a/models/migrations/v1_22/v292.go +++ b/models/migrations/v1_22/v292.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 // NOTE: noop the original migration has bug which some projects will be skip, so // these projects will have no default board. diff --git a/models/migrations/v1_22/v293.go b/models/migrations/v1_22/v293.go index 9f38c3db56..e9c9746b26 100644 --- a/models/migrations/v1_22/v293.go +++ b/models/migrations/v1_22/v293.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go index 444146737d..6b1931b761 100644 --- a/models/migrations/v1_22/v293_test.go +++ b/models/migrations/v1_22/v293_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go index 314b4519f1..6c52372306 100644 --- a/models/migrations/v1_22/v294.go +++ b/models/migrations/v1_22/v294.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go index ef7b67ca5b..e87a4bc85f 100644 --- a/models/migrations/v1_22/v294_test.go +++ b/models/migrations/v1_22/v294_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "slices" @@ -45,7 +45,8 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) { for _, index := range tables[0].Indexes { if index.Type == schemas.UniqueType { found = true - slices.Equal(index.Cols, []string{"project_id", "issue_id"}) + slices.Sort(index.Cols) + assert.Equal(t, []string{"issue_id", "project_id"}, index.Cols) break } } diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go index 17bdadb4ad..319b1a399b 100644 --- a/models/migrations/v1_22/v295.go +++ b/models/migrations/v1_22/v295.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go index 1ecacab95f..75350f9f65 100644 --- a/models/migrations/v1_22/v296.go +++ b/models/migrations/v1_22/v296.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go index b9f3b95ade..7700173a00 100644 --- a/models/migrations/v1_22/v298.go +++ b/models/migrations/v1_22/v298.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go index 0fd90a4a67..5fb4fec999 100644 --- a/models/migrations/v1_23/main_test.go +++ b/models/migrations/v1_23/main_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "testing" diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go index f6db960c3b..73ce19c875 100644 --- a/models/migrations/v1_23/v299.go +++ b/models/migrations/v1_23/v299.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go index f1f1cccdbf..404d8dbea8 100644 --- a/models/migrations/v1_23/v300.go +++ b/models/migrations/v1_23/v300.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go index b7797f6c6b..f2a4d8c559 100644 --- a/models/migrations/v1_23/v301.go +++ b/models/migrations/v1_23/v301.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go index c8ed786d63..1b056993bd 100644 --- a/models/migrations/v1_23/v302.go +++ b/models/migrations/v1_23/v302.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go index fae0131bdd..03197d2857 100644 --- a/models/migrations/v1_23/v303.go +++ b/models/migrations/v1_23/v303.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package v1_23 //nolint +package v1_23 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_23/v303_test.go b/models/migrations/v1_23/v303_test.go index f105d11830..f2c764bae3 100644 --- a/models/migrations/v1_23/v303_test.go +++ b/models/migrations/v1_23/v303_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Forgejo Authors. // SPDX-License-Identifier: GPL-3.0-or-later -package v1_23 //nolint +package v1_23 import ( "testing" diff --git a/models/migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go index ec6bd09bb5..eb669f57b6 100644 --- a/models/migrations/v1_6/v70.go +++ b/models/migrations/v1_6/v70.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go index 3706ad4406..42fe8cd1ba 100644 --- a/models/migrations/v1_6/v71.go +++ b/models/migrations/v1_6/v71.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go index 4df2a0f6e9..7cd2331376 100644 --- a/models/migrations/v1_6/v72.go +++ b/models/migrations/v1_6/v72.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go index b5a748aae3..e0b7a28537 100644 --- a/models/migrations/v1_7/v73.go +++ b/models/migrations/v1_7/v73.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go index f0567e3c9b..376be37a24 100644 --- a/models/migrations/v1_7/v74.go +++ b/models/migrations/v1_7/v74.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import "xorm.io/xorm" diff --git a/models/migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go index fa7430970c..ef11575466 100644 --- a/models/migrations/v1_7/v75.go +++ b/models/migrations/v1_7/v75.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import ( "xorm.io/builder" diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go index 61ad006a47..8d47280b41 100644 --- a/models/migrations/v1_8/v76.go +++ b/models/migrations/v1_8/v76.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "fmt" diff --git a/models/migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go index 8b19993924..4fe5ebe635 100644 --- a/models/migrations/v1_8/v77.go +++ b/models/migrations/v1_8/v77.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go index 8102b19335..840fc20d96 100644 --- a/models/migrations/v1_8/v78.go +++ b/models/migrations/v1_8/v78.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "forgejo.org/models/migrations/base" diff --git a/models/migrations/v1_8/v79.go b/models/migrations/v1_8/v79.go index f7d2d68f96..c8e0db531f 100644 --- a/models/migrations/v1_8/v79.go +++ b/models/migrations/v1_8/v79.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "forgejo.org/modules/setting" diff --git a/models/migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go index cebbbead28..6f9df47a93 100644 --- a/models/migrations/v1_8/v80.go +++ b/models/migrations/v1_8/v80.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import "xorm.io/xorm" diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go index 734fc24641..8152a47ad7 100644 --- a/models/migrations/v1_8/v81.go +++ b/models/migrations/v1_8/v81.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "fmt" diff --git a/models/migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go index 78a90bdde9..235c73c504 100644 --- a/models/migrations/v1_9/v82.go +++ b/models/migrations/v1_9/v82.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "fmt" diff --git a/models/migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go index fa24a92d28..9640564a44 100644 --- a/models/migrations/v1_9/v83.go +++ b/models/migrations/v1_9/v83.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "forgejo.org/modules/timeutil" diff --git a/models/migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go index c7155fe9cf..423915ae57 100644 --- a/models/migrations/v1_9/v84.go +++ b/models/migrations/v1_9/v84.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go index d8e9d91840..9d5adc82dd 100644 --- a/models/migrations/v1_9/v85.go +++ b/models/migrations/v1_9/v85.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "fmt" diff --git a/models/migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go index cf2725d158..9464ff0cf6 100644 --- a/models/migrations/v1_9/v86.go +++ b/models/migrations/v1_9/v86.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go index fa01b6e5e3..81a4ebf80d 100644 --- a/models/migrations/v1_9/v87.go +++ b/models/migrations/v1_9/v87.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/tests/integration/pull_commit_test.go b/tests/integration/pull_commit_test.go index f82fc08df4..8a632fa338 100644 --- a/tests/integration/pull_commit_test.go +++ b/tests/integration/pull_commit_test.go @@ -65,7 +65,7 @@ func TestPullCommitLinks(t *testing.T) { func TestPullCommitSignature(t *testing.T) { t.Cleanup(func() { // Cannot use t.Context(), it is in the done state. - require.NoError(t, git.InitFull(context.Background())) //nolint:usetesting + require.NoError(t, git.InitFull(context.Background())) }) defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "UwU")() diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index 598a508294..3d5922c0b1 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -690,7 +690,7 @@ func TestViewCommit(t *testing.T) { func TestViewCommitSignature(t *testing.T) { t.Cleanup(func() { // Cannot use t.Context(), it is in the done state. - require.NoError(t, git.InitFull(context.Background())) //nolint:usetesting + require.NoError(t, git.InitFull(context.Background())) }) defer test.MockVariableValue(&setting.Repository.Signing.SigningName, "UwU")() diff --git a/tests/integration/signing_git_test.go b/tests/integration/signing_git_test.go index 8b6b30ecab..7018b10376 100644 --- a/tests/integration/signing_git_test.go +++ b/tests/integration/signing_git_test.go @@ -32,7 +32,7 @@ import ( func TestInstanceSigning(t *testing.T) { t.Cleanup(func() { // Cannot use t.Context(), it is in the done state. - require.NoError(t, git.InitFull(context.Background())) //nolint:usetesting + require.NoError(t, git.InitFull(context.Background())) }) onGiteaRun(t, func(t *testing.T, u *url.URL) { From 74981d9e97b9e8047852ac9eb9ceb7b1a454e5bb Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 6 Jul 2025 07:16:43 +0200 Subject: [PATCH 070/297] chore: Refactor `Is{Reference,Branch}Exist` (#8425) - Instead of invoking a new git command to check if a reference exists, use the already opened git repository that uses a batch check to check if the reference exists. - Instead of invoking a new git command to check if a branch exists, use the already opened git repository that uses a batch check to check if the branch exists. - Do not depend on `IsReferenceExist` as its no longer used by any other function. - Specify `--quiet` to not waste Git's time on printing something we do not use. - Run it via `Run` so no buffers are created for stdout and stderr. - Add a test that verifies `IsBranchExist` works and does not work for other types of references. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8425 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/git/repo_branch.go | 8 +------- modules/git/repo_branch_test.go | 15 +++++++++++++++ routers/api/v1/repo/branch.go | 4 ++-- routers/web/repo/pull.go | 2 +- services/automerge/automerge.go | 2 +- services/pull/commit_status.go | 3 +-- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 3a9aa3e4e6..1e38bf2946 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -19,15 +19,9 @@ import ( // BranchPrefix base dir of the branch information file store on git const BranchPrefix = "refs/heads/" -// IsReferenceExist returns true if given reference exists in the repository. -func IsReferenceExist(ctx context.Context, repoPath, name string) bool { - _, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath}) - return err == nil -} - // IsBranchExist returns true if given branch exists in the repository. func IsBranchExist(ctx context.Context, repoPath, name string) bool { - return IsReferenceExist(ctx, repoPath, BranchPrefix+name) + return NewCommand(ctx, "show-ref", "--verify", "--quiet").AddDashesAndList(BranchPrefix+name).Run(&RunOpts{Dir: repoPath}) == nil } // Branch represents a Git branch. diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go index 1e0fea7cd4..e61ea6f5d7 100644 --- a/modules/git/repo_branch_test.go +++ b/modules/git/repo_branch_test.go @@ -1,4 +1,5 @@ // Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package git @@ -195,3 +196,17 @@ func TestRepository_IsReferenceExist(t *testing.T) { }) } } + +func TestIsBranchExist(t *testing.T) { + repo1Path := filepath.Join(testReposDir, "repo1_bare") + + assert.True(t, IsBranchExist(t.Context(), repo1Path, "branch1")) + assert.True(t, IsBranchExist(t.Context(), repo1Path, "branch2")) + assert.True(t, IsBranchExist(t.Context(), repo1Path, "master")) + + assert.False(t, IsBranchExist(t.Context(), repo1Path, "HEAD")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "153f451b9ee7fa1da317ab17a127e9fd9d384310")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "153f451b9ee7fa1da317ab17a127e9fd9d384310")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "signed-tag")) + assert.False(t, IsBranchExist(t.Context(), repo1Path, "test")) +} diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 7c9593d625..e043448590 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -594,7 +594,7 @@ func CreateBranchProtection(ctx *context.APIContext) { isPlainRule := !git_model.IsRuleNameSpecial(ruleName) var isBranchExist bool if isPlainRule { - isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), ruleName) + isBranchExist = ctx.Repo.GitRepo.IsBranchExist(ruleName) } protectBranch, err := git_model.GetProtectedBranchRuleByName(ctx, repo.ID, ruleName) @@ -982,7 +982,7 @@ func EditBranchProtection(ctx *context.APIContext) { isPlainRule := !git_model.IsRuleNameSpecial(bpName) var isBranchExist bool if isPlainRule { - isBranchExist = git.IsBranchExist(ctx.Req.Context(), ctx.Repo.Repository.RepoPath(), bpName) + isBranchExist = ctx.Repo.GitRepo.IsBranchExist(bpName) } if isBranchExist { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 4e365f24ea..a9507332f8 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -638,7 +638,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pull.Flow == issues_model.PullRequestFlowGithub { headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch) } else { - headBranchExist = git.IsReferenceExist(ctx, baseGitRepo.Path, pull.GetGitRefName()) + headBranchExist = headGitRepo.IsReferenceExist(pull.GetGitRefName()) } if headBranchExist { diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go index cbfe3bd54e..96a0e0498d 100644 --- a/services/automerge/automerge.go +++ b/services/automerge/automerge.go @@ -162,7 +162,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) { return } case issues_model.PullRequestFlowAGit: - headBranchExist := git.IsReferenceExist(ctx, baseGitRepo.Path, pr.GetGitRefName()) + headBranchExist := headGitRepo.IsReferenceExist(pr.GetGitRefName()) if !headBranchExist { log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch) return diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 3c864c8ef2..0a95ea1152 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -12,7 +12,6 @@ import ( "forgejo.org/models/db" git_model "forgejo.org/models/git" issues_model "forgejo.org/models/issues" - "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" "forgejo.org/modules/log" "forgejo.org/modules/structs" @@ -105,7 +104,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR if pr.Flow == issues_model.PullRequestFlowGithub && !headGitRepo.IsBranchExist(pr.HeadBranch) { return "", errors.New("head branch does not exist, can not merge") } - if pr.Flow == issues_model.PullRequestFlowAGit && !git.IsReferenceExist(ctx, headGitRepo.Path, pr.GetGitRefName()) { + if pr.Flow == issues_model.PullRequestFlowAGit && !headGitRepo.IsReferenceExist(pr.GetGitRefName()) { return "", errors.New("head branch does not exist, can not merge") } From 288c56f5d330b72defa9656994a4ba53cd2f19cb Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 6 Jul 2025 07:19:23 +0200 Subject: [PATCH 071/297] feat: if OAuth2 is disabled return 'Not found' for openid configuration (#8426) - If a Forgejo has disabled being a OAuth2 provider via `[oauth2].ENABLED = false` then return 'Not found' when clients requests `.well-known/openid-configuration` to reflect that OAuth2 is not supported. - This allows clients to query if Forgejo has OAuth2 enabled. - Resolves forgejo/forgejo#6978 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8426 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- routers/web/auth/oauth.go | 5 +++++ tests/integration/oauth_test.go | 30 +++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index e8e5d2c54b..f287e0e900 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -668,6 +668,11 @@ func GrantApplicationOAuth(ctx *context.Context) { // OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities func OIDCWellKnown(ctx *context.Context) { + if !setting.OAuth2.Enabled { + ctx.Status(http.StatusNotFound) + return + } + ctx.Data["SigningKey"] = oauth2.DefaultSigningKey ctx.Data["Issuer"] = strings.TrimSuffix(setting.AppURL, "/") ctx.JSONTemplate("user/auth/oidc_wellknown") diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index 2b44863ec2..188b0426da 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -632,17 +632,29 @@ func TestSignInOAuthCallbackPKCE(t *testing.T) { }) } -func TestWellKnownDocumentIssuerDoesNotEndWithASlash(t *testing.T) { +func TestWellKnownOpenIDConfiguration(t *testing.T) { defer tests.PrepareTestEnv(t)() - req := NewRequest(t, "GET", "/.well-known/openid-configuration") - resp := MakeRequest(t, req, http.StatusOK) - type response struct { - Issuer string `json:"issuer"` - } - parsed := new(response) - DecodeJSON(t, resp, parsed) - assert.Equal(t, strings.TrimSuffix(setting.AppURL, "/"), parsed.Issuer) + t.Run("Issuer does not end with a slash", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/.well-known/openid-configuration") + resp := MakeRequest(t, req, http.StatusOK) + type response struct { + Issuer string `json:"issuer"` + } + parsed := new(response) + + DecodeJSON(t, resp, parsed) + assert.Equal(t, strings.TrimSuffix(setting.AppURL, "/"), parsed.Issuer) + }) + + t.Run("Not found if OAuth2 is not enabled", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer test.MockVariableValue(&setting.OAuth2.Enabled, false)() + + MakeRequest(t, NewRequest(t, "GET", "/.well-known/openid-configuration"), http.StatusNotFound) + }) } func TestSignInOAuthCallbackRedirectToEscaping(t *testing.T) { From 466e7bfcb80ff997cf6d393806c426fc6f833d91 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 6 Jul 2025 07:25:09 +0200 Subject: [PATCH 072/297] Update module github.com/go-webauthn/webauthn to v0.13.1 (forgejo) (#8427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [github.com/go-webauthn/webauthn](https://github.com/go-webauthn/webauthn) | `v0.13.0` -> `v0.13.1` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-webauthn%2fwebauthn/v0.13.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-webauthn%2fwebauthn/v0.13.0/v0.13.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
go-webauthn/webauthn (github.com/go-webauthn/webauthn) ### [`v0.13.1`](https://github.com/go-webauthn/webauthn/releases/tag/v0.13.1) [Compare Source](https://github.com/go-webauthn/webauthn/compare/v0.13.0...v0.13.1) ##### Bug Fixes - **protocol:** conditional create uv check ([#​434](https://github.com/go-webauthn/webauthn/issues/434)) ([2e13a60](https://github.com/go-webauthn/webauthn/commit/2e13a60aecef52d91467d444a9fc66150ecee17b)), closes [#​361](https://github.com/go-webauthn/webauthn/issues/361)
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8427 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index de6331722b..5f62066994 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-openapi/spec v0.21.0 github.com/go-sql-driver/mysql v1.9.3 - github.com/go-webauthn/webauthn v0.13.0 + github.com/go-webauthn/webauthn v0.13.1 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 @@ -170,7 +170,7 @@ require ( github.com/go-openapi/jsonpointer v0.21.1 // indirect 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.21 // indirect + github.com/go-webauthn/x v0.1.22 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index 38a9dd5708..9e72ee5c2c 100644 --- a/go.sum +++ b/go.sum @@ -250,10 +250,10 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-webauthn/webauthn v0.13.0 h1:cJIL1/1l+22UekVhipziAaSgESJxokYkowUqAIsWs0Y= -github.com/go-webauthn/webauthn v0.13.0/go.mod h1:Oy9o2o79dbLKRPZWWgRIOdtBGAhKnDIaBp2PFkICRHs= -github.com/go-webauthn/x v0.1.21 h1:nFbckQxudvHEJn2uy1VEi713MeSpApoAv9eRqsb9AdQ= -github.com/go-webauthn/x v0.1.21/go.mod h1:sEYohtg1zL4An1TXIUIQ5csdmoO+WO0R4R2pGKaHYKA= +github.com/go-webauthn/webauthn v0.13.1 h1:Q3/GLXsckVJUPE+BGR6ex26yRIiZ/X2ITaMeSkOftuc= +github.com/go-webauthn/webauthn v0.13.1/go.mod h1:HeaBromTjgMg1sHZOzyjEiqcrk4Og7mxafDTWDepaXI= +github.com/go-webauthn/x v0.1.22 h1:rHilV/rYXawarI0uA3uZ5nhLb30Ex8RgbVAsOSt/57o= +github.com/go-webauthn/x v0.1.22/go.mod h1:+iV9BF4OsvLYzETdc0lmQO2webTos10oH6QydSoWxDM= 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= From ff5ef8fe8bf8c20d1986499e3cbd6ff0dc21eb7d Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 6 Jul 2025 14:23:47 +0200 Subject: [PATCH 073/297] fix: check PR reference on base repository (#8431) - Regression of forgejo/forgejo#8425 (I was fooled by the github flow being `headGitRepo`). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8431 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- routers/web/repo/pull.go | 2 +- services/automerge/automerge.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index a9507332f8..c9a2bb4e08 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -638,7 +638,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C if pull.Flow == issues_model.PullRequestFlowGithub { headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch) } else { - headBranchExist = headGitRepo.IsReferenceExist(pull.GetGitRefName()) + headBranchExist = baseGitRepo.IsReferenceExist(pull.GetGitRefName()) } if headBranchExist { diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go index 96a0e0498d..0cdc113379 100644 --- a/services/automerge/automerge.go +++ b/services/automerge/automerge.go @@ -162,7 +162,7 @@ func handlePullRequestAutoMerge(pullID int64, sha string) { return } case issues_model.PullRequestFlowAGit: - headBranchExist := headGitRepo.IsReferenceExist(pr.GetGitRefName()) + headBranchExist := baseGitRepo.IsReferenceExist(pr.GetGitRefName()) if !headBranchExist { log.Warn("Head branch of auto merge %-v does not exist [HeadRepoID: %d, Branch(Agit): %s]", pr, pr.HeadRepoID, pr.HeadBranch) return From f5cbb9604db5d306c6a478769374b2aba6c4f0ef Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Sun, 6 Jul 2025 14:26:20 +0200 Subject: [PATCH 074/297] fix(ui): visually distinguish the branch name in action description (#8418) ## Checklist ### Tests I don't think this deserves a test. ### 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/.md` to be be used for the release notes instead of the title. ### What does it fix In the dashboard, when someone deletes a branch, the message that is displayed doesn't distinguish the branch name from the rest of the message visually. Because that's user-provided content, I find it odd, as it blends in with the system-generated message. For consistency with other messages (such as the one obtained when renaming a repository), I propose to show the branch name in monospaced font via the `` markup. I didn't update the other languages by fear of interfering with Weblate but I would be happy to update the other languages accordingly if you wish so. I didn't add any tests because I don't think it's necessary for such a small change. #### Before ![image](/attachments/f158626e-fa58-4559-81b7-dd99045e1a33) #### After ![image](/attachments/f12fe539-5c9a-4d13-a912-414591b8726d) #### Renaming a repository (unchanged, just for reference) ![image](/attachments/a2366afb-6e24-4ad7-a41d-1c0ac6f04725) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8418 Reviewed-by: Earl Warren Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Antonin Delpeuch Co-committed-by: Antonin Delpeuch --- options/locale/locale_bg.ini | 2 +- options/locale/locale_cs-CZ.ini | 2 +- options/locale/locale_da.ini | 2 +- options/locale/locale_de-DE.ini | 2 +- options/locale/locale_el-GR.ini | 2 +- options/locale/locale_en-US.ini | 2 +- options/locale/locale_es-ES.ini | 2 +- options/locale/locale_fi-FI.ini | 2 +- options/locale/locale_fil.ini | 2 +- options/locale/locale_fr-FR.ini | 2 +- options/locale/locale_ga-IE.ini | 2 +- options/locale/locale_hu-HU.ini | 2 +- options/locale/locale_id-ID.ini | 2 +- options/locale/locale_it-IT.ini | 2 +- options/locale/locale_ja-JP.ini | 2 +- options/locale/locale_lv-LV.ini | 2 +- options/locale/locale_nds.ini | 2 +- options/locale/locale_nl-NL.ini | 2 +- options/locale/locale_pl-PL.ini | 2 +- options/locale/locale_pt-BR.ini | 2 +- options/locale/locale_pt-PT.ini | 2 +- options/locale/locale_ru-RU.ini | 2 +- options/locale/locale_si-LK.ini | 2 +- options/locale/locale_sv-SE.ini | 2 +- options/locale/locale_tr-TR.ini | 2 +- options/locale/locale_uk-UA.ini | 2 +- options/locale/locale_zh-CN.ini | 2 +- options/locale/locale_zh-TW.ini | 2 +- 28 files changed, 28 insertions(+), 28 deletions(-) diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 1b9767f674..943994e061 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -2035,7 +2035,7 @@ merge_pull_request = `сля заявка за сливане %[ auto_merge_pull_request = `сля автоматично заявка за сливане %[3]s#%[2]s` watched_repo = започна да наблюдава %[2]s delete_tag = изтри маркера %[2]s от %[3]s -delete_branch = изтри клона %[2]s от %[3]s +delete_branch = изтри клона %[2]s от %[3]s create_branch = създаде клон %[3]s на %[4]s publish_release = `публикува издание %[4]s на %[3]s` push_tag = изтласка маркер %[3]s към %[4]s diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 168965a740..81430bc96c 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`automaticky sloučen požadavek na natažení %s push_tag=nahrál/a značku %[3]s do %[4]s delete_tag=smazal/a značku %[2]s z %[3]s -delete_branch=smazal/a větev %[2]s z %[3]s +delete_branch=smazal/a větev %[2]s z %[3]s compare_branch=Porovnat compare_commits=Porovnat %d revizí compare_commits_general=Porovnat revize diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index c82779ab60..df51b5f2bf 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -2768,7 +2768,7 @@ close_pull_request = `lukket pull request %[3]s#%[2]s` starred_repo = stjernemarkerede %[2]s close_issue = `lukket problem %[3]s#%[2]s` comment_issue = `kommenterede problem %[3]s#%[2]s` -delete_branch = slettede gren %[2]s fra %[3]s +delete_branch = slettede gren %[2]s fra %[3]s compare_commits = Sammenlign %d commits compare_commits_general = Sammenlign commits review_dismissed = `afvist anmeldelse fra %[4]s for %[3]s#%[2]s` diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f8bfc9258a..9be888684c 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -3640,7 +3640,7 @@ auto_merge_pull_request=`führte Pull-Request %[3]s#%[2]s au transfer_repo=hat Repository %s übertragen zu %s push_tag=hat Tag %[3]s auf %[4]s gepusht delete_tag=hat Tag %[2]s in %[3]s gelöscht -delete_branch=hat Branch %[2]s in %[3]s gelöscht +delete_branch=hat Branch %[2]s in %[3]s gelöscht compare_branch=Vergleichen compare_commits=Vergleiche %d Commits compare_commits_general=Commits vergleichen diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 398a0d9ce4..5638d35c40 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -3563,7 +3563,7 @@ auto_merge_pull_request=`αυτόματη συγχώνευση του pull reque transfer_repo=μετέφερε το repository %s σε %s push_tag=ώθησε την ετικέτα %[3]s σε %[4]s delete_tag=διέγραψε την ετικέτα %[2]s από %[3]s -delete_branch=διέγραψε το κλάδο %[2]s από %[3]s +delete_branch=διέγραψε το κλάδο %[2]s από %[3]s compare_branch=Σύγκριση compare_commits=Σύγκριση %d commit compare_commits_general=Σύγκριση commits diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5fd2ebd163..cce62685bf 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3564,7 +3564,7 @@ auto_merge_pull_request = `automatically merged pull request %[3 transfer_repo = transferred repository %s to %s push_tag = pushed tag %[3]s to %[4]s delete_tag = deleted tag %[2]s from %[3]s -delete_branch = deleted branch %[2]s from %[3]s +delete_branch = deleted branch %[2]s from %[3]s compare_branch = Compare compare_commits = Compare %d commits compare_commits_general = Compare commits diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index bdafba93b4..c572cf6385 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -3586,7 +3586,7 @@ auto_merge_pull_request=`fusionado automáticamente pull request %s
a %s push_tag=hizó push la etiqueta %[3]s a %[4]s delete_tag=etiqueta eliminada %[2]s de %[3]s -delete_branch=rama %[2]s eliminada, de %[3]s +delete_branch=rama %[2]s eliminada, de %[3]s compare_branch=Comparar compare_commits=Comparar %d commits compare_commits_general=Comparar commits diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index efd8a16526..27942ead99 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -3180,7 +3180,7 @@ close_issue = `sulki ongelman %[3]s#%[2]s` merge_pull_request = `yhdisti vetopyynnön %[3]s#%[2]s` comment_pull = `kommentoi vetopyyntöä %[3]s#%[2]s` auto_merge_pull_request = `automaattisesti yhdisti vetopyynnön %[3]s#%[2]s` -delete_branch = poisti haaran %[2]s tietovarastosta %[3]s +delete_branch = poisti haaran %[2]s tietovarastosta %[3]s watched_repo = aloitti tietovaraston %[2]s tarkkailun approve_pull_request = `hyväksyi %[3]s#%[2]s` starred_repo = lisäsi tähden tietovarastolle %[2]s diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 8c9badb04b..35378c5653 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -3758,7 +3758,7 @@ close_issue = `sinara ang isyu na %[3]s#%[2]s` review_dismissed = `na-dismiss ang pagsusuri mula %[4]s para sa %[3]s#%[2]s` close_pull_request = `sinara ang hiling sa paghila na %[3]s#%[2]s` transfer_repo = nilipat ang repositoryo na %s sa %s -delete_branch = binura ang branch %[2]s mula %[3]s +delete_branch = binura ang branch %[2]s mula %[3]s mirror_sync_push = na-sync ang mga commit sa %[3]s sa %[4]s mula sa mirror mirror_sync_create = na-syng ang bagong reference %[3]s sa %[4]s mula sa mirror publish_release = `inilabas ang %[4]s sa %[3]s` diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index e522d5ab92..544e590e85 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`a fusionné automatiquement la demande d’ajout %s vers %s push_tag=a poussé l’étiquette %[3]s de %[4]s delete_tag=a supprimé l’étiquette %[2]s de %[3]s -delete_branch=a supprimée la branche %[2]s de %[3]s +delete_branch=a supprimée la branche %[2]s de %[3]s compare_branch=Comparer compare_commits=Comparer %d révisions compare_commits_general=Comparer les révisions diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index 3bb06e8c21..deadb72a8f 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -2678,7 +2678,7 @@ auto_merge_pull_request = `iarratas tarraingthe cumasctha go huathoibríoch %s go %s push_tag = brú %[3]s go %[4]s delete_tag = scriosta clib %[2]s ó %[3]s -delete_branch = brainse scriosta %[2]s ó %[3]s +delete_branch = brainse scriosta %[2]s ó %[3]s compare_branch = Déan comparáid compare_commits = Déan comparáid idir tiomáintí %d compare_commits_general = Déan comparáid idir tiomáintí diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 3e93ee8ba9..b8c69cfe4a 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -1694,7 +1694,7 @@ create_repo=létrehozott tárolót: %s rename_repo=átnevezte a(z) %[1]s tárolót %[3]s-ra/re transfer_repo=áthelyezett egy tárolót innen: %s ide: %s delete_tag=címke %[2]s törölve innen: %[3]s -delete_branch=ág %[2]s törölve innen: %[3]s +delete_branch=ág %[2]s törölve innen: %[3]s compare_branch=Összehasonlítás compare_commits=%d commit összehasonlítása compare_commits_general=Commitok összehasonlítása diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index f1a392105e..32d28837e1 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -1368,7 +1368,7 @@ create_repo=repositori dibuat %s rename_repo=ganti nama gudang penyimpanan dari %[1]s ke %[3]s transfer_repo=ditransfer repositori %s ke %s delete_tag=tag dihapus %[2]s dari %[3]s -delete_branch=cabang dihapus %[2]s dari %[3]s +delete_branch=cabang dihapus %[2]s dari %[3]s compare_commits=Bandingkan %d melakukan [tool] diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index d46f709cde..94ee88c4b6 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -3624,7 +3624,7 @@ merge_pull_request=`ha fuso la richiesta di modifica %[3]s#%[2]s transfer_repo=repository %s trasferito in %s push_tag=ha inviato il tag %[3]s su %[4]s delete_tag=tag eliminato %[2]s da %[3]s -delete_branch=branch eliminato %[2]s da %[3]s +delete_branch=branch eliminato %[2]s da %[3]s compare_branch=Confronta compare_commits=Confronta %d commits compare_commits_general=Confronta commit diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 555f5c6a75..6e30f76b9a 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -3536,7 +3536,7 @@ auto_merge_pull_request=`がプルリクエスト %[3]s#%[2]s%s を %s へ移転しました push_tag=がタグ %[3]s%[4]s にプッシュしました delete_tag=がタグ %[2]s を %[3]s から削除しました -delete_branch=がブランチ %[2]s を %[3]s から削除しました +delete_branch=がブランチ %[2]s%[3]s から削除しました compare_branch=比較 compare_commits=%d件のコミットを比較 compare_commits_general=コミットを比較 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 4a98e1aa9d..e8ee85c61a 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -3635,7 +3635,7 @@ auto_merge_pull_request=`automātiski iekļāva izmaiņu pieprasījumu %s push_tag=aizgādāja birku %[3]s uz %[4]s delete_tag=izdzēsa birku %[2]s no %[3]s -delete_branch=izdzēsa zaru %[2]s no %[3]s +delete_branch=izdzēsa zaru %[2]s no %[3]s compare_branch=Salīdzināt compare_commits=Salīdzināt %d iesūtījumus compare_commits_general=Salīdzināt iesūtījumus diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index cd91f7c35a..57afa69f78 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -3304,7 +3304,7 @@ comment_issue = `hett up Gefall %[3]s #%[2]s kommenteert` comment_pull = `hett up Haalvörslag %[3]s #%[2]s kommenteert` auto_merge_pull_request = `hett Haalvörslag %[3]s #%[2]s automatisk tosamenföhrt` transfer_repo = hett Repositorium %s na %s överdragen -delete_branch = hett Twieg %[2]s vun %[3]s lösket +delete_branch = hett Twieg %[2]s vun %[3]s lösket compare_branch = Verglieken compare_commits = %d Kommitterens verglieken compare_commits_general = Kommitterens verglieken diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 7e08c8036c..d547b5bf77 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -3625,7 +3625,7 @@ create_repo=repository aangemaakt in %s rename_repo=hernoemde repository van %[1]s naar %[3]s transfer_repo=repository verplaatst naar %s naar %s delete_tag=heeft label %[2]s van %[3]s verwijderd -delete_branch=heeft branch %[2]s in %[3]s verwijderd +delete_branch=heeft branch %[2]s in %[3]s verwijderd compare_branch=Vergelijk compare_commits=Vergelijk %d commits compare_commits_general=Vergelijk commits diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 189e663618..f1a92a79f3 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -3552,7 +3552,7 @@ create_repo=tworzy repozytorium %s rename_repo=zmienia nazwę repozytorium %[1]s na %[3]s transfer_repo=przenosi repozytorium %s do %s delete_tag=usuwa tag %[2]s z %[3]s -delete_branch=usuwa gałąź %[2]s z %[3]s +delete_branch=usuwa gałąź %[2]s z %[3]s compare_branch=Porównaj compare_commits=Porównaj %d commitów compare_commits_general=Porównaj commity diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index cd5cc14833..242b915137 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`fez merge automático do pull request % transfer_repo=transferiu repositório de %s para %s push_tag=fez push da tag %[3]s to %[4]s delete_tag=excluiu tag %[2]s de %[3]s -delete_branch=excluiu branch %[2]s de %[3]s +delete_branch=excluiu branch %[2]s de %[3]s compare_branch=Comparar compare_commits=Compare %d commits compare_commits_general=Comparar commits diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 9e1c7f7f63..6dbc92e1f5 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -3637,7 +3637,7 @@ auto_merge_pull_request=`fez automaticamente a integração constante no pedido transfer_repo=transferiu o repositório %s para %s push_tag=enviou a etiqueta %[3]s para %[4]s delete_tag=eliminou a etiqueta %[2]de %[3]s -delete_branch=eliminou o ramo %[2]s de %[3]s +delete_branch=eliminou o ramo %[2]s de %[3]s compare_branch=Comparar compare_commits=Comparar %d comentimentos compare_commits_general=Comparar comentimentos diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 2ef1b868d4..33418a0621 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -3639,7 +3639,7 @@ auto_merge_pull_request=`автоматически принят запрос н transfer_repo=репозиторий %s был передан: %s push_tag=отправлен тег %[3]s в %[4]s delete_tag=удалён тег %[2]s в %[3]s -delete_branch=удалена ветвь %[2]s в %[3]s +delete_branch=удалена ветвь %[2]s в %[3]s compare_branch=Сравнить compare_commits=Сравнить %d коммитов compare_commits_general=Сравнить коммиты diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 54b0b246db..cd8c9bf7d4 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -2488,7 +2488,7 @@ merge_pull_request=`ඒකාබද්ධ අදින්න ඉල්ලීම transfer_repo=මාරු කරන ලද ගබඩාව %s සිට %s push_tag=තල්ලු ටැගය %[3]s ගේ %[4]s ගේ delete_tag=මකාදැමුවා ටැගය%[2]s සිට %[3]s -delete_branch=මකාදැමූ ශාඛාව %[2]s සිට %[3]s +delete_branch=මකාදැමූ ශාඛාව %[2]s සිට %[3]s compare_branch=සසඳන්න compare_commits=%d විවරයන් සසඳා බලන්න compare_commits_general=විවරයන් සසඳා බලන්න diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 2e212c8c49..b548e0fbf3 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -2226,7 +2226,7 @@ create_repo=skapade utvecklingskatalog %s rename_repo=döpte om utvecklingskalatogen från %[1]s till %[3]s transfer_repo=överförde utvecklingskalatogen %s till %s delete_tag=tog bort taggen %[2]s från %[3]s -delete_branch=tog bort branchen %[2]s from %[3]s +delete_branch=tog bort branchen %[2]s from %[3]s compare_branch=Jämför compare_commits=Jämför %d commits compare_commits_general=Jämför commits diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index c07cefdab9..462187e120 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -3417,7 +3417,7 @@ auto_merge_pull_request=`%[3]s#%[2]s değişiklik isteği ot transfer_repo=depo %s %s'a aktarıldı push_tag=%[3]s etiketini %[4]s dalına gönderdi delete_tag=%[2]s etiketi %[3]s deposundan silindi -delete_branch=%[3]s deposundan %[2]s dalı silindi +delete_branch=%[3]s deposundan %[2]s dalı silindi compare_branch=Karşılaştır compare_commits=%d işlemeyi karşılaştır compare_commits_general=İşlemeleri karşılaştır diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 331c7403eb..9b0d6159ff 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -3355,7 +3355,7 @@ merge_pull_request=`прийняв запит злиття %[3]s transfer_repo=перенесено репозиторій %s у %s push_tag=надсилає тег %[3]s в %[4]s delete_tag=видаляє тег %[2]s із %[3]s -delete_branch=видалено гілку %[2]s з %[3]s +delete_branch=видалено гілку %[2]s з %[3]s compare_branch=Порівняти compare_commits=Порівняти %d комітів compare_commits_general=Порівняти коміти diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index d87ad53676..b12bce88ec 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`自动合并了拉取请求 %[3]s#%[2]s transfer_repo=将仓库 %s 转移至 %s push_tag=推送了标签 %[3]s 至仓库 %[4]s delete_tag=从%[3]s 删除了标签 %[2]s -delete_branch=从 %[3]s 删除分支 %[2]s +delete_branch=从 %[3]s 删除分支 %[2]s compare_branch=比较 compare_commits=比较 %d 提交 compare_commits_general=比较提交 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index fba51a391e..0903f27396 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -3633,7 +3633,7 @@ auto_merge_pull_request=`自動合併了合併請求 %[3]s#%[2]s transfer_repo=將儲存庫 %s 轉移至 %s push_tag=推送了標籤 %[3]s%[4]s delete_tag=刪除了 %[3]s 的標籤 %[2]s -delete_branch=刪除了 %[3]s 的 %[2]s 分支 +delete_branch=刪除了 %[3]s%[2]s 分支 compare_branch=比較 compare_commits=比較 %d 個提交 compare_commits_general=比較提交 From da1c0f7f184ccac34cacadecb48bf25f277859ef Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 6 Jul 2025 22:00:09 +0200 Subject: [PATCH 075/297] fix: enable multi-line math equations in wiki (#8424) - When math equation support was added into Gitea it allowed for math equations to be typed over multiple lines via `\[ ... \]` and `$$ ... $$`. Specifically the former delimiters caused problems with legitimate markdown input to be seen as a math equation and therefore was disabled in e1a82a15d3837a51409b128d4cf66d86e63788e0. - Enable this multi-line parsing for wiki as it's less likely to cause issues in the context of the wiki. - It is hard to fix this issue in a proper way without investing a good amount of time in Goldmark as explained in https://codeberg.org/forgejo/forgejo/issues/6902#issuecomment-2845317 - Added unit test. - Resolves forgejo/forgejo#6902 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8424 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/markup/markdown/goldmark.go | 5 +-- modules/markup/markdown/markdown.go | 10 ++---- modules/markup/markdown/markdown_test.go | 34 ++++++++++++++++++++ modules/markup/markdown/math/block_parser.go | 10 ++++++ modules/markup/markdown/util/text.go | 6 ++++ 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index d229afa8e3..67d81488fd 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -9,6 +9,7 @@ import ( "strings" "forgejo.org/modules/markup" + markdownutil "forgejo.org/modules/markup/markdown/util" "forgejo.org/modules/setting" "github.com/yuin/goldmark/ast" @@ -35,8 +36,8 @@ func (g *ASTTransformer) applyElementDir(n ast.Node) { func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { firstChild := node.FirstChild() tocMode := "" - ctx := pc.Get(renderContextKey).(*markup.RenderContext) - rc := pc.Get(renderConfigKey).(*RenderConfig) + ctx := pc.Get(markdownutil.RenderContextKey).(*markup.RenderContext) + rc := pc.Get(markdownutil.RenderConfigKey).(*RenderConfig) tocList := make([]markup.Header, 0, 20) if rc.yamlNode != nil { diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index e811d29994..2b19e0f1c9 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -16,6 +16,7 @@ import ( "forgejo.org/modules/markup/common" "forgejo.org/modules/markup/markdown/callout" "forgejo.org/modules/markup/markdown/math" + markdownutil "forgejo.org/modules/markup/markdown/util" "forgejo.org/modules/setting" giteautil "forgejo.org/modules/util" @@ -34,11 +35,6 @@ var ( specMarkdownOnce sync.Once ) -var ( - renderContextKey = parser.NewContextKey() - renderConfigKey = parser.NewContextKey() -) - type limitWriter struct { w io.Writer sum int64 @@ -64,7 +60,7 @@ func (l *limitWriter) Write(data []byte) (int, error) { // newParserContext creates a parser.Context with the render context set func newParserContext(ctx *markup.RenderContext) parser.Context { pc := parser.NewContext(parser.WithIDs(newPrefixedIDs())) - pc.Set(renderContextKey, ctx) + pc.Set(markdownutil.RenderContextKey, ctx) return pc } @@ -192,7 +188,7 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer) } rc.metaLength = metaLength - pc.Set(renderConfigKey, rc) + pc.Set(markdownutil.RenderConfigKey, rc) if err := converter.Convert(buf, lw, parser.WithContext(pc)); err != nil { log.Error("Unable to render: %v", err) diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index f7955115e0..c854861031 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -561,6 +561,14 @@ func TestMathBlock(t *testing.T) { "test $$a$$", `

test a

` + nl, }, + { + `\[ +[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)} +\]`, + `

[
+[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)}
+]

` + nl, + }, } for _, test := range testcases { @@ -568,6 +576,32 @@ func TestMathBlock(t *testing.T) { require.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) } + + t.Run("Wiki context", func(t *testing.T) { + testcases := []struct { + testcase string + expected string + }{ + { + "$a$", + `

a

` + nl, + }, + { + `\[ +[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)} +\]`, + `

+[\triangle ABC] = \sqrt{s(s-a)(s-b)(s-c)}
+
` + nl, + }, + } + + for _, test := range testcases { + res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext, IsWiki: true}, test.testcase) + require.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) + assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) + } + }) } func TestFootnote(t *testing.T) { diff --git a/modules/markup/markdown/math/block_parser.go b/modules/markup/markdown/math/block_parser.go index 527df84975..b0fe1d588a 100644 --- a/modules/markup/markdown/math/block_parser.go +++ b/modules/markup/markdown/math/block_parser.go @@ -6,6 +6,9 @@ package math import ( "bytes" + "forgejo.org/modules/markup" + markdownutil "forgejo.org/modules/markup/markdown/util" + "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/text" @@ -61,6 +64,13 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex return node, parser.Close | parser.NoChildren } + ctx := pc.Get(markdownutil.RenderContextKey).(*markup.RenderContext) + if ctx.IsWiki { + reader.Advance(segment.Len() - 1) + segment.Start += 2 + node.Lines().Append(segment) + return node, parser.NoChildren + } return nil, parser.NoChildren } diff --git a/modules/markup/markdown/util/text.go b/modules/markup/markdown/util/text.go index 8a42e5835b..db6e432e79 100644 --- a/modules/markup/markdown/util/text.go +++ b/modules/markup/markdown/util/text.go @@ -7,6 +7,7 @@ import ( "bytes" "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" ) func textOfChildren(n ast.Node, src []byte, b *bytes.Buffer) { @@ -24,3 +25,8 @@ func Text(n ast.Node, src []byte) []byte { textOfChildren(n, src, &b) return b.Bytes() } + +var ( + RenderContextKey = parser.NewContextKey() + RenderConfigKey = parser.NewContextKey() +) From 1d310e6df5569d8f99528e1e1eb3b9fe5ebf1eba Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 7 Jul 2025 06:50:50 +0200 Subject: [PATCH 076/297] Update renovate to v41.23.1 (forgejo) (#8434) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 729cba3723..d3eac5357a 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:41.17.2 + image: data.forgejo.org/renovate/renovate:41.23.2 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 54becdc8dc..c09e0180eb 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.17.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@41.23.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 63236ed693e01bcccc838b0c2c5165c46d259f45 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 7 Jul 2025 07:39:56 +0200 Subject: [PATCH 077/297] Lock file maintenance (forgejo) (#8436) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on Monday ( * 0-3 * * 1 ) (UTC), 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. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8436 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 425 ++++++++++++++--------------- web_src/fomantic/package-lock.json | 18 +- 2 files changed, 221 insertions(+), 222 deletions(-) diff --git a/package-lock.json b/package-lock.json index bc5f65fea9..2d0e10c3ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -216,12 +216,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.7.tgz", - "integrity": "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.7" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -240,9 +240,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.7.tgz", - "integrity": "sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", + "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -1884,9 +1884,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.9.tgz", - "integrity": "sha512-xpz6C/vXOegF9VEtlMBlkNNIjHrLhKaFBsO4lmQGr00x5BHp7p+oliR6i7LwIcM5cZU2VjLSwm2R+/zj5IjPWg==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1903,9 +1903,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.7.tgz", - "integrity": "sha512-maArE+jvYbj06DXh2iFlXSSDjTWXODlPTQHdDRQdGoYw7KvT4SfYCnPHfCyww8Z3JqFsW0BBjPLj8A2fwAvv7Q==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", + "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1913,15 +1913,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.1.tgz", - "integrity": "sha512-mBLKRHc7Ffw/hObYb9+cunuGNjshQk+vZdwZBJoqiysK/mW3Jq0UXosq8aIhMnLevANhR9yoYfdUEOHg6M9y0g==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.26", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.26.tgz", - "integrity": "sha512-Z9rjt4BUVEbLFpw0qjCklVxxf421wrmcbP4w+LmBUxYCyJTYYSclgJD0YsCgGqQCtCIPiz7kjbYYJiAKhjJ3kA==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2263,9 +2263,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", - "integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", + "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", "cpu": [ "arm" ], @@ -2277,9 +2277,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz", - "integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", + "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", "cpu": [ "arm64" ], @@ -2291,9 +2291,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz", - "integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", + "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", "cpu": [ "arm64" ], @@ -2305,9 +2305,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz", - "integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", + "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", "cpu": [ "x64" ], @@ -2319,9 +2319,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz", - "integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", + "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", "cpu": [ "arm64" ], @@ -2333,9 +2333,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz", - "integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", + "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", "cpu": [ "x64" ], @@ -2347,9 +2347,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz", - "integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", + "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", "cpu": [ "arm" ], @@ -2361,9 +2361,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz", - "integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", + "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", "cpu": [ "arm" ], @@ -2375,9 +2375,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz", - "integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", + "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", "cpu": [ "arm64" ], @@ -2389,9 +2389,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz", - "integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", + "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", "cpu": [ "arm64" ], @@ -2403,9 +2403,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz", - "integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", + "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", "cpu": [ "loong64" ], @@ -2417,9 +2417,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz", - "integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", + "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", "cpu": [ "ppc64" ], @@ -2431,9 +2431,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz", - "integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", + "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", "cpu": [ "riscv64" ], @@ -2445,9 +2445,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz", - "integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", + "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", "cpu": [ "riscv64" ], @@ -2459,9 +2459,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz", - "integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", + "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", "cpu": [ "s390x" ], @@ -2473,9 +2473,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz", - "integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", + "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", "cpu": [ "x64" ], @@ -2487,9 +2487,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz", - "integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", + "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", "cpu": [ "x64" ], @@ -2501,9 +2501,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz", - "integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", + "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", "cpu": [ "arm64" ], @@ -2515,9 +2515,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz", - "integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", + "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", "cpu": [ "ia32" ], @@ -2529,9 +2529,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz", - "integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", + "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", "cpu": [ "x64" ], @@ -3494,9 +3494,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.2.tgz", - "integrity": "sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==", + "version": "20.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.4.tgz", + "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3810,9 +3810,9 @@ } }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.2.tgz", - "integrity": "sha512-tS+lqTU3N0kkthU+rYp0spAYq15DU8ld9kXkaKg9sbQqJNF+WPMuNHZQGCgdxrUOEO0j22RKMwRVhF1HTl+X8A==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.0.tgz", + "integrity": "sha512-LRw5BW29sYj9NsQC6QoqeLVQhEa+BwVINYyMlcve+6stwdBsSt5UB7zw4UZB4+4PNqIVilHoMaPWCb/KhABHQw==", "cpu": [ "arm" ], @@ -3824,9 +3824,9 @@ ] }, "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.2.tgz", - "integrity": "sha512-MffGiZULa/KmkNjHeuuflLVqfhqLv1vZLm8lWIyeADvlElJ/GLSOkoUX+5jf4/EGtfwrNFcEaB8BRas03KT0/Q==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.0.tgz", + "integrity": "sha512-zYX8D2zcWCAHqghA8tPjbp7LwjVXbIZP++mpU/Mrf5jUVlk3BWIxkeB8yYzZi5GpFSlqMcRZQxQqbMI0c2lASQ==", "cpu": [ "arm64" ], @@ -3838,9 +3838,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.2.tgz", - "integrity": "sha512-dzJYK5rohS1sYl1DHdJ3mwfwClJj5BClQnQSyAgEfggbUwA9RlROQSSbKBLqrGfsiC/VyrDPtbO8hh56fnkbsQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.0.tgz", + "integrity": "sha512-YsYOT049hevAY/lTYD77GhRs885EXPeAfExG5KenqMJ417nYLS2N/kpRpYbABhFZBVQn+2uRPasTe4ypmYoo3w==", "cpu": [ "arm64" ], @@ -3852,9 +3852,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.2.tgz", - "integrity": "sha512-gaIMWK+CWtXcg9gUyznkdV54LzQ90S3X3dn8zlh+QR5Xy7Y+Efqw4Rs4im61K1juy4YNb67vmJsCDAGOnIeffQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.0.tgz", + "integrity": "sha512-PSjvk3OZf1aZImdGY5xj9ClFG3bC4gnSSYWrt+id0UAv+GwwVldhpMFjAga8SpMo2T1GjV9UKwM+QCsQCQmtdA==", "cpu": [ "x64" ], @@ -3866,9 +3866,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.2.tgz", - "integrity": "sha512-S7QpkMbVoVJb0xwHFwujnwCAEDe/596xqY603rpi/ioTn9VDgBHnCCxh+UFrr5yxuMH+dliHfjwCZJXOPJGPnw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.0.tgz", + "integrity": "sha512-KC/iFaEN/wsTVYnHClyHh5RSYA9PpuGfqkFua45r4sweXpC0KHZ+BYY7ikfcGPt5w1lMpR1gneFzuqWLQxsRKg==", "cpu": [ "x64" ], @@ -3880,9 +3880,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.2.tgz", - "integrity": "sha512-+XPUMCuCCI80I46nCDFbGum0ZODP5NWGiwS3Pj8fOgsG5/ctz+/zzuBlq/WmGa+EjWZdue6CF0aWWNv84sE1uw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.0.tgz", + "integrity": "sha512-CDh/0v8uot43cB4yKtDL9CVY8pbPnMV0dHyQCE4lFz6PW/+9tS0i9eqP5a91PAqEBVMqH1ycu+k8rP6wQU846w==", "cpu": [ "arm" ], @@ -3894,9 +3894,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.2.tgz", - "integrity": "sha512-sqvUyAd1JUpwbz33Ce2tuTLJKM+ucSsYpPGl2vuFwZnEIg0CmdxiZ01MHQ3j6ExuRqEDUCy8yvkDKvjYFPb8Zg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.0.tgz", + "integrity": "sha512-+TE7epATDSnvwr3L/hNHX3wQ8KQYB+jSDTdywycg3qDqvavRP8/HX9qdq/rMcnaRDn4EOtallb3vL/5wCWGCkw==", "cpu": [ "arm" ], @@ -3908,9 +3908,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.2.tgz", - "integrity": "sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.0.tgz", + "integrity": "sha512-VBAYGg3VahofpQ+L4k/ZO8TSICIbUKKTaMYOWHWfuYBFqPbSkArZZLezw3xd27fQkxX4BaLGb/RKnW0dH9Y/UA==", "cpu": [ "arm64" ], @@ -3922,9 +3922,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.2.tgz", - "integrity": "sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.0.tgz", + "integrity": "sha512-9IgGFUUb02J1hqdRAHXpZHIeUHRrbnGo6vrRbz0fREH7g+rzQy53/IBSyadZ/LG5iqMxukriNPu4hEMUn+uWEg==", "cpu": [ "arm64" ], @@ -3936,9 +3936,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.2.tgz", - "integrity": "sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.0.tgz", + "integrity": "sha512-LR4iQ/LPjMfivpL2bQ9kmm3UnTas3U+umcCnq/CV7HAkukVdHxrDD1wwx74MIWbbgzQTLPYY7Ur2MnnvkYJCBQ==", "cpu": [ "ppc64" ], @@ -3950,9 +3950,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.2.tgz", - "integrity": "sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.0.tgz", + "integrity": "sha512-HCupFQwMrRhrOg7YHrobbB5ADg0Q8RNiuefqMHVsdhEy9lLyXm/CxsCXeLJdrg27NAPsCaMDtdlm8Z2X8x91Tg==", "cpu": [ "riscv64" ], @@ -3964,9 +3964,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.2.tgz", - "integrity": "sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.0.tgz", + "integrity": "sha512-Ckxy76A5xgjWa4FNrzcKul5qFMWgP5JSQ5YKd0XakmWOddPLSkQT+uAvUpQNnFGNbgKzv90DyQlxPDYPQ4nd6A==", "cpu": [ "riscv64" ], @@ -3978,9 +3978,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.2.tgz", - "integrity": "sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.0.tgz", + "integrity": "sha512-HfO0PUCCRte2pMJmVyxPI+eqT7KuV3Fnvn2RPvMe5mOzb2BJKf4/Vth8sSt9cerQboMaTVpbxyYjjLBWIuI5BQ==", "cpu": [ "s390x" ], @@ -3992,9 +3992,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.2.tgz", - "integrity": "sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.0.tgz", + "integrity": "sha512-9PZdjP7tLOEjpXHS6+B/RNqtfVUyDEmaViPOuSqcbomLdkJnalt5RKQ1tr2m16+qAufV0aDkfhXtoO7DQos/jg==", "cpu": [ "x64" ], @@ -4006,9 +4006,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.2.tgz", - "integrity": "sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.0.tgz", + "integrity": "sha512-qkE99ieiSKMnFJY/EfyGKVtNra52/k+lVF/PbO4EL5nU6AdvG4XhtJ+WHojAJP7ID9BNIra/yd75EHndewNRfA==", "cpu": [ "x64" ], @@ -4020,9 +4020,9 @@ ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.2.tgz", - "integrity": "sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.0.tgz", + "integrity": "sha512-MjXek8UL9tIX34gymvQLecz2hMaQzOlaqYJJBomwm1gsvK2F7hF+YqJJ2tRyBDTv9EZJGMt4KlKkSD/gZWCOiw==", "cpu": [ "wasm32" ], @@ -4037,9 +4037,9 @@ } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.2.tgz", - "integrity": "sha512-EdFbGn7o1SxGmN6aZw9wAkehZJetFPao0VGZ9OMBwKx6TkvDuj6cNeLimF/Psi6ts9lMOe+Dt6z19fZQ9Ye2fw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.0.tgz", + "integrity": "sha512-9LT6zIGO7CHybiQSh7DnQGwFMZvVr0kUjah6qQfkH2ghucxPV6e71sUXJdSM4Ba0MaGE6DC/NwWf7mJmc3DAng==", "cpu": [ "arm64" ], @@ -4051,9 +4051,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.2.tgz", - "integrity": "sha512-JY9hi1p7AG+5c/dMU8o2kWemM8I6VZxfGwn1GCtf3c5i+IKcMo2NQ8OjZ4Z3/itvY/Si3K10jOBQn7qsD/whUA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.0.tgz", + "integrity": "sha512-HYchBYOZ7WN266VjoGm20xFv5EonG/ODURRgwl9EZT7Bq1nLEs6VKJddzfFdXEAho0wfFlt8L/xIiE29Pmy1RA==", "cpu": [ "ia32" ], @@ -4065,9 +4065,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.2.tgz", - "integrity": "sha512-ryoo+EB19lMxAd80ln9BVf8pdOAxLb97amrQ3SFN9OCRn/5M5wvwDgAe4i8ZjhpbiHoDeP8yavcTEnpKBo7lZg==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.0.tgz", + "integrity": "sha512-+oLKLHw3I1UQo4MeHfoLYF+e6YBa8p5vYUw3Rgt7IDzCs+57vIZqQlIo62NDpYM0VG6BjWOwnzBczMvbtH8hag==", "cpu": [ "x64" ], @@ -5334,9 +5334,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001726", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", - "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", "funding": [ { "type": "opencollective", @@ -5539,9 +5539,9 @@ } }, "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -6904,9 +6904,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.177", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.177.tgz", - "integrity": "sha512-7EH2G59nLsEMj97fpDuvVcYi6lwTcM1xuWw3PssD8xzboAW7zj7iB3COEEEATUfjLHrs5uKBLQT03V/8URx06g==", + "version": "1.5.179", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz", + "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7913,9 +7913,9 @@ } }, "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -11340,9 +11340,9 @@ } }, "node_modules/napi-postinstall": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.5.tgz", - "integrity": "sha512-kmsgUvCRIJohHjbZ3V8avP0I1Pekw329MVAMDzVxsrkjgdnqiwvMX5XwR+hWV66vsAtZ+iM+fVnq8RTQawUmCQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", + "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", "dev": true, "license": "MIT", "bin": { @@ -11939,9 +11939,9 @@ } }, "node_modules/pkg-types": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.1.tgz", - "integrity": "sha512-eY0QFb6eSwc9+0d/5D2lFFUq+A3n3QNGSy/X2Nvp+6MfzGw2u6EbA7S80actgjY1lkvvI0pqB+a4hioMh443Ew==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz", + "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==", "license": "MIT", "dependencies": { "confbox": "^0.2.2", @@ -14966,38 +14966,38 @@ } }, "node_modules/unrs-resolver": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.2.tgz", - "integrity": "sha512-VUyWiTNQD7itdiMuJy+EuLEErLj3uwX/EpHQF8EOf33Dq3Ju6VW1GXm+swk6+1h7a49uv9fKZ+dft9jU7esdLA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.0.tgz", + "integrity": "sha512-uw3hCGO/RdAEAb4zgJ3C/v6KIAFFOtBoxR86b2Ejc5TnH7HrhTWJR2o0A9ullC3eWMegKQCw/arQ/JivywQzkg==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.2.4" + "napi-postinstall": "^0.3.0" }, "funding": { "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.9.2", - "@unrs/resolver-binding-android-arm64": "1.9.2", - "@unrs/resolver-binding-darwin-arm64": "1.9.2", - "@unrs/resolver-binding-darwin-x64": "1.9.2", - "@unrs/resolver-binding-freebsd-x64": "1.9.2", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.2", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.2", - "@unrs/resolver-binding-linux-arm64-gnu": "1.9.2", - "@unrs/resolver-binding-linux-arm64-musl": "1.9.2", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.2", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.2", - "@unrs/resolver-binding-linux-riscv64-musl": "1.9.2", - "@unrs/resolver-binding-linux-s390x-gnu": "1.9.2", - "@unrs/resolver-binding-linux-x64-gnu": "1.9.2", - "@unrs/resolver-binding-linux-x64-musl": "1.9.2", - "@unrs/resolver-binding-wasm32-wasi": "1.9.2", - "@unrs/resolver-binding-win32-arm64-msvc": "1.9.2", - "@unrs/resolver-binding-win32-ia32-msvc": "1.9.2", - "@unrs/resolver-binding-win32-x64-msvc": "1.9.2" + "@unrs/resolver-binding-android-arm-eabi": "1.11.0", + "@unrs/resolver-binding-android-arm64": "1.11.0", + "@unrs/resolver-binding-darwin-arm64": "1.11.0", + "@unrs/resolver-binding-darwin-x64": "1.11.0", + "@unrs/resolver-binding-freebsd-x64": "1.11.0", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.0", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.0", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.0", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.0", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.0", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-x64-musl": "1.11.0", + "@unrs/resolver-binding-wasm32-wasi": "1.11.0", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.0", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.0", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.0" } }, "node_modules/update-browserslist-db": { @@ -15104,9 +15104,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.0.tgz", - "integrity": "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.2.tgz", + "integrity": "sha512-hxdyZDY1CM6SNpKI4w4lcUc3Mtkd9ej4ECWVHSMrOdSinVc2zYOAppHeGc/hzmRo3pxM5blMzkuWHOJA/3NiFw==", "dev": true, "license": "MIT", "dependencies": { @@ -15288,9 +15288,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", - "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", + "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", "dev": true, "license": "MIT", "dependencies": { @@ -15304,26 +15304,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.1", - "@rollup/rollup-android-arm64": "4.44.1", - "@rollup/rollup-darwin-arm64": "4.44.1", - "@rollup/rollup-darwin-x64": "4.44.1", - "@rollup/rollup-freebsd-arm64": "4.44.1", - "@rollup/rollup-freebsd-x64": "4.44.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", - "@rollup/rollup-linux-arm-musleabihf": "4.44.1", - "@rollup/rollup-linux-arm64-gnu": "4.44.1", - "@rollup/rollup-linux-arm64-musl": "4.44.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-musl": "4.44.1", - "@rollup/rollup-linux-s390x-gnu": "4.44.1", - "@rollup/rollup-linux-x64-gnu": "4.44.1", - "@rollup/rollup-linux-x64-musl": "4.44.1", - "@rollup/rollup-win32-arm64-msvc": "4.44.1", - "@rollup/rollup-win32-ia32-msvc": "4.44.1", - "@rollup/rollup-win32-x64-msvc": "4.44.1", + "@rollup/rollup-android-arm-eabi": "4.44.2", + "@rollup/rollup-android-arm64": "4.44.2", + "@rollup/rollup-darwin-arm64": "4.44.2", + "@rollup/rollup-darwin-x64": "4.44.2", + "@rollup/rollup-freebsd-arm64": "4.44.2", + "@rollup/rollup-freebsd-x64": "4.44.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", + "@rollup/rollup-linux-arm-musleabihf": "4.44.2", + "@rollup/rollup-linux-arm64-gnu": "4.44.2", + "@rollup/rollup-linux-arm64-musl": "4.44.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-musl": "4.44.2", + "@rollup/rollup-linux-s390x-gnu": "4.44.2", + "@rollup/rollup-linux-x64-gnu": "4.44.2", + "@rollup/rollup-linux-x64-musl": "4.44.2", + "@rollup/rollup-win32-arm64-msvc": "4.44.2", + "@rollup/rollup-win32-ia32-msvc": "4.44.2", + "@rollup/rollup-win32-x64-msvc": "4.44.2", "fsevents": "~2.3.2" } }, @@ -15511,16 +15511,16 @@ } }, "node_modules/vue-component-type-helpers": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.10.tgz", - "integrity": "sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==", + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", "dev": true, "license": "MIT" }, "node_modules/vue-eslint-parser": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.1.4.tgz", - "integrity": "sha512-EIZvCukIEMHEb3mxOKemtvWR1fcUAdWWAgkfyjmRHzvyhrZvBvH9oz69+thDIWhGiIQjZnPkCn8yHqvjM+a9eg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", + "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", "dev": true, "license": "MIT", "peer": true, @@ -15530,7 +15530,6 @@ "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.6.0", - "lodash": "^4.17.21", "semver": "^7.6.3" }, "engines": { diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index d91eb98866..314afd5869 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -494,9 +494,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.0.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.7.tgz", - "integrity": "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw==", + "version": "24.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz", + "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", "license": "MIT", "dependencies": { "undici-types": "~7.8.0" @@ -1249,9 +1249,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001726", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", - "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", "funding": [ { "type": "opencollective", @@ -2005,9 +2005,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.177", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.177.tgz", - "integrity": "sha512-7EH2G59nLsEMj97fpDuvVcYi6lwTcM1xuWw3PssD8xzboAW7zj7iB3COEEEATUfjLHrs5uKBLQT03V/8URx06g==", + "version": "1.5.179", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz", + "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==", "license": "ISC" }, "node_modules/emoji-regex": { From c69e8cde7a5f0512d668ca73c5fc85cabf716d7e Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Mon, 7 Jul 2025 14:29:57 +0200 Subject: [PATCH 078/297] chore(ci): temporarily disable flaky ActivityPub related tests (#8395) As discussed in forgejo/forgejo#8274 A workaround till analysis of test timeouts helped us to fix the root cause. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8395 Reviewed-by: Earl Warren Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- .../integration/api_activitypub_actor_test.go | 77 -------- .../api_activitypub_person_test.go | 114 ------------ .../api_activitypub_repository_test.go | 174 ------------------ .../api_federation_httpsig_test.go | 82 --------- tests/integration/repo_settings_test.go | 65 ------- 5 files changed, 512 deletions(-) delete mode 100644 tests/integration/api_activitypub_actor_test.go delete mode 100644 tests/integration/api_activitypub_person_test.go delete mode 100644 tests/integration/api_activitypub_repository_test.go delete mode 100644 tests/integration/api_federation_httpsig_test.go diff --git a/tests/integration/api_activitypub_actor_test.go b/tests/integration/api_activitypub_actor_test.go deleted file mode 100644 index 42232bd640..0000000000 --- a/tests/integration/api_activitypub_actor_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "strconv" - "testing" - - "forgejo.org/modules/forgefed" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - "forgejo.org/services/contexttest" - "forgejo.org/services/federation" - "forgejo.org/tests" - - ap "github.com/go-ap/activitypub" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActivityPubActor(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - defer tests.PrepareTestEnv(t)() - - req := NewRequest(t, "GET", "/api/v1/activitypub/actor") - resp := MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), "@context") - - var actor ap.Actor - err := actor.UnmarshalJSON(resp.Body.Bytes()) - require.NoError(t, err) - - assert.Equal(t, ap.ApplicationType, actor.Type) - assert.Equal(t, "ghost", actor.PreferredUsername.String()) - keyID := actor.GetID().String() - assert.Regexp(t, "activitypub/actor$", keyID) - assert.Regexp(t, "activitypub/actor/inbox$", actor.Inbox.GetID().String()) - - pubKey := actor.PublicKey - assert.NotNil(t, pubKey) - publicKeyID := keyID + "#main-key" - assert.Equal(t, pubKey.ID.String(), publicKeyID) - - pubKeyPem := pubKey.PublicKeyPem - assert.NotNil(t, pubKeyPem) - assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem) -} - -func TestActorNewFromKeyId(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - ctx, _ := contexttest.MockAPIContext(t, "/api/v1/activitypub/actor") - sut, err := federation.NewActorIDFromKeyID(ctx.Base, fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", u)) - require.NoError(t, err) - - port, err := strconv.ParseUint(u.Port(), 10, 16) - require.NoError(t, err) - - assert.Equal(t, forgefed.ActorID{ - ID: "actor", - HostSchema: "http", - Path: "api/v1/activitypub", - Host: setting.Domain, - HostPort: uint16(port), - UnvalidatedInput: fmt.Sprintf("http://%s:%d/api/v1/activitypub/actor", setting.Domain, port), - IsPortSupplemented: false, - }, sut) - }) -} diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go deleted file mode 100644 index ca3bc844d7..0000000000 --- a/tests/integration/api_activitypub_person_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "testing" - - "forgejo.org/models/db" - "forgejo.org/models/unittest" - user_model "forgejo.org/models/user" - "forgejo.org/modules/activitypub" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - "forgejo.org/tests" - - ap "github.com/go-ap/activitypub" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActivityPubPerson(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - onGiteaRun(t, func(t *testing.T, localUrl *url.URL) { - defer test.MockVariableValue(&setting.AppURL, localUrl.String())() - - localUserID := 2 - localUserName := "user2" - localUserURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", localUrl, localUserID) - - // distantURL := federatedSrv.URL - // distantUser15URL := fmt.Sprintf("%s/api/v1/activitypub/user-id/15", distantURL) - - cf, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - - c, err := cf.WithKeysDirect(db.DefaultContext, mock.Persons[0].PrivKey, - mock.Persons[0].KeyID(federatedSrv.URL)) - require.NoError(t, err) - - // Unsigned request - t.Run("UnsignedRequest", func(t *testing.T) { - req := NewRequest(t, "GET", localUserURL) - MakeRequest(t, req, http.StatusBadRequest) - }) - - t.Run("SignedRequestValidation", func(t *testing.T) { - // Signed request - resp, err := c.GetBody(localUserURL) - require.NoError(t, err) - - var person ap.Person - err = person.UnmarshalJSON(resp) - require.NoError(t, err) - - assert.Equal(t, ap.PersonType, person.Type) - assert.Equal(t, localUserName, person.PreferredUsername.String()) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d$", localUserID), person.GetID()) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/inbox$", localUserID), person.Inbox.GetID().String()) - - assert.NotNil(t, person.PublicKey) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d#main-key$", localUserID), person.PublicKey.ID) - - assert.NotNil(t, person.PublicKey.PublicKeyPem) - assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", person.PublicKey.PublicKeyPem) - }) - }) -} - -func TestActivityPubMissingPerson(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - defer tests.PrepareTestEnv(t)() - - req := NewRequest(t, "GET", "/api/v1/activitypub/user-id/999999999") - resp := MakeRequest(t, req, http.StatusNotFound) - assert.Contains(t, resp.Body.String(), "user does not exist") -} - -func TestActivityPubPersonInbox(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - defer test.MockVariableValue(&setting.AppURL, u.String())() - user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - - user1url := u.JoinPath("/api/v1/activitypub/user-id/1").String() + "#main-key" - cf, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - c, err := cf.WithKeys(db.DefaultContext, user1, user1url) - require.NoError(t, err) - user2inboxurl := u.JoinPath("/api/v1/activitypub/user-id/2/inbox").String() - - // Signed request succeeds - resp, err := c.Post([]byte{}, user2inboxurl) - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - - // Unsigned request fails - req := NewRequest(t, "POST", user2inboxurl) - MakeRequest(t, req, http.StatusBadRequest) - }) -} diff --git a/tests/integration/api_activitypub_repository_test.go b/tests/integration/api_activitypub_repository_test.go deleted file mode 100644 index 14ea1a4b66..0000000000 --- a/tests/integration/api_activitypub_repository_test.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "testing" - "time" - - "forgejo.org/models/db" - "forgejo.org/models/forgefed" - "forgejo.org/models/unittest" - "forgejo.org/models/user" - "forgejo.org/modules/activitypub" - forgefed_modules "forgejo.org/modules/forgefed" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - "forgejo.org/tests" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActivityPubRepository(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - repositoryID := 2 - - cf, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - - c, err := cf.WithKeysDirect(db.DefaultContext, mock.Persons[0].PrivKey, - mock.Persons[0].KeyID(federatedSrv.URL)) - require.NoError(t, err) - - resp, err := c.GetBody(fmt.Sprintf("%sapi/v1/activitypub/repository-id/%d", u, repositoryID)) - require.NoError(t, err) - assert.Contains(t, string(resp), "@context") - - var repository forgefed_modules.Repository - err = repository.UnmarshalJSON(resp) - require.NoError(t, err) - - assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%d$", repositoryID), repository.GetID().String()) - }) -} - -func TestActivityPubMissingRepository(t *testing.T) { - defer tests.PrepareTestEnv(t)() - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - repositoryID := 9999999 - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)) - resp := MakeRequest(t, req, http.StatusNotFound) - assert.Contains(t, resp.Body.String(), "repository does not exist") -} - -func TestActivityPubRepositoryInboxValid(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - repositoryID := 2 - timeNow := time.Now().UTC() - - cf, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - - c, err := cf.WithKeysDirect(db.DefaultContext, mock.Persons[0].PrivKey, - mock.Persons[0].KeyID(federatedSrv.URL)) - require.NoError(t, err) - - repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() - - activity1 := []byte(fmt.Sprintf( - `{"type":"Like",`+ - `"startTime":"%s",`+ - `"actor":"%s/api/v1/activitypub/user-id/15",`+ - `"object":"%s"}`, - timeNow.Format(time.RFC3339), - federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) - t.Logf("activity: %s", activity1) - resp, err := c.Post(activity1, repoInboxURL) - - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - - federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) - federatedUser := unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "15", FederationHostID: federationHost.ID}) - unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) - - // A like activity by a different user of the same federated host. - activity2 := []byte(fmt.Sprintf( - `{"type":"Like",`+ - `"startTime":"%s",`+ - `"actor":"%s/api/v1/activitypub/user-id/30",`+ - `"object":"%s"}`, - // Make sure this activity happens later then the one before - timeNow.Add(time.Second).Format(time.RFC3339), - federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) - t.Logf("activity: %s", activity2) - resp, err = c.Post(activity2, repoInboxURL) - - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - - federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) - unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) - - // The same user sends another like activity - otherRepositoryID := 3 - otherRepoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", otherRepositoryID)).String() - activity3 := []byte(fmt.Sprintf( - `{"type":"Like",`+ - `"startTime":"%s",`+ - `"actor":"%s/api/v1/activitypub/user-id/30",`+ - `"object":"%s"}`, - // Make sure this activity happens later then the ones before - timeNow.Add(time.Second*2).Format(time.RFC3339), - federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", otherRepositoryID)).String())) - t.Logf("activity: %s", activity3) - resp, err = c.Post(activity3, otherRepoInboxURL) - - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - - federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) - unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) - - // Replay activity2. - resp, err = c.Post(activity2, repoInboxURL) - require.NoError(t, err) - assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) - }) -} - -func TestActivityPubRepositoryInboxInvalid(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - apServerActor := user.NewAPServerActor() - repositoryID := 2 - - cf, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - - c, err := cf.WithKeys(db.DefaultContext, apServerActor, apServerActor.KeyID()) - require.NoError(t, err) - - repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() - activity := []byte(`{"type":"Wrong"}`) - resp, err := c.Post(activity, repoInboxURL) - require.NoError(t, err) - assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) - }) -} diff --git a/tests/integration/api_federation_httpsig_test.go b/tests/integration/api_federation_httpsig_test.go deleted file mode 100644 index a8deaa315f..0000000000 --- a/tests/integration/api_federation_httpsig_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "testing" - - "forgejo.org/models/db" - "forgejo.org/models/forgefed" - "forgejo.org/models/unittest" - "forgejo.org/models/user" - "forgejo.org/modules/activitypub" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestFederationHttpSigValidation(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - userID := 2 - userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID) - - user1 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 1}) - - clientFactory, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - - apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.KeyID()) - require.NoError(t, err) - - // Unsigned request - t.Run("UnsignedRequest", func(t *testing.T) { - req := NewRequest(t, "GET", userURL) - MakeRequest(t, req, http.StatusBadRequest) - }) - - // Signed request - t.Run("SignedRequest", func(t *testing.T) { - resp, err := apClient.Get(userURL) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - }) - - // HACK HACK HACK: the host part of the URL gets set to which IP forgejo is - // listening on, NOT localhost, which is the Domain given to forgejo which - // is then used for eg. the keyID all requests - applicationKeyID := fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", setting.AppURL) - actorKeyID := fmt.Sprintf("%sapi/v1/activitypub/user-id/1#main-key", setting.AppURL) - - // Check for cached public keys - t.Run("ValidateCaches", func(t *testing.T) { - host, err := forgefed.FindFederationHostByKeyID(db.DefaultContext, applicationKeyID) - require.NoError(t, err) - assert.NotNil(t, host) - assert.True(t, host.PublicKey.Valid) - - _, user, err := user.FindFederatedUserByKeyID(db.DefaultContext, actorKeyID) - require.NoError(t, err) - assert.NotNil(t, user) - assert.True(t, user.PublicKey.Valid) - }) - - // Disable signature validation - defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() - - // Unsigned request - t.Run("SignatureValidationDisabled", func(t *testing.T) { - req := NewRequest(t, "GET", userURL) - MakeRequest(t, req, http.StatusOK) - }) - }) -} diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go index 63cc5332bc..6b467d78b2 100644 --- a/tests/integration/repo_settings_test.go +++ b/tests/integration/repo_settings_test.go @@ -6,21 +6,16 @@ package integration import ( "fmt" "net/http" - "strings" "testing" "forgejo.org/models/db" - "forgejo.org/models/forgefed" git_model "forgejo.org/models/git" repo_model "forgejo.org/models/repo" unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" - fm "forgejo.org/modules/forgefed" "forgejo.org/modules/optional" "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/modules/validation" gitea_context "forgejo.org/services/context" repo_service "forgejo.org/services/repository" user_service "forgejo.org/services/user" @@ -311,63 +306,3 @@ func TestProtectedBranch(t *testing.T) { unittest.AssertCount(t, &git_model.ProtectedBranch{RuleName: "master", RepoID: repo.ID}, 1) }) } - -func TestRepoFollowing(t *testing.T) { - defer tests.PrepareTestEnv(t)() - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) - session := loginUser(t, user.Name) - - t.Run("Add a following repo", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - link := fmt.Sprintf("/%s/settings", repo.FullName()) - - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": GetCSRF(t, session, link), - "action": "federation", - "following_repos": fmt.Sprintf("%s/api/v1/activitypub/repository-id/1", federatedSrv.URL), - }) - session.MakeRequest(t, req, http.StatusSeeOther) - - // Verify it was added. - federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) - unittest.AssertExistsAndLoadBean(t, &repo_model.FollowingRepo{ - ExternalID: "1", - FederationHostID: federationHost.ID, - }) - }) - - t.Run("Star a repo having a following repo", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - repoLink := fmt.Sprintf("/%s", repo.FullName()) - link := fmt.Sprintf("%s/action/star", repoLink) - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": GetCSRF(t, session, repoLink), - }) - - session.MakeRequest(t, req, http.StatusOK) - - // Verify distant server received a like activity - like := fm.ForgeLike{} - err := like.UnmarshalJSON([]byte(mock.LastPost)) - if err != nil { - t.Errorf("Error unmarshalling ForgeLike: %q", err) - } - if isValid, err := validation.IsValid(like); !isValid { - t.Errorf("ForgeLike is not valid: %q", err) - } - activityType := like.Type - object := like.Object.GetLink().String() - isLikeType := activityType == "Like" - isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1") - if !isLikeType || !isCorrectObject { - t.Error("Activity is not a like for this repo") - } - }) -} From cee2aae4cacd9caab98a4cbb273aab1cf0d0137b Mon Sep 17 00:00:00 2001 From: oliverpool Date: Mon, 7 Jul 2025 18:04:00 +0200 Subject: [PATCH 079/297] fix: corrupted wiki unit default permission (#8234 follow-up) (#8258) Closes #8119, follow-up of #8234 #8234 "only" fixed the case when a new wiki setting was applied. However it lacked 2 aspects: - fixing the already corrupted unit permission in the database (required a migration) - fixing the API route Both aspects should now be covered. Additionally, I commented out the unused `UnitAccessMode` and indicated that they are only used for the wiki-unit (hopefully saving some time to future code-readers). ### Testing - go to a commit before #8234 (e.g. 285f66b782491d05e35aa81a6e1a0fede85069b4) - create 3 repositories - save the wiki settings of the second, without any change - set the wiki of the third to be globally writable - verify the `default_permissions` column in the database, of the unit `5`: 0, 2, 3 - stop Forgejo, switch to this PR and start Forgejo - verify that the logs writes `Migration[35]: Fix wiki unit default permission` - verify the `default_permissions` column in the database, of the unit `5`: 0, 0, 3 Before: ![2025-06-23-150055.png](/attachments/1eb92507-b8b4-422c-921c-4296a91172e7) After: ![2025-06-23-150853.png](/attachments/c7b53397-54fe-487d-89da-e10b26538cde) - [x] I did not document these changes and I do not expect someone else to do it. - [x] I do not want this change to show in the release notes. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8258 Reviewed-by: Earl Warren Co-authored-by: oliverpool Co-committed-by: oliverpool --- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v36.go | 55 ++++++++++++++++++++++++++++ models/repo/repo_unit.go | 17 +++++---- models/repo/repo_unit_test.go | 4 +- routers/api/v1/repo/repo.go | 2 +- 5 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 models/forgejo_migrations/v36.go diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 50391ff650..fcea69d23f 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -109,6 +109,8 @@ var migrations = []*Migration{ NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), // v34 -> v35 NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), + // v35 -> v36 + NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v36.go b/models/forgejo_migrations/v36.go new file mode 100644 index 0000000000..1a798147cf --- /dev/null +++ b/models/forgejo_migrations/v36.go @@ -0,0 +1,55 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "xorm.io/xorm" +) + +func FixWikiUnitDefaultPermission(x *xorm.Engine) error { + // Type is Unit's Type + type Type int + + // Enumerate all the unit types + const ( + TypeInvalid Type = iota // 0 invalid + TypeCode // 1 code + TypeIssues // 2 issues + TypePullRequests // 3 PRs + TypeReleases // 4 Releases + TypeWiki // 5 Wiki + TypeExternalWiki // 6 ExternalWiki + TypeExternalTracker // 7 ExternalTracker + TypeProjects // 8 Projects + TypePackages // 9 Packages + TypeActions // 10 Actions + ) + + // RepoUnitAccessMode specifies the users access mode to a repo unit + type UnitAccessMode int + + const ( + // UnitAccessModeUnset - no unit mode set + UnitAccessModeUnset UnitAccessMode = iota // 0 + // UnitAccessModeNone no access + UnitAccessModeNone // 1 + // UnitAccessModeRead read access + UnitAccessModeRead // 2 + // UnitAccessModeWrite write access + UnitAccessModeWrite // 3 + ) + _ = UnitAccessModeNone + _ = UnitAccessModeWrite + + type RepoUnit struct { + DefaultPermissions UnitAccessMode `xorm:"NOT NULL DEFAULT 0"` + } + _, err := x.Where("type = ?", TypeWiki). + Where("default_permissions = ?", UnitAccessModeRead). + Cols("default_permissions"). + Update(RepoUnit{ + DefaultPermissions: UnitAccessModeUnset, + }) + return err +} diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index c11ad70627..e50f79e945 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -41,27 +41,30 @@ func (err ErrUnitTypeNotExist) Unwrap() error { } // RepoUnitAccessMode specifies the users access mode to a repo unit +// Only UnitAccessModeWrite is used by the wiki, to mark it as instance-writable type UnitAccessMode int const ( // UnitAccessModeUnset - no unit mode set UnitAccessModeUnset UnitAccessMode = iota // 0 + // UnitAccessModeNone no access - UnitAccessModeNone // 1 + // UnitAccessModeNone UnitAccessMode = 1 // UnitAccessModeRead read access - UnitAccessModeRead // 2 + // UnitAccessModeRead UnitAccessMode = 2 + // UnitAccessModeWrite write access - UnitAccessModeWrite // 3 + UnitAccessModeWrite UnitAccessMode = 3 ) func (mode UnitAccessMode) ToAccessMode(modeIfUnset perm.AccessMode) perm.AccessMode { switch mode { case UnitAccessModeUnset: return modeIfUnset - case UnitAccessModeNone: - return perm.AccessModeNone - case UnitAccessModeRead: - return perm.AccessModeRead + // case UnitAccessModeNone: + // return perm.AccessModeNone + // case UnitAccessModeRead: + // return perm.AccessModeRead case UnitAccessModeWrite: return perm.AccessModeWrite default: diff --git a/models/repo/repo_unit_test.go b/models/repo/repo_unit_test.go index a1964519bd..3d6d408fcb 100644 --- a/models/repo/repo_unit_test.go +++ b/models/repo/repo_unit_test.go @@ -34,8 +34,8 @@ func TestActionsConfig(t *testing.T) { } func TestRepoUnitAccessMode(t *testing.T) { - assert.Equal(t, perm.AccessModeNone, UnitAccessModeNone.ToAccessMode(perm.AccessModeAdmin)) - assert.Equal(t, perm.AccessModeRead, UnitAccessModeRead.ToAccessMode(perm.AccessModeAdmin)) + // assert.Equal(t, perm.AccessModeNone, UnitAccessModeNone.ToAccessMode(perm.AccessModeAdmin)) + // assert.Equal(t, perm.AccessModeRead, UnitAccessModeRead.ToAccessMode(perm.AccessModeAdmin)) assert.Equal(t, perm.AccessModeWrite, UnitAccessModeWrite.ToAccessMode(perm.AccessModeAdmin)) assert.Equal(t, perm.AccessModeRead, UnitAccessModeUnset.ToAccessMode(perm.AccessModeRead)) } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 3d6a40e9ab..42385d54a6 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -861,7 +861,7 @@ func updateRepoUnits(ctx *context.APIContext, owner string, repo *repo_model.Rep if *opts.GloballyEditableWiki { wikiPermissions = repo_model.UnitAccessModeWrite } else { - wikiPermissions = repo_model.UnitAccessModeRead + wikiPermissions = repo_model.UnitAccessModeUnset } } From eb8ed7e5e9e535f84da7795e16681af8a4df6fb0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 8 Jul 2025 07:59:34 +0200 Subject: [PATCH 080/297] Update module code.forgejo.org/forgejo/act to v1.29.0 (forgejo) (#8442) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [code.forgejo.org/forgejo/act](https://code.forgejo.org/forgejo/act) | `v1.28.0` -> `v1.29.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/code.forgejo.org%2fforgejo%2fact/v1.29.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/code.forgejo.org%2fforgejo%2fact/v1.28.0/v1.29.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
forgejo/act (code.forgejo.org/forgejo/act) ### [`v1.29.0`](https://code.forgejo.org/forgejo/act/compare/v1.28.0...v1.29.0) [Compare Source](https://code.forgejo.org/forgejo/act/compare/v1.28.0...v1.29.0)
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8442 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5f62066994..952082987b 100644 --- a/go.mod +++ b/go.mod @@ -244,7 +244,7 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.28.0 +replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.29.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 diff --git a/go.sum b/go.sum index 9e72ee5c2c..cddd87d4ce 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFs code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/act v1.28.0 h1:96njNC7C1YNyjWq5OWvLZMF/nw0PMthzIA8Nwbnn7jo= -code.forgejo.org/forgejo/act v1.28.0/go.mod h1:dFuiwAmD5vyrzecysHB2kL/GM3wRpoVPl+WdbCTC8Bs= +code.forgejo.org/forgejo/act v1.29.0 h1:CPiI0LRPU0f6gUdQj1ZVax0ySc8CfegY4hiRsymdZU0= +code.forgejo.org/forgejo/act v1.29.0/go.mod h1:RPqtuaI2FkC1SVOaYCRODo5jIfoMTBVgEOOP3Sdiuh4= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M= From f8d6a61157d69094be64a1112600122d4113dbb7 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Tue, 8 Jul 2025 16:58:49 +0200 Subject: [PATCH 081/297] fix(ui): make releases filtering responsive (#8399) * Improve consistency with other list pages (issues, PRs) * Improve mobile usability * Refresh the look a bit --- Bug fix: Currently on mobile filters get squashed and are not very usable: https://codeberg.org/attachments/5d6ad65e-6ae5-4819-a3dc-75fb4fe77960 This commit: https://codeberg.org/attachments/6eb0843e-49fd-4687-ab38-9ba60a6cc6c8 --- Consistency fix: Use same element order and spacing as in issue list: https://codeberg.org/attachments/9cdb042d-cdce-4243-a610-a1eabe3f4fc3 On desktop this means full width search bar: https://codeberg.org/attachments/bdd50c3d-76f3-4ad0-884e-5717f9c86154 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8399 Reviewed-by: Gusted --- templates/repo/release_tag_header.tmpl | 23 +++++++++++------------ tests/integration/release_test.go | 2 +- tests/integration/repo_tag_test.go | 4 ++-- web_src/css/repo/release-tag.css | 6 ++++++ 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl index 63d0689c50..75a115e31e 100644 --- a/templates/repo/release_tag_header.tmpl +++ b/templates/repo/release_tag_header.tmpl @@ -2,24 +2,23 @@ {{$canReadCode := $.Permission.CanRead $.UnitTypeCode}} {{if $canReadReleases}} -
-
- +
+ {{if .ShowReleaseSearch}} -
+ {{template "shared/search/combo" dict "Value" .Keyword}}
{{end}} -
+
{{if .EnableFeed}} - - {{svg "octicon-rss" 16}} {{ctx.Locale.Tr "rss_feed"}} + + {{svg "octicon-rss" 16}} + {{end}} {{if and (not .PageIsTagList) .CanCreateRelease}} diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go index b5cccc65e8..f9ce3c81de 100644 --- a/tests/integration/release_test.go +++ b/tests/integration/release_test.go @@ -70,7 +70,7 @@ func checkLatestReleaseAndCount(t *testing.T, session *TestSession, repoURL, ver // Check release count in the counter on the Release/Tag switch, as well as that the tab is highlighted if count < 10 { // Only check values less than 10, should be enough attempts before this test cracks // 10 is the pagination limit, but the counter can have more than that - releaseTab := htmlDoc.doc.Find(".repository.releases .ui.compact.menu a.active.item[href$='/releases']") + releaseTab := htmlDoc.doc.Find(".repository.releases .switch a.active.item[href$='/releases']") assert.Contains(t, releaseTab.Text(), strconv.Itoa(count)+" release") // Could be "1 release" or "4 releases" } diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 9be33ec8a8..4f72b58d12 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -41,11 +41,11 @@ func TestTagViewWithoutRelease(t *testing.T) { // Test that the tags sub-menu is active and has a counter htmlDoc := NewHTMLParser(t, resp.Body) - tagsTab := htmlDoc.Find(".small-menu-items .active.item[href$='/tags']") + tagsTab := htmlDoc.Find(".switch .active.item[href$='/tags']") assert.Contains(t, tagsTab.Text(), "4 tags") // Test that the release sub-menu isn't active - releaseLink := htmlDoc.Find(".small-menu-items .item[href$='/releases']") + releaseLink := htmlDoc.Find(".switch .item[href$='/releases']") assert.False(t, releaseLink.HasClass("active")) // Test that the title is displayed diff --git a/web_src/css/repo/release-tag.css b/web_src/css/repo/release-tag.css index 9860813e98..4a4158f4ba 100644 --- a/web_src/css/repo/release-tag.css +++ b/web_src/css/repo/release-tag.css @@ -110,6 +110,12 @@ .repository.new.release .field button { margin-bottom: 1em; } + .release-list-search { + order: 2 !important; + } + .release-list-buttons { + margin-left: auto; + } } .repository.new.release .field .attachment_edit { From 34b77270e85a527b5ed3c505abd32a7ae78ad2a4 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 9 Jul 2025 00:14:17 +0200 Subject: [PATCH 082/297] Update dependency mermaid to v11.8.1 (forgejo) (#8440) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8440 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d0e10c3ba..b0d24f002d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.8.0", + "mermaid": "11.8.1", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", @@ -2075,9 +2075,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.0.tgz", - "integrity": "sha512-7DNESgpyZ5WG1SIkrYafVBhWmImtmQuoxOO1lawI3gQYWxBX3v1FW3IyuuRfKJAO06XrZR71W0Kif5VEGGd4VA==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.1.tgz", + "integrity": "sha512-lCQNpV8R4lgsGcjX5667UiuDLk2micCtjtxR1YKbBXvN5w2v+FeLYoHrTSSrjwXdMcDYvE4ZBPvKT31dfeSmmA==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -10558,14 +10558,14 @@ } }, "node_modules/mermaid": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.8.0.tgz", - "integrity": "sha512-uAZUwnBiqREZcUrFw3G5iQ5Pj3hTYUP95EZc3ec/nGBzHddJZydzYGE09tGZDBS1VoSoDn0symZ85FmypSTo5g==", + "version": "11.8.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.8.1.tgz", + "integrity": "sha512-VSXJLqP1Sqw5sGr273mhvpPRhXwE6NlmMSqBZQw+yZJoAJkOIPPn/uT3teeCBx60Fkt5zEI3FrH2eVT0jXRDzw==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.6.0", + "@mermaid-js/parser": "^0.6.1", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", diff --git a/package.json b/package.json index 8fceed4534..229ae56354 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.8.0", + "mermaid": "11.8.1", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", From 0636b3087fa8c1cc1c5fe9582b3ac0925f3504b1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 9 Jul 2025 07:32:03 +0200 Subject: [PATCH 083/297] Update dependency go to v1.24.5 (forgejo) (#8449) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [go](https://go.dev/) ([source](https://github.com/golang/go)) | toolchain | patch | `1.24.4` -> `1.24.5` | --- ### 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8449 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 952082987b..d09ec592aa 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module forgejo.org go 1.24 -toolchain go1.24.4 +toolchain go1.24.5 require ( code.forgejo.org/f3/gof3/v3 v3.11.0 From 11934670ec55c2b607168919db74ad6a94800317 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 9 Jul 2025 07:38:00 +0200 Subject: [PATCH 084/297] fix: correctly mark reviews as stale for AGit PRs (#8450) The argument order for `ValidatePullRequest` is to first give the new commitID and then the old commit ID. This results in reviews not being marked as stale when they are not stale and reviews as not stale when they are stale. The test will fail if the fix is not present. Add testing for the following three scenarios: 1. A review is made, the PR is updated and as a consequence the PR's diff is changed. The review is now marked as stale. 2. A review is made but in the meantime the PR is updated and the review is submitted on a older commit ID. If the diff changed the review is marked as stale. 3. A review that was made against a older commit ID is no longer marked as stale if the PR is force-pushed to that older commit ID. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8450 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- services/agit/agit.go | 2 +- tests/integration/pull_review_test.go | 159 ++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) diff --git a/services/agit/agit.go b/services/agit/agit.go index 20e87642c3..8ef641629a 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -221,7 +221,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. } // Validate pull request. - pull_service.ValidatePullRequest(ctx, pr, oldCommitID, opts.NewCommitIDs[i], pusher) + pull_service.ValidatePullRequest(ctx, pr, opts.NewCommitIDs[i], oldCommitID, pusher) // TODO: call `InvalidateCodeComments` diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 603252f45f..4af0a1100e 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -5,19 +5,23 @@ package integration import ( + "bytes" "fmt" "io" "net/http" "net/http/httptest" "net/url" + "os" "path" "strconv" "strings" "testing" + "time" "forgejo.org/models/db" issues_model "forgejo.org/models/issues" repo_model "forgejo.org/models/repo" + unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/git" @@ -761,3 +765,158 @@ func updateFileInBranch(user *user_model.User, repo *repo_model.Repository, tree _, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, user, opts) return err } + +func TestPullRequestStaleReview(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user2.Name) + + // Create temporary repository. + repo, _, f := tests.CreateDeclarativeRepo(t, user2, "", + []unit_model.Type{unit_model.TypePullRequests}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "FUNFACT", + ContentReader: strings.NewReader("Smithy was the runner up to be Forgejo's name"), + }, + }, + ) + defer f() + + // Clone it. + dstPath := t.TempDir() + r := fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name) + cloneURL, _ := url.Parse(r) + cloneURL.User = url.UserPassword("user2", userPassword) + require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + + // Create first commit. + require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o600)) + require.NoError(t, git.AddChanges(dstPath, true)) + require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add README.", + })) + stdout := &bytes.Buffer{} + require.NoError(t, git.NewCommand(t.Context(), "rev-parse", "HEAD").Run(&git.RunOpts{Dir: dstPath, Stdout: stdout})) + firstCommitID := strings.TrimSpace(stdout.String()) + + // Create agit PR. + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath})) + + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Index: 1, BaseRepoID: repo.ID}) + + req := NewRequest(t, "GET", "/"+repo.FullName()+"/pulls/1/files/reviews/new_comment") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + + t.Run("Mark review as stale", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Create a approved review against against this commit. + req = NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/comments", map[string]string{ + "_csrf": doc.GetCSRF(), + "origin": doc.GetInputValueByName("origin"), + "latest_commit_id": firstCommitID, + "side": "proposed", + "line": "1", + "path": "FUNFACT", + "diff_start_cid": doc.GetInputValueByName("diff_start_cid"), + "diff_end_cid": doc.GetInputValueByName("diff_end_cid"), + "diff_base_cid": doc.GetInputValueByName("diff_base_cid"), + "content": "nitpicking comment", + "pending_review": "", + }) + session.MakeRequest(t, req, http.StatusOK) + + req = NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/submit", map[string]string{ + "_csrf": doc.GetCSRF(), + "commit_id": firstCommitID, + "content": "looks good", + "type": "comment", + }) + session.MakeRequest(t, req, http.StatusOK) + + // Review is not stale. + review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID}) + assert.False(t, review.Stale) + + // Create second commit + require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## I prefer this heading"), 0o600)) + require.NoError(t, git.AddChanges(dstPath, true)) + require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add README.", + })) + + // Push to agit PR. + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath})) + + // Review is stale. + review = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID}) + assert.True(t, review.Stale) + }) + + t.Run("Create stale review", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Review based on the first commit, which is a stale review because the + // PR's head is at the seconnd commit. + req := NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/submit", map[string]string{ + "_csrf": doc.GetCSRF(), + "commit_id": firstCommitID, + "content": "looks good", + "type": "approve", + }) + session.MakeRequest(t, req, http.StatusOK) + + // There does not exist a review that is not stale, because all reviews + // are based on the first commit and the PR's head is at the second commit. + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = false") + }) + + t.Run("Mark unstale", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Force push the PR to the first commit. + require.NoError(t, git.NewCommand(t.Context(), "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})) + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr", "-o", "force-push").Run(&git.RunOpts{Dir: dstPath})) + + // There does not exist a review that is stale, because all reviews + // are based on the first commit and thus all reviews are no longer marked + // as stale. + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true") + }) + + t.Run("Diff did not change", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Create a empty commit and push it to the PR. + require.NoError(t, git.NewCommand(t.Context(), "commit", "--allow-empty", "-m", "Empty commit").Run(&git.RunOpts{Dir: dstPath})) + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath})) + + // There does not exist a review that is stale, because the diff did not + // change. + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true") + }) + }) +} From 32e8610b20e6da88b6910151c0bf9412dca3f433 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 9 Jul 2025 09:43:33 +0200 Subject: [PATCH 085/297] fix(email): actions notification template confuses branch with PR (#8448) When a mail notification is sent because of a failed pull request run, the body will show: Branch: #661 (f57df45) where #661 is the number of the pull request and not the branch. This is because run.PrettyRef() is used and has a misleading special case returning a PR number instead of a ref. Remove the textual description as it can easily be discovered from the run web page linked in the mail. ## 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8448 Reviewed-by: Christopher Besch Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- options/locale_next/locale_en-US.json | 2 +- services/mailer/mail_actions.go | 2 -- services/mailer/mail_actions_now_done_test.go | 2 +- templates/mail/actions/now_done.tmpl | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index a551db87dc..e08c8b2aee 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -92,7 +92,7 @@ "mail.actions.not_successful_run": "Workflow %[1]s failed in repository %[2]s", "mail.actions.run_info_cur_status": "This Run's Status: %[1]s (just updated from %[2]s)", "mail.actions.run_info_previous_status": "Previous Run's Status: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", + "mail.actions.run_info_sha": "Commit: %[1]s", "mail.actions.run_info_trigger": "Triggered because: %[1]s by: %[2]s", "repo.diff.commit.next-short": "Next", "repo.diff.commit.previous-short": "Prev", diff --git a/services/mailer/mail_actions.go b/services/mailer/mail_actions.go index fa0d2635f1..a99af823b3 100644 --- a/services/mailer/mail_actions.go +++ b/services/mailer/mail_actions.go @@ -60,7 +60,6 @@ func sendMailActionRun(to *user_model.User, run *actions_model.ActionRun, priorS if len(commitSHA) > 7 { commitSHA = commitSHA[:7] } - branch := run.PrettyRef() data := map[string]any{ "locale": locale, @@ -73,7 +72,6 @@ func sendMailActionRun(to *user_model.User, run *actions_model.ActionRun, priorS "LastRun": lastRun, "PriorStatus": priorStatus, "CommitSHA": commitSHA, - "Branch": branch, "IsSuccess": run.Status.IsSuccess(), } diff --git a/services/mailer/mail_actions_now_done_test.go b/services/mailer/mail_actions_now_done_test.go index f4c597c99c..e84441f460 100644 --- a/services/mailer/mail_actions_now_done_test.go +++ b/services/mailer/mail_actions_now_done_test.go @@ -54,7 +54,7 @@ func getActionsNowDoneTestOrg(t *testing.T, name, email string, owner *user_mode } func assertTranslatedLocaleMailActionsNowDone(t *testing.T, msgBody string) { - AssertTranslatedLocale(t, msgBody, "mail.actions.successful_run_after_failure", "mail.actions.not_successful_run", "mail.actions.run_info_cur_status", "mail.actions.run_info_ref", "mail.actions.run_info_previous_status", "mail.actions.run_info_trigger", "mail.view_it_on") + AssertTranslatedLocale(t, msgBody, "mail.actions.successful_run_after_failure", "mail.actions.not_successful_run", "mail.actions.run_info_cur_status", "mail.actions.run_info_sha", "mail.actions.run_info_previous_status", "mail.actions.run_info_trigger", "mail.view_it_on") } func TestActionRunNowDoneStatusMatrix(t *testing.T) { diff --git a/templates/mail/actions/now_done.tmpl b/templates/mail/actions/now_done.tmpl index a890411055..adc990c545 100644 --- a/templates/mail/actions/now_done.tmpl +++ b/templates/mail/actions/now_done.tmpl @@ -21,7 +21,7 @@
{{.locale.Tr "mail.actions.run_info_cur_status" .Run.Status .PriorStatus}}
- {{.locale.Tr "mail.actions.run_info_ref" .Branch .CommitSHA}}
+ {{.locale.Tr "mail.actions.run_info_sha" .CommitSHA}}
{{if .LastRun}} {{.locale.Tr "mail.actions.run_info_previous_status" .LastRun.Status}}
{{end}} From 48035bbd4ea0c034062537f4922cd2a3ae3c3a52 Mon Sep 17 00:00:00 2001 From: joneshf Date: Wed, 9 Jul 2025 20:55:10 +0200 Subject: [PATCH 086/297] chore: add tests for `admin auth` subcommands (#8433) This PR is adds almost all tests in `cmd/admin_auth_oauth_test.go` with a bit of refactoring beforehand to make the tests easier to write. These should be legitimate refactors where the implementation changes but the public API/behavior does not change. All of the changes in this PR are done to align with how tests are written in `cmd/admin_auth_ldap_test.go`. Since `cmd/admin_auth_ldap.go` is a sibling file to `cmd/admin_auth_oauth.go`, it seems like their test files should also be aligned. There are some tests added that show the current behavior as not ideal. E.g. not being able to update certain fields, or being able to set fields that are ultimately ignored. These are added so that the behavior is at least shown a bit more visibly. There should likely be a follow-up to fix some of these issues. But that will almost certainly be a breaking change that I'd rather avoid in this PR. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8433 Reviewed-by: Earl Warren Co-authored-by: joneshf Co-committed-by: joneshf --- cmd/admin_auth.go | 19 + cmd/admin_auth_ldap.go | 19 - cmd/admin_auth_oauth.go | 18 +- cmd/admin_auth_oauth_test.go | 704 +++++++++++++++++++++++++++++++++++ 4 files changed, 732 insertions(+), 28 deletions(-) create mode 100644 cmd/admin_auth_oauth_test.go diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index cb95b3b3c8..91b344b1e9 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -17,6 +17,15 @@ import ( "github.com/urfave/cli/v3" ) +type ( + authService struct { + initDB func(ctx context.Context) error + createAuthSource func(context.Context, *auth_model.Source) error + updateAuthSource func(context.Context, *auth_model.Source) error + getAuthSourceByID func(ctx context.Context, id int64) (*auth_model.Source, error) + } +) + func microcmdAuthDelete() *cli.Command { return &cli.Command{ Name: "delete", @@ -60,6 +69,16 @@ func microcmdAuthList() *cli.Command { } } +// newAuthService creates a service with default functions. +func newAuthService() *authService { + return &authService{ + initDB: initDB, + createAuthSource: auth_model.CreateSource, + updateAuthSource: auth_model.UpdateSource, + getAuthSourceByID: auth_model.GetSourceByID, + } +} + func runListAuth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go index 997d6b3a16..9af6c331d3 100644 --- a/cmd/admin_auth_ldap.go +++ b/cmd/admin_auth_ldap.go @@ -14,15 +14,6 @@ import ( "github.com/urfave/cli/v3" ) -type ( - authService struct { - initDB func(ctx context.Context) error - createAuthSource func(context.Context, *auth.Source) error - updateAuthSource func(context.Context, *auth.Source) error - getAuthSourceByID func(ctx context.Context, id int64) (*auth.Source, error) - } -) - func commonLdapCLIFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ @@ -184,16 +175,6 @@ func microcmdAuthUpdateLdapSimpleAuth() *cli.Command { } } -// newAuthService creates a service with default functions. -func newAuthService() *authService { - return &authService{ - initDB: initDB, - createAuthSource: auth.CreateSource, - updateAuthSource: auth.UpdateSource, - getAuthSourceByID: auth.GetSourceByID, - } -} - // parseAuthSource assigns values on authSource according to command line flags. func parseAuthSource(c *cli.Command, authSource *auth.Source) { if c.IsSet("name") { diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go index a638bf0210..8a756480cd 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -132,7 +132,7 @@ func microcmdAuthAddOauth() *cli.Command { return &cli.Command{ Name: "add-oauth", Usage: "Add new Oauth authentication source", - Action: runAddOauth, + Action: newAuthService().addOauth, Flags: oauthCLIFlags(), } } @@ -141,7 +141,7 @@ func microcmdAuthUpdateOauth() *cli.Command { return &cli.Command{ Name: "update-oauth", Usage: "Update existing Oauth authentication source", - Action: runUpdateOauth, + Action: newAuthService().updateOauth, Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{idFlag()}, oauthCLIFlags()[1:]...)...), } } @@ -179,11 +179,11 @@ func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source { } } -func runAddOauth(ctx context.Context, c *cli.Command) error { +func (a *authService) addOauth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() - if err := initDB(ctx); err != nil { + if err := a.initDB(ctx); err != nil { return err } @@ -195,7 +195,7 @@ func runAddOauth(ctx context.Context, c *cli.Command) error { } } - return auth_model.CreateSource(ctx, &auth_model.Source{ + return a.createAuthSource(ctx, &auth_model.Source{ Type: auth_model.OAuth2, Name: c.String("name"), IsActive: true, @@ -203,7 +203,7 @@ func runAddOauth(ctx context.Context, c *cli.Command) error { }) } -func runUpdateOauth(ctx context.Context, c *cli.Command) error { +func (a *authService) updateOauth(ctx context.Context, c *cli.Command) error { if !c.IsSet("id") { return errors.New("--id flag is missing") } @@ -211,11 +211,11 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error { ctx, cancel := installSignals(ctx) defer cancel() - if err := initDB(ctx); err != nil { + if err := a.initDB(ctx); err != nil { return err } - source, err := auth_model.GetSourceByID(ctx, c.Int64("id")) + source, err := a.getAuthSourceByID(ctx, c.Int64("id")) if err != nil { return err } @@ -310,5 +310,5 @@ func runUpdateOauth(ctx context.Context, c *cli.Command) error { oAuth2Config.CustomURLMapping = customURLMapping source.Cfg = oAuth2Config - return auth_model.UpdateSource(ctx, source) + return a.updateAuthSource(ctx, source) } diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go new file mode 100644 index 0000000000..ea5442e62d --- /dev/null +++ b/cmd/admin_auth_oauth_test.go @@ -0,0 +1,704 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package cmd + +import ( + "context" + "testing" + + "forgejo.org/models/auth" + "forgejo.org/modules/test" + "forgejo.org/services/auth/source/oauth2" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v3" +) + +func TestAddOauth(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + source *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source full", + "--provider", "openidConnect", + "--key", "client id", + "--secret", "client secret", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--use-custom-urls", "", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + "--icon-url", "https://example.com/icon.svg", + "--skip-local-2fa", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + "--attribute-ssh-public-key", "ssh_public_key", + "--required-claim-name", "can_access", + "--required-claim-value", "yes", + "--group-claim-name", "groups", + "--admin-group", "admin", + "--restricted-group", "restricted", + "--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + "--group-team-map-removal", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source full", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + ClientID: "client id", + ClientSecret: "client secret", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + CustomURLMapping: &oauth2.CustomURLMapping{ + AuthURL: "https://example.com/auth", + TokenURL: "https://example.com/token", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "tenant id", + }, + IconURL: "https://example.com/icon.svg", + Scopes: []string{"address", "email", "phone", "profile"}, + AttributeSSHPublicKey: "ssh_public_key", + RequiredClaimName: "can_access", + RequiredClaimValue: "yes", + GroupClaimName: "groups", + AdminGroup: "admin", + GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + GroupTeamMapRemoval: true, + RestrictedGroup: "restricted", + SkipLocalTwoFA: true, + }, + }, + }, + // case 1 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source min", + "--provider", "openidConnect", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source min", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + Scopes: []string{}, + }, + }, + }, + // case 2 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + IsActive: true, + Cfg: &oauth2.Source{ + Scopes: []string{}, + }, + }, + }, + // case 3 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + "--provider", "openidConnect", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 4 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + "--provider", "openidConnect", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--scopes", "address,email,phone,profile", + }, + source: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + IsActive: true, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 5 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source", + "--provider", "openidConnect", + }, + errMsg: "invalid Auto Discovery URL: (this must be a valid URL starting with http:// or https://)", + }, + // case 6 + { + args: []string{ + "oauth-test", + "--name", "oauth2 (via openidConnect) source", + "--provider", "openidConnect", + "--auto-discover-url", "example.com", + }, + errMsg: "invalid Auto Discovery URL: example.com (this must be a valid URL starting with http:// or https://)", + }, + } + + for n, c := range cases { + // Mock functions. + var createdAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + createdAuthSource = authSource + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "should not call updateAuthSource", "case: %d", n) + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + assert.FailNow(t, "should not call getAuthSourceByID", "case: %d", n) + return nil, nil + }, + } + + // Create a copy of command to test + app := cli.Command{} + app.Flags = microcmdAuthAddOauth().Flags + app.Action = service.addOauth + + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + require.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.source, createdAuthSource, "case %d: wrong authSource", n) + } + } +} + +func TestUpdateOauth(t *testing.T) { + // Mock cli functions to do not exit on error + defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() + + // Test cases + cases := []struct { + args []string + id int64 + existingAuthSource *auth.Source + authSource *auth.Source + errMsg string + }{ + // case 0 + { + args: []string{ + "oauth-test", + "--id", "23", + "--name", "oauth2 (via openidConnect) source full", + "--provider", "openidConnect", + "--key", "client id", + "--secret", "client secret", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + "--use-custom-urls", "", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + "--icon-url", "https://example.com/icon.svg", + "--skip-local-2fa", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + "--attribute-ssh-public-key", "ssh_public_key", + "--required-claim-name", "can_access", + "--required-claim-value", "yes", + "--group-claim-name", "groups", + "--admin-group", "admin", + "--restricted-group", "restricted", + "--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + "--group-team-map-removal", + }, + id: 23, + existingAuthSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{}, + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source full", + Cfg: &oauth2.Source{ + Provider: "openidConnect", + ClientID: "client id", + ClientSecret: "client secret", + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + CustomURLMapping: &oauth2.CustomURLMapping{ + AuthURL: "https://example.com/auth", + TokenURL: "https://example.com/token", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "tenant id", + }, + IconURL: "https://example.com/icon.svg", + Scopes: []string{"address", "email", "phone", "profile"}, + AttributeSSHPublicKey: "ssh_public_key", + RequiredClaimName: "can_access", + RequiredClaimValue: "yes", + GroupClaimName: "groups", + AdminGroup: "admin", + GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + GroupTeamMapRemoval: true, + RestrictedGroup: "restricted", + // `--skip-local-2fa` is currently ignored. + // SkipLocalTwoFA: true, + }, + }, + }, + // case 1 + { + args: []string{ + "oauth-test", + "--id", "1", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 2 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source full", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source full", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 3 + { + args: []string{ + "oauth-test", + "--id", "1", + "--provider", "openidConnect", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + Provider: "openidConnect", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 4 + { + args: []string{ + "oauth-test", + "--id", "1", + "--key", "client id", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + ClientID: "client id", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 5 + { + args: []string{ + "oauth-test", + "--id", "1", + "--secret", "client secret", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + ClientSecret: "client secret", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 6 + { + args: []string{ + "oauth-test", + "--id", "1", + "--auto-discover-url", "https://example.com/.well-known/openid-configuration", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + OpenIDConnectAutoDiscoveryURL: "https://example.com/.well-known/openid-configuration", + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 7 + { + args: []string{ + "oauth-test", + "--id", "1", + "--use-custom-urls", "", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{ + AuthURL: "https://example.com/auth", + TokenURL: "https://example.com/token", + ProfileURL: "https://example.com/profile", + EmailURL: "https://example.com/email", + Tenant: "tenant id", + }, + }, + }, + }, + // case 8 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + "--custom-tenant-id", "tenant id", + "--custom-auth-url", "https://example.com/auth", + "--custom-token-url", "https://example.com/token", + "--custom-profile-url", "https://example.com/profile", + "--custom-email-url", "https://example.com/email", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--use-custom-urls` required for `--custom-*` flags", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + }, + }, + }, + // case 9 + { + args: []string{ + "oauth-test", + "--id", "1", + "--icon-url", "https://example.com/icon.svg", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + IconURL: "https://example.com/icon.svg", + }, + }, + }, + // case 10 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored", + "--skip-local-2fa", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--skip-local-2fa` is currently ignored", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + // `--skip-local-2fa` is currently ignored. + // SkipLocalTwoFA: true, + }, + }, + }, + // case 11 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + "--scopes", "address", + "--scopes", "email", + "--scopes", "phone", + "--scopes", "profile", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` aggregates multiple uses", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 12 + { + args: []string{ + "oauth-test", + "--id", "1", + "--name", "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + "--scopes", "address,email,phone,profile", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Name: "oauth2 (via openidConnect) source `--scopes` supports commas as separators", + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + Scopes: []string{"address", "email", "phone", "profile"}, + }, + }, + }, + // case 13 + { + args: []string{ + "oauth-test", + "--id", "1", + "--attribute-ssh-public-key", "ssh_public_key", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + AttributeSSHPublicKey: "ssh_public_key", + }, + }, + }, + // case 14 + { + args: []string{ + "oauth-test", + "--id", "1", + "--required-claim-name", "can_access", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + RequiredClaimName: "can_access", + }, + }, + }, + // case 15 + { + args: []string{ + "oauth-test", + "--id", "1", + "--required-claim-value", "yes", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + RequiredClaimValue: "yes", + }, + }, + }, + // case 16 + { + args: []string{ + "oauth-test", + "--id", "1", + "--group-claim-name", "groups", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupClaimName: "groups", + }, + }, + }, + // case 17 + { + args: []string{ + "oauth-test", + "--id", "1", + "--admin-group", "admin", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + AdminGroup: "admin", + }, + }, + }, + // case 18 + { + args: []string{ + "oauth-test", + "--id", "1", + "--restricted-group", "restricted", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + RestrictedGroup: "restricted", + }, + }, + }, + // case 19 + { + args: []string{ + "oauth-test", + "--id", "1", + "--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupTeamMap: `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, + }, + }, + }, + // case 20 + { + args: []string{ + "oauth-test", + "--id", "1", + "--group-team-map-removal", + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupTeamMapRemoval: true, + }, + }, + }, + // case 21 + { + args: []string{ + "oauth-test", + "--id", "23", + "--group-team-map-removal=false", + }, + id: 23, + existingAuthSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + GroupTeamMapRemoval: true, + }, + }, + authSource: &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{ + CustomURLMapping: &oauth2.CustomURLMapping{}, + GroupTeamMapRemoval: false, + }, + }, + }, + // case 22 + { + args: []string{ + "oauth-test", + }, + errMsg: "--id flag is missing", + }, + } + + for n, c := range cases { + // Mock functions. + var updatedAuthSource *auth.Source + service := &authService{ + initDB: func(context.Context) error { + return nil + }, + createAuthSource: func(ctx context.Context, authSource *auth.Source) error { + assert.FailNow(t, "should not call createAuthSource", "case: %d", n) + return nil + }, + updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { + updatedAuthSource = authSource + return nil + }, + getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { + if c.id != 0 { + assert.Equal(t, c.id, id, "case %d: wrong id", n) + } + if c.existingAuthSource != nil { + return c.existingAuthSource, nil + } + return &auth.Source{ + Type: auth.OAuth2, + Cfg: &oauth2.Source{}, + }, nil + }, + } + + // Create a copy of command to test + app := cli.Command{} + app.Flags = microcmdAuthUpdateOauth().Flags + app.Action = service.updateOauth + + // Run it + err := app.Run(t.Context(), c.args) + if c.errMsg != "" { + assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) + } else { + require.NoError(t, err, "case %d: should have no errors", n) + assert.Equal(t, c.authSource, updatedAuthSource, "case %d: wrong authSource", n) + } + } +} From f324ee73c56da387a11b0b59ff455c321fefa3d4 Mon Sep 17 00:00:00 2001 From: Gnarwhal Date: Wed, 9 Jul 2025 22:58:49 +0200 Subject: [PATCH 087/297] fix: file action button spacing when wrapped (#8415) The download, edit, and trash icon buttons had an extraneous `` that was inconsistent with the copy and RSS button which caused spacing issues when wrapping. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8415 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Gnarwhal Co-committed-by: Gnarwhal --- templates/repo/view_file.tmpl | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 2a9d7d02ba..36809b769e 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -59,8 +59,12 @@ {{end}}
- {{svg "octicon-download"}} - {{svg "octicon-copy" 14}} + + {{svg "octicon-download"}} + + + {{svg "octicon-copy" 14}} + {{if .EnableFeed}} {{if .IsViewBranch}} @@ -74,14 +78,22 @@ {{end}} {{if .Repository.CanEnableEditor}} {{if .CanEditFile}} - {{svg "octicon-pencil"}} + + {{svg "octicon-pencil"}} + {{else}} - {{svg "octicon-pencil"}} + + {{svg "octicon-pencil"}} + {{end}} {{if .CanDeleteFile}} - {{svg "octicon-trash"}} + + {{svg "octicon-trash"}} + {{else}} - {{svg "octicon-trash"}} + + {{svg "octicon-trash"}} + {{end}} {{end}} {{else if .EscapeStatus.Escaped}} From 24d6972f6b31521f96a626dd31746506d3145fa2 Mon Sep 17 00:00:00 2001 From: Nis Wechselberg Date: Wed, 9 Jul 2025 23:01:03 +0200 Subject: [PATCH 088/297] fix: ASCII equal fold for authorization header (#8391) For the "Authorization:" header only lowercase "token" was accepted. This change allows uppercase "Token" as well. Signed-off-by: Nis Wechselberg Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8391 Reviewed-by: Gusted Reviewed-by: Michael Kriese Co-authored-by: Nis Wechselberg Co-committed-by: Nis Wechselberg --- modules/util/string.go | 22 ++++++++++++++++++++++ modules/util/string_test.go | 26 ++++++++++++++++++++++++++ services/auth/oauth2.go | 3 ++- services/auth/oauth2_test.go | 28 ++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/modules/util/string.go b/modules/util/string.go index cf50f591c6..ca3d43ec6e 100644 --- a/modules/util/string.go +++ b/modules/util/string.go @@ -95,3 +95,25 @@ func UnsafeBytesToString(b []byte) string { func UnsafeStringToBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } + +// AsciiEqualFold is taken from Golang, but reimplemented here, since the original is not exposed to public +// Taken from: https://cs.opensource.google/go/go/+/refs/tags/go1.24.4:src/net/http/internal/ascii/print.go +func ASCIIEqualFold(s, t string) bool { + if len(s) != len(t) { + return false + } + for i := 0; i < len(s); i++ { + if ASCIILower(s[i]) != ASCIILower(t[i]) { + return false + } + } + return true +} + +// AsciiLower returns the ASCII lowercase version of b. +func ASCIILower(b byte) byte { + if 'A' <= b && b <= 'Z' { + return b + ('a' - 'A') + } + return b +} diff --git a/modules/util/string_test.go b/modules/util/string_test.go index 0a4a8bbcfb..1012ab32a4 100644 --- a/modules/util/string_test.go +++ b/modules/util/string_test.go @@ -45,3 +45,29 @@ func TestToSnakeCase(t *testing.T) { assert.Equal(t, expected, ToSnakeCase(input)) } } + +func TestASCIIEqualFold(t *testing.T) { + cases := map[string]struct { + First string + Second string + Expected bool + }{ + "Empty String": {First: "", Second: "", Expected: true}, + "Single Letter Ident": {First: "h", Second: "h", Expected: true}, + "Single Letter Equal": {First: "h", Second: "H", Expected: true}, + "Single Letter Unequal": {First: "h", Second: "g", Expected: false}, + "Simple Match Ident": {First: "someString", Second: "someString", Expected: true}, + "Simple Match Equal": {First: "someString", Second: "someSTRIng", Expected: true}, + "Simple Match Unequal": {First: "someString", Second: "sameString", Expected: false}, + "Different Length": {First: "abcdef", Second: "abcdefg", Expected: false}, + "Unicode Kelvin": {First: "ghijklm", Second: "GHIJ\u212ALM", Expected: false}, + } + + for name := range cases { + c := cases[name] + t.Run(name, func(t *testing.T) { + Actual := ASCIIEqualFold(c.First, c.Second) + assert.Equal(t, c.Expected, Actual) + }) + } +} diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 093940aa18..4fdd15d7ec 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -17,6 +17,7 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "forgejo.org/modules/web/middleware" "forgejo.org/services/actions" "forgejo.org/services/auth/source/oauth2" @@ -125,7 +126,7 @@ func parseToken(req *http.Request) (string, bool) { // check header token if auHead := req.Header.Get("Authorization"); auHead != "" { auths := strings.Fields(auHead) - if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { + if len(auths) == 2 && (util.ASCIIEqualFold(auths[0], "token") || util.ASCIIEqualFold(auths[0], "bearer")) { return auths[1], true } } diff --git a/services/auth/oauth2_test.go b/services/auth/oauth2_test.go index d6455b33ad..3fce7df50b 100644 --- a/services/auth/oauth2_test.go +++ b/services/auth/oauth2_test.go @@ -4,6 +4,7 @@ package auth import ( + "net/http" "testing" "forgejo.org/models/unittest" @@ -52,3 +53,30 @@ func TestCheckTaskIsRunning(t *testing.T) { }) } } + +func TestParseToken(t *testing.T) { + cases := map[string]struct { + Header string + ExpectedToken string + Expected bool + }{ + "Token Uppercase": {Header: "Token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Token Lowercase": {Header: "token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Token Unicode": {Header: "to\u212Aen 1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false}, + "Bearer Uppercase": {Header: "Bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Bearer Lowercase": {Header: "bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Missing type": {Header: "1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false}, + "Three Parts": {Header: "abc 1234567890 test", ExpectedToken: "", Expected: false}, + } + + for name := range cases { + c := cases[name] + t.Run(name, func(t *testing.T) { + req, _ := http.NewRequest("GET", "/", nil) + req.Header.Add("Authorization", c.Header) + ActualToken, ActualSuccess := parseToken(req) + assert.Equal(t, c.ExpectedToken, ActualToken) + assert.Equal(t, c.Expected, ActualSuccess) + }) + } +} From 13b560c191ae885900d9f8213b4ae97418734bc8 Mon Sep 17 00:00:00 2001 From: fruzitent Date: Wed, 9 Jul 2025 23:15:26 +0200 Subject: [PATCH 089/297] feat: add _URI entries for mail config (#8116) For the mailer and incoming_mailer config, allow passwords to be read from a file. Add `_URI` config values and use the existing `loadSecret` function to do this. Resolves https://codeberg.org/forgejo/forgejo/issues/8113 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8116 Reviewed-by: Gusted Reviewed-by: oliverpool Co-authored-by: fruzitent Co-committed-by: fruzitent --- custom/conf/app.example.ini | 6 ++++++ modules/setting/incoming_email.go | 7 ++++++- modules/setting/incoming_email_test.go | 18 ++++++++++++++++++ modules/setting/mailer.go | 4 ++++ modules/setting/mailer_test.go | 20 ++++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 37d67df5f0..71598663b2 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1768,6 +1768,9 @@ LEVEL = Info ;; Use PASSWD = `your password` for quoting if you use special characters in the password. ;PASSWD = ;; +;; Alternative location to specify mailer password. You cannot specify both this and PASSWD, and must pick one +;PASSWD_URI = file:/etc/forgejo/mailer_passwd +;; ;; Send mails only in plain text, without HTML alternative ;SEND_AS_PLAIN_TEXT = false ;; @@ -1820,6 +1823,9 @@ LEVEL = Info ;; Password of the receiving account ;PASSWORD = ;; +;; Alternative location to specify password of the receiving account. You cannot specify both this and PASSWORD, and must pick one +;PASSWORD_URI = file:/etc/forgejo/email_incoming_password +;; ;; Whether the IMAP server uses TLS. ;USE_TLS = false ;; diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index e592220de6..a890a4a328 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -44,9 +44,14 @@ func loadIncomingEmailFrom(rootCfg ConfigProvider) { if sec.HasKey("USER") && !sec.HasKey("USERNAME") { IncomingEmail.Username = sec.Key("USER").String() } + if sec.HasKey("PASSWD") && !sec.HasKey("PASSWORD") { - IncomingEmail.Password = sec.Key("PASSWD").String() + sec.Key("PASSWORD").SetValue(sec.Key("PASSWD").String()) } + if sec.HasKey("PASSWD_URI") && !sec.HasKey("PASSWORD_URI") { + sec.Key("PASSWORD_URI").SetValue(sec.Key("PASSWD_URI").String()) + } + IncomingEmail.Password = loadSecret(sec, "PASSWORD_URI", "PASSWORD") // Infer Port if not set if IncomingEmail.Port == 0 { diff --git a/modules/setting/incoming_email_test.go b/modules/setting/incoming_email_test.go index 6d181cae3c..4ea740bafd 100644 --- a/modules/setting/incoming_email_test.go +++ b/modules/setting/incoming_email_test.go @@ -4,6 +4,8 @@ package setting import ( + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -35,6 +37,22 @@ func Test_loadIncomingEmailFrom(t *testing.T) { assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) }) + t.Run("Secrets", func(t *testing.T) { + uri := filepath.Join(t.TempDir(), "email_incoming_password") + + if err := os.WriteFile(uri, []byte("th1S gUess n3v3r y0u'll!!1"), 0o644); err != nil { + t.Fatal(err) + } + + cfg, sec := makeBaseConfig() + sec.NewKey("PASSWORD_URI", "file:"+uri) + + IncomingEmail.Password = "" + loadIncomingEmailFrom(cfg) + + assert.Equal(t, "th1S gUess n3v3r y0u'll!!1", IncomingEmail.Password) + }) + t.Run("Port settings", func(t *testing.T) { t.Run("no port, no tls", func(t *testing.T) { defer resetIncomingEmailPort()() diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index 9c004c6ce0..b43484a90f 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -147,6 +147,10 @@ func loadMailerFrom(rootCfg ConfigProvider) { if sec.HasKey("PASSWORD") && !sec.HasKey("PASSWD") { sec.Key("PASSWD").SetValue(sec.Key("PASSWORD").String()) } + if sec.HasKey("PASSWORD_URI") && !sec.HasKey("PASSWD_URI") { + sec.Key("PASSWD_URI").SetValue(sec.Key("PASSWORD_URI").String()) + } + sec.Key("PASSWD").SetValue(loadSecret(sec, "PASSWD_URI", "PASSWD")) // Set default values & validate sec.Key("NAME").MustString(AppName) diff --git a/modules/setting/mailer_test.go b/modules/setting/mailer_test.go index 4523cc91dd..47eaf3ffbb 100644 --- a/modules/setting/mailer_test.go +++ b/modules/setting/mailer_test.go @@ -4,6 +4,8 @@ package setting import ( + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -52,6 +54,24 @@ func Test_loadMailerFrom(t *testing.T) { assert.Equal(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) }) + t.Run("Secrets", func(t *testing.T) { + uri := filepath.Join(t.TempDir(), "mailer_passwd") + + if err := os.WriteFile(uri, []byte("th1S gUess n3v3r y0u'll!!1"), 0o644); err != nil { + t.Fatal(err) + } + + cfg, _ := NewConfigProviderFromData("") + sec := cfg.Section("mailer") + sec.NewKey("ENABLED", "true") + sec.NewKey("PASSWD_URI", "file:"+uri) + + MailService.Passwd = "" + loadMailerFrom(cfg) + + assert.Equal(t, "th1S gUess n3v3r y0u'll!!1", MailService.Passwd) + }) + t.Run("sendmail argument sanitization", func(t *testing.T) { cfg, _ := NewConfigProviderFromData("") sec := cfg.Section("mailer") From ba42ebf1ae95b0dae23a870f82635e281a9027e9 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 10 Jul 2025 00:47:39 +0200 Subject: [PATCH 090/297] chore: do not navigate to same URL in E2E test (#8461) - In `test.beforeEach` the browser is navigated to `/user2/repo1/pulls/5` and the test environment is prepared by making sure the pull request is not WIP. At the start of `simple toggle` the browser is navigated to the same URL (and the browser is already on this URL) which the Mobile Chrome browser does not handle gracefully and reliably errors with `net::ERR_ABORTED`. - Because we are already at that URL, do not try to navigate to it again. - I cannot offer a rationale why the Mobile Chrome browser does give a error when this happens in subsequent tests of 'Pull: Toggle WIP'. - Ref forgejo/forgejo#8359 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8461 Reviewed-by: Otto Co-authored-by: Gusted Co-committed-by: Gusted --- tests/e2e/issue-sidebar.test.e2e.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/issue-sidebar.test.e2e.ts b/tests/e2e/issue-sidebar.test.e2e.ts index 34885d0d5d..9d2975b612 100644 --- a/tests/e2e/issue-sidebar.test.e2e.ts +++ b/tests/e2e/issue-sidebar.test.e2e.ts @@ -50,7 +50,6 @@ test.describe('Pull: Toggle WIP', () => { test('simple toggle', async ({page}, workerInfo) => { test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636'); - await page.goto('/user2/repo1/pulls/5'); // toggle to WIP await toggle_wip_to({page}, true); await check_wip({page}, true); From 3c7e3ec9e2dd2486fc855893bf15b8d80219645b Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 10 Jul 2025 01:56:29 +0200 Subject: [PATCH 091/297] chore: disable mismatched root URL e2e test for safari (#8460) - I cannot give a good rationale why Mobile Safari and webkit is failing this test quite consistently and also succeed quite often. We add a script to ensure that we get a mismatched URL - but these two browsers either ignore this script sometime or delays the execution until after the root URL check is done. - Because it is very hard to run webkit and mobile safari without running a heavily containerized environment (without a graphical interface) it is near impossible to debug this issue properly; save ourselves a headache and disable it instead. I find it more likely to be a problem with my playwright test than it to be a problem with the mismatched root URL code. - Ref forgejo/forgejo#8359 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8460 Reviewed-by: Otto Reviewed-by: Beowulf Co-authored-by: Gusted Co-committed-by: Gusted --- tests/e2e/login.test.e2e.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/login.test.e2e.ts b/tests/e2e/login.test.e2e.ts index 1ffa0b2e5d..01cf4d7b8d 100644 --- a/tests/e2e/login.test.e2e.ts +++ b/tests/e2e/login.test.e2e.ts @@ -10,7 +10,8 @@ import {expect} from '@playwright/test'; import {test, save_visual, test_context} from './utils_e2e.ts'; -test('Mismatched ROOT_URL', async ({browser}) => { +test('Mismatched ROOT_URL', async ({browser}, workerInfo) => { + test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'init script gets randomly ignored'); const context = await test_context(browser); const page = await context.newPage(); From ddc9240a1481e3e6565ebb8e73911aac4ac60ce1 Mon Sep 17 00:00:00 2001 From: forgejo-release-manager Date: Thu, 10 Jul 2025 10:50:11 +0200 Subject: [PATCH 092/297] chore(release-notes): Forgejo v7.0.16 (#8473) https://codeberg.org/forgejo/forgejo/milestone/17405 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8473 Reviewed-by: Earl Warren Co-authored-by: forgejo-release-manager Co-committed-by: forgejo-release-manager --- release-notes-published/7.0.16.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 release-notes-published/7.0.16.md diff --git a/release-notes-published/7.0.16.md b/release-notes-published/7.0.16.md new file mode 100644 index 0000000000..5d09ddc245 --- /dev/null +++ b/release-notes-published/7.0.16.md @@ -0,0 +1,11 @@ +## Git update fixing CVE-2025-48385 + +Git vulnerabilities were [disclosed 8 July 2025](https://groups.google.com/g/git-packagers/c/cYJ6peBtyxk/m/xVukiATcBQAJ) and require an update of the Git version used by Forgejo to Git [v2.43.7, v2.44.4, v2.45.4, v2.46.4, v2.47.3, v2.48.2, v2.49.1, or v2.50.1](https://nvd.nist.gov/vuln/detail/CVE-2025-48385). The [containers of this release](https://codeberg.org/forgejo/-/packages/container/forgejo/7.0.16) include a Git binary that is not vulnerable. If Forgejo was installed using a container, it is enough to upgrade the container to get the latest Git binary. + +Security bug fixes are only for Git, there are no security fixes for Forgejo itself in this release. + + + +## Release notes + + From 86a13589fa5218b676c3e5c3ec159d4c84266c9b Mon Sep 17 00:00:00 2001 From: forgejo-release-manager Date: Thu, 10 Jul 2025 10:50:24 +0200 Subject: [PATCH 093/297] chore(release-notes): Forgejo v11.0.3 (#8472) https://codeberg.org/forgejo/forgejo/milestone/20958 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8472 Reviewed-by: Earl Warren Co-authored-by: forgejo-release-manager Co-committed-by: forgejo-release-manager --- release-notes-published/11.0.3.md | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 release-notes-published/11.0.3.md diff --git a/release-notes-published/11.0.3.md b/release-notes-published/11.0.3.md new file mode 100644 index 0000000000..ed6f150af9 --- /dev/null +++ b/release-notes-published/11.0.3.md @@ -0,0 +1,34 @@ +## Git update fixing CVE-2025-48385 + +Git vulnerabilities were [disclosed 8 July 2025](https://groups.google.com/g/git-packagers/c/cYJ6peBtyxk/m/xVukiATcBQAJ) and require an update of the Git version used by Forgejo to Git [v2.43.7, v2.44.4, v2.45.4, v2.46.4, v2.47.3, v2.48.2, v2.49.1, or v2.50.1](https://nvd.nist.gov/vuln/detail/CVE-2025-48385). The [containers of this release](https://codeberg.org/forgejo/-/packages/container/forgejo/11.0.3) include a Git binary that is not vulnerable. If Forgejo was installed using a container, it is enough to upgrade the container to get the latest Git binary. + +Security bug fixes are only for Git, there are no security fixes for Forgejo itself in this release. + +## Wiki permissions manual steps + +If collaborators with write access can't edit the wiki, an administrator can now go to the Units settings (`//settings/units#wiki`) and Save the wiki settings (no change is needed) to fix the problem. This is a manual step that will trigger a database update that is currently not possible to automate for Forgejo stable releases. + + + +## Release notes + +- User Interface bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8246) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8283)): fix(ui): add missing lazy load attribute to images (#8246) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8170) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8262)): fix(ui): erroneous list continuation on Cmd+Enter on macOS +- Localization + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8300): i18n: backport of translation updates +- Bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8189) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8456)): fix: do not ignore automerge while a PR is checking for conflicts + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8367) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8385)): fix: user activation with uppercase email address + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8234) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8237)): fix: collaborator can edit wiki with write access +- Included for completeness but not worth a release note + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8460) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8465)): chore: disable mismatched root URL e2e test for safari + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8461) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8462)): chore: do not navigate to same URL in E2E test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8258) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8445)): fix: corrupted wiki unit default permission (#8234 follow-up) (#8258) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8261) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8412)): fix: skip empty tokens in SearchOptions.Tokens() + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8400) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8401)): chore: improve reliability of webauthn e2e test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8326) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8333)): fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8226) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8292)): chore: sort mailer messages in test assertion + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8002) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8289)): fix(ui): release: name is overridden with tag name on edit + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8286) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8287)): Revert "fix(api): document `is_system_webhook` field (#7784)" + From 3f1ed6dde4584f046046600dec635493247b109b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 10 Jul 2025 10:58:57 +0200 Subject: [PATCH 094/297] Update dependency webpack to v5.100.0 (forgejo) (#8467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [webpack](https://github.com/webpack/webpack) | [`5.99.9` -> `5.100.0`](https://renovatebot.com/diffs/npm/webpack/5.99.9/5.100.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/webpack/5.100.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/webpack/5.99.9/5.100.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
webpack/webpack (webpack) ### [`v5.100.0`](https://github.com/webpack/webpack/releases/tag/v5.100.0) [Compare Source](https://github.com/webpack/webpack/compare/v5.99.9...v5.100.0) ##### Fixes - Fixed the case where an ES modules entry chunk depends on the runtime chunk hash - Handle function exports in webpack module wrapper - Ensure dependent chunks are imported before startup & fix duplicate export of 'default' - Generate lose closing brace when exports are unprovided - CleanPlugin doesn't unlink same file twice - Fixed unexpected error codes from fs.unlink on Windows - Typescript types ##### Features - HMR support for ES modules output - ES module output mode now fully supports `splitChunks` when external variables and runtimeChunk are not set. - Added support `using` keyword - Implemented tc39 [Defer Module Evaluation](https://github.com/tc39/proposal-defer-import-eval) (experiment) - Support dynamic template literals expressions for `new URL(...)` - Enable ES modules worker chunk loading for Node.js targets - Improved support for destructing in DefinePlugin - Added [VirtualUrlPlugin](https://github.com/webpack/webpack/tree/main/examples/virtual-modules) to support `virtual:` scheme ##### Performance Improvements - Remove useless startup entrypoint runtime for ES modules output - Cache `new URL(...)` evaluate expression
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8467 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 29 +++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0d24f002d..5ac6838b58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.99.9", + "webpack": "5.100.0", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.0" }, @@ -4671,6 +4671,18 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.3.tgz", + "integrity": "sha512-jtKLnfoOzm28PazuQ4dVBcE9Jeo6ha1GAJvq3N0LlNOszmTfx+wSycBehn+FN0RnyeR77IBxN/qVYMw0Rlj0Xw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -15597,21 +15609,22 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.99.9", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", - "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", + "version": "5.100.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.0.tgz", + "integrity": "sha512-H8yBSBTk+BqxrINJnnRzaxU94SVP2bjd7WmA+PfCphoIdDpeQMJ77pq9/4I7xjLq38cB1bNKfzYPZu8pB3zKtg==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", + "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -15625,7 +15638,7 @@ "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" diff --git a/package.json b/package.json index 229ae56354..27576923bf 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.99.9", + "webpack": "5.100.0", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.0" }, From 7cd313951aa4f12d652305be708ed6a4a8d98d24 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 10 Jul 2025 14:07:18 +0200 Subject: [PATCH 095/297] fix: use parent context for new transactions (#8464) - Older code (154 places to be exact) that want to do transactions uses `TxContext`. If the context is not already in a transaction then it uses `DefaultContext` for the new transaction. - Not reusing the context it was given leads to two problems: for tracing (forgejo/forgejo#6470) any SQL code that is executed inside this transaction is not shown in the trace. If the context is cancelled then the transaction is not cancelled and will always complete (given there's no SQL error). - When this function was introduced it didn't take a parent context, hence the usage of `DefaultContext`. When `parentCtx` was introduced it was only used to avoid nested transactions and use the parent's transaction. I do not see any reasons why the parent context cannot be reused, it is reused in the newer transaction function `WithTx` without any known problems. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8464 Reviewed-by: Otto Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- models/db/context.go | 2 +- models/db/context_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/models/db/context.go b/models/db/context.go index 35526936af..3e035cd733 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -141,7 +141,7 @@ func TxContext(parentCtx context.Context) (*Context, Committer, error) { return nil, nil, err } - return newContext(DefaultContext, sess, true), sess, nil + return newContext(parentCtx, sess, true), sess, nil } // WithTx represents executing database operations on a transaction, if the transaction exist, diff --git a/models/db/context_test.go b/models/db/context_test.go index 7ab327b7e9..d12d79ebe1 100644 --- a/models/db/context_test.go +++ b/models/db/context_test.go @@ -84,4 +84,16 @@ func TestTxContext(t *testing.T) { return nil })) } + + t.Run("Reuses parent context", func(t *testing.T) { + type unique struct{} + + ctx := context.WithValue(db.DefaultContext, unique{}, "yes!") + assert.False(t, db.InTransaction(ctx)) + + require.NoError(t, db.WithTx(ctx, func(ctx context.Context) error { + assert.Equal(t, "yes!", ctx.Value(unique{})) + return nil + })) + }) } From 7c06db89e30ada623e51b32ecd4d914657ce0847 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 10 Jul 2025 14:14:30 +0200 Subject: [PATCH 096/297] Update module golang.org/x/sync to v0.16.0 (forgejo) (#8469) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8469 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d09ec592aa..f762211788 100644 --- a/go.mod +++ b/go.mod @@ -103,7 +103,7 @@ require ( golang.org/x/image v0.27.0 golang.org/x/net v0.41.0 golang.org/x/oauth2 v0.30.0 - golang.org/x/sync v0.15.0 + golang.org/x/sync v0.16.0 golang.org/x/sys v0.33.0 golang.org/x/text v0.26.0 google.golang.org/protobuf v1.36.4 diff --git a/go.sum b/go.sum index cddd87d4ce..c261c5f1c4 100644 --- a/go.sum +++ b/go.sum @@ -627,8 +627,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From b332d1c2e4c4bff89f70986e4d11854cc44a0500 Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Thu, 10 Jul 2025 17:12:07 +0200 Subject: [PATCH 097/297] several fixes of ALT Package registry (#8475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #7946 - The `rpmsRepoPattern` regex has been fixed to handle releases with dots correctly. For example, the version `0.9.0-alt1.git.17.g2ba905d` is valid, just like `0.1.0-1.n1` mentioned in the issue (https://codeberg.org/forgejo/forgejo/issues/7946#issue-1628991) - getEntries now returns entry names. In the integration tests, there were lines like: ```go assert.Equal(t, []string{"", ""}, result.ProvideNames) ``` and it’s unclear how such test logic could have ever worked correctly (fixes problems with deps https://codeberg.org/forgejo/forgejo/issues/7946#issuecomment-5109795) - ALT is an acronym for ALT Linux Team, so `Alt` was replaced with `ALT`. Strictly speaking, it should probably be `ALT Linux`, but since we use `Arch` instead of `Arch Linux`, this seems fine. Also, Distrowatch shows `Arch`/`ALT` in its dropdown, so it’s consistent. - The strings `"Alt Linux Team"` and `"Sisyphus"` in the `Origin` and `Suite` fields have been replaced with `setting.AppName` and `"Unknown"`. `Unknown` is a valid value and is set by default, so this won’t cause any issues. - The documentation link has been fixed: (404 docs.gitea.com/usage/packages/alt/ -> 200 forgejo.org/docs/latest/user/packages/alt/) --- ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8475 Reviewed-by: Gusted Co-authored-by: Maxim Slipenko Co-committed-by: Maxim Slipenko --- models/packages/package.go | 2 +- modules/packages/rpm/metadata.go | 3 ++- routers/api/packages/api.go | 2 +- services/packages/alt/repository.go | 12 +++++++----- templates/package/content/alt.tmpl | 2 +- tests/integration/api_packages_alt_test.go | 16 ++++++++-------- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/models/packages/package.go b/models/packages/package.go index bdd1c74cad..c06dcf5eb3 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -125,7 +125,7 @@ func (pt Type) Name() string { case TypeRpm: return "RPM" case TypeAlt: - return "Alt" + return "ALT" case TypeRubyGems: return "RubyGems" case TypeSwift: diff --git a/modules/packages/rpm/metadata.go b/modules/packages/rpm/metadata.go index 4af9af620f..503b7b1a24 100644 --- a/modules/packages/rpm/metadata.go +++ b/modules/packages/rpm/metadata.go @@ -232,9 +232,10 @@ func getEntries(h *rpmutils.RpmHeader, namesTag, versionsTag, flagsTag int, repo case "alt": for i := range names { e := &Entry{ + Name: names[i], AltFlags: uint32(flags[i]), + Version: versions[i], } - e.Version = versions[i] entries = append(entries, e) } } diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 79e61cf352..c53edfbf96 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -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(`(\S+)\.repo/(\S+)\.(\S+)\/([a-zA-Z0-9_-]+)-([\d.]+-[a-zA-Z0-9_.-]+)\.(\S+)\.rpm`) ) r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) { diff --git a/services/packages/alt/repository.go b/services/packages/alt/repository.go index 317862da9d..9693f4322e 100644 --- a/services/packages/alt/repository.go +++ b/services/packages/alt/repository.go @@ -714,21 +714,23 @@ func buildRelease(ctx context.Context, pv *packages_model.PackageVersion, pfs [] for architecture := range architectures.Seq() { version := time.Now().Unix() label := setting.AppName - data := fmt.Sprintf(`Archive: Alt Linux Team + origin := setting.AppName + archive := setting.AppName + + data := fmt.Sprintf(`Archive: %s Component: classic Version: %d -Origin: Alt Linux Team +Origin: %s Label: %s Architecture: %s NotAutomatic: false `, - version, label, architecture) + archive, version, origin, label, architecture) fileInfo, err := addReleaseAsFileToRepo(ctx, pv, "release.classic", data, group, architecture) if err != nil { return err } - origin := setting.AppName codename := time.Now().Unix() date := time.Now().UTC().Format(time.RFC1123) @@ -744,7 +746,7 @@ NotAutomatic: false data = fmt.Sprintf(`Origin: %s Label: %s -Suite: Sisyphus +Suite: Unknown Codename: %d Date: %s Architectures: %s diff --git a/templates/package/content/alt.tmpl b/templates/package/content/alt.tmpl index 9a5e9c7656..0a5c328e6d 100644 --- a/templates/package/content/alt.tmpl +++ b/templates/package/content/alt.tmpl @@ -24,7 +24,7 @@ apt-get install {{$.PackageDescriptor.Package.Name}}
- +
diff --git a/tests/integration/api_packages_alt_test.go b/tests/integration/api_packages_alt_test.go index c7ee2c49a5..f43759364e 100644 --- a/tests/integration/api_packages_alt_test.go +++ b/tests/integration/api_packages_alt_test.go @@ -214,7 +214,7 @@ enabled=1`, } assert.Equal(t, "classic", result.Component) - assert.Equal(t, "Alt Linux Team", result.Origin) + assert.Equal(t, "Forgejo", result.Origin) assert.Equal(t, "Forgejo", result.Label) assert.Equal(t, "x86_64", result.Architecture) assert.False(t, result.NotAutomatic) @@ -299,17 +299,17 @@ enabled=1`, assert.Equal(t, "Forgejo", result.Origin) assert.Equal(t, "Forgejo", result.Label) - assert.Equal(t, "Sisyphus", result.Suite) + assert.Equal(t, "Unknown", result.Suite) assert.Equal(t, "x86_64", result.Architectures) assert.Len(t, result.MD5Sum, 3) - assert.Equal(t, "bbf7ae6b2f540673ed1cfc0266b5f319", result.MD5Sum[0].Hash) - assert.Equal(t, 1003, result.MD5Sum[0].Size) + assert.Equal(t, "3f25f44163e8e512efb248d3b96949c3", result.MD5Sum[0].Hash) + assert.Equal(t, 1147, result.MD5Sum[0].Size) assert.Equal(t, "base/pkglist.classic", result.MD5Sum[0].File) assert.Len(t, result.BLAKE2B, 3) - assert.Equal(t, "b527bf038895ce29107ec3a6d2eebd7c365e8ce5ab767276eeddd7c549a159025225cb0ecfdbf7b71da13db7e865e77bcb0e2dae4d21335df01a4a17e0056a70", result.BLAKE2B[0].Hash) - assert.Equal(t, 1003, result.BLAKE2B[0].Size) + assert.Equal(t, "21a63e12a41f70e0697d354ae31e22ad6f024ec5ead2ea498b9a1b7db0f98a4e441f46c96d6912fba19148ff013457561fbb9bf3fca2a21d04cf15a325be7de9", result.BLAKE2B[0].Hash) + assert.Equal(t, 1147, result.BLAKE2B[0].Size) assert.Equal(t, "base/pkglist.classic", result.BLAKE2B[0].File) }) @@ -567,9 +567,9 @@ enabled=1`, assert.Equal(t, "https://gitea.io", result.URL) assert.Equal(t, "x86_64", result.Arch) assert.Equal(t, "gitea-test-1.0.2-1.src.rpm", result.SourceRpm) - assert.Equal(t, []string{"", ""}, result.ProvideNames) + assert.Equal(t, []string{"gitea-test(x86-64)", "gitea-test(x86-64)"}, result.ProvideNames) assert.Equal(t, []int{16777226, 16777226, 16777226, 16777226, 16777226, 16777226, 16777226}, result.RequireFlags) - assert.Equal(t, []string{"", "", "", "", "", "", ""}, result.RequireNames) + assert.Equal(t, []string{"rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)"}, result.RequireNames) assert.Equal(t, []string{"5.2-1", "5.2-1", "5.2-1", "5.2-1", "5.2-1", "5.2-1", "5.2-1"}, result.RequireVersions) assert.Equal(t, []int{1678276800}, result.ChangeLogTimes) assert.Equal(t, []string{"KN4CK3R "}, result.ChangeLogNames) From 2c01097315e7a6125e56f5085b63a0861a64fb8d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 10 Jul 2025 17:12:27 +0200 Subject: [PATCH 098/297] Update module golang.org/x/sys to v0.34.0 (forgejo) (#8470) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8470 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f762211788..ea05356154 100644 --- a/go.mod +++ b/go.mod @@ -104,7 +104,7 @@ require ( golang.org/x/net v0.41.0 golang.org/x/oauth2 v0.30.0 golang.org/x/sync v0.16.0 - golang.org/x/sys v0.33.0 + golang.org/x/sys v0.34.0 golang.org/x/text v0.26.0 google.golang.org/protobuf v1.36.4 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df diff --git a/go.sum b/go.sum index c261c5f1c4..52cdff2454 100644 --- a/go.sum +++ b/go.sum @@ -654,8 +654,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 049899b56b02339ab43062d75a0803a3dfdb40f8 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 10 Jul 2025 17:51:19 +0200 Subject: [PATCH 099/297] feat: AGit push options starting with `{base64}` are decoded (#8479) - When specifying push options, tooling like [git-repo-go](https://github.com/alibaba/git-repo-go) encodes pushoption values if they contain newlines or non-ASCII characters as they cannot be easily specified via the CLI otherwise. - Recognize such base64 encoded values in the pushoptions (those that have the `{base64}` prefix), if the base64 decoding fails we return the original value. - Resolves forgejo/forgejo#8161 ## Release notes - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/8479): AGit push options starting with `{base64}` are decoded Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8479 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/git/pushoptions/pushoptions.go | 20 +++++++++++++++++++- modules/git/pushoptions/pushoptions_test.go | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/modules/git/pushoptions/pushoptions.go b/modules/git/pushoptions/pushoptions.go index 9709a8be79..e96ba0a339 100644 --- a/modules/git/pushoptions/pushoptions.go +++ b/modules/git/pushoptions/pushoptions.go @@ -4,6 +4,7 @@ package pushoptions import ( + "encoding/base64" "fmt" "os" "strconv" @@ -109,5 +110,22 @@ func (o gitPushOptions) GetBool(key Key, def bool) bool { func (o gitPushOptions) GetString(key Key) (string, bool) { val, ok := o[string(key)] - return val, ok + if !ok { + return "", false + } + + // If the value is prefixed with `{base64}` then everything after that is very + // likely to be encoded via base64. + base64Value, found := strings.CutPrefix(val, "{base64}") + if !found { + return val, true + } + + value, err := base64.StdEncoding.DecodeString(base64Value) + if err != nil { + // Not valid base64? Return the original value. + return val, true + } + + return string(value), true } diff --git a/modules/git/pushoptions/pushoptions_test.go b/modules/git/pushoptions/pushoptions_test.go index 1cb36d9d1e..d7c50649d0 100644 --- a/modules/git/pushoptions/pushoptions_test.go +++ b/modules/git/pushoptions/pushoptions_test.go @@ -4,6 +4,7 @@ package pushoptions import ( + "encoding/base64" "fmt" "testing" @@ -92,6 +93,23 @@ func TestParse(t *testing.T) { assert.False(t, options.Parse("unknown=value")) assert.True(t, options.Empty()) }) + + t.Run("Base64 values", func(t *testing.T) { + options := New() + + description := `I contain +a +line` + assert.True(t, options.Parse(fmt.Sprintf("%s={base64}%s", AgitDescription, base64.StdEncoding.EncodeToString([]byte(description))))) + val, ok := options.GetString(AgitDescription) + assert.True(t, ok) + assert.Equal(t, description, val) + + assert.True(t, options.Parse(fmt.Sprintf("%s={base64}fooled you", AgitTitle))) + val, ok = options.GetString(AgitTitle) + assert.True(t, ok) + assert.Equal(t, "{base64}fooled you", val) + }) } func TestReadEnv(t *testing.T) { From 51b42e3a84f167debf61f41ca2004f25cef49ef5 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 11 Jul 2025 02:49:55 +0200 Subject: [PATCH 100/297] Update dependency sharp to v0.34.3 (forgejo) (#8482) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8482 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 224 +++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 125 insertions(+), 101 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5ac6838b58..edb57b618a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,7 +91,7 @@ "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", - "sharp": "0.34.2", + "sharp": "0.34.3", "stylelint": "16.21.1", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", @@ -537,9 +537,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", "dev": true, "license": "MIT", "optional": true, @@ -1340,9 +1340,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "cpu": [ "arm64" ], @@ -1359,13 +1359,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "cpu": [ "x64" ], @@ -1382,13 +1382,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "cpu": [ "arm64" ], @@ -1403,9 +1403,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "cpu": [ "x64" ], @@ -1420,9 +1420,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "cpu": [ "arm" ], @@ -1437,9 +1437,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "cpu": [ "arm64" ], @@ -1454,9 +1454,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "cpu": [ "ppc64" ], @@ -1471,9 +1471,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "cpu": [ "s390x" ], @@ -1488,9 +1488,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "cpu": [ "x64" ], @@ -1505,9 +1505,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], @@ -1522,9 +1522,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], @@ -1539,9 +1539,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ "arm" ], @@ -1558,13 +1558,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], @@ -1581,13 +1581,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ "s390x" ], @@ -1604,13 +1627,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ "x64" ], @@ -1627,13 +1650,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ "arm64" ], @@ -1650,13 +1673,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], @@ -1673,13 +1696,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "cpu": [ "wasm32" ], @@ -1687,7 +1710,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.3" + "@emnapi/runtime": "^1.4.4" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1697,9 +1720,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "cpu": [ "arm64" ], @@ -1717,9 +1740,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "cpu": [ "ia32" ], @@ -1737,9 +1760,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "cpu": [ "x64" ], @@ -13221,9 +13244,9 @@ } }, "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -13239,27 +13262,28 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" } }, "node_modules/shebang-command": { diff --git a/package.json b/package.json index 27576923bf..c154270c6c 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", - "sharp": "0.34.2", + "sharp": "0.34.3", "stylelint": "16.21.1", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", From a8d8a4c106ff68dbaa902d24909a8a9d8b8ed6e7 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 11 Jul 2025 02:51:56 +0200 Subject: [PATCH 101/297] Update module golang.org/x/crypto to v0.40.0 (forgejo) (#8483) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8483 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index ea05356154..221b4453de 100644 --- a/go.mod +++ b/go.mod @@ -99,13 +99,13 @@ require ( github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc gitlab.com/gitlab-org/api/client-go v0.130.1 go.uber.org/mock v0.5.2 - golang.org/x/crypto v0.39.0 + golang.org/x/crypto v0.40.0 golang.org/x/image v0.27.0 golang.org/x/net v0.41.0 golang.org/x/oauth2 v0.30.0 golang.org/x/sync v0.16.0 golang.org/x/sys v0.34.0 - golang.org/x/text v0.26.0 + golang.org/x/text v0.27.0 google.golang.org/protobuf v1.36.4 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 diff --git a/go.sum b/go.sum index 52cdff2454..84c6564f95 100644 --- a/go.sum +++ b/go.sum @@ -585,8 +585,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= @@ -665,8 +665,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -677,8 +677,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 316073a92530b659a16548590de7c80950449d58 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 11 Jul 2025 05:07:33 +0200 Subject: [PATCH 102/297] fix: replace {workflowname} with {workflowfilename} in dispatch API (#8481) The current {workflowname} in the API endpoint /repos/{owner}/{repo}/actions/workflows/{workflowname}/dispatches is misleading because it does not refer to the name of the workflow but to the filename in which the workflow is defined. Change to /repos/{owner}/{repo}/actions/workflows/{workflowfilename}/dispatches --- There is no need for testing because it is a documentation change. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8481 Reviewed-by: Gusted Reviewed-by: Beowulf Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- routers/api/v1/api.go | 2 +- routers/api/v1/repo/action.go | 8 ++++---- templates/swagger/v1_json.tmpl | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index fe13f85df4..3b66d02fba 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1177,7 +1177,7 @@ func Routes() *web.Route { }) m.Group("/workflows", func() { - m.Group("/{workflowname}", func() { + m.Group("/{workflowfilename}", func() { m.Post("/dispatches", reqToken(), reqRepoWriter(unit.TypeActions), mustNotBeArchived, bind(api.DispatchWorkflowOption{}), repo.DispatchWorkflow) }) }) diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index dbc4933de6..fe29d534ff 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -615,7 +615,7 @@ func ListActionTasks(ctx *context.APIContext) { // DispatchWorkflow dispatches a workflow func DispatchWorkflow(ctx *context.APIContext) { - // swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflowname}/dispatches repository DispatchWorkflow + // swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflowfilename}/dispatches repository DispatchWorkflow // --- // summary: Dispatches a workflow // consumes: @@ -631,7 +631,7 @@ func DispatchWorkflow(ctx *context.APIContext) { // description: name of the repo // type: string // required: true - // - name: workflowname + // - name: workflowfilename // in: path // description: name of the workflow // type: string @@ -649,13 +649,13 @@ func DispatchWorkflow(ctx *context.APIContext) { // "$ref": "#/responses/notFound" opt := web.GetForm(ctx).(*api.DispatchWorkflowOption) - name := ctx.Params("workflowname") + name := ctx.Params("workflowfilename") if len(opt.Ref) == 0 { ctx.Error(http.StatusBadRequest, "ref", "ref is empty") return } else if len(name) == 0 { - ctx.Error(http.StatusBadRequest, "workflowname", "workflow name is empty") + ctx.Error(http.StatusBadRequest, "workflowfilename", "workflow file name is empty") return } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 0e8382b8ab..ee5e919757 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -5533,7 +5533,7 @@ } } }, - "/repos/{owner}/{repo}/actions/workflows/{workflowname}/dispatches": { + "/repos/{owner}/{repo}/actions/workflows/{workflowfilename}/dispatches": { "post": { "consumes": [ "application/json" @@ -5561,7 +5561,7 @@ { "type": "string", "description": "name of the workflow", - "name": "workflowname", + "name": "workflowfilename", "in": "path", "required": true }, From 74f7250259b1723eab63e88d7160b863b37f51c7 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 11 Jul 2025 07:53:56 +0200 Subject: [PATCH 103/297] Update module golang.org/x/net to v0.42.0 (forgejo) (#8484) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) | [`v0.41.0` -> `v0.42.0`](https://cs.opensource.google/go/x/net/+/refs/tags/v0.41.0...refs/tags/v0.42.0) | [![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fnet/v0.42.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fnet/v0.41.0/v0.42.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8484 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 221b4453de..139095099b 100644 --- a/go.mod +++ b/go.mod @@ -101,7 +101,7 @@ require ( go.uber.org/mock v0.5.2 golang.org/x/crypto v0.40.0 golang.org/x/image v0.27.0 - golang.org/x/net v0.41.0 + golang.org/x/net v0.42.0 golang.org/x/oauth2 v0.30.0 golang.org/x/sync v0.16.0 golang.org/x/sys v0.34.0 diff --git a/go.sum b/go.sum index 84c6564f95..3c906b45c6 100644 --- a/go.sum +++ b/go.sum @@ -614,8 +614,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From dc6626453a1645bc48affd4583b439aae8b4bffd Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Fri, 11 Jul 2025 05:58:02 +0000 Subject: [PATCH 104/297] i18n: update of translations from Codeberg Translate Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Atul_Eterno Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Dirk Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Juno Takano Co-authored-by: Laxystem Co-authored-by: Miguel P.L Co-authored-by: Salif Mehmed Co-authored-by: SomeTr Co-authored-by: aivot-on Co-authored-by: earl-warren Co-authored-by: kne Co-authored-by: oster5 Co-authored-by: readevalprintloop Co-authored-by: tacaly Co-authored-by: volkan Co-authored-by: xtex Co-authored-by: yurtpage Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/be/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/bn/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ca/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/da/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/es/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/hu/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/id/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ja/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ko/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/sr_SP/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/tr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/vi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/zh_Hans/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/zh_Hant_HK/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/bg/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ca/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/da/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/el/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/eo/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/es/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/et/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fa/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ga/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/gl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/he/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/hu/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/id/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/is/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/it/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ja/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ko/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/lt/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ml/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/pl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ro/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/si/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/sk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/sl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/sv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/tr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hant/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_ar.ini | 25 +- options/locale/locale_bg.ini | 400 +++++++++++++++++++++++++- options/locale/locale_ca.ini | 24 +- options/locale/locale_cs-CZ.ini | 6 +- options/locale/locale_da.ini | 14 +- options/locale/locale_de-DE.ini | 2 +- options/locale/locale_el-GR.ini | 10 +- options/locale/locale_eo.ini | 12 +- options/locale/locale_es-ES.ini | 10 +- options/locale/locale_et.ini | 24 +- options/locale/locale_fa-IR.ini | 30 +- options/locale/locale_fi-FI.ini | 8 + options/locale/locale_fil.ini | 4 +- options/locale/locale_fr-FR.ini | 2 +- options/locale/locale_ga-IE.ini | 28 +- options/locale/locale_gl.ini | 24 +- options/locale/locale_he.ini | 29 +- options/locale/locale_hu-HU.ini | 24 +- options/locale/locale_id-ID.ini | 38 +-- options/locale/locale_is-IS.ini | 22 +- options/locale/locale_it-IT.ini | 32 +-- options/locale/locale_ja-JP.ini | 27 +- options/locale/locale_ko-KR.ini | 16 +- options/locale/locale_lt.ini | 24 +- options/locale/locale_ml-IN.ini | 6 +- options/locale/locale_nds.ini | 2 +- options/locale/locale_pl-PL.ini | 28 +- options/locale/locale_ro.ini | 12 +- options/locale/locale_ru-RU.ini | 8 +- options/locale/locale_si-LK.ini | 6 +- options/locale/locale_sk-SK.ini | 6 +- options/locale/locale_sl.ini | 8 +- options/locale/locale_sv-SE.ini | 6 +- options/locale/locale_tr-TR.ini | 128 ++++++--- options/locale/locale_uk-UA.ini | 168 ++++++++--- options/locale/locale_zh-CN.ini | 6 +- options/locale/locale_zh-TW.ini | 2 +- options/locale_next/locale_ar.json | 4 +- options/locale_next/locale_be.json | 4 +- options/locale_next/locale_bg.json | 1 - options/locale_next/locale_bn.json | 4 +- options/locale_next/locale_ca.json | 3 +- options/locale_next/locale_cs-CZ.json | 4 +- options/locale_next/locale_da.json | 8 +- options/locale_next/locale_de-DE.json | 4 +- options/locale_next/locale_el-GR.json | 1 - options/locale_next/locale_es-ES.json | 3 +- options/locale_next/locale_fi-FI.json | 4 +- options/locale_next/locale_fil.json | 1 - options/locale_next/locale_fr-FR.json | 1 - options/locale_next/locale_hu-HU.json | 3 +- options/locale_next/locale_id-ID.json | 9 +- options/locale_next/locale_it-IT.json | 1 - options/locale_next/locale_ja-JP.json | 3 +- options/locale_next/locale_ko-KR.json | 3 +- options/locale_next/locale_lv-LV.json | 4 +- options/locale_next/locale_nb_NO.json | 1 - options/locale_next/locale_nds.json | 4 +- options/locale_next/locale_nl-NL.json | 4 +- options/locale_next/locale_pt-BR.json | 6 +- options/locale_next/locale_pt-PT.json | 1 - options/locale_next/locale_ru-RU.json | 5 +- options/locale_next/locale_sr-SP.json | 4 +- options/locale_next/locale_sv-SE.json | 1 - options/locale_next/locale_tr-TR.json | 3 +- options/locale_next/locale_uk-UA.json | 4 +- options/locale_next/locale_vi.json | 4 +- options/locale_next/locale_zh-CN.json | 7 +- options/locale_next/locale_zh-HK.json | 5 +- options/locale_next/locale_zh-TW.json | 1 - 70 files changed, 935 insertions(+), 401 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 15d614e8bc..ba11586b44 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -297,7 +297,7 @@ twofa_disabled = عُطِّل الاستيثاق الثنائي. theme_desc = ستكون هذه السمة المبدئية لك عبر الموقع. new_password = كلمة المرور الجديدة twofa_disable_desc = تعطيل الاستيثاق الثنائي سيجعل حسابك أقل أمانًا. أتريد الاستمرار؟ -manage_themes = اختر السمة المبدئية +manage_themes = الموضوع الافتراضي delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عنها بعد ذلك. cancel = ألغ repos_none = ليس لديك أي مستودع. @@ -390,7 +390,7 @@ account = الحساب uploaded_avatar_is_too_big = حجم الملف المرفوع (%d كي‌ب) يتخطى الحجم الأقصى (%d كي‌ب). biography_placeholder = أخبرنا شيئا عن نفسك! (يمكنك استخدام ماركداون) comment_type_group_reference = الإشارات -orgs = إدارة المنظمات +orgs = المنظمات update_profile = حدِّث الملف الشخصي profile = الملف الشخصي comment_type_group_dependency = الاعتماديات @@ -421,7 +421,7 @@ keep_email_private_popup = سيؤدي هذا إلى إخفاء عنوان بري ssh_key_name_used = هناك مفتاح SSH بنفس الاسم موجود بالفعل على حسابك. authorized_oauth2_applications = تطبيقات OAuth2 المأذونة uid = المعرّف الرمزي -manage_openid = إدارة عناوين OpenID +manage_openid = عناوين OpenID webauthn = استيثاق ثنائي (مفاتيح الأمان) comment_type_group_deadline = الموعد النهائي add_key = أضف مفتاح @@ -502,6 +502,7 @@ update_oauth2_application_success = لقد حدّثت بنجاح تطبيق OAut oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع كل رابط في سطر وحده. remove_account_link = أزل الحساب المربوط remove_account_link_success = أُزيل الحساب المربوط. +quota = كوتا [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. @@ -1973,7 +1974,7 @@ component_failed_to_load = حدث خطأ غير متوقع. [search] -org_kind = بحث في المنظمات... +org_kind = بحث في المنظمات… code_search_unavailable = البحث في الكود غير متوفر حاليًا. يرجى الاتصال بمدير الموقع. search = ابحث... type_tooltip = نوع البحث @@ -1981,13 +1982,13 @@ fuzzy = أجعد fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق match = تتناسب match_tooltip = قم بتضمين النتائج التي تطابق مصطلح البحث المحدد فقط -repo_kind = بحث في المستودعات... -user_kind = بحث عن المستخدمين... -team_kind = بحث عن الفرق ... -code_kind = بحث في الكود... -project_kind = البحث ضمن المشاريع... -branch_kind = البحث ضمن الفروع... +repo_kind = بحث في المستودعات… +user_kind = بحث عن المستخدمين… +team_kind = بحث عن الفرق… +code_kind = بحث في الكود… +project_kind = البحث ضمن المشاريع… +branch_kind = البحث ضمن الفروع… no_results = لا توجد نتائج مطابقة. -issue_kind = البحث ضمن الأعطال... -pull_kind = البحث ضمن طلبات السحب... +issue_kind = البحث ضمن الأعطال… +pull_kind = البحث ضمن طلبات السحب… keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 943994e061..8c69a36d52 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -141,6 +141,7 @@ webauthn_sign_in = Натиснете бутона на вашия ключ за webauthn_error = Неуспешно прочитане на вашия ключ за сигурност. webauthn_unsupported_browser = Вашият браузър в момента не поддържа WebAuthn. webauthn_error_duplicated = Ключът за сигурност не е разрешен за тази заявка. Моля, уверете се, че ключът не е вече регистриран. +tracked_time_summary = Обобщение на проследеното време въз основа на филтрите в списъка със задачи [settings] ui = Тема @@ -324,7 +325,7 @@ permissions_list = Разрешения: edit_oauth2_application = Редактиране на OAuth2 приложение remove_oauth2_application = Премахване на OAuth2 приложение twofa_recovery_tip = Ако загубите устройството си, ще можете да използвате ключ за еднократно възстановяване, за да си върнете достъпа до акаунта. -visibility.private_tooltip = Видим само за членове на организации, в които участвате +visibility.private_tooltip = Видим само за участници в организации, в които участвате quota.applies_to_user = Следните правила за квота се прилагат за вашия акаунт quota.rule.no_limit = Неограничена hints = Подсказки @@ -380,6 +381,19 @@ hidden_comment_types = Скрити типове коментари comment_type_group_lock = Състояние на заключване can_not_add_email_activations_pending = Има чакаща активация, опитайте отново след няколко минути, ако искате да добавите нова ел. поща. storage_overview = Преглед на съхранението +webauthn = Двуфакторно удостоверяване (Ключове за сигурност) +quota.sizes.repos.public = Публични хранилища +quota.sizes.repos.private = Частни хранилища +quota.sizes.git.all = Git съдържание +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.attachments.all = Прикачени файлове +quota.sizes.assets.attachments.releases = Прикачени файлове към издания +quota.sizes.assets.artifacts = Артефакти +quota.sizes.assets.packages.all = Пакети +quota.sizes.wiki = Уики +quota.sizes.all = Всички +quota.sizes.repos.all = Хранилища +quota.sizes.assets.attachments.issues = Прикачени файлове към задачи [packages] container.labels.value = Стойност @@ -438,6 +452,112 @@ details.documentation_site = Уебсайт на документацията arch.version.conflicts = В конфликт alpine.repository.branches = Клонове arch.pacman.repo.multi.item = Конфигурация за %s +container.multi_arch = ОС / Архитектура +rpm.repository = Информация за хранилището +container.pull = Издърпайте образа от командния ред: +helm.registry = Настройте този регистър от командния ред: +debian.repository.distributions = Дистрибуции +npm.dependencies.optional = Опционални зависимости +owner.settings.cargo.title = Индекс на регистъра на Cargo +owner.settings.cleanuprules.keep.pattern.container = Версията latest винаги се запазва за Container пакети. +owner.settings.cleanuprules.remove.pattern = Премахване на версии, съответстващи на +rpm.distros.suse = на дистрибуции, базирани на SUSE +owner.settings.cleanuprules.preview.overview = %d пакета са насрочени за премахване. +owner.settings.cleanuprules.preview = Преглед на правило за почистване +arch.version.properties = Свойства на версията +conan.registry = Настройте този регистър от командния ред: +conan.details.repository = Хранилище +composer.install = За да инсталирате пакета с Composer, изпълнете следната команда: +chef.install = За да инсталирате пакета, изпълнете следната команда: +chef.registry = Настройте този регистър във вашия файл ~/.chef/config.rb: +pub.install = За да инсталирате пакета с Dart, изпълнете следната команда: +npm.details.tag = Маркер +npm.install = За да инсталирате пакета с npm, изпълнете следната команда: +maven.registry = Настройте този регистър във файла на вашия проект pom.xml: +debian.repository.components = Компоненти +debian.install = За да инсталирате пакета, изпълнете следната команда: +cran.install = За да инсталирате пакета, изпълнете следната команда: +cran.registry = Настройте този регистър във вашия файл Rprofile.site: +rpm.distros.redhat = на дистрибуции, базирани на RedHat +alt.registry = Настройте този регистър от командния ред: +rpm.repository.architectures = Архитектури +alt.registry.install = За да инсталирате пакета, изпълнете следната команда: +alt.setup = Добавете хранилище към списъка със свързани хранилища (изберете необходимата архитектура вместо „_arch_“): +alt.repository = Информация за хранилището +owner.settings.cargo.initialize.error = Неуспешно инициализиране на индекса на Cargo: %v +owner.settings.cargo.initialize = Инициализиране на индекс +settings.delete.description = Изтриването на пакет е трайно и не може да бъде отменено. +alt.repository.multiple_groups = Този пакет е наличен в няколко групи. +alt.repository.architectures = Архитектури +owner.settings.chef.title = Регистър на Chef +owner.settings.cleanuprules.remove.days = Премахване на версии, по-стари от +owner.settings.cleanuprules.keep.pattern = Запазване на версии, съответстващи на +owner.settings.cleanuprules.keep.count.n = %d версии на пакет +owner.settings.cleanuprules.keep.count.1 = 1 версия на пакет +owner.settings.cleanuprules.keep.count = Запазване на най-новите +owner.settings.cleanuprules.enabled = Включено +owner.settings.cleanuprules.preview.none = Правилото за почистване не съвпада с нито един пакет. +owner.settings.cleanuprules.none = Все още няма правила за почистване. +owner.settings.cleanuprules.add = Добавяне на правило за почистване +owner.settings.cleanuprules.title = Правила за почистване +owner.settings.cargo.rebuild.success = Индексът на Cargo беше успешно преизграден. +alpine.registry.key = Изтеглете публичния RSA ключ на регистъра в папката /etc/apk/keys/, за да проверите подписа на индекса: +alpine.registry.info = Изберете $branch и $repository от списъка по-долу. +arch.version.checkdepends = Зависимости за проверката +composer.dependencies = Зависимости +swift.install = Добавете пакета във вашия файл Package.swift: +settings.link.error = Неуспешно обновяване на връзката на хранилището. +swift.install2 = и изпълнете следната команда: +rpm.repository.multiple_groups = Този пакет е наличен в няколко групи. +conda.registry = Настройте този регистър като Conda хранилище във вашия файл .condarc: +conda.install = За да инсталирате пакета с Conda, изпълнете следната команда: +owner.settings.cargo.rebuild.error = Неуспешно преизграждане на индекса на Cargo: %v +owner.settings.cargo.rebuild = Преизграждане на индекс +settings.link.button = Обновяване на връзката на хранилището +settings.link.select = Изберете хранилище +debian.repository.architectures = Архитектури +rpm.registry = Настройте този регистър от командния ред: +debian.registry = Настройте този регистър от командния ред: +helm.install = За да инсталирате пакета, изпълнете следната команда: +swift.registry = Настройте този регистър от командния ред: +settings.link = Свързване на този пакет с хранилище +settings.link.description = Ако свържете пакет с хранилище, пакетът се изброява в списъка с пакети на хранилището. +settings.link.success = Връзката на хранилището беше успешно обновена. +owner.settings.cleanuprules.pattern_full_match = Прилагане на шаблона към пълното име на пакета +owner.settings.cleanuprules.keep.title = Версиите, които съответстват на тези правила, се запазват, дори ако съответстват на правило за премахване по-долу. +debian.repository = Информация за хранилището +maven.install = За да използвате пакета, включете следното в блока dependencies във файла pom.xml: +nuget.install = За да инсталирате пакета с NuGet, изпълнете следната команда: +alt.install = Инсталиране на пакет +owner.settings.cleanuprules.edit = Редактиране на правилото за почистване +rpm.install = За да инсталирате пакета, изпълнете следната команда: +pypi.install = За да инсталирате пакета с pip, изпълнете следната команда: +arch.version.makedepends = Зависимости за изграждането +alpine.install = За да инсталирате пакета, изпълнете следната команда: +desc = Управление на пакетите на хранилището. +owner.settings.cargo.rebuild.no_index = Не може да се преизгради, няма инициализиран индекс. +owner.settings.cargo.rebuild.description = Преизграждането може да бъде полезно, ако индексът не е синхронизиран със съхранените Cargo пакети. +owner.settings.cargo.initialize.description = Необходимо е специално Git хранилище за индекс, за да се използва регистърът на Cargo. Използването на тази опция ще (пре)създаде хранилището и ще го конфигурира автоматично. +pypi.requires = Изисква Python +debian.registry.info = Изберете $distribution и $component от списъка по-долу. +alpine.registry = Настройте този регистър, като добавите URL адреса във вашия файл /etc/apk/repositories: +owner.settings.cargo.initialize.success = Индексът на Cargo беше успешно създаден. +npm.registry = Настройте този регистър във файла на вашия проект .npmrc: +owner.settings.chef.keypair = Генериране на двойка ключове +owner.settings.chef.keypair.description = Заявките, изпратени до регистъра на Chef, трябва да бъдат криптографски подписани като средство за удостоверяване. При генериране на двойка ключове, само публичният ключ се съхранява във Forgejo. Частният ключ ви се предоставя, за да се използва с knife. Генерирането на нова двойка ключове ще презапише предишната. +owner.settings.cleanuprules.remove.title = Версиите, които съответстват на тези правила, се премахват, освен ако правило по-горе не казва да се запазят. +nuget.registry = Настройте този регистър от командния ред: +owner.settings.cleanuprules.success.update = Правилото за почистване е обновено. +settings.delete.notice = На път сте да изтриете %s (%s). Тази операция е необратима, сигурни ли сте? +npm.install2 = или го добавете във файла package.json: +owner.settings.cleanuprules.success.delete = Правилото за почистване е изтрито. +vagrant.install = За да добавите Vagrant box, изпълнете следната команда: +nuget.dependency.framework = Целева платформа +maven.install2 = Изпълнете през командния ред: +maven.download = За да изтеглите зависимостта, изпълнете през командния ред: +container.layers = Слоеве на образа +conan.install = За да инсталирате пакета с Conan, изпълнете следната команда: +composer.registry = Настройте този регистър във вашия файл ~/.composer/config.json: [tool] hours = %d часа @@ -940,9 +1060,9 @@ editor.no_changes_to_show = Няма промени за показване. issues.choose.get_started = Първи стъпки issues.change_milestone_at = `промени етапа от %s на %s %s` issues.change_project_at = `промени проекта от %s на %s %s` -issues.self_assign_at = `си само-възложи това %s` +issues.self_assign_at = `си самовъзложи това %s` issues.remove_assignee_at = `е премахнат като изпълнител от %s %s` -issues.remove_self_assignment = `се само-премахна като изпълнител %s` +issues.remove_self_assignment = `се самопремахна като изпълнител %s` issues.add_assignee_at = `му бе възложено това от %s %s` pulls.merged_by = от %[3]s бе слята %[1]s pulls.merged_by_fake = от %[2]s бе слята %[1]s @@ -1272,7 +1392,7 @@ issues.review.show_resolved = Показване на решено issues.review.hide_resolved = Скриване на решено issues.review.resolve_conversation = Решаване на обсъждането diff.comment.markdown_info = Поддържа се стилизиране с Маркдаун. -diff.file_suppressed = Разликите не са показани, защото са твърде много +diff.file_suppressed = Разликите във файла са потиснати, защото са твърде много pulls.reject_count_n = %d поискани промени settings.pulls.default_allow_edits_from_maintainers = Позволяване на редакции от поддържащите по подразбиране fork_branch = Клон за клониране в разклонението @@ -1347,7 +1467,7 @@ settings.default_branch_desc = Изберете стандартен клон з settings.transfer.button = Прехвърляне на притежанието settings.transfer.modal.title = Прехвърляне на притежанието ambiguous_runes_line = `Този ред съдържа двусмислени Уникод знаци` -ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан с %[2]c [U+%04[2]X]` +ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан със %[2]c [U+%04[2]X]` invisible_runes_header = `Този файл съдържа невидими Уникод знаци` issues.all_title = Общо issues.new.assign_to_me = Възлагане на мен @@ -1446,7 +1566,7 @@ generated_from = генерирано от clear_ref = `Изчистване на текущата препратка` file_follow = Последване на символната връзка commitstatus.failure = Неуспех -issues.filter_label_exclude = `Използвайте alt + click/enter, за да изключите етикети` +issues.filter_label_exclude = Използвайте Alt + Click, за да изключите етикети migrate.migrating_failed = Мигрирането от %s е неуспешно. migrate.migrating_issues = Мигриране на задачи mirror_from = огледално на @@ -1575,6 +1695,177 @@ migrate.migrating_failed_no_addr = Мигрирането е неуспешно. issues.force_push_compare = Сравняване pulls.status_checking = Някои проверки са в очакване pulls.nothing_to_compare = Тези клонове са равни. Не е нужно да създавате заявка за сливане. +admin.flags_replaced = Флаговете на хранилището са заменени +editor.cannot_edit_lfs_files = LFS файлове не могат да се редактират в уеб интерфейса. +commits.ssh_key_fingerprint = Отпечатък на SSH ключ +issues.comment_on_locked = Не можете да коментирате заключена задача. +commit.revert = Връщане +migrate.cancel_migrating_title = Отказ от миграцията +migrate.cancel_migrating_confirm = Искате ли да откажете тази миграция? +issues.choose.invalid_config = Конфигурацията на задачите съдържа грешки: +unit_disabled = Администраторът на сайта е изключил тази секция на хранилището. +issues.blocked_by_user = Не можете да създавате задачи в това хранилище, защото сте блокирани от притежателя на хранилището. +commits.signed_by = Подписано от +commits.signed_by_untrusted_user = Подписано от недоверен потребител +commits.signed_by_untrusted_user_unmatched = Подписано от недоверен потребител, който не съвпада с подаващия +issues.lock.notice_1 = - Други потребители не могат да добавят нови коментари към тази задача. +issues.unlock.notice_2 = - Винаги можете да заключите тази задача отново в бъдеще. +issues.unlock.title = Отключване на обсъждането по тази задача. +issues.dependency.no_permission_1 = Нямате разрешение да прочетете %d зависимост +issues.reopen.blocked_by_user = Не можете да отворите наново тази задача, защото сте блокирани от притежателя на хранилището или от автора на тази задача. +compare.compare_base = основа +compare.compare_head = сравняване +template.one_item = Трябва да изберете поне един елемент от шаблона +admin.failed_to_replace_flags = Неуспешна замяна на флаговете на хранилището +mirror_interval_invalid = Интервалът на огледалото не е валиден. +mirror_use_ssh.not_available = SSH удостоверяването не е налично. +mirror_address_desc = Поставете всички необходими данни за удостоверяване в секцията „Упълномощаване“. +template.git_hooks = Git куки +template.invalid = Трябва да изберете шаблонно хранилище +issues.review.outdated = Остарял +issues.dependency.add_error_dep_issue_not_exist = Зависимата задача не съществува. +template.items = Елементи на шаблона +issues.review.dismissed = отхвърли рецензията на %s %s +audio_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „audio“. +stored_lfs = Съхранено с Git LFS +commit_graph.select = Изберете клонове +issues.content_history.options = Опции +editor.commit_email = Ел. поща на подаването +commit.revert-header = Връщане: %s +commits.desc = Разглеждане на историята на промените в програмния код. +commits.search.tooltip = Можете да добавите префикс към ключовите думи с „author:“, „committer:“, „after:“ или „before:“, напр. „revert author:Alice before:2019-01-13“. +issues.unlock_error = Не може да се отключи задача, която не е заключена. +issues.lock.unknown_reason = Не може да се заключи задача с неизвестна причина. +issues.cancel_tracking_history = `отмени проследяването на времето %s` +issues.dependency.add_error_dep_not_same_repo = И двете задачи трябва да са в едно и също хранилище. +issues.review.remove_review_requests = премахна заявките за рецензия за %[1]s %[2]s +issues.review.content.empty = Трябва да оставите коментар, посочващ исканите промени. +issues.review.hide_outdated = Скриване на остарели +pulls.desc = Включване на заявки за сливане и рецензии на код. +issues.review.show_outdated = Показване на остарели +ambiguous_runes_header = `Този файл съдържа двусмислени Уникод знаци` +admin.update_flags = Обновяване на флаговете +issues.dependency.blocked_by_short = Зависи от +mirror_lfs = Съхранение на големи файлове (LFS) +mirror_use_ssh.text = Използване на SSH удостоверяване +mirror_denied_combination = Не може да се използва удостоверяване с публичен ключ и парола едновременно. +rss.must_be_on_branch = Трябва да сте на клон, за да имате RSS емисия. +admin.manage_flags = Управление на флаговете +admin.enabled_flags = Флагове, включени за хранилището: +mirror_password_help = Променете потребителското име, за да изтриете запазена парола. +issues.review.remove_review_request = премахна заявката за рецензия за %[1]s %[2]s +issues.review.outdated_description = Съдържанието е променено, след като е направен този коментар +issues.dependency.setting = Включване на зависимости за задачи и заявки за сливане +issues.dependency.add_error_same_issue = Не можете да направите задача зависима от самата нея. +issues.review.self.rejection = Не можете да поискате промени в собствената си заявка за сливане. +issues.filter_type.all_pull_requests = Всички заявки за сливане +fork_to_different_account = Разклоняване в друг акаунт +mirror_sync_on_commit = Синхронизиране при изтласкване на подавания +mirror_address_protocol_invalid = Предоставеният URL е невалиден. Само http(s):// или git:// адреси могат да се използват за огледални хранилища. +template.git_hooks_tooltip = В момента не можете да променяте или премахвате Git куки, след като са добавени. Изберете това само ако се доверявате на шаблонното хранилище. +editor.commit_signed_changes = Подаване на подписани промени +editor.require_signed_commit = Клонът изисква подписано подаване +issues.desc = Организирайте доклади за грешки, задачи и етапи. +issues.lock_duplicate = Задача не може да бъде заключена два пъти. +issues.lock.notice_2 = - Вие и други сътрудници с достъп до това хранилище все още можете да оставяте коментари, които другите да виждат. +issues.due_date_invalid = Крайният срок е невалиден или извън обхвата. Моля, използвайте формата „гггг-мм-дд“. +mirror_interval = Интервал на огледалото (валидни единици за време са „h“, „m“, „s“). 0 за изключване на периодичната синхронизация. (Минимален интервал: %s) +summary_card_alt = Карта с обобщение на хранилище %s +file_copy_permalink = Копиране на постоянна връзка +view_git_blame = Преглед на git blame +commit.revert-content = Изберете клон, върху който да се върне: +issues.unlock.notice_1 = - Всеки ще може отново да коментира тази задача. +issues.delete.text = Наистина ли искате да изтриете тази задача? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +issues.add_time_sum_to_small = Не е въведено време. +issues.dependency.no_permission_n = Нямате разрешение да прочетете %d зависимости +issues.review.pending.tooltip = Този коментар в момента не е видим за други потребители. За да изпратите изчакващите си коментари, изберете „%s“ -> „%s/%s/%s“ в горната част на страницата. +invisible_runes_description = `Този файл съдържа невидими Уникод знаци, които са неразличими за хората, но могат да бъдат обработени по различен начин от компютър. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +video_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „video“. +editor.filename_help = Добавете директория, като въведете името ѝ, последвано от наклонена черта („/“). Премахнете директория, като натиснете backspace в началото на полето за въвеждане. +commits.view_single_diff = Преглед на промените в този файл, въведени в това подаване +issues.choose.ignore_invalid_templates = Невалидните шаблони са игнорирани +issues.due_date_form = гггг-мм-дд +issues.dependency.no_permission.can_remove = Нямате разрешение да прочетете тази зависимост, но можете да я премахнете +issues.review.remove_review_request_self = отказа да рецензира %s +mirror_use_ssh.helper = Forgejo ще създаде огледало на хранилището чрез Git през SSH и ще генерира двойка ключове за вас, когато изберете тази опция. Трябва да се уверите, че генерираният публичен ключ е упълномощен да изтласква към целевото хранилище. Не можете да използвате удостоверяване, базирано на парола, когато избирате това. +mirror_address_url_invalid = Предоставеният URL е невалиден. Трябва да екранирате правилно всички компоненти на URL адреса. +template.git_content = Git съдържание (стандартен клон) +ambiguous_runes_description = `Този файл съдържа Уникод знаци, които могат да бъдат объркани с други знаци. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +issues.lock.notice_3 = - Винаги можете да отключите тази задача отново в бъдеще. +issues.lock.title = Заключване на обсъждането по тази задача. +issues.dependency.issue_batch_close_blocked = Не могат да бъдат затворени групово избраните задачи, защото задача #%d все още има отворени зависимости +issues.dependency.add_error_cannot_create_circular = Не можете да създадете зависимост с две задачи, които се блокират взаимно. +issues.review.add_review_requests = поиска рецензии от %[1]s %[2]s +comment.blocked_by_user = Коментирането не е възможно, защото сте блокирани от притежателя на хранилището или от автора. +pulls.view = Преглед на заявката за сливане +pulls.no_merge_desc = Тази заявка за сливане не може да бъде слята, защото всички опции за сливане в хранилището са изключени. +pulls.no_merge_wip = Тази заявка за сливане не може да бъде слята, защото е отбелязана като в процес на работа. +pulls.switch_comparison_type = Превключване на типа сравнение +pulls.has_changed_since_last_review = Променено след последната ви рецензия +pulls.filter_branch = Филтриране на клон +pulls.squash_merge_pull_request = Създаване на сплескано подаване +pulls.rebase_conflict_summary = Съобщение за грешка +pulls.auto_merge_button_when_succeed = (Когато проверките са успешни) +pulls.auto_merge_newly_scheduled_comment = `насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[1]s` +pulls.auto_merge_canceled_schedule_comment = `отмени автоматичното сливане на тази заявка за сливане, когато всички проверки са успешни %[1]s` +pulls.merge_manually = Ръчно слята +pulls.merge_commit_id = ID на подаването със сливане +pulls.require_signed_wont_sign = Клонът изисква подписани подавания, но това сливане няма да бъде подписано +pulls.no_merge_helper = Включете опциите за сливане в настройките на хранилището или слейте заявката за сливане ръчно. +pulls.review_only_possible_for_full_diff = Рецензирането е възможно само при преглед на пълните разлики +pulls.push_rejected = Изтласкването е неуспешно: Изтласкването е отхвърлено. Прегледайте Git куките за това хранилище. +pulls.auto_merge_canceled_schedule = Автоматичното сливане е отменено за тази заявка за сливане. +pulls.allow_edits_from_maintainers_err = Обновяването е неуспешно +pulls.auto_merge_cancel_schedule = Отмяна на автоматичното сливане +pulls.auto_merge_not_scheduled = Тази заявка за сливане не е насрочена за автоматично сливане. +pulls.outdated_with_base_branch = Този клон е остарял спрямо основния клон +pulls.update_not_allowed = Нямате разрешение да обновявате клона +pulls.wrong_commit_id = ID на подаването трябва да бъде ID на подаване в целевия клон +pulls.blocked_by_user = Не можете да създадете заявка за сливане в това хранилище, защото сте блокирани от притежателя на хранилището. +pulls.merge_conflict_summary = Съобщение за грешка +pulls.editable_explanation = Тази заявка за сливане позволява редакции от поддържащите. Можете да допринесете директно към нея. +pulls.allow_edits_from_maintainers_desc = Потребители с право на запис в основния клон могат също да изтласкват към този клон +pulls.merge_conflict = Сливането е неуспешно: Възникна конфликт по време на сливането. Подсказка: Опитайте различна стратегия +pulls.has_merged = Неуспешно: Заявката за сливане е слята, не можете да слеете отново или да промените целевия клон. +pulls.cmd_instruction_merge_warning = Предупреждение: Настройката „Автоматично откриване на ръчно сливане“ не е включена за това хранилище, ще трябва да отбележите тази заявка за сливане като ръчно слята след това. +pulls.delete.title = Да се изтрие ли тази заявка за сливане? +pulls.push_rejected_no_message = Изтласкването е неуспешно: Изтласкването е отхвърлено, но няма отдалечено съобщение. Прегледайте Git куките за това хранилище +pulls.auto_merge_newly_scheduled = Заявката за сливане е насрочена за сливане, когато всички проверки са успешни. +pulls.delete.text = Наистина ли искате да изтриете тази заявка за сливане? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +pulls.auto_merge_has_pending_schedule = %[1]s насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[2]s. +pulls.auto_merge_when_succeed = Автоматично сливане, когато всички проверки са успешни +error.csv.invalid_field_count = Не може да се визуализира този файл, защото има грешен брой полета на ред %d. +diff.bin = ДВОИЧЕН +release.add_tag_msg = Използване на заглавието и съдържанието на изданието като съобщение на маркера. +release.hide_archive_links_helper = Скрийте автоматично генерираните архиви с програмен код за това издание. Например, ако качвате свои собствени. +diff.data_not_available = Съдържанието на разликите не е налично +diff.has_escaped = Този ред има скрити Уникод знаци +branch.protected_deletion_failed = Клонът „%s“ е защитен. Не може да бъде изтрит. +branch.default_deletion_failed = Клонът „%s“ е стандартният клон. Не може да бъде изтрит. +diff.generated = генериран +diff.comment.add_line_comment = Добавяне на коментар към ред +diff.comment.add_review_comment = Добавяне на коментар +diff.review.self_approve = Авторите на заявки за сливане не могат да одобряват собствените си заявки +release.tag_name_protected = Името на маркера е защитено. +branch.warning_rename_default_branch = Преименувате стандартния клон. +find_file.no_matching = Не е намерен съвпадащ файл +issues.role.member_helper = Този потребител е участник в организацията, притежаваща това хранилище. +diff.image.overlay = Наслагване +diff.image.swipe = Плъзгане +branch.included = Включен +diff.file_suppressed_line_too_long = Разликите във файла са потиснати, защото един или повече редове са твърде дълги +error.broken_git_hook = Git куките на това хранилище изглеждат повредени. Моля, последвайте документацията, за да ги поправите, след което изтласкайте подавания, за да обновите статуса. +error.csv.unexpected = Не може да се визуализира този файл, защото съдържа неочакван знак на ред %d и колона %d. +topic.count_prompt = Не можете да изберете повече от 25 теми +release.hide_archive_links = Скриване на автоматично генерираните архиви +diff.show_more = Показване на още +diff.too_many_files = Някои файлове не бяха показани, защото твърде много файлове имат промени в тези разлики +diff.review.self_reject = Авторите на заявки за сливане не могат да поискват промени в собствените си заявки +branch.included_desc = Този клон е част от стандартния клон +diff.image.side_by_side = Едно до друго +release.summary_card_alt = Карта с обобщение на издание със заглавие „%s“ в хранилище %s +release.asset_external_url = Външен URL адрес +error.csv.too_large = Не може да се визуализира този файл, защото е твърде голям. [modal] confirm = Потвърждаване @@ -1663,18 +1954,18 @@ follow_blocked_user = Не можете да следвате тази орга settings.delete_prompt = Организацията ще бъде премахната завинаги. Това НЕ МОЖЕ да бъде отменено! settings.labels_desc = Добавете етикети, които могат да се използват за задачи за всички хранилища в тази организация. teams.none_access = Без достъп -teams.members.none = Няма членове в този екип. +teams.members.none = Няма участници в този екип. repo_updated = Обновено %s teams.delete_team_success = Екипът е изтрит. teams.search_repo_placeholder = Потърсете хранилище… teams.delete_team_title = Изтриване на екипа -teams.add_team_member = Добавяне на член на екипа +teams.add_team_member = Добавяне на участник в екипа teams.read_access_helper = Членовете могат да преглеждат и клонират хранилищата на екипа. teams.invite.description = Моля, щракнете върху бутона по-долу, за да се присъедините към екипа. teams.invite.title = Поканени сте да се присъедините към екип %s в организация %s. team_permission_desc = Разрешение members.public_helper = Да е скрит -teams.members = Членове на екипа +teams.members = Участници в екипа teams.delete_team = Изтриване на екипа members.owner = Притежател members.member_role = Роля на участника: @@ -1684,6 +1975,37 @@ teams.no_desc = Този екип няма описание settings.delete_org_desc = Тази организация ще бъде изтрита перманентно. Продължаване? open_dashboard = Отваряне на таблото settings.change_orgname_prompt = Бележка: Промяната на името на организацията ще промени и URL адреса на вашата организация и ще освободи старото име. +teams.add_duplicate_users = Потребителят вече е участник в екипа. +team_unit_disabled = (Изключено) +form.name_reserved = Името на организацията „%s“ е резервирано. +settings.update_avatar_success = Профилната снимка на организацията е обновена. +teams.invite_team_member.list = Чакащи покани +teams.remove_all_repos_desc = Това ще премахне всички хранилища от екипа. +form.create_org_not_allowed = Нямате разрешение да създавате организация. +form.name_pattern_not_allowed = Шаблонът „%s“ не е разрешен в име на организация. +members.invite_now = Поканване сега +teams.specific_repositories = Конкретни хранилища +teams.repos.none = Няма хранилища, до които този екип да има достъп. +teams.add_all_repos_title = Добавяне на всички хранилища +settings.hooks_desc = Добавете уеб-куки, които ще се задействат за всички хранилища в тази организация. +teams.add_all_repos_desc = Това ще добави всички хранилища на организацията към екипа. +members.invite_desc = Добавяне на нов участник към %s: +members.private = Скрит +settings.change_orgname_redirect_prompt.with_cooldown.few = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d дни. Все още можете да си върнете старото име по време на периода на изчакване. +team_access_desc = Достъп до хранилище +teams.specific_repositories_helper = Участниците ще имат достъп само до хранилища, изрично добавени към екипа. Избирането на това няма автоматично да премахне хранилища, вече добавени с Всички хранилища. +teams.delete_team_desc = Изтриването на екип отнема достъпа до хранилището от неговите участници. Продължаване? +members.membership_visibility = Видимост на участничеството: +members.public = Видим +teams.all_repositories_helper = Екипът има достъп до всички хранилища. Избирането на това ще добави всички съществуващи хранилища към екипа. +team_unit_desc = Разрешаване на достъп до секции на хранилището +settings.update_setting_success = Настройките на организацията са обновени. +settings.change_orgname_redirect_prompt = Старото име ще се пренасочва, докато не бъде взето. +teams.invite_team_member = Поканване в %s +teams.admin_access = Администраторски достъп +settings.change_orgname_redirect_prompt.with_cooldown.one = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d ден. Все още можете да си върнете старото име по време на периода на изчакване. +teams.add_nonexistent_repo = Хранилището, което се опитвате да добавите, не съществува, моля, първо го създайте. +teams.invite.by = Поканен от %s [install] admin_password = Парола @@ -1978,14 +2300,14 @@ Pronouns = Местоимения Biography = Биография Website = Уебсайт Location = Местоположение -cannot_add_org_to_team = Организация не може да бъде добавена като член на екип. +cannot_add_org_to_team = Организация не може да бъде добавена като участник в екип. auth_failed = Неуспешно удостоверяване: %v team_no_units_error = Разрешете достъп до поне една секция на хранилището. password_uppercase_one = Поне един голям знак CommitSummary = Обобщение на подаването username_error = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“), долна черта („_“) и точка („.“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` username_error_no_dots = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“) и долна черта („_“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` -duplicate_invite_to_team = Потребителят вече е поканен като член на екипа. +duplicate_invite_to_team = Потребителят вече е поканен като участник в екипа. must_use_public_key = Ключът, който предоставихте, е частен ключ. Моля, не качвайте частния си ключ никъде. Вместо това използвайте публичния си ключ. org_still_own_packages = Тази организация все още притежава един или повече пакети, първо ги изтрийте. admin_cannot_delete_self = Не можете да изтриете себе си, когато сте администратор. Моля, първо премахнете администраторските си привилегии. @@ -2006,7 +2328,7 @@ enterred_invalid_repo_name = Името на хранилището, което enterred_invalid_org_name = Името на организацията, което въведохте, е неправилно. enterred_invalid_password = Паролата, която въведохте, е неправилна. organization_leave_success = Успешно напуснахте организацията %s. -still_has_org = Вашият акаунт е член на една или повече организации, първо ги напуснете. +still_has_org = Вашият акаунт е участник в една или повече организации, първо ги напуснете. org_still_own_repo = Тази организация все още притежава едно или повече хранилища, първо ги изтрийте или прехвърлете. target_branch_not_exist = Целевият клон не съществува. glob_pattern_error = ` glob шаблонът е невалиден: %s.` @@ -2016,6 +2338,17 @@ TreeName = Път до файла AdminEmail = Администраторски адрес за ел. поща email_domain_is_not_allowed = Домейнът на адреса за ел. поща на потребителя %s е в конфликт с EMAIL_DOMAIN_ALLOWLIST или EMAIL_DOMAIN_BLOCKLIST. Уверете се, че сте въвели правилно адреса за ел. поща. email_been_used = Адресът за ел. поща вече се използва. +unable_verify_ssh_key = Не може да се потвърди SSH ключът, проверете го отново за грешки. +enterred_invalid_owner_name = Името на новия притежател не е валидно. +NewBranchName = Име на новия клон +invalid_ssh_key = Не може да се потвърди вашият SSH ключ: %s +required_prefix = Въведеният текст трябва да започва с „%s“ +regex_pattern_error = ` шаблонът на регулярния израз е невалиден: %s.` +repository_files_already_exist = Вече съществуват файлове за това хранилище. Свържете се със системния администратор. +repository_files_already_exist.delete = Вече съществуват файлове за това хранилище. Трябва да ги изтриете. +invalid_gpg_key = Не може да се потвърди вашият GPG ключ: %s +git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` +last_org_owner = Не можете да премахнете последния потребител от екипа на „притежателите“. Трябва да има поне един притежател за организация. [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -2044,6 +2377,11 @@ reject_pull_request = `предложи промени за %[3] compare_branch = Сравняване compare_commits_general = Сравняване на подавания compare_commits = Сравнете %d подавания +transfer_repo = прехвърли хранилище %s към %s +mirror_sync_push = синхронизира подавания към %[3]s на %[4]s от огледало +mirror_sync_create = синхронизира нова препратка %[3]s към %[4]s от огледало +review_dismissed = `отхвърли рецензия от %[4]s за %[3]s#%[2]s` +mirror_sync_delete = синхронизира и изтри препратка %[2]s на %[3]s от огледало [auth] tab_openid = OpenID @@ -2177,6 +2515,14 @@ variables.management = Управление на променливи variables.not_found = Променливата не е открита. variables.id_not_exist = Променлива с идентификатор %d не съществува. runners.owner_type = Тип +status.cancelled = Отменено +status.running = Изпълнява се +status.success = Успешно +status.waiting = Изчаква се +status.unknown = Неизвестно +status.failure = Неуспешно +status.skipped = Пропуснато +unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. [heatmap] less = По-малко @@ -2258,3 +2604,33 @@ eib = ЕиБ [translation_meta] test = окей + + +[gpg] +default_key = Подписано с ключ по подразбиране +error.no_gpg_keys_found = Не е намерен известен ключ за този подпис в базата данни +error.not_signed_commit = Не е подписано подаване +error.generate_hash = Неуспешно генериране на хеш на подаването +error.extract_sign = Неуспешно извличане на подпис +error.probable_bad_signature = ВНИМАНИЕ! Въпреки че има ключ с това ID в базата данни, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.failed_retrieval_gpg_keys = Неуспешно извличане на ключ, свързан с акаунта на подаващия +error.probable_bad_default_signature = ВНИМАНИЕ! Въпреки че ключът по подразбиране има това ID, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.no_committer_account = Няма акаунт, свързан с адреса за ел. поща на подаващия + +[repo.permissions] +projects.read = Четене: Достъп до проектните табла на хранилището. +wiki.write = Писане: Създаване, обновяване и изтриване на страници в интегрираното уики. +issues.read = Четене: Четене и създаване на задачи и коментари. +pulls.read = Четене: Четене и създаване на заявки за сливане. +pulls.write = Писане: Затваряне на заявки за сливане и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +projects.write = Писане: Създаване и редактиране на проекти и колони. +releases.read = Четене: Преглед и изтегляне на издания. +wiki.read = Четене: Четене на интегрираното уики и неговата история. +code.read = Четене: Достъп и клониране на кода на хранилището. +code.write = Писане: Изтласкване към хранилището, създаване на клонове и маркери. +issues.write = Писане: Затваряне на задачи и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. + +[units] +error.no_unit_allowed_repo = Нямате разрешение за достъп до никоя секция на това хранилище. +unit = Елемент +error.unit_not_allowed = Нямате разрешение за достъп до тази секция на хранилището. \ No newline at end of file diff --git a/options/locale/locale_ca.ini b/options/locale/locale_ca.ini index 9cb7d5e50c..ea2af3b645 100644 --- a/options/locale/locale_ca.ini +++ b/options/locale/locale_ca.ini @@ -153,26 +153,26 @@ fuzzy = Difusa search = Cerca... type_tooltip = Tipus de cerca fuzzy_tooltip = Inclou resultats que s'assemblen al terme de la cerca -repo_kind = Cerca repos... -user_kind = Cerca usuaris... +repo_kind = Cerca repos… +user_kind = Cerca usuaris… code_search_unavailable = La cerca de codi no està disponible actualment. Si us plau concteu amb l'administrador del lloc. code_search_by_git_grep = Els resultats actuals de la cerca de codi són proporcionats per "git grep". Podríen haver-hi millors resultats si l'administrador del lloc habilita l'indexador de codi. -package_kind = Cerca paquets... -project_kind = Cerca projectes... -branch_kind = Cerca branques... -commit_kind = Cerca commits... -runner_kind = Cerca executors... +package_kind = Cerca paquets… +project_kind = Cerca projectes… +branch_kind = Cerca branques… +commit_kind = Cerca commits… +runner_kind = Cerca executors… no_results = Cap resultat coincident trobat. keyword_search_unavailable = La cerca per paraula clau no està disponible ara mateix. Si us plau contacteu amb l'administrador del lloc. union = Paraules clau union_tooltip = Inclou resultats que encaixen amb qualsevol paraula clau separada per espais -org_kind = Cerca organitzacions... -team_kind = Cerca teams... -code_kind = Cerca codi... -pull_kind = Cerca "pulls"... +org_kind = Cerca organitzacions… +team_kind = Cerca teams… +code_kind = Cerca codi… +pull_kind = Cerca "pulls"… exact = Exacte exact_tooltip = Inclou només resultats que són exactament el terme de cerca -issue_kind = Cerca problemes... +issue_kind = Cerca problemes… regexp = RegExp regexp_tooltip = Interpreta el terme de cerca com una expressió regular diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 81430bc96c..c7770c5ea1 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1556,7 +1556,7 @@ issues.label_templates.info=Zatím nebyly vytvořeny žádné štítky. Vytvořt issues.label_templates.helper=Vyberte přednastavené značky issues.label_templates.use=Použít přednastavené štítky issues.label_templates.fail_to_load_file=Nepodařilo se načíst soubor šablony popisku „%s“: %v -issues.add_label=přidal/a %s štítek %s +issues.add_label=přidal/a štítek %s %s issues.add_labels=přidal/a %s štítky %s issues.remove_label=odstranil/a %s štítek %s issues.remove_labels=odstranil/a %s štítky %s @@ -1763,7 +1763,7 @@ issues.error_modifying_due_date=Změna termínu dokončení selhala. issues.error_removing_due_date=Odstranění termínu dokončení selhalo. issues.push_commit_1=přidal/a %d revizi %s issues.push_commits_n=přidal/a %d revize %s -issues.force_push_codes=`vynucené nahrání %[1]s od %[2]s do %[4]s %[6]s` +issues.force_push_codes=`vynutil/a nahrání %[1]s od %[2]s do %[4]s %[6]s` issues.force_push_compare=Porovnat issues.due_date_form=rrrr-mm-dd issues.due_date_form_add=Přidat termín dokončení @@ -1813,7 +1813,7 @@ issues.review.approve=schválil/a tyto změny %s issues.review.comment=posoudil/a %s issues.review.dismissed=zamítl/a posouzení uživatele %s %s issues.review.dismissed_label=Zamítnuto -issues.review.left_comment=zanechal komentář +issues.review.left_comment=zanechal/a komentář issues.review.content.empty=Je potřeba zanechat poznámku s uvedením požadované změny (požadovaných změn). issues.review.reject=požádal/a o změny %s issues.review.wait=byl/a požádán/a o posouzení %s diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index df51b5f2bf..1a92305515 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -978,8 +978,8 @@ delete_with_all_comments = Din konto er yngre end %s. For at undgå spøgelsesko delete_account_title = Slet brugerkonto user_block_yourself = Du kan ikke blokere dig selv. pronouns_custom_label = Brugerdefinerede stedord -change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. -change_username_redirect_prompt.with_cooldown.few = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. +change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, år. Du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. +change_username_redirect_prompt.with_cooldown.few = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, år. Du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. keep_pronouns_private = Vis kun stedord til godkendte brugere keep_pronouns_private.description = Dette vil skjule dine stedord for besøgende, der ikke er logget ind. quota.applies_to_user = Følgende kvoteregler gælder for din konto @@ -1520,13 +1520,13 @@ issues.add_labels = tilføjede %s etiketterne %s issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s issues.add_milestone_at = `føjede dette til %s milepælen %s` issues.add_project_at = `føjede dette til %s- projektet %s` -issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne den, %[1]s` +issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne det, %[1]s` issues.ref_closed_from = `lukkede dette problem %[4]s %[2 ]s` issues.ref_reopened_from = `genåbnede dette problem %[4]s %[2 ]s` issues.ref_from = `fra %[1]s` issues.author = Forfatter issues.commit_ref_at = `henviste til dette problem fra en commit %s` -issues.ref_issue_from = `henviste til dette problem %[3]s %[2 ]s` +issues.ref_issue_from = `henviste til dette problem %[3]s %[1]s` issues.ref_pull_from = `henviste til denne pull-anmodning %[3]s %[1]s` issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil lukke det, %[1]s` issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem. @@ -1582,7 +1582,7 @@ issues.change_ref_at = `ændret reference fra %s til issues.remove_ref_at = `fjernet reference %s %s` issues.add_ref_at = `tilføjet reference %s %s` issues.delete_branch_at = `slettet gren %s %s` -issues.filter_label_exclude = `Brug alt + klik/enter for at ekskludere etiketter` +issues.filter_label_exclude = Brug Alt + klik for at ekskludere etiketter issues.filter_milestone = Milepæl issues.filter_milestone_all = Alle milepæle issues.filter_milestone_none = Ingen milepæle @@ -2833,8 +2833,8 @@ team_permission_desc = Tilladelse members.member = Medlem settings.change_orgname_prompt = Bemærk: Ændring af organisationens navn vil også ændre din organisations URL og frigøre det gamle navn. settings.change_orgname_redirect_prompt = Det gamle navn vil omdirigere, indtil det gøres krav på. -settings.change_orgname_redirect_prompt.with_cooldown.one = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, du kan stadig kræve det gamle navn tilbage i nedkølingsperioden. -settings.change_orgname_redirect_prompt.with_cooldown.few = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, du kan stadig kræve det gamle navn tilbage i . +settings.change_orgname_redirect_prompt.with_cooldown.one = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, år. Du kan stadig kræve det gamle navn tilbage i nedkølingsperioden. +settings.change_orgname_redirect_prompt.with_cooldown.few = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, år. Du kan stadig kræve det gamle navn tilbage i. settings.update_avatar_success = Organisationens avatar er blevet opdateret. members.public_helper = Gør skjult members.private = Skjult diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 9be888684c..8f4dfda0d1 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -752,7 +752,7 @@ organization=Organisationen uid=UID webauthn=Hardware-Sicherheitsschlüssel -public_profile=Öffentliches Profil +public_profile=Öffentliches profil biography_placeholder=Erzähle anderen ein wenig über dich selbst! (Markdown wird unterstützt) location_placeholder=Teile deinen ungefähren Standort mit anderen profile_desc=Über dich diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 5638d35c40..116d5ba5a8 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -3939,12 +3939,12 @@ submodule=Υπομονάδα [search] code_search_unavailable = Η αναζήτηση κώδικα δεν είναι επί του παρόντος διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή σας. keyword_search_unavailable = Η αναζήτηση με την χρήση λέξεων-κλειδιών δεν είναι επί του παρόντος διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή σας. -runner_kind = Αναζήτηση runner... +runner_kind = Αναζήτηση runner… code_search_by_git_grep = Για την αναζήτηση κώδικα, χρησιμοποιείται η εντολή «git grep». Ίσως να παρουσιαστούν καλύτερα αποτελέσματα, αν ο διαχειριστής σας ενεργοποιήσει ένα ευρετήριο για αποθετήρια («Repository Indexer»). package_kind = Αναζήτηση πακέτων… -project_kind = Αναζήτηση έργων... +project_kind = Αναζήτηση έργων… branch_kind = Αναζήτηση κλάδων… -commit_kind = Αναζήτηση commit... +commit_kind = Αναζήτηση commit… no_results = Δεν βρέθηκαν κατάλληλα αποτελέσματα. search = Αναζήτηση… type_tooltip = Είδος αναζήτησης @@ -3958,8 +3958,8 @@ org_kind = Αναζήτηση οργανισμών… team_kind = Αναζήτηση ομαδών… code_kind = Αναζήτηση κώδικα… exact_tooltip = Να συμπεριληφθούν μόνο αποτελέσματα που ταιριάζουν με τον όρο αναζήτησης -issue_kind = Αναζήτηση ζητημάτων... -pull_kind = Αναζήτηση pull... +issue_kind = Αναζήτηση ζητημάτων… +pull_kind = Αναζήτηση pull… exact = Ακριβής milestone_kind = Αναζήτηση ορόσημων... union = Ένωση diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index 6393765d63..5a06120e9e 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -630,7 +630,7 @@ account = Konto ssh_gpg_keys = SSH / GPG-ŝlosiloj twofa_disable = Malaktivigi duoblan aŭtentikigon twofa_enroll = Ŝalti duoblan aŭtentikigon -orgs = Mastrumi organizaĵojn +orgs = Organizaĵoj blocked_users = Blokitaj uzantoj profile = Profilo ui = Haŭto @@ -686,7 +686,7 @@ verify_ssh_key_success = SSH-ŝlosilo «%s» jam konfirmiĝis. edit_oauth2_application = Redakti OAuth2-programon gpg_key_deletion = Forigi GPG-ŝlosilon gpg_key_matched_identities = Akordaj identecoj: -manage_themes = Elekti implicitan haŭton +manage_themes = Defaŭlta temo ssh_key_deletion = Forigi SSH-ŝlosilon key_state_desc = Ĉi tiu ŝlosilo uziĝis dum la pasintaj 7 tagoj valid_forever = Validos dumĉiame @@ -700,7 +700,7 @@ primary = Ĉefa ssh_disabled = SSH malaktivigita update_avatar_success = Via profilbildo konserviĝis. keep_email_private = Kaŝi retpoŝtadreson -manage_openid = Mastrumi OpenID-adresojn +manage_openid = OpenID-adresoj delete_current_avatar = Forigi nunan profilbildon email_preference_set_success = Retpoŝta prefero konserviĝis sukcese. permissions_access_all = Ĉiu (publika, privata, kaj limigita) @@ -884,9 +884,9 @@ commit_kind = Serĉi enmetojn… no_results = Ne trovis kongruantajn rezultojn. exact = Ĝusta exact_tooltip = Inkluzivas nur rezultojn kongruantajn kun la ĝustaj serĉoterminoj -issue_kind = Serĉi erarojn... +issue_kind = Serĉi erarojn… regexp_tooltip = Interpretas la serĉoterminoj kiel regulesprimo fuzzy = Svaga branch_kind = Serĉi disbranĉigojn… -runner_kind = Serĉi rulantojn... -pull_kind = Serĉi tirpetojn... \ No newline at end of file +runner_kind = Serĉi rulantojn… +pull_kind = Serĉi tirpetojn… \ No newline at end of file diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index c572cf6385..582baeebdd 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -740,7 +740,7 @@ avatar=Avatar ssh_gpg_keys=Claves SSH / GPG social=Redes sociales applications=Aplicaciones -orgs=Administrar organizaciones +orgs=Organizaciones repos=Repositorios delete=Eliminar cuenta twofa=Autenticación de dos factores (TOTP) @@ -1075,8 +1075,8 @@ keep_pronouns_private = Mostrar pronombres solo a personas autenticadas storage_overview = Resumen del almacenamiento quota.sizes.assets.artifacts = Artefactos quota.sizes.assets.attachments.releases = Archivos adjuntos del lanzamiento -change_username_redirect_prompt.with_cooldown.few = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d días, aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. -change_username_redirect_prompt.with_cooldown.one = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d día, aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. +change_username_redirect_prompt.with_cooldown.few = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d días. Aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. +change_username_redirect_prompt.with_cooldown.one = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d día. Aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. quota.rule.exceeded = Excedido quota.rule.no_limit = Ilimitado quota.sizes.assets.all = Activos @@ -1574,7 +1574,7 @@ issues.remove_ref_at=`eliminó la referencia %s %s` issues.add_ref_at=`añadió la referencia %s %s` issues.delete_branch_at=`eliminó la rama %s %s` issues.filter_label=Etiqueta -issues.filter_label_exclude=`Usa alt + clic/enter para excluir etiquetas` +issues.filter_label_exclude=Usa Alt + Click para excluir etiquetas issues.filter_label_no_select=Todas las etiquetas issues.filter_label_select_no_label=Sin etiqueta issues.filter_milestone=Hito @@ -2887,6 +2887,8 @@ pulls.delete_after_merge.head_branch.is_default = La rama actual que desea elimi summary_card_alt = Tarjeta de resumen del repositorio %s settings.pull_mirror_sync_quota_exceeded = Cuota excedida, no se empujan los cambios. archive.nocomment = No es posible hacer comentarios porque el repositorio está archivado. +sync_fork.branch_behind_one = Esta rama esta %[1]d cambios detrás de %[2]s +sync_fork.branch_behind_few = Esta rama está %[1]d confirmaciones detrás de %[2]s [graphs] component_loading = Cargando %s… diff --git a/options/locale/locale_et.ini b/options/locale/locale_et.ini index e54ceadbb5..58e5776d33 100644 --- a/options/locale/locale_et.ini +++ b/options/locale/locale_et.ini @@ -145,25 +145,25 @@ fuzzy_tooltip = Lisage tulemused mis vastavad ka otsingu terminile union = Märksõnad exact = Täpne exact_tooltip = Sisaldab ainult tulemusi mis vastavad täpsele otsingusõnale -repo_kind = Otsi hoidlad... -user_kind = Otsi kasutajaid... -org_kind = Otsi organisatsioone... -team_kind = Otsi meeskonnad... -code_kind = Otsi koodi... +repo_kind = Otsi hoidlad… +user_kind = Otsi kasutajaid… +org_kind = Otsi organisatsioone… +team_kind = Otsi meeskonnad… +code_kind = Otsi koodi… code_search_by_git_grep = Praeguse koodi otsingu tulemused annab "git grep". Paremaid tulemusi võib saada, kui saidi administraator lubab koodi indekseerija. -package_kind = Otsi pakette... -project_kind = Otsi projekte... -branch_kind = Otsi harusid... -commit_kind = Otsi kommiteid... -runner_kind = Otsi jooksjaid... +package_kind = Otsi pakette… +project_kind = Otsi projekte… +branch_kind = Otsi harusid… +commit_kind = Otsi kommiteid… +runner_kind = Otsi jooksjaid… no_results = Sobivaid tulemusi ei leitud. -issue_kind = Otsi probleeme... +issue_kind = Otsi probleeme… milestone_kind = Otsi verstapostid... type_tooltip = Otsingu tüüp code_search_unavailable = Koodide otsing ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. union_tooltip = Sisaldab tulemused mis vastavad mis tahes tühikutega eraldatud võtmesõnadele keyword_search_unavailable = Otsing märksõna järgi ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. -pull_kind = Otsi tõmbepäringuid... +pull_kind = Otsi tõmbepäringuid… [aria] navbar = Navigatsiooniriba diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index dae0695495..23cb74f814 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -608,7 +608,7 @@ avatar=آواتار ssh_gpg_keys=کلید‌های SSH / GPG social=حساب های اجتماعی applications=برنامه‌ها -orgs=مدیریت سازمان‌ها +orgs=سازمان‌ها repos=مخازن delete=حذف حساب کاربری twofa=احراز هویت دوگانه @@ -653,8 +653,8 @@ password_change_disabled=کاربران غیر محلی نمیتوانند گذ emails=نشانی‌های ایمیل manage_emails=مدیریت نشانی‌های ایمیل -manage_themes=تم پیش فرض را انتخاب کنید -manage_openid=مدیریت نشانی‌های OpenID +manage_themes=تم پیش فرض +manage_openid=آدرس‌های OpenID theme_desc=این پوشته پیش فرض شما در سراسر سایت می باشد. primary=اصلی activated=فعال شده @@ -2766,23 +2766,23 @@ search = جستجو... fuzzy = درهم fuzzy_tooltip = پیامدهایی را درج کنید که دقیقا با عبارت جستجو همخوانی داشته باشند regexp = عبارات باقاعده -pull_kind = جستجو واکشی‌ها... +pull_kind = جستجو واکشی‌ها… no_results = نتیجه درخوری یافت نشد. -runner_kind = جستجو دونده‌ها... +runner_kind = جستجو دونده‌ها… keyword_search_unavailable = جستجو کلیدواژه اکنون در درسترس نیست. لطفا با مدیر سایت در میان بگذارید. -repo_kind = جستجو مخازن... +repo_kind = جستجو مخازن… regexp_tooltip = اصطلاح جستجو شده را با عبارات باقاعده تفسیر کن -user_kind = جستجو کاربران... -org_kind = جستجو سازمان‌ها... -team_kind = جستجو گروه‌ها... -package_kind = جستجو بسته‌ها... -project_kind = جستجو پروژه‌ها... +user_kind = جستجو کاربران… +org_kind = جستجو سازمان‌ها… +team_kind = جستجو گروه‌ها… +package_kind = جستجو بسته‌ها… +project_kind = جستجو پروژه‌ها… code_search_unavailable = جستجوی کد اکنون در دسترس نیست. لطفا با مدیر سایت درمیان بگذارید. -code_kind = جستجو کدها... +code_kind = جستجو کدها… union = بهم پیوستگی union_tooltip = نتایجی را در بر بگیر که با هر یک از کلیدواژه‌های جدا شده از فضای‌خالی مطابقت دارد -branch_kind = جستجو شاخه‌ها... -commit_kind = جستجو سپرده‌ها... -issue_kind = جستجو مشکلات... +branch_kind = جستجو شاخه‌ها… +commit_kind = جستجو سپرده‌ها… +issue_kind = جستجو مشکلات… exact = مو به مو exact_tooltip = نتایجی را در بر بگیر که مو به مو با اصطلاح جستجو شده یکی باشد diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 27942ead99..263fab8774 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -667,6 +667,12 @@ username_change_not_local_user = Ei-paikallisten käyttäjien ei sallita vaihtaa admin_cannot_delete_self = Et voi poistaa itseäsi, kun olet ylläpitäjä. Poista ensin ylläpito-oikeudet itseltäsi. username_claiming_cooldown = Käyttäjänimeä ei voi ottaa käyttöön, koska siihen kohdistuva suojaamisjakso ei ole vielä päättynyt. Käyttäjänimen voi ottaa käyttöön %[1]s. email_domain_is_not_allowed = Käyttäjän sähköpostiosoitteen %s verkkotunnus on ristiriidassa EMAIL_DOMAIN_ALLOWLIST:in tai EMAIL_DOMAIN_BLOCKLIST:in kanssa. Varmista, että olen asettanut sähköpostiosoitteen oikein. +unsupported_login_type = Tällä kirjautumistavalla ei voi poistaa tunnusta. +invalid_ssh_principal = Väärä toimija: %s +unset_password = Kirjautuneen käyttäjän salasanaa ei ole asetettu. +invalid_group_team_map_error = ` kuvaus ei ole kelvollinen: %s` +2fa_auth_required = Etävierailu vaati kaksivaiheisen todennuksen. +visit_rate_limit = Etävierailujen pyyntörajoitukset. [user] @@ -1037,6 +1043,8 @@ passcode_invalid = Virheellinen pääsykoodi. Yritä uudelleen. then_enter_passcode = Kirjoita sovelluksessa näkyvä pääsykoodi: gpg_key_matched_identities_long = Tähän avaimeen upotetut identiteetit vastaavat tämän käyttäjän seuraavia aktivoituja sähköpostiosoitteita. Kommitit, jotka vastaavat näitä sähköpostiosoitteita, voidaan vahvistaa tällä avaimella. twofa_failed_get_secret = Salaisuuden saaminen epäonnistui. +uid = UID +hidden_comment_types.ref_tooltip = Kommentit missä tähän ongelmaan viitattiin toisesta ongelmasta/kommitista/… [repo] owner=Omistaja diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 35378c5653..032b8b0435 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -699,7 +699,7 @@ security = Seguridad avatar = Avatar ssh_gpg_keys = Mga SSH / GPG key applications = Mga Aplikasyon -orgs = Ipamahala ang mga organisasyon +orgs = Mga organisasyon repos = Mga Repositoryo delete = Burahin ang account twofa = Authentikasyong two-factor (TOTP) @@ -707,7 +707,7 @@ account_link = Mga naka-link na account uid = UID webauthn = Authentikasyong two-factor (Mga security key) blocked_users = Mga hinarang na user -public_profile = Pampublikong Profile +public_profile = Pampublikong profile location_placeholder = Ibahagi ang iyong tinatayang lokasyon sa iba password_username_disabled = Ang mga di-lokal na gumagamit ay hindi pinapayagan na baguhin ang kanilang username. Mangyaring makipag-ugnayan sa iyong tagapangasiwa ng site para sa higit pang mga detalye. full_name = Buong pangalan diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 544e590e85..0c9df0afa0 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -4091,4 +4091,4 @@ issues.write = Écrire : Fermer des tickets et gérer les métadonnées t pulls.read = Lire : Lire et créer des demandes de tirage. [translation_meta] -test = Ceci est une chaîne de test. Elle n'est pas affichée dans Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) +test = Ceci est une chaîne de test. Elle n'est pas affichée dans Forgejo même mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index deadb72a8f..1d7414a0d8 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -132,20 +132,20 @@ fuzzy = Doiléir fuzzy_tooltip = Cuir san áireamh torthaí a mheaitseálann an téarma cuardaigh go dlúth freisin exact = Beacht exact_tooltip = Ní chuir san áireamh ach torthaí a mheaitseálann leis an téarma -repo_kind = Cuardaigh stórtha... -user_kind = Cuardaigh úsáideoirí... -org_kind = Cuardaigh eagraíochtaí... -team_kind = Cuardaigh foirne... -code_kind = Cód cuardaigh... +repo_kind = Cuardaigh stórtha… +user_kind = Cuardaigh úsáideoirí… +org_kind = Cuardaigh eagraíochtaí… +team_kind = Cuardaigh foirne… +code_kind = Cód cuardaigh… code_search_unavailable = Níl cuardach cód ar fáil faoi láthair. Déan teagmháil le riarthóir an láithreáin. -package_kind = Cuardaigh pacáistí... -project_kind = Cuardaigh tionscadail... -branch_kind = Cuardaigh brainsí... -commit_kind = Cuardaigh tiomáintí... -runner_kind = Cuardaigh reathaithe... +package_kind = Cuardaigh pacáistí… +project_kind = Cuardaigh tionscadail… +branch_kind = Cuardaigh brainsí… +commit_kind = Cuardaigh tiomáintí… +runner_kind = Cuardaigh reathaithe… no_results = Níl aon torthaí meaitseála le fáil. -issue_kind = Saincheisteanna cuardaigh... -pull_kind = Cuardaigh iarratais tarraingthe... +issue_kind = Saincheisteanna cuardaigh… +pull_kind = Cuardaigh iarratais tarraingthe… keyword_search_unavailable = Níl cuardach de réir eochairfhocal ar fáil faoi láthair. Déan teagmháil le riarthóir an láithreáin. [aria] @@ -507,12 +507,12 @@ avatar = Abhatár ssh_gpg_keys = Eochracha SSH/GPG applications = Iarratais repos = Stórais -delete = Scrios Cuntas +delete = Scrios cuntas twofa = Fíordheimhniú Dhá Fachtóir (TOTP) organization = Eagraíochtaí uid = UID webauthn = Fíordheimhniú Dhá-Fachtóir (Eochracha Slándála) -public_profile = Próifíl Phoiblí +public_profile = Próifíl phoiblí location_placeholder = Comhroinn do shuíomh thart le daoine eile full_name = Ainm Iomlán website = Láithreán Gréasáin diff --git a/options/locale/locale_gl.ini b/options/locale/locale_gl.ini index 3854b375af..c380e9b2ff 100644 --- a/options/locale/locale_gl.ini +++ b/options/locale/locale_gl.ini @@ -196,29 +196,29 @@ link_modal.paste_reminder = Consello: Coa URL no portapapeis, podes pegala direc [search] search = Buscar... type_tooltip = Tipo de procura -repo_kind = Buscar repositorios... -user_kind = Buscar usuarios... +repo_kind = Buscar repositorios… +user_kind = Buscar usuarios… regexp = RegExp regexp_tooltip = Interpretar o termo da procura como expresión regular -org_kind = Procurar organizacións... -team_kind = Procurar equipos... -code_kind = Procurar código... +org_kind = Procurar organizacións… +team_kind = Procurar equipos… +code_kind = Procurar código… code_search_unavailable = A procura de código non está dispoñible neste momento. Por favor contacte coa persoa responsable da administración da páxina. -package_kind = Buscar paquetes... +package_kind = Buscar paquetes… fuzzy = Difusa fuzzy_tooltip = Incluír resultados que tamén coincidan estreitamente co termo da procura union = Palabras clave union_tooltip = Incluír resultados correspondentes a calquera dal palabras clave separadas por espazos en branco exact = Exacta exact_tooltip = Incluír só resultados correspondentes ao termo exacto da procura -issue_kind = Procurar incidencias... -project_kind = Buscar proxectos... -branch_kind = Buscar ramas... +issue_kind = Procurar incidencias… +project_kind = Buscar proxectos… +branch_kind = Buscar ramas… no_results = Non se atoparon resultados coincidentes. keyword_search_unavailable = A busca por palabra clave non está dispoñible actualmente. Póñase en contacto co administrador do sitio. -commit_kind = Buscar achegas... -runner_kind = Buscar executores... -pull_kind = Buscar pulls... +commit_kind = Buscar achegas… +runner_kind = Buscar executores… +pull_kind = Buscar pulls… [startpage] platform = Multiplataforma diff --git a/options/locale/locale_he.ini b/options/locale/locale_he.ini index 19c4815277..8a5bc86163 100644 --- a/options/locale/locale_he.ini +++ b/options/locale/locale_he.ini @@ -150,24 +150,24 @@ union = מילות מפתח exact = מדויק exact_tooltip = תוצאות יתאימו במדויק לתוכן תיבת החיפוש regexp = רג'קס -user_kind = חיפוש אנשים... -code_kind = חיפוש קוד... -team_kind = חיפוש צוותים... +user_kind = חיפוש אנשים… +code_kind = חיפוש קוד… +team_kind = חיפוש צוותים… no_results = לא נמצאו תוצאות. union_tooltip = תוצאות יכללו לפחות מילת מפתח אחת; אפשר להפריד מילות מפתח עם רווחים -org_kind = חיפוש ארגונים... -package_kind = חיפוש חבילות... -project_kind = חיפוש פרוייקטים... -branch_kind = חיפוש ענפים... -commit_kind = חיפוש קומיטים... -issue_kind = חיפוש סוגיות... +org_kind = חיפוש ארגונים… +package_kind = חיפוש חבילות… +project_kind = חיפוש פרוייקטים… +branch_kind = חיפוש ענפים… +commit_kind = חיפוש קומיטים… +issue_kind = חיפוש סוגיות… fuzzy_tooltip = תוצאות יתאימו לתוכן תיבת החיפוש בקירוב; מומלץ כנגד שגיאות כתיב -repo_kind = חיפוש קרפיפים... +repo_kind = חיפוש קרפיפים… code_search_by_git_grep = תוצאות החיפוש יוצרו על ידי "git grep"; יכול להיות שיתקבלו תוצאות טובות יותר אם מנהלי המערכת יפעילו את המפתחן. -runner_kind = חיפוש מריצים... +runner_kind = חיפוש מריצים… keyword_search_unavailable = חיפוש מילות מפתח לא זמין. נא לדווח למנהלי המערכת. code_search_unavailable = חיפוש קוד לא זמין. נא לדווח למנהלי המערכת. -pull_kind = חיפוש בקשות מיזוג... +pull_kind = חיפוש בקשות מיזוג… [heatmap] number_of_contributions_in_the_last_12_months = % תרומות ב־12 החודשים האחרונים @@ -384,6 +384,8 @@ allow_password_change = הכרחת המשתמש לעדכן את סיסמת חש account_activated = חשבונך הופעל resent_limit_prompt = כבר ביקשת מייל אימות בשלושת הדקות האחרונות. נא לחכות ולנסות שוב. has_unconfirmed_mail = שלום %s, חשבונך משויך לכתובת אימייל לא מאומתת (%s). אם לא קיבלת הודעת אימות באימייל, או שאתה צריך חדשה, נא ללחוץ על הכפתור למטה. +confirmation_mail_sent_prompt = אימייל אימות חדש נשלח ל־%s. יש לבדוק את תיבת הדואר וללחוץ על הלינק תוך %s על מנת להשלים את רישום החשבון. אם כתובת המייל שגוייה, אפשר להיכנס לחשבון ולבקש דוא"ל אימות לכתובת אחרת. +reset_password_mail_sent_prompt = אימייל אימות חדש נשלח ל־%s. יש לבדוק את תיבת הדואר וללחוץ על הלינק תוך %s על מנת להשלים את רישום החשבון. [settings] key_content = תוכן @@ -661,6 +663,9 @@ issues.label_exclusive = בחירה בודדת issues.label_archive = לארכיון issues.label_archived_filter = הצגת תוויות מהארכיון issues.label_archive_tooltip = תוויות בארכיון לא מוצעות בחיפוש על־בסיס תווית כברירת מחדל. +issues.deleted_milestone = נמחק +issues.self_assign_at = `שייךה עצמית %s` +issues.deleted_project = נמחק [translation_meta] test = ואהבת לרעך כמוך diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index b8c69cfe4a..841fb76bbe 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -330,7 +330,7 @@ code_no_results=Nincs találat a keresési kifejezésedre. code_last_indexed_at=Utoljára indexelve: %s [auth] -create_new_account=Regisztráció +create_new_account=Fiók regisztrálása register_helper_msg=Van már felhasználói fiókja? Jelentkezzen be! social_register_helper_msg=Van már felhasználói fiókja? Csatlakoztassa most! disable_register_prompt=Regisztráció le van tiltva. Kérjük, lépjen kapcsolatba az oldal adminisztrátorával. @@ -532,8 +532,8 @@ password_change_disabled=A nem helyi felhasználók nem frissíthetik jelszavuka emails=E-mail címek manage_emails=E-mail címek kezelése -manage_themes=Válassza ki az alapértelmezett témát -manage_openid=OpenID címek kezelése +manage_themes=Alapértelmezett téma +manage_openid=OpenID címek theme_desc=Ez lesz az alapértelmezett téma az oldalon. primary=Elsődleges activated=Aktivált @@ -1779,14 +1779,14 @@ directory = Könyvtár [search] search = Keresés... type_tooltip = Keresés típusa -code_kind = Kód keresése... +code_kind = Kód keresése… code_search_unavailable = A kódban való keresés jelenleg nem elérhető. Kérem vegye fel a kapcsolatot az oldal adminisztrátorával. -package_kind = Csomagok keresése... -project_kind = Projektek keresése... -user_kind = Felhasználók keresése... -repo_kind = Tárak keresése... -org_kind = Szervezetek keresése... -team_kind = Csapatok keresése... +package_kind = Csomagok keresése… +project_kind = Projektek keresése… +user_kind = Felhasználók keresése… +repo_kind = Tárak keresése… +org_kind = Szervezetek keresése… +team_kind = Csapatok keresése… exact = Pontos code_search_by_git_grep = A kódkeresés jelenleg a "git grep" parancsot használja. Lehet, hogy jobb találatok is lennének, ha a webhely adminisztrátora bekapcsolja a forráskód indexelését. milestone_kind = Mérföldkövek keresése... @@ -1794,8 +1794,8 @@ fuzzy_tooltip = A keresési kifejezéshez hasonló találatok mutatása fuzzy = Hasonlók union = Kulcsszavakra union_tooltip = A szóközzel elválasztott kulcsszavak bármelyikét tartalmazó találatok mutatása -branch_kind = Ágak keresése... +branch_kind = Ágak keresése… no_results = Nincsenek megfelelő találatok. -issue_kind = Hibajegyek keresése... +issue_kind = Hibajegyek keresése… exact_tooltip = Csak a keresési kifejezést pontosan tartalmazó találatok mutatása keyword_search_unavailable = A kulcsszó alapú keresés jelenleg nem elérhető. Kérlek értesítsd az oldal rendszergazdáját. diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 32d28837e1..c935bd9540 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -206,9 +206,9 @@ email_title = Pengaturan email smtp_from = Kirim Email Sebagai [home] -uname_holder=Nama Pengguna atau Alamat Surel +uname_holder=Nama pengguna atau alamat surel password_holder=Kata Sandi -switch_dashboard_context=Alihkan Dasbor Konteks +switch_dashboard_context=Alihkan dasbor konteks my_repos=Repositori show_more_repos=Tampilkan repositori lainnya… collaborative_repos=Repositori Kolaboratif @@ -236,7 +236,7 @@ org_no_results=Tidak ada organisasi yang cocok ditemukan. code_no_results=Tidak ada kode sumber yang cocok dengan istilah yang anda cari. [auth] -create_new_account=Daftar Akun +create_new_account=Daftar akun register_helper_msg=Sudah memiliki akun? Masuk sekarang! social_register_helper_msg=Sudah memiliki akun? Hubungkan sekarang! disable_register_prompt=Maaf, pendaftaran telah dinonaktifkan. Silakan hubungi administrator situs. @@ -269,11 +269,11 @@ twofa_passcode_incorrect=Kata sandi Anda salah. Jika Anda salah tempatkan perang twofa_scratch_token_incorrect=Kode coretan anda tidak tepat. login_userpass=Masuk tab_openid=OpenID -oauth_signup_tab=Daftar Akun Baru -oauth_signup_submit=Akun Lengkap -oauth_signin_tab=Tautkan ke Akun yang Tersedia -oauth_signin_title=Masuk untuk Izinkan Akun Tertaut -oauth_signin_submit=Taut Akun +oauth_signup_tab=Daftar akun baru +oauth_signup_submit=Akun lengkap +oauth_signin_tab=Tautkan ke akun yang tersedia +oauth_signin_title=Masuk untuk izinkan akun tertaut +oauth_signin_submit=Taut akun openid_connect_submit=Sambungkan openid_connect_title=Sambungkan ke akun yang sudah ada openid_connect_desc=OpenID URI yang dipilih tak dikenal. Asosiasikan dengan akun baru disini. @@ -398,14 +398,14 @@ avatar=Avatar ssh_gpg_keys=Kunci SSH / GPG social=Akun Sosial applications=Aplikasi -orgs=Kelola organisasi +orgs=Organisasi repos=Repositori -delete=Hapus Akun +delete=Hapus akun twofa=Otentikasi Dua-Faktor account_link=Akun Tertaut organization=Organisasi -public_profile=Profil Publik +public_profile=Profil publik password_username_disabled=Pengguna non-lokal tidak diizinkan untuk mengubah nama pengguna mereka. Silakan hubungi administrator sistem anda untuk lebih lanjut. full_name=Nama Lengkap website=Situs Web @@ -438,8 +438,8 @@ password_change_disabled=Pengguna non-lokal tidak dapat mengganti kata sandi mer emails=Alamat Surel manage_emails=Kelola Alamat Surel -manage_themes=Pilih tema default -manage_openid=Kelola alamat OpenID +manage_themes=Tema default +manage_openid=Alamat OpenID theme_desc=Ini akan menjadi tema asal Anda pada keseluruhan situs. primary=Utama activated=Diaktifkan @@ -1484,10 +1484,10 @@ search = Cari... type_tooltip = Tipe pencarian fuzzy_tooltip = Termasuk juga hasil yang mendekati kata pencarian exact_tooltip = Hanya menampilkan hasil yang cocok dengan istilah pencarian -repo_kind = Cari repo... -user_kind = Telusuri pengguna... -org_kind = Cari organisasi... -team_kind = Cari tim... -code_kind = Cari kode... +repo_kind = Cari repo… +user_kind = Telusuri pengguna… +org_kind = Cari organisasi… +team_kind = Cari tim… +code_kind = Cari kode… code_search_unavailable = Pencarian kode saat ini tidak tersedia. Silahkan hubungi administrator. -branch_kind = Cari cabang... +branch_kind = Cari cabang… diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index baf8286923..f3333dbea2 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -223,7 +223,7 @@ default_keep_email_private.description=Fela sjálfgefið netföng nýrra notenda no_reply_address_helper=Lén fyrir notendur með falið netfang. Til dæmis notandanafnið „joe“ verður skráð í Git sem „joe@noreply.example.org“ ef falið tölvupóstlén er stillt á „noreply.example.org“. [home] -uname_holder=Notandanafn eða Netfang +uname_holder=Notandanafn eða netfang password_holder=Lykilorð my_repos=Hugbúnaðarsöfn show_more_repos=Sýna fleiri hugbúnaðarsöfn… @@ -255,7 +255,7 @@ org_no_results=Engar samsvarandi stofnanir fundust. code_no_results=Enginn samsvarandi frumkóði fannst eftur þínum leitarorðum. [auth] -create_new_account=Skrá Notanda +create_new_account=Skrá notanda register_helper_msg=Ertu nú þegar með notanda? Skráðu þig inn núna! social_register_helper_msg=Ertu nú þegar með reikning? Tengdu hann núna! manual_activation_only=Hafðu samband við stjórnanda vefsvæðisins til að ljúka virkjun. @@ -276,13 +276,13 @@ verify=Staðfesta scratch_code=Skrapkóði use_scratch_code=Nota skrapkóða twofa_scratch_token_incorrect=Skrapkóði þinn er rangur. -login_userpass=Skrá Inn +login_userpass=Skrá inn tab_openid=OpenID -oauth_signup_tab=Skrá Nýjan Notanda -oauth_signup_title=Klára Nýjum Notanda -oauth_signup_submit=Klára Notanda -oauth_signin_tab=Tengja Núverandi Reikning -oauth_signin_submit=Tengja Notanda +oauth_signup_tab=Skrá nýjan notanda +oauth_signup_title=Klára nýjum notanda +oauth_signup_submit=Klára notanda +oauth_signin_tab=Tengja núverandi reikning +oauth_signin_submit=Tengja notanda openid_connect_submit=Tengjast openid_register_title=Skrá nýjan notanda disable_forgot_password_mail=Endurheimting reiknings er óvirk vegna þess að enginn tölvupóstur er uppsettur. Vinsamlegast hafðu samband við síðustjórann þinn. @@ -434,15 +434,15 @@ avatar=Notandamynd ssh_gpg_keys=SSH og GPG Lyklar social=Félagsreikningar applications=Forrit -orgs=Stjórna Stofnunum +orgs=Stofnanir repos=Hugbúnaðarsöfn -delete=Eyða Reikningi +delete=Eyða reikningi twofa=Tvíþætt Auðkenning account_link=Tengdir Reikningar organization=Stofnanir webauthn=Öryggislyklar -public_profile=Opinber Notandasíða +public_profile=Opinber notandasíða password_username_disabled=Notendum utan staðarins er ekki heimilt að breyta notendanafni sínu. Vinsamlegast hafðu samband við síðustjórann þinn til að fá frekari upplýsingar. full_name=Fullt Nafn website=Vefsíða diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 94ee88c4b6..d9acc4c430 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -423,7 +423,7 @@ allow_password_change=Richiede all'utente di cambiare la password (scelta consig reset_password_mail_sent_prompt=Un'e-mail di conferma è stata inviata a %s. Per completare il processo di recupero dell'account, controlla la tua posta in arrivo e clicca sul link entro i prossimi %s secondi. active_your_account=Attiva il tuo account account_activated=L'account è stato attivato -prohibit_login=L'accesso è proibito +prohibit_login=L'account è sospeso resent_limit_prompt=Hai già richiesto un'e-mail d'attivazione recentemente. Si prega di attenere 3 minuti e poi riprovare. has_unconfirmed_mail=Ciao %s, hai un indirizzo di posta elettronica non confermato (%s). Se non hai ricevuto una e-mail di conferma o vuoi riceverla nuovamente, fare clic sul pulsante qui sotto. resend_mail=Clicca qui per inviare nuovamente l'e-mail di attivazione @@ -742,7 +742,7 @@ avatar=Avatar ssh_gpg_keys=Chiavi SSH / GPG social=Account Sociali applications=Applicazioni -orgs=Gestisci le organizzazioni +orgs=Organizzazioni repos=Repositori delete=Elimina account twofa=Verifica in due passaggi @@ -803,8 +803,8 @@ password_change_disabled=Gli utenti non locali non possono cambiare la loro pass emails=Indirizzi email manage_emails=Gestisci indirizzi email -manage_themes=Seleziona il tema predefinito -manage_openid=Gestisci gli indirizzi OpenID +manage_themes=Tema predefinito +manage_openid=Indirizzi OpenID theme_desc=Questo sarà il tuo tema di predefinito in tutto il sito. primary=Primario activated=Attivato @@ -4014,25 +4014,25 @@ type_tooltip = Tipo ricerca search = Cerca… fuzzy = Approssimativa match = Precisa -org_kind = Cerca organizzazioni... -package_kind = Ricerca pacchetti... +org_kind = Cerca organizzazioni… +package_kind = Ricerca pacchetti… code_search_unavailable = La ricerca del codice non è attualmente disponibile. Contatta l'amministratorə del sito. -code_kind = Cerca nel codice... -team_kind = Cerca team... +code_kind = Cerca nel codice… +team_kind = Cerca team… code_search_by_git_grep = I risultati della ricerca del codice sono forniti da "git grep". Potrebbero esserci risultati migliori se l'amministratore del sito avesse abilitato l'indicizzatore del codice. -project_kind = Ricerca progetti... -commit_kind = Ricerca commit... -branch_kind = Ricerca rami... +project_kind = Ricerca progetti… +commit_kind = Ricerca commit… +branch_kind = Ricerca rami… no_results = Non è stato trovato alcun risultato. keyword_search_unavailable = La ricerca per parole chiave non è attualmente disponibile. Contatta l'amministratore del sito. -runner_kind = Ricerca esecutori... +runner_kind = Ricerca esecutori… match_tooltip = Includi solo risultati che corrispondono precisamente al termine di ricerca fuzzy_tooltip = Includi anche risultati che corrispondono approssimativamente al termine di ricerca -user_kind = Cerca utenti... -repo_kind = Cerca repo... +user_kind = Cerca utenti… +repo_kind = Cerca repo… exact_tooltip = Includi solo i risultati che corrispondono esattamente al termine di ricerca -issue_kind = Cerca segnalazioni... -pull_kind = Cerca richieste... +issue_kind = Cerca segnalazioni… +pull_kind = Cerca richieste… exact = Esatto milestone_kind = Ricerca tappe... regexp_tooltip = Interpreta i termini di ricerca come un'espressione regolare diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 6e30f76b9a..d7c657b149 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -250,7 +250,7 @@ err_empty_db_path=SQLite3のデータベースパスを空にすることはで no_admin_and_disable_registration=管理者アカウントを作成せずに、セルフ登録を無効にすることはできません。 err_empty_admin_password=管理者パスワードは空にできません。 err_empty_admin_email=管理者のメールアドレスは空にできません。 -err_admin_name_is_reserved=管理者のユーザー名が不正です。予約済みのユーザー名です。 +err_admin_name_is_reserved=管理者のユーザー名が不正です。予約済みのユーザー名です err_admin_name_pattern_not_allowed=管理者のユーザー名が不正です。 予約済みのパターンにマッチしています err_admin_name_is_invalid=管理者のユーザー名が不正です @@ -1047,6 +1047,7 @@ language.title = 既定の言語 keep_activity_private.description = 公開アクティビティは、あなたとインスタンス管理者にのみ表示されます。 language.description = この言語はアカウントに保存され、ログイン後にデフォルトとして使用されます。 language.localization_project = Forgejo をあなたの言語に翻訳するのを手伝ってください。詳細はこちら。 +quota = クオータ [repo] new_repo_helper=リポジトリには、プロジェクトのすべてのファイルとリビジョン履歴が入ります。 すでにほかの場所でホストしていますか? リポジトリを移行 もどうぞ。 @@ -3903,20 +3904,20 @@ submodule=サブモジュール [search] search = 検索... type_tooltip = 検索タイプ -org_kind = 組織の検索... -code_kind = コードの検索... +org_kind = 組織を検索… +code_kind = コードを検索… fuzzy = あいまい -repo_kind = リポジトリの検索... +repo_kind = リポジトリを検索… code_search_unavailable = コード検索は現在利用できません。サイト管理者にお問い合わせください。 -branch_kind = ブランチの検索... -commit_kind = コミットの検索... -user_kind = ユーザーの検索... -team_kind = チームの検索... +branch_kind = ブランチを検索… +commit_kind = コミットを検索… +user_kind = ユーザーを検索… +team_kind = チームを検索… code_search_by_git_grep = 現在のコード検索結果は「git grep」によって提供されます。サイト管理者がコード インデクサーを有効にすると、より良い結果が得られる可能性があります。 -package_kind = パッケージの検索... -project_kind = プロジェクトの検索... +package_kind = パッケージを検索… +project_kind = プロジェクトを検索… keyword_search_unavailable = キーワードによる検索は現在ご利用いただけません。サイト管理者にお問い合わせください。 -runner_kind = ランナーの検索... +runner_kind = ランナーを検索… no_results = 一致する結果が見つかりませんでした。 fuzzy_tooltip = 入力された語句に近いものも結果に含める match = 一致 @@ -3924,8 +3925,8 @@ match_tooltip = 検索語句に厳密に一致するもののみ結果に含め milestone_kind = マイルストーンを検索... union_tooltip = 空白で区切られたキーワードのいずれかに一致する結果を含める exact_tooltip = 検索語句と完全に一致する結果のみを含める -issue_kind = イシューを検索... -pull_kind = プルを検索... +issue_kind = イシューを検索… +pull_kind = プルを検索… exact = 完全一致 regexp_tooltip = 検索語句を正規表現として解釈する regexp = 正規表現 diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index be0400bea4..8e8d9b1d6b 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -588,8 +588,8 @@ password_change_disabled=로컬 유저가 아닌 경우 Forgejo 웹 인터페이 emails=이메일 주소 manage_emails=이메일 주소 관리 -manage_themes=기본 테마 선택 -manage_openid=OpenID 주소 관리 +manage_themes=기본 테마 +manage_openid=OpenID 주소 theme_desc=이 테마가 사이트 전체 기본 테마가 됩니다. primary=대표 activated=활성화됨 @@ -1900,9 +1900,9 @@ runs.commit=커밋 [search] code_search_by_git_grep = 현재 코드 검색 결과는 "git grep"에 의해 제공됩니다.관리자가 코드 인덱서를 활성화하면 더 나은 결과가 제공될 수 있습니다. -branch_kind = 브랜치 검색... +branch_kind = 브랜치 검색… keyword_search_unavailable = 지금은 키워드로 검색이 지원되지 않습니다. 사이트 관리자에게 문의하십시오. -commit_kind = 커밋 검색... +commit_kind = 커밋 검색… no_results = 일치하는 결과를 찾을 수 없습니다. search = 검색… type_tooltip = 검색 타입 @@ -1910,11 +1910,11 @@ fuzzy_tooltip = 검색어와 밀접하게 일치하는 결과도 포함 repo_kind = 저장소 검색… user_kind = 사용자 검색… org_kind = 조직 검색… -team_kind = 팀 검색... -code_kind = 코드 검색... +team_kind = 팀 검색… +code_kind = 코드 검색… code_search_unavailable = 코드 검색은 현재 허용되지 않았습니다. 사이트 관리자와 연락하세요. -package_kind = 패키지 검색... -project_kind = 프로젝트 검색... +package_kind = 패키지 검색… +project_kind = 프로젝트 검색… exact_tooltip = 검색어와 정확하게 일치하는 결과만 포함 issue_kind = 이슈 검색… pull_kind = 풀 검색… diff --git a/options/locale/locale_lt.ini b/options/locale/locale_lt.ini index 868e5bff6e..9d1c938379 100644 --- a/options/locale/locale_lt.ini +++ b/options/locale/locale_lt.ini @@ -149,24 +149,24 @@ fuzzy = Tikslintinas union_tooltip = Įtraukti rezultatus, atitinkančius bet kurį iš matomą tarpą atskirtų raktažodžių exact = Tiksliai exact_tooltip = Įtraukti tik tuos rezultatus, kurie atitinka tikslią paieškos frazę -user_kind = Ieškoti naudotojų... -team_kind = Ieškoti komandų... -code_kind = Ieškoti kodo... +user_kind = Ieškoti naudotojų… +team_kind = Ieškoti komandų… +code_kind = Ieškoti kodo… fuzzy_tooltip = Įtraukti rezultatus, kurie taip pat labai atitinka paieškos terminą -repo_kind = Ieškoti saugyklų... +repo_kind = Ieškoti saugyklų… code_search_unavailable = Kodų paieška šiuo metu nepasiekiama. Kreipkis į svetainės administratorių. -org_kind = Ieškoti organizacijų... +org_kind = Ieškoti organizacijų… union = Bendrinis code_search_by_git_grep = Dabartiniai kodo paieškos rezultatai pateikiami atliekant „git grep“. Rezultatai gali būti geresni, jei svetainės administratorius įjungs kodo indeksuotoją. -package_kind = Ieškoti paketų... -project_kind = Ieškoti projektų... -commit_kind = Ieškoti įsipareigojimų... -runner_kind = Ieškoti vykdyklių... +package_kind = Ieškoti paketų… +project_kind = Ieškoti projektų… +commit_kind = Ieškoti įsipareigojimų… +runner_kind = Ieškoti vykdyklių… no_results = Nerasta atitinkamų rezultatų. -issue_kind = Ieškoti problemų... -branch_kind = Ieškoti šakų... +issue_kind = Ieškoti problemų… +branch_kind = Ieškoti šakų… milestone_kind = Ieškoti gairių... -pull_kind = Ieškoti sujungimų... +pull_kind = Ieškoti sujungimų… keyword_search_unavailable = Ieškoti pagal raktažodį šiuo metu nepasiekiamas. Susisiekite su svetainės administratoriumi. regexp = Reguliarusis reiškinys regexp_tooltip = Interpretuoti paieškos terminą kaip reguliariąją reiškinį diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini index fcc9888d8e..c4c266ad86 100644 --- a/options/locale/locale_ml-IN.ini +++ b/options/locale/locale_ml-IN.ini @@ -356,7 +356,7 @@ avatar=അവതാര്‍ ssh_gpg_keys=SSH / GPG കീകള്‍ social=സോഷ്യൽ അക്കൗണ്ടുകൾ applications=അപ്ലിക്കേഷനുകൾ -orgs=സംഘടനകളെ നിയന്ത്രിക്കുക +orgs=സംഘടനകൾ repos=കലവറകള്‍ delete=അക്കൗണ്ട് ഇല്ലാതാക്കുക twofa=ഇരട്ട ഘടക പ്രാമാണീകരണം @@ -400,8 +400,8 @@ password_change_disabled=പ്രാദേശിക ഇതര ഉപയോക emails=ഇ-മെയില്‍ വിലാസങ്ങള്‍ manage_emails=ഇമെയിൽ വിലാസങ്ങൾ നിയന്ത്രിക്കുക -manage_themes=സ്ഥിരസ്ഥിതി പ്രമേയം തിരഞ്ഞെടുക്കുക -manage_openid=ഓപ്പൺഐഡി വിലാസങ്ങൾ നിയന്ത്രിക്കുക +manage_themes=ഡിഫോൾട്ട് തീം +manage_openid=OpenID വിലാസങ്ങൾ email_desc=അറിയിപ്പുകൾക്കും മറ്റ് പ്രവർത്തനങ്ങൾക്കുമായി നിങ്ങളുടെ പ്രാഥമിക ഇമെയിൽ വിലാസം ഉപയോഗിക്കും. theme_desc=സൈറ്റിലുടനീളം ഇത് നിങ്ങളുടെ സ്ഥിരസ്ഥിതി പ്രമേയം ആയിരിക്കും. primary=പ്രാഥമികം diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index 57afa69f78..791f1b84d3 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -568,7 +568,7 @@ organization = Vereenigungen uid = UID webauthn = Twee-Faktooren-Anmellen (Sekerheids-Slötels) blocked_users = Blockeert Brukers -public_profile = Publikes Profil +public_profile = Publikes profil location_placeholder = Deel waar du umslags büst mit Annerns pronouns = Pronomens pronouns_custom = Eegene diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index f1a92a79f3..cf491d2f43 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1043,8 +1043,8 @@ language.title = Domyślny język language.localization_project = Pomóż nam przetłumaczyć Forgejo na twój język! Dowiedz się więcej. update_hints = Zaktualizuj wskazówki update_hints_success = Wskazówki zostały zaktualizowane. -change_username_redirect_prompt.with_cooldown.one = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dzień, nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. -change_username_redirect_prompt.with_cooldown.few = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dni, nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. +change_username_redirect_prompt.with_cooldown.one = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dzień. Nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. +change_username_redirect_prompt.with_cooldown.few = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dni. Nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. language.description = Ten język zostanie zapisany na twoim koncie i będzie używany jako domyślny po zalogowaniu. hidden_comment_types_description = Rodzaje komentarzy zaznaczone tutaj nie będą wyświetlały się na stronach zgłoszeń. Zaznaczenie "Etykieta" na przykład usunie wszystkie komentarze " dodał/usunął ". principal_desc = Te podmioty certyfikatu SSH będą powiązane z twoim kontem i pozwolą na pełen dostęp do twoich repozytoriów. @@ -3951,29 +3951,29 @@ normal_file = Zwykły plik search = Wyszukaj... type_tooltip = Typ wyszukiwania fuzzy = Przybliżone -package_kind = Wyszukaj pakiety... +package_kind = Wyszukaj pakiety… fuzzy_tooltip = Uwzględnij wyniki, które są bliskie wyszukiwanemu hasłu match = Dopasuj match_tooltip = Uwzględniaj tylko wyniki pasujące do wyszukiwanego hasła -repo_kind = Wyszukaj repozytoria... -user_kind = Wyszukaj użytkownilków... +repo_kind = Wyszukaj repozytoria… +user_kind = Wyszukaj użytkownilków… code_search_unavailable = Wyszukiwanie kodu jest obecnie niedostępne. Skontakuj sie z administratorem strony. no_results = Nie znaleziono pasujących wyników. -org_kind = Wyszukaj organizacje... -team_kind = Wyszukaj zespoły... -code_kind = Wyszukaj kod... +org_kind = Wyszukaj organizacje… +team_kind = Wyszukaj zespoły… +code_kind = Wyszukaj kod… code_search_by_git_grep = Obecne wyniki wyszukiwania kodu są dostarczane przez "git grep". Wyniki mogą być lepsze, jeśli administrator witryny włączy indeksator kodu. -project_kind = Wyszukaj projekty... -branch_kind = Wyszukaj gałęzie... -commit_kind = Wyszukaj commity... -runner_kind = Wyszukaj runnery... +project_kind = Wyszukaj projekty… +branch_kind = Wyszukaj gałęzie… +commit_kind = Wyszukaj commity… +runner_kind = Wyszukaj runnery… keyword_search_unavailable = Wyszukiwanie według słów kluczowych jest obecnie niedostępne. Skontaktuj się z administratorem strony. milestone_kind = Wyszukaj kamienie milowe... union_tooltip = Uwzględnia wyniki pasujące do dowolnego słowa kluczowego rozdzielonego białymi znakami exact = Dokładne exact_tooltip = Uwzględniaj tylko wyniki pasujące do wyszukiwanego hasła -issue_kind = Wyszukaj zgłoszenia... -pull_kind = Wyszukaj pull requesty... +issue_kind = Wyszukaj zgłoszenia… +pull_kind = Wyszukaj pull requesty… union = Unia regexp = RegExp regexp_tooltip = Interpretuj wyszukiwane hasło jako wyrażenie regularne diff --git a/options/locale/locale_ro.ini b/options/locale/locale_ro.ini index 305c34d013..06fdb45785 100644 --- a/options/locale/locale_ro.ini +++ b/options/locale/locale_ro.ini @@ -223,12 +223,12 @@ invalid_db_setting = Setările pentru bază de date sunt invalide: %v no_reply_address = Domeniu pentru adrese de email ascunse [search] -user_kind = Caută utilizatori... -team_kind = Caută echipe... -code_kind = Caută cod... -project_kind = Caută proiecte... -package_kind = Caută pachete... -org_kind = Caută organizații... +user_kind = Caută utilizatori… +team_kind = Caută echipe… +code_kind = Caută cod… +project_kind = Caută proiecte… +package_kind = Caută pachete… +org_kind = Caută organizații… code_search_unavailable = Căutarea de cod nu este disponibilă momentan. Te rog contactează administratorul site-ului. keyword_search_unavailable = Căutarea după cuvânt cheie nu este disponibilă momentan. Te rog contactează administratorul site-ului. no_results = Nu a fost găsit niciun rezultat corespunzător. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 33418a0621..c7cccc012c 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1063,7 +1063,7 @@ language.description = Выбранный язык будет сохранён language.localization_project = Помогите с переводом Forgejo на свой язык! Подробнее. user_block_yourself = Нельзя заблокировать себя. pronouns_custom_label = Другие местоимения -change_username_redirect_prompt.with_cooldown.one = Прежнее имя будет доступно для использования другим пользователям после истечения защиты в %[1]d день. Вы сможете вернуть его себе во время срока защиты. +change_username_redirect_prompt.with_cooldown.one = Прежнее имя будет доступно для использования другим пользователям после истечения простоя в %[1]d день. Вы сможете вернуть его себе во время срока простоя. change_username_redirect_prompt.with_cooldown.few = Прежнее имя будет доступно для использования другим пользователям после истечения защиты в %[1]d дней. Вы сможете вернуть его себе во время срока защиты. keep_pronouns_private = Показывать местоимения только зарегистрированным пользователям keep_pronouns_private.description = Местоимения будут скрыты от пользователей, не имеющих учётных записей на сервере. @@ -1641,9 +1641,9 @@ issues.closed_at=`задача была закрыта %s` issues.reopened_at=`задача была открыта снова %s` issues.commit_ref_at=`упоминание этой задачи в коммите %s` issues.ref_issue_from=`упоминание этой задачи %[3]s %[1]s` -issues.ref_pull_from=`упоминание этого запроса слияния %[3]s %[1]s` -issues.ref_closing_from=`упоминание из запроса на слияние %[3]s, который закроет эту задачу %[1]s` -issues.ref_reopening_from=`упоминание из запроса на слияние %[3]s, который повторно откроет эту задачу %[1]s` +issues.ref_pull_from=`упомянул этот запрос на слияние %[3]s %[1]s` +issues.ref_closing_from=`упомянул эту задачу в запросе на слияние %[3]s, который закроет её %[1]s` +issues.ref_reopening_from=`упомянул эту задачу в запросе на слияние %[3]s, который переоткроет эту задачу %[1]s` issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s` issues.ref_reopened_from=`задача была открыта снова %[4]s %[2]s` issues.ref_from=`из %[1]s` diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index cd8c9bf7d4..d6f4525c4d 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -482,7 +482,7 @@ avatar=අවතාර් ssh_gpg_keys=SSH/ජීපීජී යතුරු social=සමාජ ගිණුම් applications=යෙදුම් -orgs=සංවිධාන කළමනාකරණය +orgs=සංවිධාන repos=කෝෂ්ඨ delete=ගිණුම මකන්න twofa=ද්වි-සාධක සත්යාපනය @@ -527,8 +527,8 @@ password_change_disabled=දේශීය නොවන පරිශීලකය emails=වි-තැපැල් ලිපින manage_emails=වි-තැපැල් ලිපින කළමනාකරණය -manage_themes=පෙරනිමි තේමාව තෝරන්න -manage_openid=OpenID ලිපිනයන් කළමනාකරණය කරන්න +manage_themes=පෙරනිමි තේමාව +manage_openid=OpenID ලිපින theme_desc=මෙම වෙබ් අඩවිය හරහා ඔබගේ පෙරනිමි තේමාව වනු ඇත. primary=ප්රාථමික activated=සක්රිය diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 0c44df326a..aa2f863b14 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -597,7 +597,7 @@ avatar=Avatar ssh_gpg_keys=SSH / GPG kľúče social=Sociálne účty applications=Aplikácie -orgs=Spravovať organizácie +orgs=Organizácie repos=Repozitáre delete=Zmazať účet twofa=Dvojfaktorové overenie @@ -656,8 +656,8 @@ password_change_disabled=Externe overovaní používatelia nemôžu aktualizova emails=E-mailové adresy manage_emails=Správa e-mailových adries -manage_themes=Nastavenie predvolenej témy -manage_openid=Správa OpenID adries +manage_themes=Predvolená téma +manage_openid=Adresy OpenID theme_desc=Toto bude vaša predvolená téma vzhľadu naprieč stránkou. primary=Primárny activated=Aktivovaný diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index 608e05afa4..07ccaed259 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -133,7 +133,7 @@ reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta p require_db_desc = Forgejo zahteva MySQL, PostgreSQL, SQLite3 ali TiDB (protokol MySQL). password_algorithm_helper = Nastavite algoritem za stiskanje gesla. Algoritmi imajo različne zahteve in moč. Algoritem argon2 je precej varen, vendar porabi veliko pomnilnika in je lahko neprimeren za majhne sisteme. reinstall_confirm_message = Ponovna namestitev z obstoječo zbirko podatkov Forgejo lahko povzroči več težav. V večini primerov morate za zagon programa Forgejo uporabiti obstoječi "app.ini". Če veste, kaj počnete, potrdite naslednje: -err_admin_name_is_reserved = Administrator Uporabniško ime je neveljavno, uporabniško ime je rezervirano +err_admin_name_is_reserved = Administrator uporabniško ime je neveljavno, uporabniško ime je rezervirano disable_gravatar.description = Onemogočite vire avatarjev Gravatar in avatarje tretjih oseb. Uporabi se privzeti avatar, razen če uporabnik lokalno naloži avatar. install = Namestitev title = Začetna nastavitev @@ -367,7 +367,7 @@ delete = Brisanje računa uploaded_avatar_is_too_big = Velikost naložene datoteke (%d KiB) presega največjo velikost (%d KiB). webauthn = Dvofaktorsko preverjanje pristnosti (varnostni ključi) change_username_redirect_prompt = Staro uporabniško ime bo preusmerjeno, dokler ga nekdo ne prevzame. -orgs = Upravljanje organizacij +orgs = Organizacije public_profile = Javni profil gpg_key_verified_long = Ključ je bil preverjen z žetonom in ga je mogoče uporabiti za preverjanje zavez, ki ustrezajo vsem aktiviranim e-poštnim naslovom tega uporabnika, poleg vseh ujemajočih se identitet za ta ključ. @@ -495,14 +495,14 @@ tab_openid = Odprta identiteta [home] show_both_archived_unarchived = Prikazovanje arhiviranih in nearhiviranih -switch_dashboard_context = Kontekst stikala Nadzorna plošča +switch_dashboard_context = Kontekst stikala nadzorna plošča search_repos = Poiščite skladišče… filter_by_team_repositories = Filtriranje po skupinskih skladiščih show_archived = Arhivirano collaborative_repos = Sodelovalni repozitoriji my_mirrors = Moja ogledala show_only_public = Prikazovanje samo javnih -uname_holder = Uporabniško ime ali E-poštovni naslov +uname_holder = Uporabniško ime ali e-poštni naslov password_holder = Geslo my_repos = Repozitoriji show_more_repos = Prikaži več skladišč… diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index b548e0fbf3..5b06c5e3b5 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -2326,15 +2326,15 @@ repo_kind = Sök repon… user_kind = Sök användare… code_kind = Sök kod… package_kind = Sök paket… -runner_kind = Sök exekutorer... +runner_kind = Sök exekutorer… branch_kind = Sök grenar… -commit_kind = Sök commiter... +commit_kind = Sök commiter… project_kind = Sök projekt… search = Sök… type_tooltip = Söktyp team_kind = Sök team… org_kind = Sök organisationer… -issue_kind = Sök ärenden... +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. fuzzy_tooltip = Inkludera resultat som är närliggande till söktermen diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 462187e120..aa41d7dc63 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -163,8 +163,8 @@ new_repo.link = Yeni depo new_org.link = Yeni organizasyon error413 = Kotanızı doldurdunuz. toggle_menu = Menüyü aç-kapa -new_migrate.title = Yeni geçiş -new_migrate.link = Yeni geçiş +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? @@ -179,8 +179,9 @@ number_of_contributions_in_the_last_12_months=son 12 ayda %s katkı contributions_zero=Katkı yapılmamış less=Daha az more=Daha Fazla -contributions_one = katılım -contributions_few = katılımlar +contributions_one = katkı +contributions_few = katkı +contributions_format = {day} {month} {year} tarihinde {contributions} katkı [editor] buttons.heading.tooltip=Başlık ekle @@ -203,6 +204,12 @@ table_modal.placeholder.header = Başlık table_modal.placeholder.content = İçerik table_modal.label.rows = Satırlar table_modal.label.columns = Sütunlar +link_modal.header = Bağlantı ekle +link_modal.url = Url +link_modal.description = Açıklama +link_modal.paste_reminder = İpucu: Panonuzdaki bir URL'yi doğrudan düzenleyiciye yapıştırarak bir bağlantı oluşturabilirsiniz. +buttons.unindent.tooltip = Bir seviye girinti azalt +buttons.indent.tooltip = Bir seviye girinti artır [filter] string.asc=A - Z @@ -226,6 +233,7 @@ lightweight=Hafif lightweight_desc=Forgejo'nın minimal gereksinimleri çok düşüktür ve ucuz bir Raspberry Pi üzerinde çalışabilmektedir. Makine enerjinizden tasarruf edin! license=Açık Kaynak license_desc=Gidin ve Forgejo'yı edinin! Bu projeyi daha da iyi yapmak için katkıda bulunarak bize katılın. Katkıda bulunmaktan çekinmeyin! +platform_desc = Forgejo'nun Linux ve FreeBSD gibi özgür işletim sistemlerinde ve farklı CPU mimarilerinde çalıştığı doğrulandı. Sevdiğinizi seçin! [install] install=Kurulum @@ -252,9 +260,9 @@ err_empty_db_path=SQLite3 veritabanı dosya yolu boş olamaz. no_admin_and_disable_registration=Bir yönetici hesabı oluşturmadan kullanıcı kaydını kapatamazsınız. err_empty_admin_password=Yönetici parolası boş olamaz. err_empty_admin_email=Yönetici e-postası boş olamaz. -err_admin_name_is_reserved=Yönetici Kullanıcı Adı geçersiz, bu kullanıcı adı rezerv edilen bir kelimedir +err_admin_name_is_reserved=Yönetici kullanıcı adı geçersiz, bu kullanıcı adı rezerv edilen bir kelimedir err_admin_name_pattern_not_allowed=Yönetici kullanıcı adı geçersiz, kullanıcı adı ayrılmış bir desenle eşleşiyor -err_admin_name_is_invalid=Yönetici Kullanıcı Adı geçersiz +err_admin_name_is_invalid=Yönetici kullanıcı adı geçersiz general_title=Genel ayarlar app_name=Site Başlığı @@ -344,6 +352,7 @@ enable_update_checker_helper_forgejo = release.forgejo.org adresindeki TXT DNS k allow_dots_in_usernames = Kullanıcı isimlerinde noktaya izin ver. Var olan kullanıcıları etkilemez. smtp_from_invalid = `"E-posta Olarak Gönder" adresi geçersiz` +config_location_hint = Bu yapılandırma seçenekleri şuraya kaydedilecek: [home] uname_holder=Kullanıcı adı veya e-posta adresi @@ -477,6 +486,7 @@ hint_register = Hesaba ihtiyacın var mı? Hemen kaydol. sign_in_openid = OpenID ile giriş yap hint_login = Mevcut hesabın var mı? Hemen giriş yap! use_onetime_code = Tek kullanımlık kod kullan +change_unconfirmed_email = Kayıt sırasında yanlış e-posta adresi verdiyseniz, aşağıdan değiştirebilirsiniz; yeni adresinize bir onay mesajı gönderilecektir. [mail] view_it_on=%s üzerinde görüntüle @@ -722,16 +732,16 @@ avatar=Avatar ssh_gpg_keys=SSH / GPG Anahtarları social=Sosyal Medya Hesapları applications=Uygulamalar -orgs=Organizasyonları Yönet +orgs=Kuruluşlar repos=Depolar -delete=Hesabı Sil -twofa=İki Aşamalı Doğrulama +delete=Hesabı sil +twofa=İki aşamalı doğrulama account_link=Bağlı Hesaplar organization=Organizasyonlar uid=UID webauthn=Güvenlik Anahtarları -public_profile=Herkese Açık Profil +public_profile=Herkese açık profil biography_placeholder=Bize kendiniz hakkında birşeyler söyleyin! (Markdown kullanabilirsiniz) location_placeholder=Yaklaşık konumunuzu başkalarıyla paylaşın profile_desc=Profilinizin başkalarına nasıl gösterildiğini yönetin. Ana e-posta adresiniz bildirimler, parola kurtarma ve web tabanlı Git işlemleri için kullanılacaktır. @@ -796,8 +806,8 @@ password_change_disabled=Yerel olmayan kullanıcılar parolalarını Forgejo web emails=E-Posta Adresleri manage_emails=E-posta Adreslerini Yönet -manage_themes=Varsayılan temayı seç -manage_openid=OpenID Adreslerini Yönet +manage_themes=Varsayılan tema +manage_openid=OpenID adresleri email_desc=Ana e-posta adresiniz bildirimler, parola kurtarma ve gizlenmemişse eğer web tabanlı Git işlemleri için kullanılacaktır. theme_desc=Bu, sitedeki varsayılan temanız olacak. primary=Birincil @@ -1166,7 +1176,7 @@ form.name_reserved=Depo adı "%s" rezerve edilmiş. form.name_pattern_not_allowed=Depo adında "%s" deseni kullanılamaz. need_auth=Yetkilendirme -migrate_options=Göç Seçenekleri +migrate_options=Göç seçenekleri migrate_service=Göç Hizmeti migrate_options_mirror_helper=Bu depo bir yansı olacaktır migrate_options_lfs=LFS dosyalarını taşı @@ -1174,7 +1184,7 @@ migrate_options_lfs_endpoint.label=LFS Uç Noktası migrate_options_lfs_endpoint.description=Taşıma, LFS sunucusunu belirlemek için Git uzak sunucusunu kullanmaya çalışacak. Eğer LFS veri deposu başka yerdeyse özel bir uç nokta da belirtebilirsiniz. migrate_options_lfs_endpoint.description.local=Yerel bir sunucu yolu da destekleniyor. migrate_options_lfs_endpoint.placeholder=Boş bırakılırsa, uç nokta klon URL'sinden türetilecektir -migrate_items=Göç Öğeleri +migrate_items=Göç öğeleri migrate_items_wiki=Wiki migrate_items_milestones=Kilometre Taşları migrate_items_labels=Etiketler @@ -1215,7 +1225,7 @@ migrate.migrating_labels=Etiketleri Taşıma migrate.migrating_releases=Sürümleri Taşıma migrate.migrating_issues=Konuları Taşıma migrate.migrating_pulls=Değişiklik İsteklerini Taşıma -migrate.cancel_migrating_title=Göçü İptal Et +migrate.cancel_migrating_title=Göçü iptal et migrate.cancel_migrating_confirm=Bu göçü iptal etmek istiyor musunuz? mirror_from=şunun yansıması @@ -1252,7 +1262,7 @@ find_tag=Etiketi bul branches=Dal tags=Etiket issues=Konular -pulls=Değişiklik İstekleri +pulls=Değişiklik istekleri project_board=Projeler packages=Paketler actions=İşlemler @@ -1443,7 +1453,7 @@ projects.column.set_default=Varsayılanı Ayarla projects.column.set_default_desc=Bu sütunu kategorize edilmemiş konular ve değişiklik istekleri için varsayılan olarak ayarlayın projects.column.unset_default=Varsayılanları Geri Al projects.column.unset_default_desc=Bu sütunu varsayılan olarak geri al -projects.column.delete=Sutün Sil +projects.column.delete=Sütunu sil projects.column.deletion_desc=Bir proje sütununun silinmesi, ilgili tüm konuları 'Kategorize edilmemiş'e taşır. Devam edilsin mi? projects.column.color=Renk projects.open=Aç @@ -1595,7 +1605,7 @@ issues.reopen_issue=Yeniden aç issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç issues.create_comment=Yorum yap issues.closed_at=`%s konusunu kapattı` -issues.reopened_at=`%s konusunu yeniden açtı` +issues.reopened_at=%s sorununu yeniden açtı issues.commit_ref_at=`%s işlemesinde bu konuyu işaret etti` issues.ref_issue_from=`bu konuya referansta bulundu %[3]s %[1]s` issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[3]s %[1]s` @@ -1647,7 +1657,7 @@ issues.label.filter_sort.alphabetically=Alfabetik issues.label.filter_sort.reverse_alphabetically=Ters alfabetik issues.label.filter_sort.by_size=En küçük boyut issues.label.filter_sort.reverse_by_size=En büyük boyut -issues.num_participants_few=%d Katılımcı +issues.num_participants_few=%d katılımcı issues.attachment.open_tab=`Yeni bir sekmede "%s" görmek için tıkla` issues.attachment.download=`"%s" indirmek için tıkla` issues.subscribe=Abone Ol @@ -1984,7 +1994,7 @@ ext_wiki=Harici Vikiye Erişim ext_wiki.desc=Harici bir wiki'ye bağlantı. wiki=Wiki -wiki.welcome=Wiki'ye Hoşgeldiniz. +wiki.welcome=Viki'ye Hoş geldiniz. wiki.welcome_desc=Wiki, katkıcılarla belge yazmanıza ve paylaşmanıza olanak tanır. wiki.desc=Katkıcılarla belgeler yazın ve paylaşın. wiki.create_first_page=İlk sayfayı oluştur @@ -2140,15 +2150,15 @@ settings.use_external_wiki=Harici Wiki Kullan settings.external_wiki_url=Harici Wiki bağlantısı settings.external_wiki_url_error=Harici wiki URL'si geçerli bir URL değil. settings.external_wiki_url_desc=Ziyaretçiler, wiki sekmesine tıklandığında harici wiki URL'sine yönlendirilir. -settings.issues_desc=Depo Konu İzleyicisini Etkinleştir -settings.use_internal_issue_tracker=Yerleşik Konu İzleyici Kullan -settings.use_external_issue_tracker=Harici Konu İzleyici Kullan -settings.external_tracker_url=Harici Konu İzleyici URLsi +settings.issues_desc=Depo sorun izleyicisini etkinleştir +settings.use_internal_issue_tracker=Yerleşik sorun izleyici kullan +settings.use_external_issue_tracker=Harici sorun izleyici kullan +settings.external_tracker_url=Harici sorun izleyici URL'si settings.external_tracker_url_error=Harici konu izleyici URL'si geçerli bir URL değil. settings.external_tracker_url_desc=Ziyaretçiler, konular sekmesine tıkladığında harici konu izleyici URL'sine yönlendirilir. -settings.tracker_url_format=Harici Konu İzleyici URL Biçimi +settings.tracker_url_format=Harici sorun izleyici URL Biçimi settings.tracker_url_format_error=Harici konu izleyici URL biçimi geçerli bir URL değil. -settings.tracker_issue_style=Harici Konu İzleyici Numara Biçimi +settings.tracker_issue_style=Harici sorun izleyici Numara Biçimi settings.tracker_issue_style.numeric=Sayısal settings.tracker_issue_style.alphanumeric=Alfanumerik settings.tracker_issue_style.regexp=Düzenli ifade @@ -2158,13 +2168,13 @@ settings.tracker_url_format_desc=Kullanıcı adı, depo adı ve yayın dizini i settings.enable_timetracker=Zaman Takibini Etkinleştir settings.allow_only_contributors_to_track_time=Sadece Katkıcılar İçin Zaman Takibine İzin Ver settings.pulls_desc=Değişiklik İsteklerini Etkinleştir -settings.pulls.ignore_whitespace=Çakışmalar için Boşlukları Gözardı Et +settings.pulls.ignore_whitespace=Çakışmalar için boşlukları gözardı et settings.pulls.enable_autodetect_manual_merge=Kendiliğinden algılamalı elle birleştirmeyi etkinleştir (Not: Bazı özel durumlarda yanlış kararlar olabilir) settings.pulls.allow_rebase_update=Değişiklik isteği dalının yeniden yapılandırmayla güncellenmesine izin ver settings.pulls.default_delete_branch_after_merge=Varsayılan olarak birleştirmeden sonra değişiklik isteği dalını sil settings.pulls.default_allow_edits_from_maintainers=Bakımcıların düzenlemelerine izin ver settings.releases_desc=Depo Sürümlerini Etkinleştir -settings.packages_desc=Depo paket kütüğünü etkinleştir +settings.packages_desc=Depo paket kayıt defterini etkinleştir settings.projects_desc=Depo Projelerini Etkinleştir settings.actions_desc=Depo İşlemlerini Etkinleştir settings.admin_settings=Yönetici Ayarları @@ -2188,7 +2198,7 @@ settings.convert_fork_desc=Bu çatalı normal bir depoya dönüştürebilirsiniz settings.convert_fork_notices_1=Bu işlem çatalı normal bir depoya dönüştürür ve geri alınamaz. settings.convert_fork_confirm=Depoyu Dönüştür settings.convert_fork_succeed=Çatal normal bir depoya dönüştürüldü. -settings.transfer.title=Sahipliği Aktar +settings.transfer.title=Sahipliği aktar settings.transfer.rejected=Depo aktarımı reddedildi. settings.transfer.success=Depo aktarımı başarıyla tamamlandı. settings.transfer_abort=Aktarımı iptal et @@ -2448,7 +2458,7 @@ settings.block_on_official_review_requests_desc=Yeterli onay olsa bile, resmi in settings.block_outdated_branch=Değişiklik isteği güncel değilse birleştirmeyi engelle settings.block_outdated_branch_desc=Baş dal taban dalın arkasındayken birleştirme mümkün olmayacaktır. settings.default_branch_desc=Değişiklik istekleri ve kod işlemeleri için varsayılan bir depo dalı seçin: -settings.merge_style_desc=Biçimleri Birleştir +settings.merge_style_desc=Birleştirme biçemleri settings.default_merge_style_desc=Değişiklik istekleri için varsayılan birleştirme tarzı: settings.choose_branch=Bir dal seç… settings.no_protected_branch=Korumalı dal yok. @@ -2701,7 +2711,7 @@ settings.new_owner_blocked_doer = Yeni sahip sizi engelledi. open_with_editor = %s ile aç object_format = Nesne Biçimi -mirror_sync = eşitlendi +mirror_sync = eşitlenme: stars = Yıldızlar desc.sha256 = SHA256 vendored = Sağlanmış @@ -2721,6 +2731,25 @@ settings.mirror_settings.pushed_repository = İtilmiş depo settings.ignore_stale_approvals = Eskimiş onayları yoksay settings.ignore_stale_approvals_desc = Daha eski işlemelere (eski incelemelere) yapılmış olan onayları, Dİ'nin kaç onayı olduğunu belirlerken sayma. Eskimiş incelemeler atıldıysa bu ilgisizdir. error.broken_git_hook = Bu deponun Git İstemcileri bozuk gibi gözüküyor. Onarmak için lütfen belgelere bakın, daha sonra durumu yenilemek için bazı işlemeler itin. +commits.browse_further = Daha fazlasına göz at +settings.units.units = Birimler +commits.renamed_from = %s adından yeniden adlandırıldı +mirror_use_ssh.text = SSH yetkilendirme kullan +settings.default_update_style_desc = Temel dalın ardındaki çekme isteklerini güncellemek için kullanılan varsayılan güncelleme stili. +mirror_denied_combination = Ortak anahtar ve parola tabanlı kimlik doğrulama birlikte kullanılamaz. +mirror_public_key = Ortak SSH anahtarı +n_branch_few = %s dal +n_commit_few = %s gönderi +n_tag_few = %s etiket +settings.wiki_rename_branch_main = Viki dal adını normalleştir +mirror_use_ssh.not_available = SSH yetkilendirme kullanılamıyor. +mirror_use_ssh.helper = Forgejo, bu seçeneği seçtiğinizde deponuzu SSH üzerinden Git üzerinden yansıtacak ve sizin için bir anahtar çifti oluşturacaktır. Oluşturulan ortak anahtarın hedef depoya gönderilmek üzere yetkilendirildiğinden emin olmalısınız. Bunu seçerken parola tabanlı yetkilendirme kullanamazsınız. +n_branch_one = %s dal +settings.units.add_more = Daha fazlasını etkinleştir +settings.wiki_globally_editable = Vikiyi herkesin düzenlemesine izin ver +issues.filter_no_results_placeholder = Arama filtrelerini değiştirmeyi deneyin. +issues.filter_no_results = Sonuç yok +issues.num_participants_one = %d katılımcı [graphs] component_loading = %s yükleniyor... @@ -2736,7 +2765,7 @@ org_name_holder=Organizasyon Adı org_full_name_holder=Organizasyon Tam Adı org_name_helper=Organizasyon adları kısa ve hatırlanabilir olmalıdır. create_org=Organizasyon Oluştur -repo_updated=Güncellendi %s +repo_updated=%s güncellendi members=Üyeler teams=Takımlar code=Kod @@ -2792,7 +2821,7 @@ members.membership_visibility=Üyelik Görünürlüğü: members.public=Görünür members.public_helper=gizle members.private=Gizlenmiş -members.private_helper=görünür yap +members.private_helper=Görünür yap members.member_role=Üye Rolü: members.owner=Sahibi members.member=Üye @@ -3311,7 +3340,7 @@ config.git_max_diff_lines=Maksimum Değişiklik Satırı (tek bir dosya için) config.git_max_diff_line_characters=Maksimum Değişiklik Karakteri (tek bir satır için) config.git_max_diff_files=Maksimum Değişiklik Dosyaları (gösterilecek) config.git_gc_args=GC Argümanları -config.git_migrate_timeout=Göç İşlemi Zaman Aşımı +config.git_migrate_timeout=Göç işlemi zaman aşımı config.git_mirror_timeout=Yansı Güncelleme Zaman Aşımı config.git_clone_timeout=Klonlama İşlemi Zaman Aşımı config.git_pull_timeout=Çekme İşlemi Zaman Aşımı @@ -3363,7 +3392,7 @@ monitor.queue.settings.maxnumberworkers=En fazla çalışan Sayısı monitor.queue.settings.maxnumberworkers.placeholder=Şu anda %[1]d monitor.queue.settings.maxnumberworkers.error=En fazla çalışan sayısı bir sayı olmalıdır monitor.queue.settings.submit=Ayarları Güncelle -monitor.queue.settings.changed=Ayarlar Güncellendi +monitor.queue.settings.changed=Ayarlar güncellendi monitor.queue.settings.remove_all_items=Tümünü kaldır monitor.queue.settings.remove_all_items_done=Kuyruktaki tüm öğeler kaldırıldı. @@ -3771,23 +3800,23 @@ submodule=Alt modül [search] -project_kind = Projeleri ara... +project_kind = Projeleri ara… org_kind = Organizasyonları ara… team_kind = Takımları ara… search = Ara… code_kind = Kod ara… type_tooltip = Arama türü -repo_kind = Depoları ara... +repo_kind = Depoları ara… user_kind = Kullanıcıları ara… milestone_kind = Kilometre taşlarını ara... -branch_kind = Dalları ara... -package_kind = Paketleri ara... -commit_kind = Katkıları ara... -runner_kind = Çalıştırıcıları ara... +branch_kind = Dalları ara… +package_kind = Paketleri ara… +commit_kind = Katkıları ara… +runner_kind = Çalıştırıcıları ara… no_results = Eşleşen sonuç bulunamadı. code_search_unavailable = Kod araması şu anda kullanıma açık değildir. Lütfen site yöneticisi ile iletişime geçin. -issue_kind = Sorunları ara... -pull_kind = Birleştirme isteklerini ara... +issue_kind = Sorunları ara… +pull_kind = Birleştirme isteklerini ara… code_search_by_git_grep = Anlık kod araması sonuçları "git grep" komutu tarafından sağlanmaktadır. Site yöneticisinin kod endekslemesini açması durumunda daha iyi sonuçlar verilmesi mümkün olabilir. keyword_search_unavailable = Anahtar kelime ile arama şu anda kullanıma açık değildir. Lütfen site yöneticisi ile iletişime geçin. fuzzy_tooltip = Arama terimine yakın olan eşleşmeleri dahil et @@ -3795,3 +3824,16 @@ union_tooltip = Boşlukla ayrılmış anahtar kelime eşleşmelerini dahil et exact_tooltip = Sadece arama terimiyle tam uyuşan sonuçları dahit et. fuzzy = Bulanık exact = Tam +regexp = Düzenliİfade +regexp_tooltip = Arama terimini düzenli ifade olarak yorumla +union = Anahtar sözcük + + +[munits.data] +tib = TiB +kib = KiB +pib = PiB +mib = MiB +b = B +gib = GiB +eib = EiB \ No newline at end of file diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 9b0d6159ff..b94cb64af1 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -149,7 +149,7 @@ filter.not_archived = Не архівовано filter.public = Загальнодоступні filter.private = Приватні more_items = Більше пунктів -remove_label_str = Видалити об'єкт «%s» +remove_label_str = Видалити елемент «%s» new_repo.title = Новий репозиторій new_migrate.title = Нова міграція new_org.title = Нова організація @@ -195,8 +195,8 @@ buttons.list.task.tooltip = Додати список завдань buttons.heading.tooltip = Додати заголовок buttons.switch_to_legacy.tooltip = Використовувати застарілий редактор замість поточного buttons.disable_monospace_font = Вимкнути моноширинний шрифт -buttons.indent.tooltip = Вкласти предмет на один рівень -buttons.unindent.tooltip = Викласти об'єкт на один рівень +buttons.indent.tooltip = Вкласти елемент на один рівень +buttons.unindent.tooltip = Викласти елемент на один рівень buttons.mention.tooltip = Згадати користувача чи команду buttons.ref.tooltip = Послатися на задачу чи на запит на злиття buttons.enable_monospace_font = Увімкнути моноширинний шрифт @@ -348,7 +348,7 @@ app_slogan = Гасло екземпляра app_slogan_helper = Уведіть гасло вашого екземпляра тут. Залиште порожнім, аби вимкнути. run_user_helper = Імʼя користувача операційної системи, від якого запущено Forgejo. Зауважте, що цей користувач повинен мати доступ до кореневої теки репозиторію. smtp_from_invalid = Адреса з «Відправляти email від імені» недійсна -allow_dots_in_usernames = Дозволити користувачам використовувати крапки у своїх іменах. Не впливає на облікові записи, що вже існують. +allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на облікові записи, що вже існують. invalid_password_algorithm = Недійсний варіант алгоритму хешування паролів enable_update_checker_helper_forgejo = Наявність нових версій Forgejo періодично перевірятиметься через перевірку запису TXT DNS на release.forgejo.org. @@ -392,7 +392,7 @@ user_no_results=Відповідних користувачів не знайд org_no_results=Відповідних організацій не знайдено. code_no_results=Відповідний пошуковому запитанню код не знайдено. code_last_indexed_at=Останні індексовані %s -relevant_repositories = Відображаються лише релевантні репозиторії, переглянути результати без фільтру. +relevant_repositories = Показано лише релевантні репозиторії, переглянути результати без фільтру. relevant_repositories_tooltip = Приховано форки, а також репозиторії без теми, значка й опису. go_to = Перейти до stars_one = %d зірка @@ -469,7 +469,7 @@ invalid_code_forgot_password = Ваш код підтвердження неді reset_password_wrong_user = Ви ввійшли як %s, але посилання на відновлення було передбачене для %s back_to_sign_in = Назад до входу sign_in_openid = Продовжити з OpenID -openid_signin_desc = Введіть ваше посилання OpenID. Наприклад: alice.openid.example.org чи https://openid.example.org/alice. +openid_signin_desc = Уведіть свій OpenID URI. Наприклад: alice.openid.example.org чи https://openid.example.org/alice. invalid_password = Ваш пароль не відповідає тому, що був заданий при створенні облікового запису. hint_login = Вже маєте обліковий запис? Увійдіть зараз! hint_register = Потрібен обліковий запис? Зареєструйтеся зараз. @@ -813,9 +813,9 @@ manage_ssh_keys=Керування ключами SSH manage_ssh_principals=Управління SSH сертифікатами користувачів manage_gpg_keys=Керування ключами GPG add_key=Додати ключ -ssh_desc=Ці відкриті ключі SSH повʼязані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. Підтверджені ключі можна використати для підтвердження комітів Git, підписані з SSH. +ssh_desc=Ці відкриті ключі SSH повʼязані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. Підтверджені ключі можна використати для підтвердження комітів Git, підписаних із SSH. principal_desc=Ці настройки SSH сертифікатів вказані у вашому обліковому записі та надають повний доступ до ваших репозиторіїв. -gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють здійснювати перевірку комітів. +gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. ssh_helper=Потрібна допомога? Дивіться гід на GitHub з генерації ключів SSH або виправлення типових неполадок SSH. gpg_helper= Потрібна допомога? Перегляньте посібник GitHub про GPG . add_new_key=Додати SSH ключ @@ -893,7 +893,7 @@ manage_oauth2_applications=Керування програмами OAuth2 edit_oauth2_application=Редагувати програму OAuth2 oauth2_applications_desc=Програми OAuth2 дають можливість вашим стороннім програмам надійно аутентифікувати користувачів у цьому екземплярі Forgejo. remove_oauth2_application=Видалити програму OAuth2 -remove_oauth2_application_desc=Видалення програми OAuth2 скасовує доступ до всіх підписаних маркерів доступу. Продовжити? +remove_oauth2_application_desc=Видалення програми OAuth2 скасує доступ до всіх підписаних токенів доступу. Продовжити? remove_oauth2_application_success=Програму видалено. create_oauth2_application=Створити новий додаток OAuth2 create_oauth2_application_button=Створити програму @@ -1076,6 +1076,8 @@ quota.rule.exceeded = Перевищено regenerate_token = Згенерувати знову access_token_regeneration = Згенерувати новий токен доступу quota.sizes.assets.all = Ресурси +access_token_regeneration_desc = Регенерація токена скасує доступ програм, які використовують цей токен, до вашого облікового запису. Це незворотна дія. Продовжити? +regenerate_token_success = Токен згенеровано наново. Програми, які його використовують, більше не мають доступу до вашого облікового запису; ви повинні відновити доступ за допомогою нового токена. [repo] owner=Власник @@ -1143,7 +1145,7 @@ forks=Форки reactions_more=додати %d більше unit_disabled=Адміністратор сайту вимкнув цей розділ репозиторію. language_other=Інші -adopt_search=Введіть ім'я користувач_ки для пошуку неприйнятих репозиторіїв… (залиште порожнім, щоб знайти всі) +adopt_search=Уведіть ім'я користувач_ки для пошуку неприйнятих репозиторіїв… (залиште порожнім, щоб знайти всі) adopt_preexisting_label=Прийняті файли adopt_preexisting=Прийняти вже існуючі файли adopt_preexisting_content=Створити репозиторій з %s @@ -1168,7 +1170,7 @@ desc.archived=Архівний template.items=Елементи шаблону template.git_content=Вміст Git (типова гілка) -template.git_hooks=Перехоплювачі Git +template.git_hooks=Git-хуки template.webhooks=Webhook'и template.topics=Теми template.avatar=Аватар @@ -1285,7 +1287,7 @@ video_not_supported_in_browser=Ваш браузер не підтримує т audio_not_supported_in_browser=Ваш браузер не підтримує тег HTML5 «audio». stored_lfs=Збережено з Git LFS symbolic_link=Символічне посилання -commit_graph=Графік комітів +commit_graph=Граф комітів commit_graph.select=Виберіть гілки commit_graph.hide_pr_refs=Приховати запити на злиття commit_graph.monochrome=Монохром @@ -1402,9 +1404,9 @@ issues.new.open_projects=Відкриті проєкти issues.new.closed_projects=Закриті проєкти issues.new.no_items=Немає елементів issues.new.milestone=Етап -issues.new.no_milestone=Етап відсутній +issues.new.no_milestone=Немає етапу issues.new.clear_milestone=Очистити етап -issues.new.open_milestone=Активні етапи +issues.new.open_milestone=Відкриті етапи issues.new.closed_milestone=Закриті етапи issues.new.assignees=Виконавці issues.new.clear_assignees=Прибрати виконавців @@ -1504,7 +1506,7 @@ issues.closed_at=`закриває цю задачу %s` issues.reopened_at=`повторно відкриває цю задачу %s` issues.commit_ref_at=`посилається на цю задачу в коміті %s` issues.ref_issue_from=`посилається на цю задачу %[3]s %[1]s` -issues.ref_pull_from=`посилається на цей запит злиття %[3]s %[1]s` +issues.ref_pull_from=`посилається на цей запит на злиття %[3]s %[1]s` issues.ref_closing_from=`посилається в запиті на злиття %[3]s, який закриє цю задачу, %[1]s` issues.ref_reopening_from=`посилається в запиті на злиття %[3]s, який повторно відкриє цю задачу, %[1]s` issues.ref_closed_from=`закрив цю задачу %[4]s %[2]s` @@ -1799,7 +1801,7 @@ wiki.delete_page_button=Видалити сторінку wiki.page_already_exists=Вікі-сторінка з таким самим ім'ям вже існує. wiki.pages=Сторінки wiki.last_updated=Останні оновлення %s -wiki.page_name_desc=Введіть назву вікі-сторінки. Деякі зі спеціальних імен: «Home», «_Sidebar» та «_Footer». +wiki.page_name_desc=Уведіть назву вікі-сторінки. Деякі зі спеціальних імен: «Home», «_Sidebar» та «_Footer». activity=Активність activity.period.filter_label=Період: @@ -1844,7 +1846,7 @@ activity.unresolved_conv_label=Відкрити activity.title.releases_1=%d випуск activity.title.releases_n=%d випусків activity.title.releases_published_by=%s опубліковано %s -activity.published_release_label=Опубліковано +activity.published_release_label=Випуск activity.no_git_activity=У цей період не було здійснено жодних дій. activity.git_stats_exclude_merges=Не враховуючи злиття, activity.git_stats_author_1=%d автор @@ -1886,7 +1888,7 @@ settings.collaboration.read=Читати settings.collaboration.owner=Власник settings.collaboration.undefined=Не визначено settings.hooks=Веб-хуки -settings.githooks=Git хуки +settings.githooks=Git-хуки settings.basic_settings=Основні налаштування settings.mirror_settings=Налаштування дзеркала settings.mirror_settings.mirrored_repository=Віддзеркалений репозиторій @@ -1896,7 +1898,7 @@ settings.mirror_settings.direction.push=Push settings.mirror_settings.last_update=Останнє оновлення settings.mirror_settings.push_mirror.none=Не налаштовано дзеркало push settings.mirror_settings.push_mirror.remote_url=URL віддаленого репозиторію Git -settings.mirror_settings.push_mirror.add=Додати Push дзеркало +settings.mirror_settings.push_mirror.add=Додати push-дзеркало settings.sync_mirror=Синхронізувати зараз settings.site=Веб-сайт @@ -1927,9 +1929,9 @@ settings.pulls_desc=Увімкнути запити на злиття в реп settings.pulls.ignore_whitespace=Ігнорувати пробіл у конфліктах settings.pulls.enable_autodetect_manual_merge=Увімкнути автовизначення ручного злиття (Примітка: у деяких особливий випадках можуть виникнуть помилки) settings.pulls.default_delete_branch_after_merge=Видаляти гілку запиту злиття, коли його прийнято -settings.projects_desc=Увімкнути проєкти у репозиторії +settings.projects_desc=Увімкнути проєкти в репозиторії settings.admin_settings=Налаштування адміністратора -settings.admin_enable_health_check=Включити перевірки працездатності репозиторію (git fsck) +settings.admin_enable_health_check=Увімкнути перевірки стану репозиторію (git fsck) settings.admin_enable_close_issues_via_commit_in_any_branch=Закрити задачу за допомогою коміта, зробленого не в головній гілці settings.danger_zone=Небезпечна зона settings.new_owner_has_same_repo=Новий власник вже має репозиторій з такою назвою. Будь ласка, виберіть інше ім'я. @@ -2075,7 +2077,7 @@ settings.event_pull_request_review_desc=Запит на злиття схвал settings.event_pull_request_sync=Синхронізовано settings.event_pull_request_sync_desc=Гілку автоматично оновлено цільовою гілкою. settings.branch_filter=Фільтр гілок -settings.branch_filter_desc=Білий список гілок для push, створення гілок та видалення гілок, визначається як шаблон glob. Якщо він порожній або містить *, то реєструються події для всіх гілок. Дивіться синтаксис у документації на %[2]s. Наприклад: master, {master,release*}. +settings.branch_filter_desc=Білий список гілок для push, створення гілок та видалення гілок, визначається як шаблон glob. Якщо він порожній або містить *, то реєструються події для всіх гілок. Дивіться синтаксис у документації %[2]s. Приклади: master, {master,release*}. settings.active=Активний settings.active_helper=Інформацію про викликані події буде надіслано за цією веб-хук URL-адресою. settings.add_hook_success=Веб-хук було додано. @@ -2154,7 +2156,7 @@ settings.edit_protected_branch=Редагувати settings.protected_branch_required_approvals_min=Число необхідних схвалень не може бути від'ємним. settings.tags=Теги settings.tags.protection=Захист тегу -settings.tags.protection.pattern=Шаблон тега +settings.tags.protection.pattern=Шаблон тегів settings.tags.protection.allowed=Дозволено settings.tags.protection.allowed.users=Дозволені користувачі settings.tags.protection.allowed.teams=Дозволені команди @@ -2263,7 +2265,7 @@ release.detail=Деталі релізу release.tags=Теги release.new_release=Новий випуск release.draft=Чернетка -release.prerelease=Пре-реліз +release.prerelease=Попередній випуск release.stable=Стабільний release.compare=Порівняти release.edit=Редагувати @@ -2275,15 +2277,15 @@ release.edit_subheader=Публікація релізів допоможе ва release.tag_name=Назва тегу release.target=Ціль release.tag_helper=Виберіть існуючий тег або створіть новий. -release.prerelease_desc=Позначити як пре-реліз +release.prerelease_desc=Позначити як попередній випуск release.prerelease_helper=Позначте цей випуск непридатним для ПРОД використання. release.cancel=Відмінити -release.publish=Опублікувати реліз +release.publish=Опублікувати випуск release.save_draft=Зберегти чернетку -release.edit_release=Оновити реліз -release.delete_release=Видалити реліз +release.edit_release=Оновити випуск +release.delete_release=Видалити випуск release.delete_tag=Видалити тег -release.deletion=Видалити реліз +release.deletion=Видалити випуск release.deletion_success=Реліз, було видалено. release.deletion_tag_desc=Буде видалено цей тег із репозиторію. Вміст репозиторія та історія залишаться незмінними. Продовжити? release.deletion_tag_success=Тег видалено. @@ -2446,7 +2448,7 @@ settings.event_package = Пакунок settings.event_package_desc = Пакунок у репозиторії створено або видалено. settings.new_owner_blocked_doer = Новий власник заблокував вас. settings.transfer_quota_exceeded = Новий власник (%s) перевищив квоту. Репозиторій не передано. -release.title_empty = Заголовок не може бути порожнім. +release.title_empty = Назва не може бути порожньою. issues.role.member_helper = Цей користувач є членом організації, що володіє цим репозиторієм. wiki.page_content = Вміст сторінки wiki.page_title = Заголовок сторінки @@ -2530,7 +2532,7 @@ settings.add_web_hook_desc = Інтегрувати Увійдіть, щоб створити новий запит на злиття. @@ -2541,8 +2543,8 @@ auto_init_description = Почніть історію Git з README і за ба new_from_template_description = Можете вибрати наявний шаблон репозиторію на цьому екземплярі і застосувати його налаштування. form.string_too_long = Довжина введеного рядка більша за %d символів. form.name_reserved = Назву репозиторію «%s» зарезервовано. -form.name_pattern_not_allowed = Шаблон «%s» не допускається у назві репозиторію. -settings.wiki_rename_branch_main_desc = Перейменувати внутрішню гілку, яка використовується у вікі, на «%s». Ця зміна є остаточною і її неможливо скасувати. +form.name_pattern_not_allowed = Вираз «%s» не може бути частиною назви репозиторію. +settings.wiki_rename_branch_main_desc = Перейменувати внутрішню гілку, яка використовується для вікі, на «%s». Ця зміна є остаточною і її неможливо скасувати. wiki.reserved_page = Назву вікі-сторінки «%s» зарезервовано. stars = Зірки mirror_public_key = Відкритий SSH-ключ @@ -2694,6 +2696,83 @@ settings.remove_protected_branch_failed = Не вдалося видалити summary_card_alt = Підсумкова картка репозиторію %s issues.summary_card_alt = Підсумкова картка задачі «%s» в репозиторії %s release.summary_card_alt = Підсумкова картка випуску «%s» в репозиторії %s +release.hide_archive_links_helper = Приховати для цього випуску архіви вихідного коду, що генеруються автоматично. Наприклад, якщо ви завантажуєте свої архіви. +activity.published_prerelease_label = Пре-реліз +release.title = Назва випуску +release.system_generated = Це вкладення згенеровано автоматично. +release.hide_archive_links = Приховати автоматично генеровані архіви +branch.delete_desc = Видалення гілки є остаточним. Хоча видалена гілка може існувати ще деякий час до того, як її буде видалено, цю дію НЕМОЖЛИВО скасувати в більшості випадків. Продовжити? +release.deletion_desc = Видалення випуску видаляє його лише з Forgejo. При цьому тег Git, вміст репозиторію чи його історію не буде змінено. Продовжити? +settings.sourcehut_builds.manifest_path = Шлях до маніфесту збірки +settings.wiki_branch_rename_failure = Не вдалося нормалізувати назву вікі-гілки репозиторію. +settings.sourcehut_builds.visibility = Видимість завдань +settings.unarchive.header = Розархівувати цей репозиторій +settings.unarchive.success = Репозиторій успішно розархівовано. +diff.git-notes.remove-body = Цю примітку буде видалено. +tag.ahead.target = до %s після цього тегу +branch.tag_collision = Неможливо створити гілку «%s», оскільки у репозиторії вже є тег із такою назвою. +branch.branch_name_conflict = Назва гілки «%s» конфліктує з наявною гілкою «%s». +settings.wiki_branch_rename_success = Назву вікі-гілки репозиторію успішно нормалізовано. +diff.has_escaped = У цьому рядку є приховані символи Юнікоду +settings.unarchive.text = Розархівування репозиторію відновить можливість надсилати до нього коміти і виконувати push, а також створювати задачі і запити на злиття. +settings.wiki_rename_branch_main_notices_1 = Цю операцію НЕМОЖЛИВО скасувати. +settings.unarchive.error = Сталася помилка при спробі розархівувати репозиторій. Докладнішу інформацію див. у журналі. +settings.wiki_rename_branch_main = Нормалізувати назву вікі-гілки +settings.wiki_rename_branch_main_notices_2 = Внутрішню вікі-гілку репозиторію %s буде назавжди перейменовано. Існуючі перевірки потрібно буде оновити. +settings.pull_mirror_sync_in_progress = Триває отримання змін з віддаленого репозиторію %s. +settings.enter_repo_name = Уведіть ім'я власника і назву репозиторію, як показано: +settings.pulls.allow_rebase_update = Увімкнути оновлення гілки запиту на злиття за допомогою перебазування +settings.mirror_settings.docs.pulling_remote_title = Отримання з віддаленого репозиторію +settings.mirror_settings.docs.doc_link_pull_section = розділ документації «Отримання з віддаленого репозиторію». +settings.pull_mirror_sync_quota_exceeded = Перевищено квоту, отримання змін неможливе. +settings.mirror_settings.push_mirror.none_ssh = Немає +settings.admin_stats_indexer = Індексатор статистики коду +settings.admin_indexer_commit_sha = Останній індексований коміт +settings.discord_icon_url.exceeds_max_length = URL-адреса значка не може бути довшою, ніж 2048 символи +settings.protect_invalid_status_check_pattern = Недійсний шаблон перевірки стану: «%s». +settings.protect_no_valid_status_check_patterns = Немає дійсних шаблонів перевірки стану. +settings.admin_indexer_unindexed = Не індексовано +settings.push_mirror_sync_in_progress = Триває надсилання змін до віддаленого репозиторію %s. +settings.releases_desc = Увімкнути випуски в репозиторії +settings.admin_code_indexer = Індексатор коду +settings.protect_status_check_patterns_desc = Уведіть шаблони, щоб вказати, які перевірки стану повинні пройти гілки, перш ніж їх буде об'єднано у гілку, що відповідає цьому правилу. Кожен рядок визначає шаблон. Шаблони не можуть бути порожніми. +settings.add_webhook.invalid_path = Шлях не повинен містити частини «.» або «..» і не повинен бути порожнім. Він не може починатися або закінчуватися косою рискою. +settings.matrix.access_token_helper = Рекомендується створити окремий обліковий запис Matrix. Токен доступу можна отримати у вебклієнті Element (у приватній/інкогніто вкладці): User menu (вгорі ліворуч) > All settings > Help & About > Advanced > Access Token (під URL-адресою Homeserver). Закрийте приватну вкладку (вихід із системи зробить токен недійсним). +settings.protect_patterns = Шаблони +settings.sourcehut_builds.access_token_helper = Токен доступу, який має дозвіл JOBS:RW. Згенеруйте токен builds.sr.ht або токен builds.sr.ht з доступом до секретів на meta.sr.ht. +editor.unable_to_upload_files = Не вдалося завантажити файли в «%s» через помилку: %v +view_git_blame = Переглянути git blame +commits.ssh_key_fingerprint = Відбиток ключа SSH +settings.web_hook_name_telegram = Telegram +pulls.status_checks_hide_all = Приховати всі перевірки +settings.thread_id = Thread ID +settings.matrix.room_id_helper = ID кімнати можна отримати у вебклієнті Element: Room Settings > Advanced > Internal room ID. Приклад: %s. +editor.push_rejected_no_message = Зміну відхилено сервером без повідомлення. Будь ласка, перевірте Git-хуки. +editor.upload_files_to_dir = Завантажити файли в «%s» +issues.filter_sort.relevance = За відповідністю +editor.cannot_commit_to_protected_branch = Неможливо здійснити коміт до захищеної гілки «%s». +editor.file_deleting_no_longer_exists = Файл «%s», який ви видаляєте, більше не існує у цьому репозиторії. +editor.file_editing_no_longer_exists = Файл «%s», який ви редагуєте, більше не існує у цьому репозиторії. +diff.show_file_tree = Показати дерево файлів +milestones.filter_sort.name = За назвою +pulls.allow_edits_from_maintainers_err = Не вдалося оновити +pulls.has_viewed_file = Переглянуто +editor.file_already_exists = Файл із назвою «%s» вже є у цьому репозиторії. +commits.view_path = Переглянути на цьому етапі історії +editor.push_rejected = Зміну відхилено сервером. Будь ласка, перевірте Git-хуки. +commits.renamed_from = Перейменовано з %s +diff.hide_file_tree = Приховати дерево файлів +pulls.viewed_files_label = %[1]d з %[2]d файлів переглянуто +settings.protected_branch_duplicate_rule_name = Для цього набору гілок уже є правило +settings.mirror_settings.push_mirror.edit_sync_time = Змінити інтервал синхронізації дзеркала +wiki.delete_page_notice_1 = Видалення вікі-сторінки «%s» неможливо скасувати. Продовжити? +issues.label_archived_filter = Показати архівовані мітки +milestones.new_subheader = Етапи допомагають організувати задачі та відстежувати прогрес їх виконання. +issues.closed_by_fake = від %[2]s закрито %[1]s +issues.closed_by = від %[3]s закрито %[1]s +issues.action_check = Поставити/зняти позначку +issues.action_check_all = Поставити/зняти позначку з усіх елементів +vendored = Сторонній [graphs] contributors.what = внески @@ -2823,6 +2902,8 @@ teams.invite_team_member = Запросити до %s teams.write_access = Запис teams.invite.by = Вас запрошує %s teams.invite_team_member.list = Запрошення в очікуванні +form.name_pattern_not_allowed = Вираз «%s» не може бути частиною назви організації. +teams.add_nonexistent_repo = Репозиторій, який ви намагаєтеся додати, не існує. Спочатку створіть його. [admin] dashboard=Панель управління @@ -2934,7 +3015,7 @@ users.is_activated=Обліковий запис користувача увім users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора users.is_restricted=Обмежений -users.allow_git_hook=Може створювати Git хуки +users.allow_git_hook=Може створювати Git-хуки users.allow_git_hook_tooltip=Git хуки виконуються від імені користувача OS сервісу Forgejo і мають однаковий рівень доступу до хоста. Як результат, користувачі з доступом до Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних, що використовуються в Forgejo. Отже, вони також здатні отримати права адміністратора Forgejo. users.allow_import_local=Може імпортувати локальні репозиторії users.allow_create_organization=Може створювати організації @@ -3076,7 +3157,7 @@ auths.sspi_default_language_helper=Типова мова для користув auths.tips=Поради auths.tips.oauth2.general=Автентифікація OAuth2 auths.tip.oauth2_provider=Постачальник OAuth2 -auths.tip.bitbucket=Створіть OAuth URI на сторінці %s +auths.tip.bitbucket=Зареєструйте нового споживача OAuth на %s і додайте дозвіл «Обліковий запис» — «Читання» auths.tip.nextcloud=`Зареєструйте нового споживача OAuth у вашому екземплярі за допомогою наступного меню "Налаштування -> Безпека -> клієнт OAuth 2.0"` auths.tip.dropbox=Створіть новий додаток на %s auths.tip.facebook=Зареєструйте новий додаток на %s і додайте модуль «Facebook Login» @@ -3084,7 +3165,7 @@ auths.tip.github=Зареєструйте новий додаток OAuth на % auths.tip.gitlab=Додайте новий додаток на https://gitlab.com/profile/applications auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 в консолі Google API на сторінці %s auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (/.well-known/openid-configuration) для автоматичної настройки входу OAuth -auths.tip.twitter=Перейдіть на %s, створіть програму і переконайтеся, що включена опція «Дозволити цю програму для входу в систему за допомогою Twitter» +auths.tip.twitter=Перейдіть на %s, створіть програму і переконайтеся, що ввімкнено опцію «Дозволити використання цієї програми для входу через Twitter» auths.tip.discord=Зареєструйте новий додаток на %s auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) @@ -3281,7 +3362,7 @@ users.purge_help = Примусово видалити користувача і dashboard.cleanup_packages = Очистити непридатні пакунки monitor.last_execution_result = Результат repos.lfs_size = Розмір LFS -config.allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на існуючі облікові записи. +config.allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на облікові записи, що вже існують. config.mailer_enable_helo = Увімкнути HELO users.organization_creation.description = Дозволити створення нових організацій. users.cannot_delete_self = Ви не можете видалити себе @@ -3337,6 +3418,11 @@ config.set_setting_failed = Не вдалося встановити парам config.access_log_template = Шаблон журналу доступу dashboard.cancel_abandoned_jobs = Скасувати покинуті завдання дій monitor.download_diagnosis_report = Завантажити діагностичний звіт +auths.oauth2_map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи. +config.mailer_smtp_addr = Адреса SMTP +dashboard.update_checker = Перевірка оновлень +auths.map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи LDAP +auths.enable_ldap_groups = Увімкнути групи LDAP [action] @@ -3441,7 +3527,7 @@ settings.delete.notice = Ви збираєтеся видалити %s (%s). Ц details.author = Автор title = Пакунки arch.version.backup = Резервне копіювання -arch.version.conflicts = Суперечки +arch.version.conflicts = Конфлікти arch.version.replaces = Заміни arch.version.provides = Надає arch.version.groups = Група @@ -3559,6 +3645,12 @@ chef.registry = Налаштуйте цей реєстр у файлі ~/. owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки публічний ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. nuget.dependency.framework = Цільовий фреймворк owner.settings.cleanuprules.preview.overview = Заплановано видалити %d пакунків. +owner.settings.cleanuprules.pattern_full_match = Застосувати шаблон до повної назви пакунка +maven.download = Щоб завантажити залежність, запустіть із командного рядка: +maven.install2 = Запустити з командного рядка: +npm.dependencies.bundle = Пакетні залежності +npm.dependencies.peer = Однорангові залежності +arch.pacman.repo.multi = %s має одну й ту саму версію в різних дистрибутивах. [secrets] deletion = Видалити секрет diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index b12bce88ec..59901392c2 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -70,7 +70,7 @@ your_starred=点赞 your_settings=设置 all=所有 -sources=来源 +sources=原创 mirrors=镜像 collaborative=协作 forks=派生 @@ -1062,7 +1062,7 @@ language.description = 此语言将保存到您的账号中,并在您登录后 language.localization_project = 帮助我们将 Forgejo 翻译成您的语言!了解更多。 user_block_yourself = 您不能屏蔽自己。 pronouns_custom_label = 自定义代词 -change_username_redirect_prompt.with_cooldown.one = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 +change_username_redirect_prompt.with_cooldown.one = 旧用户名将在 %[1]d 天的保护期后对所有人可用。您仍可以在此期间重新认领旧用户名。 change_username_redirect_prompt.with_cooldown.few = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 keep_pronouns_private = 仅向已认证用户显示代词 keep_pronouns_private.description = 这将对未登录的访问者隐藏您的代词。 @@ -3636,7 +3636,7 @@ auto_merge_pull_request=`自动合并了拉取请求 %[3]s#%[2]s transfer_repo=将仓库 %s 转移至 %s push_tag=推送了标签 %[3]s 至仓库 %[4]s delete_tag=从%[3]s 删除了标签 %[2]s -delete_branch=从 %[3]s 删除分支 %[2]s +delete_branch=删除了 %[3]s%[2]s 分支 compare_branch=比较 compare_commits=比较 %d 提交 compare_commits_general=比较提交 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 0903f27396..5f52a02018 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1062,7 +1062,7 @@ language.localization_project = 幫助我們翻譯 Forgejo 至您的語言!公開活動只有你和站點管理員可見。 quota.rule.exceeded = 已超出 diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json index 3d87c7ee95..cef0f06e23 100644 --- a/options/locale_next/locale_ar.json +++ b/options/locale_next/locale_ar.json @@ -3,5 +3,7 @@ "home.welcome.no_activity": "لا يوجد نشاط", "home.explore_repos": "اكتشف المستودعات", "home.explore_users": "اكتشف المستخدمين", - "home.explore_orgs": "اكتشف المنظمات" + "home.explore_orgs": "اكتشف المنظمات", + "moderation.abuse_category.illegal_content": "المحتوى غير المشروع", + "moderation.abuse_category.malware": "برمجية خبيثة" } diff --git a/options/locale_next/locale_be.json b/options/locale_next/locale_be.json index 0967ef424b..40dfec0b41 100644 --- a/options/locale_next/locale_be.json +++ b/options/locale_next/locale_be.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "Шкодная праграма" +} diff --git a/options/locale_next/locale_bg.json b/options/locale_next/locale_bg.json index 1faf269aba..be8477a98a 100644 --- a/options/locale_next/locale_bg.json +++ b/options/locale_next/locale_bg.json @@ -7,7 +7,6 @@ "one": "иска да слее %[1]d подаване от %[2]s в %[3]s", "other": "иска да слее %[1]d подавания от %[2]s в %[3]s" }, - "mail.actions.run_info_ref": "Клон: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Задействано поради: %[1]s от: %[2]s", "meta.last_line": "В България расте най-старото дърво в страната, Байкушевата мура, на възраст над 1300 години.", "relativetime.1day": "вчера", diff --git a/options/locale_next/locale_bn.json b/options/locale_next/locale_bn.json index 0967ef424b..56a6e6dae4 100644 --- a/options/locale_next/locale_bn.json +++ b/options/locale_next/locale_bn.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "ম্যালওয়্যার" +} diff --git a/options/locale_next/locale_ca.json b/options/locale_next/locale_ca.json index 8eefc65a1a..606ab99173 100644 --- a/options/locale_next/locale_ca.json +++ b/options/locale_next/locale_ca.json @@ -1,3 +1,4 @@ { - "search.milestone_kind": "Cerca fites..." + "search.milestone_kind": "Cerca fites...", + "moderation.abuse_category.malware": "Programari maliciós" } diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 24380bea1b..3ce42e6bb0 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -28,7 +28,6 @@ "mail.actions.not_successful_run": "Workflow %[1]s selhal v repozitáři %[2]s", "mail.actions.run_info_cur_status": "Stav tohoto procesu: %[1]s (právě aktualizováno z %[2]s)", "mail.actions.run_info_previous_status": "Stav předchozího procesu: %[1]s", - "mail.actions.run_info_ref": "Větev: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Spuštěno z důvodu: %[1]s od: %[2]s", "mail.actions.successful_run_after_failure_subject": "Workflow %[1]s obnoven v repozitáři %[2]s", "mail.actions.successful_run_after_failure": "Workflow %[1]s obnoven v repozitáři %[2]s", @@ -111,5 +110,6 @@ "profile.edit.link": "Upravit profil", "feed.atom.link": "Zdroj Atom", "keys.ssh.link": "Klíče SSH", - "og.repo.summary_card.alt_description": "Karta se souhrnem repozitáře %[1]s, popsaným jako: %[2]s" + "og.repo.summary_card.alt_description": "Karta se souhrnem repozitáře %[1]s, popsaným jako: %[2]s", + "mail.actions.run_info_sha": "Revize: %[1]s" } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index 8315e06bcc..2620127b8c 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -28,7 +28,6 @@ "mail.actions.not_successful_run": "Arbejdsgangen %[1]s mislykkedes i depotet %[2]s", "mail.actions.run_info_cur_status": "Status for denne kørsel: %[1]s (lige opdateret fra %[2]s)", "mail.actions.run_info_previous_status": "Status for forrige kørsel: %[1]s", - "mail.actions.run_info_ref": "Gren: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Udløst fordi: %[1]s af: %[2]s", "discussion.locked": "Denne diskussion er blevet låst. Kommentarer er begrænset til bidragydere.", "relativetime.now": "nu", @@ -97,5 +96,10 @@ "settings.visibility.description": "Profilsynlighed påvirker andres adgang til dine ikke-private depoter. Læs mere", "avatar.constraints_hint": "Brugerdefineret avatar må ikke overstige %[1]s i størrelse eller være større end %[2]dx%[3]d pixels", "repo.diff.commit.next-short": "Næste", - "repo.diff.commit.previous-short": "Forrige" + "repo.diff.commit.previous-short": "Forrige", + "profile.actions.tooltip": "Flere handlinger", + "profile.edit.link": "Redigere profil", + "feed.atom.link": "Atom feed", + "keys.ssh.link": "SSH Nøgler", + "keys.gpg.link": "GPG Nøgler" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index d256c54dc6..5d670aeedf 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -27,7 +27,6 @@ "mail.actions.successful_run_after_failure": "Arbeitsablauf %[1]s in Repository %[2]s wiederhergestellt", "mail.actions.not_successful_run": "Arbeitsablauf %[1]s in Repository %[2]s fehlgeschlagen", "mail.actions.run_info_previous_status": "Vorheriger Status des Runs: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Ausgelöst, weil: %[1]s durch: %[2]s", "mail.actions.run_info_cur_status": "Status dieses Runs: %[1]s (gerade eben von %[2]s aktualisiert)", "discussion.locked": "Diese Diskussion wurde gesperrt. Nur Mitwirkende können kommentieren.", @@ -103,5 +102,6 @@ "keys.ssh.link": "SSH-Schlüssel", "keys.gpg.link": "GPG-Schlüssel", "profile.actions.tooltip": "Mehr Aktionen", - "og.repo.summary_card.alt_description": "Zusammenfassungskarte des Repositorys %[1]s, beschrieben als %[2]s" + "og.repo.summary_card.alt_description": "Zusammenfassungskarte des Repositorys %[1]s, beschrieben als %[2]s", + "mail.actions.run_info_sha": "Commit: %[1]s" } diff --git a/options/locale_next/locale_el-GR.json b/options/locale_next/locale_el-GR.json index d3ba25b170..cca89efd0e 100644 --- a/options/locale_next/locale_el-GR.json +++ b/options/locale_next/locale_el-GR.json @@ -12,7 +12,6 @@ "mail.actions.successful_run_after_failure_subject": "Η ροή εργασίας %[1]s αποκαταστάθηκε στο αποθετήριο %[2]s", "mail.actions.not_successful_run_subject": "Η ροή εργασίας %[1]s απέτυχε στο αποθετήριο %[2]s", "mail.actions.run_info_trigger": "Ενεργοποιήθηκε επειδή: %[1]s από: %[2]s", - "mail.actions.run_info_ref": "Αποθετήριο: %[1]s (%[2]s)", "meta.last_line": "Ευχαριστούμε που μεταφράζετε το Forgejo! Αυτή η γραμμή δεν είναι ορατή από τους χρήστες αλλά εξυπηρετεί άλλους σκοπούς στη διαχείριση της μετάφρασης. Μπορείτε να γράψετε κάποιο αστείο αντί για μετάφραση.", "mail.actions.successful_run_after_failure": "Η ροή εργασίας %[1]s αποκαταστάθηκε στο αποθετήριο %[2]s", "discussion.locked": "Αυτή η συζήτηση έχει κλειδωθεί. Ο σχολιασμός περιορίζεται στους συνεισφέροντες.", diff --git a/options/locale_next/locale_es-ES.json b/options/locale_next/locale_es-ES.json index 37edef9211..82fbcfb4b6 100644 --- a/options/locale_next/locale_es-ES.json +++ b/options/locale_next/locale_es-ES.json @@ -30,5 +30,6 @@ "relativetime.future": "en el futuro", "home.explore_repos": "Explorar repositorios", "home.explore_users": "Explorar usuarios", - "home.explore_orgs": "Explorar organizaciones" + "home.explore_orgs": "Explorar organizaciones", + "moderation.abuse_category.malware": "Malware" } diff --git a/options/locale_next/locale_fi-FI.json b/options/locale_next/locale_fi-FI.json index cb26d76e66..fdc740b978 100644 --- a/options/locale_next/locale_fi-FI.json +++ b/options/locale_next/locale_fi-FI.json @@ -63,6 +63,6 @@ "mail.actions.successful_run_after_failure": "Työnkulku %[1]s palautettu tietovarastoon %[2]s", "mail.actions.run_info_cur_status": "Tämän juoksun tila: %[1]s (juuri päivitetty %[2]s:sta)", "mail.actions.run_info_previous_status": "Edellisen ajon tila: %[1]s", - "mail.actions.run_info_ref": "Haara: %[1]s (%[2]s)", - "mail.actions.run_info_trigger": "Laukaistui, koska: %[1]s, tekijänä: %[2]s" + "mail.actions.run_info_trigger": "Laukaistui, koska: %[1]s, tekijänä: %[2]s", + "moderation.abuse_category.malware": "Haittaohjelma" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 84b537fbdc..2c62514648 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -25,7 +25,6 @@ "mail.actions.successful_run_after_failure": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.run_info_previous_status": "Nakaraang Status ng Run: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Na-trigger dahil: %[1]s ni/ng: %[2]s", "mail.actions.successful_run_after_failure_subject": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run_subject": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index da26d56107..438e0433d0 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -10,7 +10,6 @@ "other": "" }, "search.milestone_kind": "Recherche dans les jalons…", - "mail.actions.run_info_ref": "Branche : %[1]s (%[2]s)", "discussion.locked": "Cette discussion a été bloqué. Les commentaires sont limités aux contributeurs.", "relativetime.now": "maintenant", "relativetime.future": "dans le future", diff --git a/options/locale_next/locale_hu-HU.json b/options/locale_next/locale_hu-HU.json index 9d52509102..1c02002e6e 100644 --- a/options/locale_next/locale_hu-HU.json +++ b/options/locale_next/locale_hu-HU.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "egyesítve %[1]d változás(ok) a %[2]s-ból %[3]s-ba %[4]s", "repo.pulls.title_desc": "egyesíteni szeretné %[1]d változás(oka)t a(z) %[2]s-ból %[3]s-ba", - "search.milestone_kind": "Mérföldkövek keresése..." + "search.milestone_kind": "Mérföldkövek keresése...", + "moderation.abuse_category.malware": "Malware" } diff --git a/options/locale_next/locale_id-ID.json b/options/locale_next/locale_id-ID.json index f2dac8114f..14bf491789 100644 --- a/options/locale_next/locale_id-ID.json +++ b/options/locale_next/locale_id-ID.json @@ -1,8 +1,5 @@ { - "repo.pulls.merged_title_desc": { - "other": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "other": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s" - } + "repo.pulls.merged_title_desc": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s", + "repo.pulls.title_desc": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s", + "moderation.abuse_category.malware": "Perangkat pembahaya" } diff --git a/options/locale_next/locale_it-IT.json b/options/locale_next/locale_it-IT.json index 8464d6244e..f4cc7755f0 100644 --- a/options/locale_next/locale_it-IT.json +++ b/options/locale_next/locale_it-IT.json @@ -82,7 +82,6 @@ "moderation.report_abuse_form.invalid": "Argomenti non validi", "moderation.reporting_failed": "Impossibile inviare segnalazione: %v", "moderation.reported_thank_you": "Grazie per la segnalazione. L'amministratore è stato avvertito.", - "mail.actions.run_info_ref": "Ramo: %[1]s (%[2]s)", "alert.asset_load_failed": "Impossibile caricare i file di risorsa da {path}. Controlla che i file di risorsa siano accessibili.", "install.invalid_lfs_path": "Non è possibile creare una root LFS nel percorso specificato: %[1]s", "home.welcome.activity_hint": "Non c'è nulla nel tuo feed. Le tue azioni e le attività dei repositori che segui verranno mostrate qui.", diff --git a/options/locale_next/locale_ja-JP.json b/options/locale_next/locale_ja-JP.json index 40edf8cb90..c4b3a0a2e0 100644 --- a/options/locale_next/locale_ja-JP.json +++ b/options/locale_next/locale_ja-JP.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "が %[1]d 個のコミットを %[2]s から %[3]s へマージ %[4]s", "repo.pulls.title_desc": "が %[2]s から %[3]s への %[1]d コミットのマージを希望しています", - "search.milestone_kind": "マイルストーンを検索..." + "search.milestone_kind": "マイルストーンを検索...", + "moderation.abuse_category.malware": "悪意のコード" } diff --git a/options/locale_next/locale_ko-KR.json b/options/locale_next/locale_ko-KR.json index 98c949d517..2e51144cb7 100644 --- a/options/locale_next/locale_ko-KR.json +++ b/options/locale_next/locale_ko-KR.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "님이 %[2]s 에서 %[3]s 로 %[1]d 커밋을 %[4]s 병합함", "repo.pulls.title_desc": "%[2]s 에서 %[3]s 로 %[1]d개의 커밋들을 병합하려함", - "home.welcome.no_activity": "활동 없음" + "home.welcome.no_activity": "활동 없음", + "moderation.abuse_category.malware": "악성 소프트웨어" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 4deae220bf..22b80f2fad 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -30,7 +30,6 @@ "mail.actions.not_successful_run": "Darbplūsmas %[1] atteice glabātavā %[2]s", "mail.actions.run_info_cur_status": "Šī izpildījuma stāvoklis: %[1]s (tikko atjaunināts no %[2]s)", "mail.actions.run_info_previous_status": "Iepriekšējā izpildījuma stāvoklis: %[1]s", - "mail.actions.run_info_ref": "Zars: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Izraisīšanas iemesls: %[2]s no: %[2]s", "discussion.locked": "Šī apspriede tika slēgta. Piebilžu pievienošana ir ļauta tikai līdzdalībniekiem.", "relativetime.future": "nākotnē", @@ -111,5 +110,6 @@ "feed.atom.link": "Atom barotne", "keys.ssh.link": "SSH atslēgas", "keys.gpg.link": "GPG atslēgas", - "og.repo.summary_card.alt_description": "Glabātavas %[1]s kopsavilkuma kartīte, aprakstīta kā: %[2]s" + "og.repo.summary_card.alt_description": "Glabātavas %[1]s kopsavilkuma kartīte, aprakstīta kā: %[2]s", + "mail.actions.run_info_sha": "Iesūtījums: %[1]s" } diff --git a/options/locale_next/locale_nb_NO.json b/options/locale_next/locale_nb_NO.json index 3349e136b5..b6b10cebb5 100644 --- a/options/locale_next/locale_nb_NO.json +++ b/options/locale_next/locale_nb_NO.json @@ -98,7 +98,6 @@ "mail.actions.not_successful_run": "Arbeidsflyten %[1]s feilet i repository %[2]s", "mail.actions.run_info_cur_status": "Status for denne kjøringen: %[1]s (oppdatert fra %[2]s)", "mail.actions.run_info_previous_status": "Status for forrige kjøring: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "repo.diff.commit.next-short": "Neste", "repo.diff.commit.previous-short": "Forrige", "discussion.locked": "Denne diskusjonen er låst. Kommentarer kan kun gjøres av bidragsytere.", diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 207a138926..20c1208c42 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -28,7 +28,6 @@ "mail.actions.not_successful_run": "Warkwies %[1]s in Repositorium %[2]s is fehlslagen", "mail.actions.run_info_cur_status": "Tostand vun deesem Utföhren: %[1]s (jüüst vun %[2]s verneeit)", "mail.actions.run_info_previous_status": "Tostand vun de vörig Utföhren: %[1]s", - "mail.actions.run_info_ref": "Twieg: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Utlööst um: %[1]s vun: %[2]s", "discussion.locked": "Deeser Snack is tosloten worden. Blots Bidragers könen kommenteren.", "relativetime.future": "in Tokunft", @@ -103,5 +102,6 @@ "keys.gpg.link": "GPG-Slötels", "profile.actions.tooltip": "Mehr Aktioonen", "profile.edit.link": "Profil bewarken", - "og.repo.summary_card.alt_description": "Tosamenfatens-Kaart vun de Repositorium %[1]s, beschrieven as: %[2]s" + "og.repo.summary_card.alt_description": "Tosamenfatens-Kaart vun de Repositorium %[1]s, beschrieven as: %[2]s", + "mail.actions.run_info_sha": "Kommitteren: %[1]s" } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 5df7db2b9d..e049db3d41 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -28,7 +28,6 @@ "mail.actions.not_successful_run": "Werkstroom %[1]s mislukt in repositorie %[2]s", "mail.actions.run_info_cur_status": "De status van deze run: %[1]s (zojuist bijgewerkt van %[2]s)", "mail.actions.run_info_previous_status": "Status vorige run: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Getriggerd omdat: %[1]s door: %[2]s", "discussion.locked": "Deze discussie is afgesloten. Commentaar is alleen mogelijk voor bijdragers.", "relativetime.now": "nu", @@ -103,5 +102,6 @@ "feed.atom.link": "Atom-feed", "repo.diff.commit.previous-short": "Vorige", "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels", - "og.repo.summary_card.alt_description": "Samenvattingsoverzicht van repositorie %[1]s, omschreven als: %[2]s" + "og.repo.summary_card.alt_description": "Samenvattingsoverzicht van repositorie %[1]s, omschreven als: %[2]s", + "mail.actions.run_info_sha": "Commit: %[1]s" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 336dd5a484..6ed6c5defd 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -26,7 +26,6 @@ "meta.last_line": "real hot girl shit", "mail.actions.run_info_cur_status": "Status desta execução: %[1]s (atualizado recentemente de %[2]s)", "mail.actions.run_info_previous_status": "Status da execução anterior: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.successful_run_after_failure_subject": "Workflow %[1]s recuperado no repositório %[2]s", "mail.actions.not_successful_run_subject": "Workflow %[1]s falhou no repositório %[2]s", "mail.actions.successful_run_after_failure": "Workflow %[1]s recuperado no repositório %[2]s", @@ -83,7 +82,7 @@ "moderation.abuse_category": "Categoria", "moderation.abuse_category.placeholder": "Selecione uma categoria", "moderation.abuse_category.spam": "Spam", - "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.malware": "Software malicioso", "moderation.abuse_category.illegal_content": "Conteúdo ilegal", "moderation.abuse_category.other_violations": "Outras violações de regras da plataforma", "moderation.report_remarks": "Observações", @@ -111,5 +110,6 @@ "keys.gpg.link": "Chaves GPG", "og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s", "profile.actions.tooltip": "Mais Actions", - "keys.ssh.link": "Chaves SSH" + "keys.ssh.link": "Chaves SSH", + "mail.actions.run_info_sha": "Commit: %[1]s" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index a0caa90b68..352e948966 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -27,7 +27,6 @@ "mail.actions.successful_run_after_failure_subject": "Sequência de trabalho %[1]s foi recuperada no repositório %[2]s", "mail.actions.not_successful_run_subject": "Sequência de trabalho %[1]s falhou no repositório %[2]s", "mail.actions.not_successful_run": "Sequência de trabalho %[1]s falhou no repositório %[2]s", - "mail.actions.run_info_ref": "Ramo: %[1]s (%[2]s)", "mail.actions.successful_run_after_failure": "Sequência de trabalho %[1]s foi recuperada no repositório %[2]s", "discussion.locked": "Esta discussão foi fechada. Apenas contribuidores podem publicar comentários.", "mail.actions.run_info_cur_status": "Estado desta execução: %[1]s (atualizado recentemente de %[2]s)", diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 922e2612af..19167d7177 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -26,7 +26,6 @@ "meta.last_line": "Unskip..", "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_ref": "Ветвь: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Причина срабатывания: %[1]s by: %[2]s", "mail.actions.successful_run_after_failure": "Рабочий поток %[1]s в репозитории %[2]s был возобновлён после провала", "mail.actions.run_info_cur_status": "Текущее состояние: %[1]s (обновлено после %[2]s)", @@ -110,5 +109,7 @@ "feed.atom.link": "Atom-лента", "keys.ssh.link": "Ключи SSH", "keys.gpg.link": "Ключи GPG", - "profile.edit.link": "Изменить профиль" + "profile.edit.link": "Изменить профиль", + "og.repo.summary_card.alt_description": "Карточка со сводкой о репозитории %s. Описание: %[2]s", + "mail.actions.run_info_sha": "Коммит: %[1]s" } diff --git a/options/locale_next/locale_sr-SP.json b/options/locale_next/locale_sr-SP.json index 0967ef424b..611f997b3e 100644 --- a/options/locale_next/locale_sr-SP.json +++ b/options/locale_next/locale_sr-SP.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "Малвер" +} diff --git a/options/locale_next/locale_sv-SE.json b/options/locale_next/locale_sv-SE.json index 3bc37deff8..1195647bf5 100644 --- a/options/locale_next/locale_sv-SE.json +++ b/options/locale_next/locale_sv-SE.json @@ -27,7 +27,6 @@ "moderation.reporting_failed": "Det gick inte att skicka in den nya övergreppsrapporten: %v", "mail.actions.run_info_cur_status": "Status för denna körning: %[1]s (just uppdaterad från %[2]s)", "mail.actions.run_info_previous_status": "Status för föregående körning: %[1]s", - "mail.actions.run_info_ref": "Gren: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Utlöses på grund av: %[1]s av: %[2]s", "alert.asset_load_failed": "Misslyckades med att läsa in resursfiler från {path}. Kontrollera att resursfilerna är åtkomliga.", "install.invalid_lfs_path": "Det gick inte att skapa LFS-roten på den angivna sökvägen: %[1]s", diff --git a/options/locale_next/locale_tr-TR.json b/options/locale_next/locale_tr-TR.json index b0e34e677f..fa71cb8e7d 100644 --- a/options/locale_next/locale_tr-TR.json +++ b/options/locale_next/locale_tr-TR.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi", "repo.pulls.title_desc": "%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor", - "search.milestone_kind": "Kilometre taşlarını ara..." + "search.milestone_kind": "Kilometre taşlarını ara...", + "moderation.abuse_category.malware": "Malware" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index af219b50d0..8e99505e57 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -25,7 +25,6 @@ "alert.range_error": " має бути числом від %[1]s до %[2]s.", "meta.last_line": "Не зливай злий запити на злиття — зіллється зле.", "mail.actions.successful_run_after_failure": "Робочий потік %[1]s відновлено в репозиторії %[2]s", - "mail.actions.run_info_ref": "Гілка: %[1]s (%[2]s)", "mail.actions.successful_run_after_failure_subject": "Робочий потік %[1]s відновлено в репозиторії %[2]s", "mail.actions.run_info_previous_status": "Стан попереднього запуску: %[1]s", "mail.actions.run_info_cur_status": "Стан цього запуску: %[1]s (щойно оновлено з %[2]s)", @@ -111,5 +110,6 @@ "profile.edit.link": "Редагувати профіль", "feed.atom.link": "Стрічка Atom", "profile.actions.tooltip": "Більше дій", - "og.repo.summary_card.alt_description": "Підсумкова картка репозиторію %[1]s з описом: %[2]s" + "og.repo.summary_card.alt_description": "Підсумкова картка репозиторію %[1]s з описом: %[2]s", + "mail.actions.run_info_sha": "Коміт: %[1]s" } diff --git a/options/locale_next/locale_vi.json b/options/locale_next/locale_vi.json index 0967ef424b..7ae64113b6 100644 --- a/options/locale_next/locale_vi.json +++ b/options/locale_next/locale_vi.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "Phần mềm ác ý" +} diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 62277acb95..c235ebd0ad 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -20,7 +20,6 @@ "mail.actions.not_successful_run_subject": "仓库 %[2]s 中的工作流 %[1]s 已失败", "mail.actions.successful_run_after_failure": "仓库 %[2]s 中的工作流 %[1]s 已恢复", "mail.actions.run_info_previous_status": "上次运行的状态:%[1]s", - "mail.actions.run_info_ref": "分支:%[1]s(%[2]s)", "mail.actions.run_info_cur_status": "此次运行的状态:%[1]s(从 %[2]s 更新)", "mail.actions.run_info_trigger": "由 %[2]s %[1]s 触发", "mail.actions.not_successful_run": "仓库 %[2]s 中的工作流 %[1]s 已失败", @@ -71,12 +70,12 @@ "editor.textarea.shift_tab_hint": "此行无缩进。再次按 Shift + Tab 或按 Escape 退出编辑器。", "admin.dashboard.cleanup_offline_runners": "清理离线运行器", "settings.visibility.description": "个人资料可见性设置会影响他人对您的非私有仓库的访问。了解更多", - "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,或大于 %[2]d×%[3]d 像素", + "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,且分辨率不得大于 %[2]d×%[3]d 像素", "keys.ssh.link": "SSH 密钥", "keys.gpg.link": "GPG 密钥", "profile.actions.tooltip": "更多操作", - "repo.diff.commit.next-short": "下个", - "repo.diff.commit.previous-short": "上个", + "repo.diff.commit.next-short": "下一个", + "repo.diff.commit.previous-short": "上一个", "feed.atom.link": "Atom 订阅源", "profile.edit.link": "编辑个人资料", "og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s" diff --git a/options/locale_next/locale_zh-HK.json b/options/locale_next/locale_zh-HK.json index 6baf89e022..277385cf5f 100644 --- a/options/locale_next/locale_zh-HK.json +++ b/options/locale_next/locale_zh-HK.json @@ -1,5 +1,4 @@ { - "repo.pulls.merged_title_desc": { - "other": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s" - } + "repo.pulls.merged_title_desc": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s", + "moderation.abuse_category.malware": "惡意程式" } diff --git a/options/locale_next/locale_zh-TW.json b/options/locale_next/locale_zh-TW.json index 3ae0b00d2b..8a5a17a774 100644 --- a/options/locale_next/locale_zh-TW.json +++ b/options/locale_next/locale_zh-TW.json @@ -55,7 +55,6 @@ "mail.actions.not_successful_run": "儲存庫 %[2]s 中的工作流程 %[1]s 已失敗", "mail.actions.run_info_cur_status": "本次執行狀態:%[1]s(剛從 %[2]s 更新)", "mail.actions.run_info_previous_status": "前一次執行狀態:%[1]s", - "mail.actions.run_info_ref": "分支:%[1]s (%[2]s)", "mail.actions.run_info_trigger": "觸發原因:%[1]s,由 %[2]s 執行", "discussion.locked": "此討論已被鎖定。僅限貢獻者留言。", "alert.asset_load_failed": "無法從 {path} 載入資源檔案。請確保這些資源檔案可以被存取。", From 9dfdacf54f03bcfdae7ab050c17c2f8556f3a030 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 12 Jul 2025 00:39:35 +0200 Subject: [PATCH 105/297] feat: add configuration to only push mirror selected branches (#7823) Adds the ability to selectively choose which branches are pushed to a mirror. This change adds an additional text box on the repository settings for each push mirror. Existing behavior is preserved when the field is left blank. When the repository is being pushed, only branches matching the comma separated branch filter are pushed. Resolves forgejo/forgejo#7242 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7823 Reviewed-by: Gusted Co-authored-by: Paul Campbell Co-committed-by: Paul Campbell --- models/forgejo_migrations/migrate.go | 1 + models/forgejo_migrations/v37.go | 16 + models/repo/pushmirror.go | 6 + models/repo/pushmirror_test.go | 136 ++++ modules/structs/mirror.go | 3 + options/locale_next/locale_en-US.json | 2 + routers/api/v1/repo/mirror.go | 1 + routers/web/repo/setting/setting.go | 19 + services/convert/mirror.go | 1 + services/forms/repo_form.go | 1 + services/mirror/mirror_push.go | 63 +- templates/repo/settings/options.tmpl | 6 + .../repo/settings/push_mirror_sync_modal.tmpl | 11 +- templates/swagger/v1_json.tmpl | 8 + tests/integration/api_push_mirror_test.go | 154 ++++ tests/integration/mirror_push_test.go | 671 ++++++++++++++++++ 16 files changed, 1089 insertions(+), 10 deletions(-) create mode 100644 models/forgejo_migrations/v37.go diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index fcea69d23f..384f382c82 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -111,6 +111,7 @@ var migrations = []*Migration{ NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), // v35 -> v36 NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), + NewMigration("Add `branch_filter` to `push_mirror` table", AddPushMirrorBranchFilter), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v37.go b/models/forgejo_migrations/v37.go new file mode 100644 index 0000000000..89358991af --- /dev/null +++ b/models/forgejo_migrations/v37.go @@ -0,0 +1,16 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "xorm.io/xorm" +) + +func AddPushMirrorBranchFilter(x *xorm.Engine) error { + type PushMirror struct { + ID int64 `xorm:"pk autoincr"` + BranchFilter string `xorm:"VARCHAR(255)"` + } + return x.Sync2(new(PushMirror)) +} diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go index d6d0d1135a..e57897fb7e 100644 --- a/models/repo/pushmirror.go +++ b/models/repo/pushmirror.go @@ -32,6 +32,7 @@ type PushMirror struct { Repo *Repository `xorm:"-"` RemoteName string RemoteAddress string `xorm:"VARCHAR(2048)"` + BranchFilter string `xorm:"VARCHAR(2048)"` // A keypair formatted in OpenSSH format. PublicKey string `xorm:"VARCHAR(100)"` @@ -122,6 +123,11 @@ func UpdatePushMirrorInterval(ctx context.Context, m *PushMirror) error { return err } +func UpdatePushMirrorBranchFilter(ctx context.Context, m *PushMirror) error { + _, err := db.GetEngine(ctx).ID(m.ID).Cols("branch_filter").Update(m) + return err +} + var DeletePushMirrors = deletePushMirrors func deletePushMirrors(ctx context.Context, opts PushMirrorOptions) error { diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go index fbef835372..a7e063ff71 100644 --- a/models/repo/pushmirror_test.go +++ b/models/repo/pushmirror_test.go @@ -75,3 +75,139 @@ func TestPushMirrorPrivatekey(t *testing.T) { assert.Empty(t, actualPrivateKey) }) } + +func TestPushMirrorBranchFilter(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Create push mirror with branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-branch-filter", + BranchFilter: "main,develop", + } + unittest.AssertSuccessfulInsert(t, m) + assert.NotZero(t, m.ID) + assert.Equal(t, "main,develop", m.BranchFilter) + }) + + t.Run("Create push mirror with empty branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-empty-filter", + BranchFilter: "", + } + unittest.AssertSuccessfulInsert(t, m) + assert.NotZero(t, m.ID) + assert.Empty(t, m.BranchFilter) + }) + + t.Run("Create push mirror without branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-no-filter", + // BranchFilter: "", + } + unittest.AssertSuccessfulInsert(t, m) + assert.NotZero(t, m.ID) + assert.Empty(t, m.BranchFilter) + }) + + t.Run("Update branch filter", func(t *testing.T) { + m := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-update", + BranchFilter: "main", + } + unittest.AssertSuccessfulInsert(t, m) + + m.BranchFilter = "main,develop" + require.NoError(t, repo_model.UpdatePushMirrorBranchFilter(db.DefaultContext, m)) + + updated := unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: m.ID}) + assert.Equal(t, "main,develop", updated.BranchFilter) + }) + + t.Run("Retrieve push mirror with branch filter", func(t *testing.T) { + original := &repo_model.PushMirror{ + RepoID: 1, + RemoteName: "test-retrieve", + BranchFilter: "main,develop", + } + unittest.AssertSuccessfulInsert(t, original) + + retrieved := unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: original.ID}) + assert.Equal(t, original.BranchFilter, retrieved.BranchFilter) + assert.Equal(t, "main,develop", retrieved.BranchFilter) + }) + + t.Run("GetPushMirrorsByRepoID includes branch filter", func(t *testing.T) { + mirrors := []*repo_model.PushMirror{ + { + RepoID: 2, + RemoteName: "mirror-1", + BranchFilter: "main", + }, + { + RepoID: 2, + RemoteName: "mirror-2", + BranchFilter: "develop,feature-*", + }, + { + RepoID: 2, + RemoteName: "mirror-3", + BranchFilter: "", + }, + } + + for _, mirror := range mirrors { + unittest.AssertSuccessfulInsert(t, mirror) + } + + retrieved, count, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, 2, db.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, int64(3), count) + assert.Len(t, retrieved, 3) + + filterMap := make(map[string]string) + for _, mirror := range retrieved { + filterMap[mirror.RemoteName] = mirror.BranchFilter + } + + assert.Equal(t, "main", filterMap["mirror-1"]) + assert.Equal(t, "develop,feature-*", filterMap["mirror-2"]) + assert.Empty(t, filterMap["mirror-3"]) + }) + + t.Run("GetPushMirrorsSyncedOnCommit includes branch filter", func(t *testing.T) { + mirrors := []*repo_model.PushMirror{ + { + RepoID: 3, + RemoteName: "sync-mirror-1", + BranchFilter: "main,develop", + SyncOnCommit: true, + }, + { + RepoID: 3, + RemoteName: "sync-mirror-2", + BranchFilter: "feature-*", + SyncOnCommit: true, + }, + } + + for _, mirror := range mirrors { + unittest.AssertSuccessfulInsert(t, mirror) + } + + retrieved, err := repo_model.GetPushMirrorsSyncedOnCommit(db.DefaultContext, 3) + require.NoError(t, err) + assert.Len(t, retrieved, 2) + + filterMap := make(map[string]string) + for _, mirror := range retrieved { + filterMap[mirror.RemoteName] = mirror.BranchFilter + } + + assert.Equal(t, "main,develop", filterMap["sync-mirror-1"]) + assert.Equal(t, "feature-*", filterMap["sync-mirror-2"]) + }) +} diff --git a/modules/structs/mirror.go b/modules/structs/mirror.go index 1b6566803a..4909ae20ca 100644 --- a/modules/structs/mirror.go +++ b/modules/structs/mirror.go @@ -13,6 +13,7 @@ type CreatePushMirrorOption struct { Interval string `json:"interval"` SyncOnCommit bool `json:"sync_on_commit"` UseSSH bool `json:"use_ssh"` + BranchFilter string `json:"branch_filter"` } // PushMirror represents information of a push mirror @@ -29,4 +30,6 @@ type PushMirror struct { Interval string `json:"interval"` SyncOnCommit bool `json:"sync_on_commit"` PublicKey string `json:"public_key"` + + BranchFilter string `json:"branch_filter"` } diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index e08c8b2aee..1778a1fc6c 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -55,6 +55,8 @@ "repo.form.cannot_create": "All spaces in which you can create repositories have reached the limit of repositories.", "repo.issue_indexer.title": "Issue Indexer", "search.milestone_kind": "Search milestones…", + "repo.settings.push_mirror.branch_filter.label": "Branch filter (optional)", + "repo.settings.push_mirror.branch_filter.description": "Branches to be mirrored. Leave blank to mirror all branches. See %[2]s documentation for syntax. Examples: main, release/*", "incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.", "themes.names.forgejo-auto": "Forgejo (follow system theme)", "themes.names.forgejo-light": "Forgejo light", diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index bc48c6acb7..f08867dee4 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -389,6 +389,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro Interval: interval, SyncOnCommit: mirrorOption.SyncOnCommit, RemoteAddress: remoteAddress, + BranchFilter: mirrorOption.BranchFilter, } var plainPrivateKey []byte diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 6f35e19880..595fdace83 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -6,6 +6,7 @@ package setting import ( + go_context "context" "errors" "fmt" "net/http" @@ -589,6 +590,23 @@ func SettingsPost(ctx *context.Context) { ctx.ServerError("UpdatePushMirrorInterval", err) return } + + if m.BranchFilter != form.PushMirrorBranchFilter { + // replace `remote..push` in config and db + m.BranchFilter = form.PushMirrorBranchFilter + if err := db.WithTx(ctx, func(ctx go_context.Context) error { + // Update the DB + if err = repo_model.UpdatePushMirrorBranchFilter(ctx, m); err != nil { + return err + } + // Update the repo config + return mirror_service.UpdatePushMirrorBranchFilter(ctx, m) + }); err != nil { + ctx.ServerError("UpdatePushMirrorBranchFilter", err) + return + } + } + // Background why we are adding it to Queue // If we observed its implementation in the context of `push-mirror-sync` where it // is evident that pushing to the queue is necessary for updates. @@ -684,6 +702,7 @@ func SettingsPost(ctx *context.Context) { SyncOnCommit: form.PushMirrorSyncOnCommit, Interval: interval, RemoteAddress: remoteAddress, + BranchFilter: form.PushMirrorBranchFilter, } var plainPrivateKey []byte diff --git a/services/convert/mirror.go b/services/convert/mirror.go index 9e7d2659ab..5a815f3a5c 100644 --- a/services/convert/mirror.go +++ b/services/convert/mirror.go @@ -23,5 +23,6 @@ func ToPushMirror(ctx context.Context, pm *repo_model.PushMirror) (*api.PushMirr Interval: pm.Interval.String(), SyncOnCommit: pm.SyncOnCommit, PublicKey: pm.GetPublicKey(), + BranchFilter: pm.BranchFilter, }, nil } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index bb81e939b0..d040b41395 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -141,6 +141,7 @@ type RepoSettingForm struct { PushMirrorSyncOnCommit bool PushMirrorInterval string PushMirrorUseSSH bool + PushMirrorBranchFilter string `binding:"MaxSize(2048)" preprocess:"TrimSpace"` Private bool Template bool EnablePrune bool diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 11b8ad459a..fdd02dedea 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -33,19 +33,22 @@ var AddPushMirrorRemote = addPushMirrorRemote func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error { addRemoteAndConfig := func(addr, path string) error { - cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr) - if strings.Contains(addr, "://") && strings.Contains(addr, "@") { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path)) + var cmd *git.Command + if m.BranchFilter == "" { + cmd = git.NewCommand(ctx, "remote", "add", "--mirror").AddDynamicArguments(m.RemoteName, addr) } else { - cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path)) + cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(m.RemoteName, addr) + } + if strings.Contains(addr, "://") && strings.Contains(addr, "@") { + cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path)) + } else { + cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, addr, path)) } if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil { return err } - if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { - return err - } - if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + err := addRemotePushRefSpecs(ctx, path, m) + if err != nil { return err } return nil @@ -67,6 +70,49 @@ func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str return nil } +func addRemotePushRefSpecs(ctx context.Context, path string, m *repo_model.PushMirror) error { + if m.BranchFilter == "" { + // If there is no branch filter, set the push refspecs to mirror all branches and tags. + if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + return err + } + } else { + branches := strings.SplitSeq(m.BranchFilter, ",") + for branch := range branches { + branch = strings.TrimSpace(branch) + if branch == "" { + continue + } + refspec := fmt.Sprintf("+refs/heads/%s:refs/heads/%s", branch, branch) + if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", refspec).RunStdString(&git.RunOpts{Dir: path}); err != nil { + return err + } + } + } + if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil { + return err + } + return nil +} + +func UpdatePushMirrorBranchFilter(ctx context.Context, m *repo_model.PushMirror) error { + path := m.Repo.RepoPath() + + // First, remove all existing push refspecs for this remote + cmd := git.NewCommand(ctx, "config", "--unset-all").AddDynamicArguments("remote." + m.RemoteName + ".push") + if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil { + // Ignore error if the key doesn't exist + if !strings.Contains(err.Error(), "does not exist") { + return err + } + } + err := addRemotePushRefSpecs(ctx, path, m) + if err != nil { + return err + } + return nil +} + // RemovePushMirrorRemote removes the push mirror remote. func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error { cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName) @@ -212,7 +258,6 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error { return util.SanitizeErrorCredentialURLs(err) } - return nil } diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 797dbe403b..c8061a75b0 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -254,6 +254,7 @@ data-modal-push-mirror-edit-id="{{.ID}}" data-modal-push-mirror-edit-interval="{{.Interval}}" data-modal-push-mirror-edit-address="{{.RemoteAddress}}" + data-modal-push-mirror-edit-branch-filter="{{.BranchFilter}}" > {{svg "octicon-pencil" 14}} @@ -288,6 +289,11 @@

{{ctx.Locale.Tr "repo.mirror_address_desc"}}

+
+ + +

{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "forgejo"}}

+
{{ctx.Locale.Tr "repo.need_auth"}} diff --git a/templates/repo/settings/push_mirror_sync_modal.tmpl b/templates/repo/settings/push_mirror_sync_modal.tmpl index e8dad61a48..32f0994f03 100644 --- a/templates/repo/settings/push_mirror_sync_modal.tmpl +++ b/templates/repo/settings/push_mirror_sync_modal.tmpl @@ -15,7 +15,16 @@

- +
+ +
+
+
+ +
+ +
+

{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "forgejo"}}

+ + + {{ctx.Locale.Tr "discussion.sidebar.reference"}} + + +
+
+ {{$issueReferenceLink}} + +
From 8efb6c09db70de54aa153f05b41a33ff7dbb4989 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Wed, 16 Jul 2025 16:04:57 +0000 Subject: [PATCH 126/297] i18n: update of translations from Codeberg Translate Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Juno Takano Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: adf19 Co-authored-by: amv-bamboo Co-authored-by: justbispo Co-authored-by: oatbiscuits Co-authored-by: pixelcode Co-authored-by: xtex Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/be/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_PT/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/zh_Hans/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_ar.ini | 159 +++++++++++++----- options/locale/locale_de-DE.ini | 224 +++++++++++++------------- options/locale/locale_uk-UA.ini | 93 +++++++++-- options/locale_next/locale_ar.json | 136 +++++++++++++++- options/locale_next/locale_be.json | 7 +- options/locale_next/locale_cs-CZ.json | 4 +- options/locale_next/locale_de-DE.json | 12 +- options/locale_next/locale_fil.json | 9 +- options/locale_next/locale_lv-LV.json | 4 +- options/locale_next/locale_nds.json | 4 +- options/locale_next/locale_nl-NL.json | 4 +- options/locale_next/locale_pt-BR.json | 4 +- options/locale_next/locale_pt-PT.json | 8 +- options/locale_next/locale_ru-RU.json | 6 +- options/locale_next/locale_uk-UA.json | 4 +- options/locale_next/locale_zh-CN.json | 5 +- 16 files changed, 496 insertions(+), 187 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index ba11586b44..638c2dc6ce 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -50,7 +50,7 @@ concept_user_organization = المنظمة link_account = ربط الحساب rerun_all = أعِد تشغيل جميع الوظائف your_profile = الملف الشخصي -sign_out = سجل الخروج +sign_out = سجّل الخروج settings = الإعدادات locked = مقفول error = خطأ @@ -87,7 +87,7 @@ add_all = أضف الكل new_fork = اشتقاق جديد لمستودع new_project_column = عمود جديد add = أضف -active_stopwatch = تتبع وقت الإنجاز +active_stopwatch = متتبِّع وقت النشاط organization = منظمة new_migrate = ترحيل جديد save = احفظ @@ -114,7 +114,7 @@ twofa_scratch = الرمز الاحتياطي للمصادقة بعاملين home = الرئيسية email = عنوان البريد الإلكتروني issues = المسائل -error404 = الصفحة التي تحاول الوصول لها إما لا توجد أو أنت لست مأذون لك بعرضها. +error404 = الصفحة التي تحاول الوصول لها إما غير موجودو أو أنك غير مصرح لك بعرضها. powered_by = مدعوم بواسطة %s retry = أعد المحاولة tracked_time_summary = ملخص للتتبع الزمني وفقًا لنتائج تصفية قائمة المسائل @@ -127,8 +127,8 @@ toggle_menu = تبديل القائمة more_items = عناصر اضافية copy_generic = نسخ إلى الحافظة invalid_data = بيانات غير صالحة: %v -filter.clear = مسح المرشحات -filter = مرشح +filter.clear = مسح عوامل التصفية +filter = عامل تصفية filter.is_archived = مؤرشف filter.is_template = قوالب filter.not_mirror = ليست مرايا @@ -137,13 +137,17 @@ filter.is_mirror = مرايا filter.is_fork = الاشتقاقات filter.not_fork = ليست اشتقاقات filter.not_archived = ليس مؤرشف -filter.public = علني +filter.public = عام filter.private = خاص new_repo.title = مستودع جديد new_migrate.title = انتقال جديد new_org.title = منظمة جديدة new_repo.link = مستودع جديد new_migrate.link = انتقال جديد +copy_path = نسخ المسار +test = اختبار +new_org.link = منظمة جديدة +error413 = لقد استنفدت حصتك. [install] db_name = اسم قاعدة البيانات @@ -170,7 +174,7 @@ reinstall_confirm_check_2 = وقد يلزم إعادة تزامن المستود run_user = شغّل عبر مستخدم err_admin_name_is_invalid = اسم مستخدم المدير غير صالح reinstall_confirm_check_3 = أنتِ تؤكد أنكِ متأكد تماماً من أن فورجيو يعمل مع مسار app.ini الصحيح وأنك متأكد من أنه يجب عليك إعادة تثبيته. أنت تُؤكّدُ بأنّك تُقرّ بالمخاطر السالفة الذكر. -repo_path = المسار الجذري للمستودع +repo_path = المسار الجذر للمستودع err_empty_admin_email = عنوان بريد المدير لا يمكن أن يكون فارغ. no_admin_and_disable_registration = لا يمكنك تعطيل التسجيل الذاتي للمستخدمين بدون إنشاء حساب إداري. err_admin_name_pattern_not_allowed = اسم مستخدم المدير غير صالح، هذا الأسم يطابق نمطا محجوز @@ -179,10 +183,10 @@ repo_path_helper = ستُحفظ كلّ مستودعات جِت البعيدة ف general_title = الإعدادات العامة lfs_path_helper = الملفات التي تم تعقبها بواسطة Git LFS ستُخزن في هذا الدليل. اتركه فارغًا لتعطيله. err_empty_db_path = طريق قاعدة بيانات SQLite3 لا يمكن أن يكون فارغا. -lfs_path = مسار جذر جِت LFS -app_name_helper = يمكنك إدخال اسم شركتك هنا. +lfs_path = مسار جذر Git LFS +app_name_helper = أدخل اسم المثيل هنا. سيظهر هذا الاسم في كل الصفحات. err_admin_name_is_reserved = اسم مستخدم المدير غير صالح، هذا الأسم محجوز -app_name = عنوان الموقع +app_name = عنوان المثيل log_root_path = مسار السجل log_root_path_helper = ستُكتب ملفات السجل في هذا الدليل. smtp_addr = مضيف SMTP @@ -190,7 +194,7 @@ smtp_port = منفذ SMTP mailer_password = كلمة مرور SMTP app_url_helper = العنوان الأساسي لاستنساخ عناوين URL HTTP(S) وإشعارات البريد الإلكتروني. mailer_user = اسم مستخدم SMTP -disable_gravatar.description = عطل جرافاتار والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. +disable_gravatar.description = عطل Gravatar والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. offline_mode.description = عطل خدمات توصيل المحتوى من الجهات الخارجية، واخدم كل المحتوى محلياً. run_user_helper = اسم مستخدم نظام التشغيل الذي يشغل فورجيو. ملاحظة: هذا المستخدم يجب أن يكون له حق الوصول إلى المسار الجذري للمستودع. domain = نطاق الخادم @@ -199,28 +203,28 @@ smtp_from = أرسل البريد الإلكتروني كـ federated_avatar_lookup = تفعيل الصور الرمزية الاتحادية optional_title = إعدادات اختيارية domain_helper = نطاق أو عنوان المضيف لخادمك. -mail_notify = فعّل التنبيه عبر البريد الإلكتروني -app_url = الرابط الأساس لفورجيو +mail_notify = فعّل التنبيهات عبر البريد الإلكتروني +app_url = الرابط الأساس smtp_from_helper = عنوان البريد الإلكتروني الذي سيستخدمه فورجيو. أدخل عنوان بريد إلكتروني عادي أو استخدم صيغة"Name" . ssh_port_helper = رقم المنفذ الذي يستمع له خادم SSH. اتركه فارغاً لتعطيله. -http_port_helper = المنفذ الذي سيستمع إليه خادم الويب لفورجيو. -http_port = منفذ استماع HTTP لفورجيو +http_port_helper = المنفذ الذي سيستمع إليه خادم ويب Forgejo. +http_port = منفذ استماع HTTP ssh_port = منفذ خادم SSH email_title = إعدادات البريد الإلكتروني offline_mode = فعل الوضع المحلي server_service_title = إعدادات الخادم وخدمات الجهات الخارجية register_confirm = الزم تأكيد البريد الإلكتروني للتسجيل -allow_only_external_registration.description = لا يسمح بالتسجيل إلا من خلال الخدمات الخارجية +allow_only_external_registration.description = لن يتمكن المستخدمون من إنشاء حسابات جديدة إلا باستخدام خدمات خارجية مهيأة. disable_registration = عطّل التسجيل الذاتي -federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام ليبرافاتار. +federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام Libravatar. openid_signup = فعّل التسجيل الذاتي عبر OpenID -disable_registration.description = عطل التسجيل الذاتي. المديرون فقط سيكونون قادرين على إنشاء حسابات جديدة للمستخدمين. +disable_registration.description = سيتمكن مسؤولو المثيل فقط من إنشاء حسابات مستخدمين جديدة. يوصى بشدة بإبقاء التسجيل معطلاً إلا إذا كنت تنوي استضافة مثيل عام للجميع ومستعد للتعامل مع كميات كبيرة من الحسابات غير المرغوب بها. openid_signin = فعّل تسجيل الدخول عبر OpenID openid_signin.description = فعّل تسجيل دخول المستخدمين عبر OpenID. enable_captcha = فعّل كابتشا التسجيل -enable_captcha.description = الزم وجود كابتشا للتسجيل الذاتي للمستخدمين. +enable_captcha.description = مطالبة المستخدمين باجتياز اختبار CAPTCHA من أجل إنشاء حسابات. openid_signup.description = فعّل التسجيل الذاتي للمستخدمين عبر OpenID. -require_sign_in_view = الزم تسجيل الدخول لعرض الصفحات +require_sign_in_view = يتطلب تسجيل الدخول لعرض محتوى المثيل require_sign_in_view.description = مكّن وصول الصفحات للمستخدمين فقط. لن يرى الزائرون سوى صفحات التسجيل والتسجيل. admin_setting.description = إنشاء حساب إداري هو اختياري. أول مستخدم مُسجل سيصبح تلقائيا مديرا. admin_password = كلمة المرور @@ -233,7 +237,7 @@ test_git_failed = يتعذر اختبار أمر جِت: %v confirm_password = أكّد كلمة المرور invalid_admin_setting = إعداد حساب المدير غير صالح: %v invalid_log_root_path = مسار السجل غير صالح: %v -default_enable_timetracking = فعّل تتبع الوقت مبدئيا +default_enable_timetracking = فعّل التتبع الزمني افتراضيًا env_config_keys_prompt = ستطبق المتغيرات البيئية التالية أيضاً على ملف الإعدادات: admin_title = إعدادات حساب المدير no_reply_address_helper = النطاق للمستخدمين بعنوان بريد إلكتروني مخفي. مثلاً، اسم المستخدم "sarah" سوف يسجل في جِت كـ"sarah@noreply.example.org" لو كان نطاق البريد الإلكتروني الخفي مدخل كـ"noreply.example.org". @@ -242,9 +246,9 @@ default_enable_timetracking.description = فعل تتبع الوقت للمست run_user_not_match = مستخدم التشغيل غير مطابق لأسم المستخدم الحالي: %s -> %s invalid_db_setting = إعدادات قاعدة البيانات غير صالحة: %v invalid_db_table = جدول قاعدة البيانات "%s" غير صالح: %v -default_keep_email_private.description = أخفِ عناوين البريد الإلكتروني للحسابات الجديدة مبدئيا. +default_keep_email_private.description = قم بتمكين إخفاء عنوان البريد الإلكتروني للمستخدمين الجدد افتراضيًا حتى لا يتم تسريب هذه المعلومات فور التسجيل. env_config_keys = إعدادات بيئية -default_allow_create_organization = اسمح بإنشاء المنظمات مبدئيا +default_allow_create_organization = اسمح بإنشاء المنظمات بشكل افتراضي invalid_app_data_path = مسار بيانات التطبيق غير صالح: %v enable_update_checker_helper = يفحص لإيجاد اصدارات جديدة عن طريق الإتصال بسيرفرات فورجيو. invalid_repo_path = المسار الجزري للمستودع غير صالح: %v @@ -252,10 +256,15 @@ internal_token_failed = فشل توليد الرمز الداخلي: %v no_reply_address = نطاقات البريد الإلكتروني المخفية default_keep_email_private = أخفِ عناوين البريد الإلكتروني مبدئيا admin_name = اسم مستخدم المدير -default_allow_create_organization.description = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات مبدئيا. +default_allow_create_organization.description = السماح للمستخدمين الجدد بإنشاء منتديات المجموعة بشكل افتراضي. عند تعطيل هذا الخيار، سيتعين على المسؤول منح إذن لإنشاء منتديات المجموعة للمستخدمين الجدد. password_algorithm = خوارزمية تجزئة كلمة المرور invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. +app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله. +app_slogan = شعار المثيل +allow_only_external_registration = السماح بالتسجيل عبر الخدمات الخارجية فقط +config_location_hint = سيتم حفظ خيارات التهيئة هذه في: +smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح [editor] buttons.list.ordered.tooltip = أضف قائمة مرقمة @@ -272,10 +281,22 @@ buttons.mention.tooltip = اذكر مستخدمًا أو فريقًا buttons.italic.tooltip = أضف نصًا مائلًا buttons.link.tooltip = اضف رابط buttons.disable_monospace_font = عطّل الخط الثابت العرض +buttons.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى +buttons.indent.tooltip = تداخل العناصر بنفس المستوى +table_modal.header = إضافة جدول +table_modal.placeholder.header = الترويسة +table_modal.placeholder.content = المحتوى +table_modal.label.rows = الصفوف +table_modal.label.columns = الأعمدة +link_modal.url = Url +link_modal.description = الوصف +buttons.new_table.tooltip = إضافة جدول +link_modal.header = إضافة رابط +link_modal.paste_reminder = تلميح: باستخدام عنوان URL في حافظتك، يمكنك اللصق مباشرةً في المحرر لإنشاء رابط. [aria] navbar = شريط التنقل -footer.software = عن البرمجية +footer.software = عن هذه البرمجية footer.links = روابط footer = الذيل @@ -1386,6 +1407,21 @@ repo.transfer.subject_to = %s يود نقل ملكية "%s" إلى %s issue.action.ready_for_review = @%[1]s علّم هذا الطلب للسحب كجاهز للمراجعة. issue_assigned.pull = @%[1]s عيّنك إلى طلب سحب %[2]s في مستودع %[3]s. issue.action.review_dismissed = @%[1]s أستبعد آخر مراجعة من %[2]s لهذا الطلب للسحب. +password_change.subject = تم تغيير كلمة مرورك +totp_disabled.subject = تم تعطيل TOTP +totp_disabled.text_1 = تم تعطيل كلمة المرور لمرة واحدة المستندة إلى الوقت (TOTP) على حسابك للتو. +totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك. +totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA +removed_security_key.subject = تمت إزالة مفتاح الأمان +removed_security_key.text_1 = تم إزالة مفتاح الأمان ”%[1] s“ للتو من حسابك. +account_security_caution.text_1 = إذا كان هذا أنت، فيمكنك تجاهل هذا البريد بأمان. +totp_disabled.no_2fa = لم تعد هناك طرق أُخرى للمصادقة الثنائية (2FA) قيد التهيئة عد الآن ، أي أنه لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +removed_security_key.no_2fa = لم تعد هناك طرق أخرى للمصادقة الثنائية (2FA) قيد التهيئة بعد الآن، أي لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +primary_mail_change.subject = تم تغيير البريد الأساسي الخاص بك +password_change.text_1 = تم تغيير كلمة مرور حسابك للتو. +primary_mail_change.text_1 = تم تغيير البريد الإلكتروني الأساسي لحسابك إلى %[1]s. هذا يعني أن عنوان البريد الإلكتروني هذا لن يتلقى إشعارات البريد لحسابك بعد الآن. +account_security_caution.text_2 = إذا لم تكن أنت، فهذا يعني أن حسابك مخترق. يرجى الاتصال بمسؤولي هذا الموقع. +totp_enrolled.text_1.no_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يجب عليك استخدام TOTP كطريقة للمصادقة الثنائية. [error] not_found = تعذر العثور على الهدف. @@ -1421,7 +1457,7 @@ joined_on = انضم في %s user_bio = السيرة الذاتية repositories = المستودعات activity = النشاط العام -projects = مشاريع +projects = المشاريع unfollow = إلغِ المتابعة settings = إعدادات المستخدم following_few = %d يتابع @@ -1429,7 +1465,7 @@ follow = تابع followers_few = %d متابعين form.name_reserved = اسم المستخدم "%s" محجوز. email_visibility.limited = عنوان بريدك الإلكتروني ظاهر لكل المستخدمين المُستَوثَقين -code = البرمجية +code = الكود overview = نظرة عامة watched = المستودعات المشاهدة disabled_public_activity = هذا المستخدم عطّل الظهور العام للنشاط. @@ -1438,6 +1474,17 @@ email_visibility.private = عنوان بريدك الإلكتروني ظاهر starred = المستودعات المميّزة بنجمة form.name_chars_not_allowed = اسم المستخدم "%s" يحتوي على رموز غير صالحة. form.name_pattern_not_allowed = النمط "s%" غير مسموح به في إسم المستخدم. +followers.title.one = متابِع +public_activity.visibility_hint.admin_private = هذا النشاط مرئي لك لأنك مسؤول، ولكن المستخدم يريد أن يظل خاصاً. +public_activity.visibility_hint.self_private = نشاطك مرئي لك ولسُعاة المثيل فقط. تعديل الإعدادات. +followers_one = %d متابِع +following.title.one = متابعة +followers.title.few = متابعين +following_one = %d يُتابع +following.title.few = متابعة +public_activity.visibility_hint.self_public = نشاطك مرئي للجميع، باستثناء التفاعلات في المساحات الخاصة. اضبط الإعدادات. +public_activity.visibility_hint.admin_public = هذا النشاط مرئي للجميع، ولكن بصفتك مسؤولاً يمكنك أيضًا رؤية التفاعلات في المساحات الخاصة. +public_activity.visibility_hint.self_private_profile = نشاطك مرئي لك ولسُعاة المثيل فقط لأن ملفك الشخصي خاص. تعديل الإعدادات. [auth] change_unconfirmed_email_error = تعذر تغيير البريد الإلكتروني: %v @@ -1458,11 +1505,11 @@ active_your_account = فعّل حسابك register_helper_msg = هل لديك حساب بالفعل؟ سجل الدخول! manual_activation_only = تواصل مع مدير موقعك لإكمال التفعيل. must_change_password = حدّث كلمة المرور الخاصة بك -send_reset_mail = أرسل رسالة استعادة حساب +send_reset_mail = أرسل بريد الاستعادة resend_mail = اضغط هنا لإعادة إرسالة رسالة تفعيل حسابك has_unconfirmed_mail = أهلا يا %s، لديك عنوان بريد إلكتروني غير مؤكَّد (%s). إن لم تستلم رسالة تأكيد أو تريد إرسال واحدة جديدة، فنرجو الضغط على الزر الذي بالأسفل. email_not_associate = عنوان البريد هذا غير مرتبط بأي حساب. -reset_password = استعادة حساب +reset_password = استعادة الحساب oauth_signin_tab = أربط بحساب موجود invalid_password = كلمة المرور الخاصة بك لا تطابق كلمة المرور التي استخدمت لتسجيل الحساب. oauth_signin_title = سجّل الدخول لتأذن للحساب المربوط @@ -1483,13 +1530,13 @@ reset_password_wrong_user = أنت مُسجل كـ %s، لكن رابط أعاد openid_connect_title = اتصل بحساب موجود confirmation_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية التسجيل. إذا كان عنوان البريد خاطئ، يمكنك تسجيل الدخول وطلب بريد تأكيد جديد يُرسل إلى عنوان آخر. scratch_code = رمز الخدش -invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهى اضغط هنا للإعادة. +invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهت صلاحيته. اضغط هنا للإعادة. openid_register_title = أنشئ حسابًا جديدًا verify = تحقق twofa_scratch_used = لقد استخدمت رمز الخدش الخاص بك. لقد تم إعادة توجيهك إلى إعدادات المصادقة الثنائية حتى يمكنك إزالة تسجيل جهازك أو توليد رمز خدش جديد. oauth_signup_submit = أكمل الحساب oauth.signin.error = كان هناك خطأ في تجهيز طلب الإذن إذا استمر هذا الخطأ، يرجى الاتصال بالمدير. -invalid_code = رمز تأكيدك غير صحيح أو انتهى. +invalid_code = رمز تأكيدك غير صحيح أو انتهت صلاحيته. oauth_signup_title = أكمل حساب جديد resent_limit_prompt = لقد طلبت بالفعل بريداً إلكترونياً للتفعيل مؤخراً من فضلك انتظر 3 دقائق وحاول مرة أخرى. reset_password_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية استعادة الحساب. @@ -1513,6 +1560,13 @@ openid_register_desc = مسار الـOpenID المختار مجهول. اربط remember_me = تذكر هذا الجهاز remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف. authorization_failed_desc = فشل التفويض لأننا اكتشفنا طلبًا غير صالح. يرجى الاتصال بمشرف التطبيق الذي حاولت ترخيصه. +sign_in_openid = المتابعة باستخدام OpenID +hint_login = لديك حساب بالفعل؟ سجّل الدخول الآن! +hint_register = يلزمك حساب ؟ سجِّل الآن. +sign_up_button = سجِّل الآن. +back_to_sign_in = العودة إلى تسجيل الدخول +use_onetime_code = استخدم رمزًا لمرة واحدة +unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. @@ -1556,6 +1610,9 @@ less = أقل number_of_contributions_in_the_last_12_months = %s مساهم في آخر 12 شهر contributions_zero = بلا مساهمات more = أكثر +contributions_format = {contributions} مساهمة في {day} {month} {year} +contributions_one = المساهمة +contributions_few = المساهمات [admin] self_check.database_fix_mysql = لمستخدمين ميسكول/ماريا دي بي، يمكنك استخدام أمر "forgejo doctor convert" لإصلاح مشاكل التجمّع، أو يمكنك أيضاً إصلاح المشكلة عن طريق تعديل السيكول يدوياً. @@ -1707,7 +1764,7 @@ enterred_invalid_org_name = اسم المنظمة التي أدخلته خطأ. lang_select_error = اختر لغة من القائمة. alpha_dash_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_").` alpha_dash_dot_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_") والنقطة (".").` -repo_name_been_taken = اسم المستودع مستعمل بالفعل. +repo_name_been_taken = اسم المستودع مستخدم بالفعل. Email = البريد الإلكتروني auth_failed = فشل الاستيثاق: %v email_error = ` ليس عنوان بريد إلكتروني صالح.` @@ -1727,10 +1784,10 @@ still_has_org = "حسابك عضو في منظمة أو أكثر؛ غادرهم repository_files_already_exist.adopt_or_delete = الملفات موجودة بالفعل لهذا المستودع. إما اعتمادها أو حذفها. repository_files_already_exist.delete = الملفات موجودة بالفعل لهذا المستودع. يجب عليك حذفها. repository_files_already_exist.adopt = الملفات موجودة بالفعل لهذا المستودع ويمكن اعتمادها فقط. -repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. تواصل مع مدير النظام. +repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. اتصل بمدير النظام. TeamName = اسم الفريق username_has_not_been_changed = لم يتم تغيير اسم المستخدم -username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسماؤهم. +username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسمائهم. captcha_incorrect = الكابتشا خاطئة. AdminEmail = عنوان البريد الإلكتروني للمدير team_no_units_error = اسمح بالوصول إلى قسم واحد على الأقل في المستودعات. @@ -1757,6 +1814,23 @@ glob_pattern_error = `النمط الشامل غير صالح: %s.` CommitChoice = إختيار الإداع regex_pattern_error = ` نمط التعبير النمطي غير صالح: %s.` username_error = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") و نقطة (".") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.` +Biography = النبذة +Website = موقع الويب +To = اسم الفرع +AccessToken = رمز الوصول +repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة. +FullName = الاسم الكامل +Description = الوصف +Pronouns = الضمائر +username_claiming_cooldown = لا يمكن المطالبة باسم المستخدم، لأن فترة تباطؤه لم تنتهِ بعد. يمكن المطالبة به عند %[1]s. +Location = الموقع +invalid_group_team_map_error = ` التعيين غير صالح: %s ` +visit_rate_limit = تناولت الزيارة عن بُعد الحد من معدلها. +email_domain_is_not_allowed = نطاق البريد الإلكتروني للمستخدم %s يتعارض مع قائمة النطاقات المسموحة ، أو الممنوعة. يرجى التأكد من إدخال عنوان البريد الإلكتروني بشكل صحيح. +unset_password = المستخدم المسجل لم يقم بتعيين كلمة مرور. +unsupported_login_type = نوع تسجيل الدخول غير مدعوم لحذف الحساب. +invalid_ssh_principal = أصل غير صالح: %s +required_prefix = المُدخل يجب أن يبدأ مع "%s" [home] filter = تصفيات أخرى @@ -1803,6 +1877,10 @@ code_no_results = لم يتم العثور على برمجية تطابق الب relevant_repositories_tooltip = تم أخفاء المستودعات التي هي مشتقات وأيضاً التي ليس لها موضوع، ولا أيقونة، ولا يوجد وصف. relevant_repositories = يتم اظهار المستودعات المتعلقة فقط. أظهر النتائج غير المصفاة. code_last_indexed_at = فُهرس آخر مرة %s +stars_few = %d نجوم +forks_one = %d نسخة +forks_few = %d نُسَخ +stars_one = %d نجمة [actions] variables.none = لا توجد متغيرات بعد. @@ -1976,7 +2054,7 @@ component_failed_to_load = حدث خطأ غير متوقع. [search] org_kind = بحث في المنظمات… code_search_unavailable = البحث في الكود غير متوفر حاليًا. يرجى الاتصال بمدير الموقع. -search = ابحث... +search = البحث… type_tooltip = نوع البحث fuzzy = أجعد fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق @@ -1985,10 +2063,19 @@ match_tooltip = قم بتضمين النتائج التي تطابق مصطلح repo_kind = بحث في المستودعات… user_kind = بحث عن المستخدمين… team_kind = بحث عن الفرق… -code_kind = بحث في الكود… +code_kind = بحث ضمن الكود… project_kind = البحث ضمن المشاريع… branch_kind = البحث ضمن الفروع… no_results = لا توجد نتائج مطابقة. issue_kind = البحث ضمن الأعطال… pull_kind = البحث ضمن طلبات السحب… keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. +package_kind = البحث ضمن الحزم… +regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي +commit_kind = البحث ضمن الإيداعات… +union = مطابقة عامة +runner_kind = البحث ضمن المشغِّلات… +exact = مطابق +exact_tooltip = عرض النتائج التي تطابق مصطلح البحث بالضبط فقط +regexp = RegExp +union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 8f4dfda0d1..84bb4ccde1 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -251,12 +251,12 @@ db_schema_helper=Leer lassen, um den Datenbank-Standardwert („public“) zu ve ssl_mode=SSL path=Pfad sqlite_helper=Dateipfad zur SQLite3-Datenbank.
Gib einen absoluten Pfad an, wenn Forgejo als Service gestartet wird. -reinstall_error=Du versuchst, in eine bereits existierende Forgejo Datenbank zu installieren +reinstall_error=Du versuchst, in eine bereits existierende Forgejo-Datenbank zu installieren reinstall_confirm_message=Eine Neuinstallation mit einer bestehenden Forgejo-Datenbank kann mehrere Probleme verursachen. In den meisten Fällen solltest du deine vorhandene „app.ini“ verwenden, um Forgejo auszuführen. Wenn du weißt, was du tust, bestätige die folgenden Angaben: reinstall_confirm_check_1=Die von der SECRET_KEY in app.ini verschlüsselten Daten können verloren gehen: Benutzer können sich unter Umständen nicht mit 2FA/OTP einloggen und Spiegel könnten nicht mehr richtig funktionieren. Mit der Ankreuzung dieses Kästchens bestätigst du, dass die aktuelle app.ini-Datei den korrekten SECRET_KEY enthält. reinstall_confirm_check_2=Die Repositorys und Einstellungen müssen eventuell neu synchronisiert werden. Durch das Ankreuzen dieses Kästchens bestätigst du, dass du die Hooks für die Repositorys und die authorized_keys-Datei manuell neu synchronisierst. Du bestätigst, dass du sicherstellst, dass die Repository- und Spiegeleinstellungen korrekt sind. reinstall_confirm_check_3=Du bestätigst, dass du absolut sicher bist, dass diese Forgejo mit der richtigen app.ini läuft, und du sicher bist, dass du neu installieren musst. Du bestätigst, dass du die oben genannten Risiken anerkennst. -err_empty_db_path=Der SQLite3 Datenbankpfad darf nicht leer sein. +err_empty_db_path=Der SQLite3-Datenbankpfad darf nicht leer sein. no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen. err_empty_admin_password=Das Administrator-Passwort darf nicht leer sein. err_empty_admin_email=Die Administrator-E-Mail darf nicht leer sein. @@ -463,7 +463,7 @@ openid_register_title=Neues Konto einrichten openid_register_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu. openid_signin_desc=Gib deine OpenID-URI ein, zum Beispiel alice.openid.example.org oder https://openid.example.org/alice. disable_forgot_password_mail=Die Kontowiederherstellung ist deaktiviert, da keine E-Mail eingerichtet ist. Bitte kontaktiere den zuständigen Administrator. -disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail Adresse ein, um die Kontowiederherstellung freizuschalten. +disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail-Adresse ein, um die Kontowiederherstellung freizuschalten. email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registrieren. authorize_application=Anwendung autorisieren authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst. @@ -530,8 +530,8 @@ issue.action.merge=@%[1]s hat #%[2]d in %[3]s zusammengeführt. issue.action.approve=@%[1]s hat diesen Pull-Request genehmigt. issue.action.reject=@%[1]s hat Änderungen auf diesem Pull-Request angefordert. issue.action.review=@%[1]s hat diesen Pull-Request kommentiert. -issue.action.review_dismissed=@%[1]s hat das letzte Review von %[2]s für diesen Pull-Request verworfen. -issue.action.ready_for_review=@%[1]s hat diesen Pull-Request zum Review freigegeben. +issue.action.review_dismissed=@%[1]s hat die letzte Sichtung von %[2]s für diesen Pull-Request verworfen. +issue.action.ready_for_review=@%[1]s hat diesen Pull-Request für die Sichtung freigegeben. issue.action.new=@%[1]s hat #%[2]d geöffnet. issue.in_tree_path=In %s: @@ -540,8 +540,8 @@ release.new.text=@%[1]s hat %[2]s in %[3]s released release.title=Titel: %s release.note=Anmerkung: release.downloads=Downloads: -release.download.zip=Quellcode (ZIP Datei) -release.download.targz=Quellcode (TAR.GZ Datei) +release.download.zip=Quellcode (ZIP) +release.download.targz=Quellcode (TAR.GZ) repo.transfer.subject_to=%s möchte „%s“ an %s übertragen repo.transfer.subject_to_you=%s möchte dir „%s“ übertragen @@ -787,7 +787,7 @@ comment_type_group_time_tracking=Zeiterfassung comment_type_group_deadline=Frist comment_type_group_dependency=Abhängigkeit comment_type_group_lock=Sperrstatus -comment_type_group_review_request=Angeforderte Reviews +comment_type_group_review_request=Angeforderte Sichtungen comment_type_group_pull_request_push=Hinzugefügte Commits comment_type_group_project=Projekt comment_type_group_issue_ref=Issue-Referenz @@ -873,7 +873,7 @@ gpg_key_matched_identities_long=Die eingebetteten Identitäten in diesem Schlüs gpg_key_verified=Verifizierter Schlüssel gpg_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen. gpg_key_verify=Verifizieren -gpg_invalid_token_signature=Der GPG-Key, die Signatur, und das Token stimmen nicht überein, oder das Token ist veraltet. +gpg_invalid_token_signature=Der GPG-Key, die Signatur und das Token stimmen nicht überein, oder das Token ist veraltet. gpg_token_required=Du musst eine Signatur für das folgende Token angeben gpg_token=Token gpg_token_help=Du kannst eine Signatur wie folgt generieren: @@ -902,10 +902,10 @@ add_principal_success=Die SSH-Zertifikatsidentität „%s“ wurde hinzugefügt. delete_key=Entfernen ssh_key_deletion=SSH-Schlüssel entfernen gpg_key_deletion=GPG-Schlüssel entfernen -ssh_principal_deletion=SSH-Zertifik-Identität entfernen +ssh_principal_deletion=SSH-Zertifikats-Principal entfernen ssh_key_deletion_desc=Wenn du einen SSH-Key entfernst, hast du mit diesem Key keinen Zugriff mehr. Fortfahren? gpg_key_deletion_desc=Wenn du einen GPG-Schlüssel entfernst, können damit unterschriebene Commits nicht mehr verifiziert werden. Fortfahren? -ssh_principal_deletion_desc=Das Entfernen einer SSH-Zertifikat-Identität entzieht den Zugriff auf dein Konto. Fortfahren? +ssh_principal_deletion_desc=Das Entfernen eines SSH-Zertifikats-Principals entzieht den Zugriff auf dein Konto. Fortfahren? ssh_key_deletion_success=Der SSH-Schlüssel wurde entfernt. gpg_key_deletion_success=Der GPG-Schlüssel wurde entfernt. ssh_principal_deletion_success=Die Identität wurde entfernt. @@ -931,7 +931,7 @@ unbind_success=Das soziale Konto wurde erfolgreich entfernt. manage_access_token=Zugriffstokens generate_new_token=Neuen Token erzeugen -tokens_desc=Diese Tokens gewähren vollen Zugriff auf dein Konto via die Forgejo-API. +tokens_desc=Diese Tokens gewähren vollen Zugriff auf dein Konto mit der Forgejo-API. token_name=Token-Name generate_token=Token generieren generate_token_success=Ein neuer Token wurde generiert. Kopiere diesen jetzt, da er nicht erneut angezeigt wird. @@ -970,8 +970,8 @@ save_application=Speichern oauth2_client_id=Client-ID oauth2_client_secret=Client-Geheimnis oauth2_regenerate_secret=Geheimnis neu generieren -oauth2_regenerate_secret_hint=Secret verloren? -oauth2_client_secret_hint=Das Secret wird nach dem Verlassen oder Aktualisieren dieser Seite nicht mehr angezeigt. Bitte stelle sicher, dass du es gespeichert hast. +oauth2_regenerate_secret_hint=Geheimnis verloren? +oauth2_client_secret_hint=Das Geheimnis wird nach dem Verlassen oder Aktualisieren dieser Seite nicht mehr angezeigt. Bitte stelle sicher, dass du es gespeichert hast. oauth2_application_edit=Bearbeiten oauth2_application_create_description=OAuth2-Anwendungen geben deiner Drittanwendung Zugriff auf Benutzeraccounts dieser Forgejo-Instanz. oauth2_application_remove_description=Das Entfernen einer OAuth2-Anwendung hat zur Folge, dass diese nicht mehr auf autorisierte Benutzeraccounts auf dieser Instanz zugreifen kann. Möchtest Du fortfahren? @@ -996,11 +996,11 @@ twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird regenerate_scratch_token_desc=Wenn du deinen Wiederherstellungsschlüssel verlegst oder es bereits benutzt hast, kannst du es hier zurücksetzen. twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert. scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App: -or_enter_secret=Oder gib das Secret ein: %s +or_enter_secret=Oder gib das Geheimnis ein: %s then_enter_passcode=Und gib dann die angezeigte PIN der Anwendung ein: passcode_invalid=Die PIN ist falsch. Probiere es erneut. twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre deinen einmalig verwendbaren Wiederherstellungsschlüssel (%s) an einem sicheren Ort auf, da er nicht wieder angezeigt werden wird. -twofa_failed_get_secret=Fehler beim Abrufen des Secrets. +twofa_failed_get_secret=Fehler beim Abrufen des Geheimnisses. webauthn_desc=Sicherheitsschlüssel sind Geräte, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Der Sicherheitsschlüssel muss den Standard „WebAuthn“ unterstützen. webauthn_register_key=Sicherheitsschlüssel hinzufügen @@ -1185,9 +1185,9 @@ blame.ignore_revs=Revisionen in .git-blame-ignore-revs werden i blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in .git-blame-ignore-revs. author_search_tooltip=Zeigt maximal 30 Benutzer -tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s -tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s -tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s +tree_path_not_found_commit=Pfad %[1]s existiert nicht im Commit %[2]s +tree_path_not_found_branch=Pfad %[1]s existiert nicht im Branch %[2]s +tree_path_not_found_tag=Pfad %[1]s existiert nicht im Tag %[2]s transfer.accept=Übertragung akzeptieren transfer.accept_desc=Übertragung nach „%s“ @@ -1248,7 +1248,7 @@ migrate.clone_local_path=oder ein lokaler Serverpfad migrate.permission_denied=Du hast keine Berechtigung zum Importieren lokaler Repositorys. migrate.permission_denied_blocked=Du kannst von keinen nicht erlaubten Hosts importieren. Bitte fragen deinen Administrator, die Einstellungen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS zu überprüfen. migrate.invalid_local_path=Der lokale Pfad ist ungültig. Er existiert nicht oder ist kein Verzeichnis. -migrate.invalid_lfs_endpoint=Ungültiger LFS Endpunkt. +migrate.invalid_lfs_endpoint=Der LFS-Endpunkt ist nicht gültig. migrate.failed=Fehler bei der Migration: %v migrate.migrate_items_options=Zugangs-Token wird benötigt, um zusätzliche Elemente zu migrieren migrated_from=Migriert von %[2]s @@ -1324,7 +1324,7 @@ commit=Commit release=Release releases=Releases tag=Tag -released_this=hat released +released_this=hat releast tagged_this=hat getaggt file.title=%s an %s file_raw=Originalformat @@ -1448,7 +1448,7 @@ commits.older=Älter commits.newer=Neuer commits.signed_by=Signiert von commits.signed_by_untrusted_user=Signiert von nicht vertrauenswürdigen Benutzern -commits.signed_by_untrusted_user_unmatched=Signiert von nicht vertrauenswürdigen Benutzern, der nicht mit dem Committer übereinstimmt +commits.signed_by_untrusted_user_unmatched=Von einem nicht vertrauenswürdigen Benutzer, der nicht auf den Committer passt, signiert commits.gpg_key_id=GPG-Schlüssel-ID commits.ssh_key_fingerprint=SSH-Schlüssel-Fingerabdruck commits.view_path=An diesem Punkt im Verlauf anzeigen @@ -1459,7 +1459,7 @@ commit.revert-header=Setze zurück: %s commit.revert-content=Branch auswählen, der zurückgesetzt werden soll: commit.cherry-pick=Cherry-Pick commit.cherry-pick-header=Cherry-Picke: %s -commit.cherry-pick-content=Branch auswählen, auf dem Cherry-Picked werden soll: +commit.cherry-pick-content=Branch auswählen, zu dem das Ergebnis des Cherry-Picks angewendet werden soll: commitstatus.error=Fehler commitstatus.failure=Fehler @@ -1515,7 +1515,7 @@ issues.filter_assignees=Verantwortliche filtern issues.filter_milestones=Meilenstein filtern issues.filter_projects=Projekt filtern issues.filter_labels=Label filtern -issues.filter_reviewers=Reviewer filtern +issues.filter_reviewers=Sichter filtern issues.new=Neues Issue issues.new.title_empty=Der Titel kann nicht leer sein issues.new.labels=Labels @@ -1535,7 +1535,7 @@ issues.new.closed_milestone=Geschlossene Meilensteine issues.new.assignees=Zuständige issues.new.clear_assignees=Zuständige entfernen issues.new.no_assignees=Niemand zuständig -issues.new.no_reviewers=Keine Reviewer +issues.new.no_reviewers=Keine Sichter issues.choose.get_started=Los geht's issues.choose.open_external_link=Öffnen issues.choose.blank=Standard @@ -1598,8 +1598,8 @@ issues.filter_type.all_issues=Alle Issues issues.filter_type.assigned_to_you=Dir zugewiesen issues.filter_type.created_by_you=Von dir erstellt issues.filter_type.mentioning_you=Hat dich erwähnt -issues.filter_type.review_requested=Review angefordert -issues.filter_type.reviewed_by_you=Von dir gereviewt +issues.filter_type.review_requested=Sichtung angefordert +issues.filter_type.reviewed_by_you=Von dir gesichtet issues.filter_sort=Sortieren issues.filter_sort.latest=Neueste issues.filter_sort.oldest=Älteste @@ -1621,8 +1621,8 @@ issues.action_milestone=Meilenstein issues.action_milestone_no_select=Kein Meilenstein issues.action_assignee=Zuständig issues.action_assignee_no_select=Niemand zuständig -issues.action_check=Auswählen/Auswahl aufheben -issues.action_check_all=Alles auswählen/Auswahl aufheben +issues.action_check=Auswählen / Auswahl aufheben +issues.action_check_all=Alles auswählen / Auswahl aufheben issues.opened_by=%[1]s von %[3]s geöffnet pulls.merged_by=von %[3]s wurde %[1]s zusammengeführt pulls.merged_by_fake=von %[2]s %[1]s zusammengeführt @@ -1673,12 +1673,12 @@ issues.role.first_time_contributor=Erstmaliger Mitwirkender issues.role.first_time_contributor_helper=Dies ist der erste Beitrag dieses Benutzers zum Repository. issues.role.contributor=Mitwirkender issues.role.contributor_helper=Dieser Benutzer hat schon zuvor zu dem Repository beigetragen. -issues.re_request_review=Review erneut anfordern -issues.is_stale=Seit diesem Review gab es Änderungen an diesem PR -issues.remove_request_review=Review-Anfrage entfernen -issues.remove_request_review_block=Review-Anfrage kann nicht entfernt werden -issues.dismiss_review=Review verwerfen -issues.dismiss_review_warning=Bist du dir sicher, dass du dieses Review verwerfen willst? +issues.re_request_review=Sichtung erneut anfordern +issues.is_stale=Seit dieser Sichtung gab es Änderungen an diesem PR +issues.remove_request_review=Sichtungsanfrage entfernen +issues.remove_request_review_block=Sichtungsanfrage kann nicht entfernt werden +issues.dismiss_review=Sichtung verwerfen +issues.dismiss_review_warning=Bist du dir sicher, dass du diese Sichtung verwerfen willst? issues.sign_in_require_desc=Anmelden, um an der Diskussion teilzunehmen. issues.edit=Bearbeiten issues.cancel=Abbrechen @@ -1809,20 +1809,20 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.approve=hat die Änderungen %s genehmigt -issues.review.comment=hat %s gereviewt +issues.review.comment=hat %s gesichtet issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed_label=Verworfen issues.review.left_comment=hat einen Kommentar hinterlassen issues.review.content.empty=Du musst einen Kommentar hinterlassen, der die gewünschte(n) Änderung(en) beschreibt. issues.review.reject=hat %s Änderungen angefragt -issues.review.wait=wurde für ein Review %s angefragt -issues.review.add_review_request=hat ein Review von %[1]s %[2]s angefragt -issues.review.remove_review_request=hat die Aufforderung zum Review an %[1]s %[2]s entfernt -issues.review.remove_review_request_self=hat das Review verweigert %s +issues.review.wait=wurde für eine Sichtung von %s angefragt +issues.review.add_review_request=hat eine Sichtung von %[1]s %[2]s angefragt +issues.review.remove_review_request=hat die Sichtungsanfrage an %[1]s %[2]s entfernt +issues.review.remove_review_request_self=hat die Sichtung %s verweigert issues.review.pending=Ausstehend issues.review.pending.tooltip=Dieser Kommentar ist derzeit nicht für andere Benutzer sichtbar. Um deine ausstehenden Kommentare einzureichen, wähle „%s“ -> „%s/%s/%s“ oben auf der Seite. issues.review.review=Review -issues.review.reviewers=Reviewer +issues.review.reviewers=Sichter issues.review.outdated=Veraltet issues.review.outdated_description=Der Inhalt hat sich geändert, seit dieser Kommentar abgegeben wurde issues.review.option.show_outdated_comments=Veraltete Kommentare anzeigen @@ -1833,7 +1833,7 @@ issues.review.show_resolved=Erledigte anzeigen issues.review.hide_resolved=Erledigte ausblenden issues.review.resolve_conversation=Diskussion als „erledigt“ markieren issues.review.un_resolve_conversation=Diskussion als „nicht erledigt“ markieren -issues.review.resolved_by=markierte diese Unterhaltung als gelöst +issues.review.resolved_by=markierte diese Unterhaltung als „erledigt“ issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Zuständigen hinzugefügt werden. issues.reference_issue.body=Beschreibung issues.content_history.deleted=gelöscht @@ -1847,7 +1847,7 @@ issues.reference_link=Referenz: %s compare.compare_base=Basis compare.compare_head=vergleichen -pulls.desc=Pull-Requests und Code-Reviews aktivieren. +pulls.desc=Pull-Requests und Code-Sichtungen aktivieren. pulls.new=Neuer Pull-Request pulls.view=Pull-Request ansehen pulls.compare_changes=Neuer Pull-Request @@ -1856,7 +1856,7 @@ pulls.allow_edits_from_maintainers_desc=Nutzer mit Schreibzugriff auf den Basisb pulls.allow_edits_from_maintainers_err=Aktualisieren fehlgeschlagen pulls.compare_changes_desc=Wähle den Zielbranch, in das zusammengeführt werden soll, und den Quellbranch, von dem gepullt werden soll, aus. pulls.has_viewed_file=Gesehen -pulls.has_changed_since_last_review=Seit deinem letzten Review geändert +pulls.has_changed_since_last_review=Seit deiner letzten Sichtung geändert pulls.viewed_files_label=%[1]d / %[2]d Dateien betrachtet pulls.expand_files=Alle Dateien ausklappen pulls.collapse_files=Alle Dateien einklappen @@ -1867,11 +1867,11 @@ pulls.switch_head_and_base=Head und Base vertauschen pulls.filter_branch=Branch filtern pulls.no_results=Keine Ergebnisse verfügbar. pulls.show_all_commits=Alle Commits anzeigen -pulls.show_changes_since_your_last_review=Zeige Änderungen seit deinem letzten Review +pulls.show_changes_since_your_last_review=Zeige Änderungen seit deiner letzten Sichtung pulls.showing_only_single_commit=Nur Änderungen aus Commit %[1]s werden angezeigt pulls.showing_specified_commit_range=Zeige nur die Änderungen zwischen %[1]s..%[2]s pulls.select_commit_hold_shift_for_range=Commit auswählen. Halte Shift + klicke, um eine Reihe auszuwählen -pulls.review_only_possible_for_full_diff=Ein Review ist nur möglich, wenn das vollständige Diff angezeigt wird +pulls.review_only_possible_for_full_diff=Eine Sichtung ist nur möglich, wenn der vollständige Diff angezeigt wird pulls.filter_changes_by_commit=Nach Commit filtern pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Request erstellt werden. pulls.nothing_to_compare_and_allow_empty_pr=Diese Branches sind gleich. Der Pull-Request wird leer sein. @@ -1905,8 +1905,8 @@ pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht e pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin zusammenführen. pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt. -pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Reviewer angefragt wurden. -pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Reviewern fehlt. +pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Sichter angefragt wurden. +pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Sichtern fehlt. pulls.blocked_by_outdated_branch=Dieser Pull-Request ist blockiert, da er veraltet ist. pulls.blocked_by_changed_protected_files_1=Dieser Pull-Request ist blockiert, weil er eine geschützte Datei ändert: pulls.blocked_by_changed_protected_files_n=Dieser Pull-Request ist blockiert, weil er geschützte Dateien ändert: @@ -1919,14 +1919,14 @@ pulls.approve_count_1=%d Genehmigung pulls.approve_count_n=%d Genehmigungen pulls.reject_count_1=%d Änderungsanfrage pulls.reject_count_n=%d Änderungsanfragen -pulls.waiting_count_1=%d wartendes Review -pulls.waiting_count_n=%d wartende Reviews +pulls.waiting_count_1=%d wartende Sichtung +pulls.waiting_count_n=%d wartende Sichtungen pulls.wrong_commit_id=die Commit-ID muss eine Commit-ID auf dem Zielbranch sein pulls.no_merge_desc=Dieser Pull-Request kann nicht zusammengeführt werden, da alle Repository-Merge-Optionen deaktiviert sind. pulls.no_merge_helper=Aktiviere Mergeoptionen in den Repositoryeinstellungen oder führe den Pull-Request manuell zusammen. pulls.no_merge_wip=Dieser Pull-Request kann nicht zusammengeführt werden, da er als „Work in Progress“ (in Bearbeitung) markiert ist. -pulls.no_merge_not_ready=Dieser Pull-Request kann nicht zusammengeführt werden, überprüfe den Reviewstatus und die Statusprüfungen. +pulls.no_merge_not_ready=Dieser Pull-Request kann nicht zusammengeführt werden, überprüfe den Sichtungsstatus und die Statusprüfungen. pulls.no_merge_access=Du bist nicht berechtigt, diesen Pull-Request zusammenzuführen. pulls.merge_pull_request=Merge-Commit erstellen pulls.rebase_merge_pull_request=Rebasen und dann fast-forwarden @@ -2202,7 +2202,7 @@ settings.tracker_issue_style.numeric=Numerisch settings.tracker_issue_style.alphanumeric=Alphanumerisch settings.tracker_issue_style.regexp=Regulärer Ausdruck settings.tracker_issue_style.regexp_pattern=Regulärer Ausdruck -settings.tracker_issue_style.regexp_pattern_desc=Die erste gecapturte Gruppe wird statt {index} verwendet. +settings.tracker_issue_style.regexp_pattern_desc=Die erste gefundene Gruppe wird statt {index} verwendet. settings.tracker_url_format_desc=Du kannst die Platzhalter {user}, {repo}, {index} für den Benutzernamen, den Namen des Repositorys und die Issue-Nummer verwenden. settings.enable_timetracker=Zeiterfassung aktivieren settings.allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen @@ -2261,14 +2261,14 @@ settings.trust_model.collaborator=Mitarbeiter settings.trust_model.collaborator.long=Mitarbeiter: Vertraue Signaturen von Mitarbeitern settings.trust_model.collaborator.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als „vertrauenswürdig“ markiert (egal, ob sie mit dem Committer übereinstimmen oder nicht). Andernfalls werden gültige Signaturen als „nicht vertrauenswürdig“ markiert, falls die Signatur zum Committer passt, ansonsten werden sie als „nicht übereinstimmend“ markiert. settings.trust_model.committer=Committer -settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (Dies stimmt mit GitHub überein und zwingt signierte Commits von Forgejo dazu, Forgejo als Committer zu haben) +settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (dies stimmt mit GitHub überein und zwingt signierte Commits von Forgejo dazu, Forgejo als Committer zu haben) settings.trust_model.committer.desc=Gültige Signaturen werden nur dann als „vertrauenswürdig“ gekennzeichnet, wenn sie mit ihrem Committer übereinstimmen. Ansonsten werden sie als „nicht übereinstimmend“ markiert. Das führt dazu, dass Forgejo auf signierten Commits, bei denen der echte Committer als „Co-authored-by:“ oder „Co-committed-by:“ in der Beschreibung eingetragen wurde, als Committer gilt. Der Forgejo-Standard-Key muss zu einem Benutzer in der Datenbank passen. settings.trust_model.collaboratorcommitter=Mitarbeiter+Committer settings.trust_model.collaboratorcommitter.long=Mitarbeiter+Committer: Signaturen der Mitarbeiter vertrauen die mit dem Committer übereinstimmen settings.trust_model.collaboratorcommitter.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als „vertrauenswürdig“ markiert, wenn sie mit dem Committer übereinstimmen. Andernfalls werden gültige Signaturen als „nicht vertrauenswürdig“ markiert, wenn die Signatur mit dem Committer übereinstimmt. Ansonsten werden sie als „nicht übereinstimmend“ markiert. Dies zwingt Forgejo, als Committer bei signierten Commits mit dem echten Committer als „Co-Authored-By:“ und „Co-Committed-By:“ im Commit zu markieren. Der Standard-Forgejo-Schlüssel muss mit einem Benutzer in der Datenbank übereinstimmen. settings.wiki_delete=Wiki-Daten löschen settings.wiki_delete_desc=Das Löschen von Wiki-Daten kann nicht rückgängig gemacht werden. Bitte sei vorsichtig. -settings.wiki_delete_notices_1=– Dies löscht und deaktiviert das Wiki für %s. +settings.wiki_delete_notices_1=– Dies wird das Repository-Wiki für %s dauerhaft löschen und deaktivieren. settings.confirm_wiki_delete=Wiki-Daten löschen settings.wiki_deletion_success=Repository-Wiki-Daten wurden gelöscht. settings.delete=Dieses Repository löschen @@ -2327,7 +2327,7 @@ settings.add_webhook_desc=Forgejo sendet eine POST-Anfrage mit fest settings.payload_url=Ziel-URL settings.http_method=HTTP-Methode settings.content_type=POST-Content-Type -settings.secret=Secret +settings.secret=Geheimnis settings.slack_username=Benutzername settings.slack_icon_url=Icon-URL settings.slack_color=Farbe @@ -2374,12 +2374,12 @@ settings.event_pull_request_milestone=Meilensteine settings.event_pull_request_milestone_desc=Meilenstein hinzugefügt, entfernt oder bearbeitet. settings.event_pull_request_comment=Kommentare settings.event_pull_request_comment_desc=Pull-Request-Kommentar angelegt, geändert oder gelöscht. -settings.event_pull_request_review=Reviews -settings.event_pull_request_review_desc=Pull-Request genehmigt, abgelehnt oder Review-Kommentare hinterlassen. +settings.event_pull_request_review=Sichtungen +settings.event_pull_request_review_desc=Pull-Request genehmigt, abgelehnt oder Sichtungskommentare hinterlassen. settings.event_pull_request_sync=Synchronisiert settings.event_pull_request_sync_desc=Branch automatisch mit Zielbranch aktualisiert. -settings.event_pull_request_review_request=Review-Anfragen -settings.event_pull_request_review_request_desc=Überprüfung des Pull-Requests angefragt oder die Anfrage entfernt. +settings.event_pull_request_review_request=Sichtungsanfragen +settings.event_pull_request_review_request_desc=Pull-Request-Sichtung angefragt oder Sichtungsanfrage entfernt. settings.event_pull_request_approvals=Genehmigungen zum Pull-Request settings.event_pull_request_merge=Pull-Request-Merge settings.event_package=Paket @@ -2467,11 +2467,11 @@ settings.protect_status_check_matched=Übereinstimmung settings.protect_invalid_status_check_pattern=Ungültiges Statusprüfungspattern: „%s“. settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Muster. settings.protect_required_approvals=Erforderliche Genehmigungen -settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Reviews. +settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Sichtungen. settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Positivliste beschränken -settings.protect_approvals_whitelist_enabled_desc=Nur Reviews von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. -settings.protect_approvals_whitelist_users=Nutzer, die reviewen dürfen -settings.protect_approvals_whitelist_teams=Teams, die reviewen dürfen +settings.protect_approvals_whitelist_enabled_desc=Nur Sichtungen von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Sichtungen von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. +settings.protect_approvals_whitelist_users=Nutzer, die sichten dürfen +settings.protect_approvals_whitelist_teams=Teams, die sichten dürfen settings.dismiss_stale_approvals=Entferne alte Genehmigungen settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt. settings.require_signed_commits=Signierte Commits erforderlich @@ -2489,10 +2489,10 @@ settings.remove_protected_branch_success=Branchschutzregel „%s“ wurde entfer settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ fehlgeschlagen. settings.protected_branch_deletion=Branch-Schutz löschen settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? -settings.block_rejected_reviews=Zusammenführung bei abgelehnten Reviews blockieren -settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn genügend Genehmigungen existieren. -settings.block_on_official_review_requests=Merge bei offiziellen Review-Anfragen blockieren -settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Review-Anfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. +settings.block_rejected_reviews=Zusammenführung bei abgelehnten Sichtungen blockieren +settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Sichter angefragt werden, auch wenn genügend Genehmigungen existieren. +settings.block_on_official_review_requests=Merge bei offiziellen Sichtungsanfragen blockieren +settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Sichtungsanfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist settings.block_outdated_branch_desc=Merge ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist. settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits: @@ -2539,7 +2539,7 @@ settings.lfs_filelist=LFS-Dateien, die in diesem Repository gespeichert sind settings.lfs_no_lfs_files=In diesem Repository sind keine LFS-Dateien gespeichert settings.lfs_findcommits=Commits finden settings.lfs_lfs_file_no_commits=Keine Commits für diese LFS-Datei gefunden -settings.lfs_noattribute=Dieser Pfad hat nicht das sperrbare Attribut im Standard-Branch +settings.lfs_noattribute=Dieser Pfad hat nicht das „lockable“-Attribut im Standard-Branch settings.lfs_delete=LFS-Datei mit OID %s löschen settings.lfs_delete_warning=Das Löschen einer LFS-Datei kann dazu führen, dass „Objekt existiert nicht“-Fehler beim Checkout auftreten. Bist du sicher? settings.lfs_findpointerfiles=Pointer-Dateien finden @@ -2580,8 +2580,8 @@ diff.show_unified_view=Gesamtansicht diff.whitespace_button=Leerzeichen diff.whitespace_show_everything=Alle Änderungen anzeigen diff.whitespace_ignore_all_whitespace=Ignoriere Leerzeichen beim Zeilen vergleichen -diff.whitespace_ignore_amount_changes=Ignoriere whitespace-Änderungen -diff.whitespace_ignore_at_eol=Ignoriere EOL-whitespace-Änderungen +diff.whitespace_ignore_amount_changes=Änderungen in der Anzahl der Leerzeichen und ähnlichen Zeichen ignorieren +diff.whitespace_ignore_at_eol=Änderungen an den Leerzeichen und ähnlichen Zeichen am Zeilenende ignorieren diff.stats_desc= %d geänderte Dateien mit %d neuen und %d gelöschten Zeilen diff.stats_desc_file=%d Änderungen: %d Ergänzungen und %d Löschungen diff.bin=BIN @@ -2604,11 +2604,11 @@ diff.comment.placeholder=Kommentieren diff.comment.markdown_info=Styling mit Markdown wird unterstützt. diff.comment.add_single_comment=Einzelnen Kommentar hinzufügen diff.comment.add_review_comment=Kommentar hinzufügen -diff.comment.start_review=Review starten +diff.comment.start_review=Sichtung starten diff.comment.reply=Antworten -diff.review=Review abschließen -diff.review.header=Review einreichen -diff.review.placeholder=Kommentar zum Review +diff.review=Sichtung abschließen +diff.review.header=Sichtung einreichen +diff.review.placeholder=Kommentar zur Sichtung diff.review.comment=Kommentieren diff.review.approve=Genehmigen diff.review.self_reject=Pull-Request-Autoren können keine Änderungen an ihren eigenen Pull-Request anfordern @@ -2656,7 +2656,7 @@ release.edit_release=Release aktualisieren release.delete_release=Release löschen release.delete_tag=Tag löschen release.deletion=Release löschen -release.deletion_desc=Beim Entfernen wird ein Release nur von Forgejo gelöscht. Es betrifft weder den Git-Tag, noch den Inhalt des Repos oder seinen Änderungsverlauf. Fortfahren? +release.deletion_desc=Beim Entfernen wird ein Release nur von Forgejo gelöscht. Es betrifft weder den Git-Tag noch den Inhalt des Repos oder seinen Änderungsverlauf. Fortfahren? release.deletion_success=Das Release wurde gelöscht. release.deletion_tag_desc=Löscht dieses Tag aus dem Projektarchiv. Repository-Inhalt und Verlauf bleiben unverändert. Fortfahren? release.deletion_tag_success=Der Tag wurde gelöscht. @@ -2666,7 +2666,7 @@ release.tag_name_protected=Der Tag-Name ist geschützt. release.tag_already_exist=Dieser Tag-Name existiert bereits. release.downloads=Downloads release.download_count=Downloads: %s -release.add_tag_msg=Titel und Beschreibung des Releases als Tag Nachricht verwenden. +release.add_tag_msg=Titel und Beschreibung des Releases als Tag-Nachricht verwenden. release.add_tag=Tag erstellen release.releases_for=Releases für %s release.tags_for=Tags für %s @@ -2766,7 +2766,7 @@ wiki.cancel = Abbrechen settings.wiki_globally_editable = Allen erlauben, das Wiki zu bearbeiten settings.protect_branch_name_pattern_desc = Geschützte Branch-Namens-Patterns. Siehe die Dokumentation für Pattern-Syntax. Beispiele: main, release/** settings.ignore_stale_approvals = Abgestandene Genehmigungen ignorieren -settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Reviews), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Reviews bereits verworfen werden. +settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Sichtungen), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Sichtungen bereits verworfen werden. pulls.commit_ref_at = `referenzierte diesen Pull-Request aus einem Commit %s` pulls.fast_forward_only_merge_pull_request = Nur Fast-forward pulls.cmd_instruction_checkout_desc = Checke einen neuen Branch aus deinem Projekt-Repository aus und teste die Änderungen. @@ -2782,7 +2782,7 @@ activity.navbar.contributors = Mitwirkende contributors.contribution_type.deletions = Löschungen contributors.contribution_type.additions = Einfügungen contributors.contribution_type.filter_label = Art des Beitrags: -vendored = Vendored +vendored = Gevendort activity.navbar.pulse = Puls pulls.made_using_agit = AGit settings.confirmation_string = Bestätigungsstring @@ -2795,7 +2795,7 @@ pulls.merged_title_desc_one = hat %[1]d Commit von %[2]s nach %[2]s nach %[3]s zusammenführen open_with_editor = Öffnen mit %s commits.search_branch = Dieser Branch -pulls.ready_for_review = Bereit zum Review? +pulls.ready_for_review = Bereit zur Sichtung? settings.rename_branch_failed_protected = Branch %s kann nicht umbenannt werden, weil er ein geschützter Branch ist. editor.commit_id_not_matching = Die Datei wurde geändert, während du sie bearbeitet hast. Committe in einen neuen Branch, dann führe einen Merge durch. editor.push_out_of_date = Der Push scheint veraltet zu sein. @@ -2824,7 +2824,7 @@ settings.sourcehut_builds.secrets_helper = Dem Job zugriff auf die Build-Geheimn settings.web_hook_name_sourcehut_builds = SourceHut-Builds settings.graphql_url = GraphQL-URL settings.matrix.room_id_helper = Die Raum-ID kann über den Element-Webclient ermittelt werden: Raumeinstellungen > erweitert > interne Raum-ID. Beispielsweise %s. -settings.sourcehut_builds.access_token_helper = Zugangstoken, der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen builds.sr.ht-Token oder einen builds.sr.ht-Token mit Zugriff auf die Secrets. +settings.sourcehut_builds.access_token_helper = Zugangstoken, der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen builds.sr.ht-Token oder einen builds.sr.ht-Token mit Zugriff auf die Geheimnisse. settings.matrix.access_token_helper = Es wird empfohlen, hierfür ein dediziertes Matrix-Konto anzulegen. Der Zugangstoken kann in einem Inkognito-Tab über den Element-Webclient geholt werden: Benutzermenü (oben links) > alle Einstellungen > Hilfe & Info > erweitert > Zugriffstoken (direkt unter der Heim-Server-URL). Schließe dann den Inkognito-Tab (Abmelden würde den Token ungültig machen). release.hide_archive_links = Automatisch generierte Archive verstecken release.hide_archive_links_helper = Verstecke automatisch generierte Quellcodearchive für diesen Release. Zum Beispiel, wenn du deine eigenen hochlädst. @@ -2873,9 +2873,9 @@ mirror_use_ssh.not_available = SSH-Authentifizierung ist nicht verfügbar. issues.new.assign_to_me = Mir selbst zuweisen issues.all_title = Alle settings.discord_icon_url.exceeds_max_length = Die Icon-URL darf eine Länge von 2048 Zeichen nicht überschreiten -issues.review.add_review_requests = hat Reviews von %[1]s %[2]s angefragt -issues.review.remove_review_requests = hat Aufforderungen zum Review an %[1]s %[2]s entfernt -issues.review.add_remove_review_requests = hat Reviews von %[1]s angefragt und hat die Aufforderungen zum Review an %[2]s %[3]s entfernt +issues.review.add_review_requests = hat Sichtungen von %[1]s %[2]s angefragt +issues.review.remove_review_requests = hat die Sichtungsanfragen an %[1]s %[2]s entfernt +issues.review.add_remove_review_requests = hat Sichtungen von %[1]s angefragt und die Sichtungsanfragen an %[2]s %[3]s entfernt pulls.delete_after_merge.head_branch.is_default = Der Head-Branch, den du löschen willst, ist der Standardbranch und kann nicht gelöscht werden. pulls.delete_after_merge.head_branch.is_protected = Der Head-Branch, den du löschen willst, ist ein geschützter Branch und kann nicht gelöscht werden. pulls.delete_after_merge.head_branch.insufficient_branch = Du hast keine Erlaubnis, den Head-Branch zu löschen. @@ -2883,9 +2883,9 @@ issues.filter_sort.relevance = Relevanz diff.git-notes.add = Anmerkung hinzufügen diff.git-notes.remove-header = Anmerkung entfernen diff.git-notes.remove-body = Diese Anmerkung wird entfernt. -issues.num_reviews_one = %d Review +issues.num_reviews_one = %d Sichtung issues.summary_card_alt = Zusammenfassung eines Issues mit dem Titel „%s“ im Repository %s -issues.num_reviews_few = %d Reviews +issues.num_reviews_few = %d Sichtungen editor.add_tmpl.filename = Dateiname settings.default_update_style_desc = Standard-Aktualisierungsart um Pull-Requests zu aktualisieren, die hinter dem Base-Branch sind. new_advanced = Erweiterte Einstellungen @@ -2902,7 +2902,7 @@ issues.reaction.alt_add = Füge %[1]s Reaktion zum Kommentar hinzu. issues.reaction.alt_remove = Entferne %[1]s Reaktion von diesem Kommentar. summary_card_alt = Zusammenfassungskarte des Repositorys %s release.summary_card_alt = Übersichtskarte eines Releases mit dem Titel „%s“ im Repository %s -archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gereviewt werden. +archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gesichtet werden. editor.commit_email = Commit-E-Mail commits.view_single_diff = Änderungen an dieser Datei, die in diesem Commit eingeführt wurden, betrachten pulls.editable = Bearbeitbar @@ -2922,7 +2922,7 @@ settings.event_action_success_desc = Action-Run war erfolgreich. settings.event_action_failure = Fehlschlag settings.event_action_success = Erfolg settings.event_header_action = Action-Run-Ereignisse -settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Arbeitsablauf fehlgeschlagen ist. +settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Workflow fehlgeschlagen ist. settings.event_action_recover = Wiederherstellen issues.filter_type.all_pull_requests = Alle Pull-Requests @@ -3116,7 +3116,7 @@ dashboard.repo_health_check=Healthchecks für alle Repositorys ausführen dashboard.check_repo_stats=Überprüfe alle Repository-Statistiken dashboard.archive_cleanup=Alte Repository-Archive löschen dashboard.deleted_branches_cleanup=Gelöschte Branches bereinigen -dashboard.update_migration_poster_id=Migration Poster-IDs updaten +dashboard.update_migration_poster_id=Migrations-Poster-IDs aktualisieren dashboard.git_gc_repos=Garbage-Collection für alle Repositorys ausführen dashboard.resync_all_sshkeys=Die Datei „.ssh/authorized_keys“ mit Forgejo-SSH-Schlüsseln aktualisieren. dashboard.resync_all_sshprincipals=Aktualisiere die Datei „.ssh/authorized_principals“ mit Forgejo-SSH-Principals. @@ -3159,7 +3159,7 @@ dashboard.delete_old_actions=Alle alten Aktivitäten aus der Datenbank löschen dashboard.delete_old_actions.started=Löschen aller alten Aktivitäten aus der Datenbank gestartet. dashboard.update_checker=Update-Checker dashboard.delete_old_system_notices=Alle alten Systemmeldungen aus der Datenbank löschen -dashboard.gc_lfs=Garbage-Collection für LFS Meta-Objekte ausführen +dashboard.gc_lfs=Garbage-Collection für LFS-Meta-Objekte ausführen dashboard.stop_zombie_tasks=Zombie-Actions-Aufgaben stoppen dashboard.stop_endless_tasks=Endlose Actions-Aufgaben stoppen dashboard.cancel_abandoned_jobs=Aufgegebene Actions-Jobs abbrechen @@ -3335,7 +3335,7 @@ auths.pam_email_domain=PAM-E-Mail-Domain (optional) auths.oauth2_provider=OAuth2-Anbieter auths.oauth2_icon_url=Symbol-URL auths.oauth2_clientID=Client-ID (Schlüssel) -auths.oauth2_clientSecret=Client-Secret +auths.oauth2_clientSecret=Client-Geheimnis auths.openIdConnectAutoDiscoveryURL=OpenID-Connect-Auto-Discovery-URL auths.oauth2_use_custom_url=Benutzerdefinierte URLs anstelle von Standard-URLs verwenden auths.oauth2_tokenURL=Token-URL @@ -3343,14 +3343,14 @@ auths.oauth2_authURL=Authorisierungs-URL auths.oauth2_profileURL=Profil-URL auths.oauth2_emailURL=E-Mail-URL auths.skip_local_two_fa=Lokale 2FA überspringen -auths.skip_local_two_fa_helper=Leer lassen bedeutet, dass lokale User die 2FA immer noch bestehen müssen, um sich anzumelden +auths.skip_local_two_fa_helper=Das Leerlassen bedeutet, dass lokale User die 2FA immer noch bestehen müssen, um sich anzumelden auths.oauth2_tenant=Inhaber auths.oauth2_scopes=Zusätzliche Bereiche auths.oauth2_required_claim_name=Benötigter Claim-Name auths.oauth2_required_claim_name_helper=Setze diesen Namen, damit Nutzer aus dieser Quelle sich nur anmelden dürfen, wenn sie einen Claim mit diesem Namen besitzen auths.oauth2_required_claim_value=Benötigter Claim-Wert auths.oauth2_required_claim_value_helper=Setze diesen Wert, damit Nutzer aus dieser Quelle sich nur anmelden dürfen, wenn sie einen Claim mit diesem Namen und Wert besitzen -auths.oauth2_group_claim_name=Claim-Name, der Gruppennamen für diese Quelle angibt. (Optional) +auths.oauth2_group_claim_name=Claim-Name, der Gruppennamen für diese Quelle angibt (optional). auths.oauth2_admin_group=Gruppen-Claim-Wert für Administratoren (optional – erfordert Claim-Namen oben). auths.oauth2_restricted_group=Gruppen-Claim-Wert für eingeschränkte User. (Optional – erfordert Claim-Namen oben) auths.oauth2_map_group_to_team=Gruppen aus OAuth-Claims den Organisationsteams zuordnen (optional – oben muss der Name des Claims angegeben werden). @@ -3395,7 +3395,7 @@ auths.still_in_used=Diese Authentifizierungsquelle wird noch verwendet. Bearbeit auths.deletion_success=Die Authentifizierungsquelle „%s“ wurde gelöscht. auths.login_source_exist=Die Authentifizierungsquelle „%s“ existiert bereits. auths.login_source_of_type_exist=Eine Authentifizierungart dieses Typs existiert bereits. -auths.unable_to_initialize_openid=OpenID Connect Provider konnte nicht initialisiert werden: %s +auths.unable_to_initialize_openid=Provider für OpenID Connect konnte nicht initialisiert werden: %s auths.invalid_openIdConnectAutoDiscoveryURL=Ungültige Auto-Discovery-URL (dies muss eine gültige URL sein, die mit http:// oder https:// beginnt) config.server_config=Serverkonfiguration @@ -3526,7 +3526,7 @@ config.git_pull_timeout=Zeitlimit für Pull config.git_gc_timeout=Zeitlimit für GC config.log_config=Protokollierungs-Konfiguration -config.logger_name_fmt=Logger: %s +config.logger_name_fmt=Protokollierer: %s config.disabled_logger=Deaktiviert config.access_log_mode=Zugriffsprotokoll-Modus config.access_log_template=Zugriffsprotokoll-Vorlage @@ -3650,7 +3650,7 @@ mirror_sync_delete=hat die Referenz des Spiegels %[2]s in %[3]s#%[2]s genehmigt` reject_pull_request=`schlug Änderungen für %[3]s#%[2]s vor` publish_release=`veröffentlichte Release %[4]s in %[3]s` -review_dismissed=`verwarf das Review von %[4]s in %[3]s#%[2]s` +review_dismissed=`verwarf die Sichtung von %[4]s in %[3]s#%[2]s` review_dismissed_reason=Grund: create_branch=legte den Branch %[3]s in %[4]s an starred_repo=favorisierte %[2]s @@ -3765,9 +3765,9 @@ conda.registry=Richte diese Registry als Conda-Repository in deiner .conda conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befehl aus: container.details.type=Abbildtyp container.details.platform=Plattform -container.pull=Downloade das Container-Image aus der Kommandozeile: +container.pull=Lade das Container-Image von der Kommandozeile aus herunter: container.digest=Prüfsumme -container.multi_arch=Betriebsystem / Architektur +container.multi_arch=Betriebsystem/Architektur container.layers=Abbildebenen container.labels=Labels container.labels.key=Schlüssel @@ -3781,7 +3781,7 @@ debian.repository=Repository-Informationen debian.repository.distributions=Distributionen debian.repository.components=Komponenten debian.repository.architectures=Architekturen -generic.download=Downloade das Paket aus der Kommandozeile: +generic.download=Lade das Paket mit der Kommandozeile herunter: go.install=Installiere das Paket über die Kommandozeile: helm.registry=Diese Paketverwaltung über die Kommandozeile einrichten: helm.install=Nutze folgenden Befehl, um das Paket zu installieren: @@ -3813,10 +3813,10 @@ rubygems.install=Um das Paket mit gem zu installieren, führe den folgenden Befe rubygems.install2=oder füg es zum Gemfile hinzu: rubygems.dependencies.runtime=Laufzeitabhängigkeiten rubygems.dependencies.development=Entwicklungsabhängigkeiten -rubygems.required.ruby=Benötigt Ruby Version -rubygems.required.rubygems=Benötigt RubyGem Version +rubygems.required.ruby=Benötigt Ruby-Version +rubygems.required.rubygems=Benötigt RubyGem-Version swift.registry=Diese Registry über die Kommandozeile einrichten: -swift.install=Füge das Paket deiner Package.swift Datei hinzu: +swift.install=Füge das Paket deiner Package.swift-Datei hinzu: swift.install2=und führe den folgenden Befehl aus: vagrant.install=Um eine Vagrant-Box hinzuzufügen, führe den folgenden Befehl aus: settings.link=Dieses Paket einem Repository zuweisen @@ -3892,19 +3892,19 @@ alt.repository.architectures = Architekturen alt.repository.multiple_groups = Dieses Paket ist in verschiedenen Gruppen verfügbar. [secrets] -secrets=Secrets -description=Secrets werden an bestimmte Aktionen weitergegeben und können nicht anderweitig ausgelesen werden. -none=Noch keine Secrets vorhanden. -creation=Secret hinzufügen +secrets=Geheimnisse +description=Geheimnisse werden an bestimmte Aktionen weitergegeben und können nicht anderweitig ausgelesen werden. +none=Noch keine Geheimnisse vorhanden. +creation=Geheimnis hinzufügen creation.name_placeholder=Groß-/Kleinschreibung wird ignoriert, nur alphanumerische Zeichen oder Unterstriche, darf nicht mit GITEA_ oder GITHUB_ beginnen creation.value_placeholder=Beliebigen Inhalt eingeben. Leerzeichen am Anfang und Ende werden weggelassen. -creation.success=Das Secret „%s“ wurde hinzugefügt. -creation.failed=Secret konnte nicht hinzugefügt werden. -deletion=Secret entfernen -deletion.description=Das Entfernen eines Secrets kann nicht rückgängig gemacht werden. Fortfahren? -deletion.success=Das Secret wurde entfernt. -deletion.failed=Secret konnte nicht entfernt werden. -management=Secrets verwalten +creation.success=Das Geheimnis „%s“ wurde hinzugefügt. +creation.failed=Geheimnis konnte nicht hinzugefügt werden. +deletion=Geheimnis entfernen +deletion.description=Das Entfernen eines Geheimnisses kann nicht rückgängig gemacht werden. Fortfahren? +deletion.success=Das Geheimnis wurde entfernt. +deletion.failed=Geheimnis konnte nicht entfernt werden. +management=Geheimnisse verwalten [actions] actions=Actions diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index b94cb64af1..e071fa76f4 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -29,7 +29,7 @@ password=Пароль access_token=Токен доступу re_type=Підтвердження пароля captcha=CAPTCHA -twofa=Двофакторна авторизація +twofa=Двофакторна автентифікація twofa_scratch=Двофакторний одноразовий пароль passcode=Код доступу @@ -112,7 +112,7 @@ webauthn_error_unknown = Сталася невідома помилка. Буд webauthn_error_unable_to_process = Сервер не зміг обробити запит. webauthn_error_duplicated = Запит із наданим ключем безпеки відхилено. Впевніться, що цього ключа ще не зареєстровано. webauthn_error_empty = Ключ слід якось назвати. -new_project_column = Нова колонка +new_project_column = Новий стовпчик retry = Повторити rerun = Перезапустити rerun_all = Перезапустити всі завдання @@ -201,7 +201,7 @@ buttons.mention.tooltip = Згадати користувача чи коман buttons.ref.tooltip = Послатися на задачу чи на запит на злиття buttons.enable_monospace_font = Увімкнути моноширинний шрифт buttons.new_table.tooltip = Додати таблицю -table_modal.label.columns = Стовпці +table_modal.label.columns = Стовпчики table_modal.header = Додати таблицю table_modal.placeholder.header = Заголовок table_modal.placeholder.content = Вміст @@ -343,13 +343,13 @@ enable_update_checker = Увімкнути перевірку оновлень require_db_desc = Forgejo вимагає MySQL, PostgreSQL, SQLite3 чи TiDB (протокол MySQL). allow_only_external_registration = Дозволити реєстрацію тільки через зовнішні сервіси require_sign_in_view.description = Обмежити доступ до контенту лише користувачам, що увійшли. Гості зможуть лише відвідувати сторінки автентифікації. -password_algorithm_helper = Встановити алгоритм хешування паролів. Алгоритми мають різні вимоги та силу. Алгоритм argon2 є досить безпечним, проте споживає багато памʼяті та є недоречним для малих систем. +password_algorithm_helper = Установіть алгоритм хешування паролів. Алгоритми мають різні вимоги і стійкість. Алгоритм argon2 є досить безпечним, проте споживає багато памʼяті та є недоречним для малих систем. app_slogan = Гасло екземпляра app_slogan_helper = Уведіть гасло вашого екземпляра тут. Залиште порожнім, аби вимкнути. run_user_helper = Імʼя користувача операційної системи, від якого запущено Forgejo. Зауважте, що цей користувач повинен мати доступ до кореневої теки репозиторію. smtp_from_invalid = Адреса з «Відправляти email від імені» недійсна allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на облікові записи, що вже існують. -invalid_password_algorithm = Недійсний варіант алгоритму хешування паролів +invalid_password_algorithm = Недійсний алгоритм хешування паролів enable_update_checker_helper_forgejo = Наявність нових версій Forgejo періодично перевірятиметься через перевірку запису TXT DNS на release.forgejo.org. [home] @@ -734,7 +734,7 @@ applications=Додатки orgs=Організації repos=Репозиторії delete=Видалити обліковий запис -twofa=Двофакторна авторизація (TOTP) +twofa=Двофакторна автентифікація (TOTP) account_link=Прив'язані облікові записи organization=Організації @@ -1709,7 +1709,7 @@ pulls.reject_count_1=%d запит на зміну pulls.reject_count_n=%d запити на зміну pulls.waiting_count_1=очікується %d рецензія pulls.waiting_count_n=очікується %d рецензії(й) -pulls.wrong_commit_id=ID коміта повинен бути ID коміта в цільовій гілці +pulls.wrong_commit_id=ID коміту повинен бути ID коміту в цільовій гілці pulls.no_merge_desc=Цей запити на злиття неможливо злити, оскільки всі параметри об'єднання репозиторія вимкнено. pulls.no_merge_helper=Увімкніть параметри злиття в налаштуваннях репозиторія або злийте запити на злиття вручну. @@ -1727,7 +1727,7 @@ pulls.require_signed_wont_sign=Гілка вимагає підписаних к pulls.invalid_merge_option=Цей параметр злиття не можна використовувати для цього Pull Request'а. pulls.merge_conflict=Не вдалося об'єднати: при об'єднанні виник конфлікт. Підказка: спробуйте іншу стратегію pulls.merge_conflict_summary=Повідомлення про помилку -pulls.rebase_conflict=Не вдалося об'єднати: виник конфлікт під час перебазування коміта: %[1]s. Підказка: спробуйте іншу стратегію +pulls.rebase_conflict=Не вдалося об'єднати: виник конфлікт під час перебазування коміту: %[1]s. Підказка: спробуйте іншу стратегію pulls.rebase_conflict_summary=Повідомлення про помилку pulls.unrelated_histories=Не вдалося об'єднати: head та base злиття не мають спільної історії. Підказка: спробуйте іншу стратегію pulls.merge_out_of_date=Не вдалося об'єднати: base було оновлено, поки відбувалося об'єднання. Підказка: спробуйте знову. @@ -1896,7 +1896,7 @@ settings.mirror_settings.direction=Напрямок settings.mirror_settings.direction.pull=Pull settings.mirror_settings.direction.push=Push settings.mirror_settings.last_update=Останнє оновлення -settings.mirror_settings.push_mirror.none=Не налаштовано дзеркало push +settings.mirror_settings.push_mirror.none=Push-дзеркала не налаштовано settings.mirror_settings.push_mirror.remote_url=URL віддаленого репозиторію Git settings.mirror_settings.push_mirror.add=Додати push-дзеркало @@ -2343,7 +2343,7 @@ issues.author = Автор issues.author_helper = Цей користувач - автор. issues.close = Закрити задачу issues.role.owner_helper = Цей користувач є власником цього репозиторію. -settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push та pull дзеркала можна дізнатися тут: +settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push- та pull-дзеркала можна дізнатися тут: issues.comment.blocked_by_user = Ви не можете коментувати цю задачу, оскільки вас заблокував власник репозиторію або автор цієї задачі. editor.add_file = Додати файл from_comment = (коментар) @@ -2431,7 +2431,7 @@ pulls.is_ancestor = Цю гілку вже включено в цільову г pulls.has_merged = Помилка: запит на злиття вже об'єднано, неможливо об'єднати знову чи змінити цільову гілку. pulls.head_out_of_date = Не вдалося об'єднати: head було оновлено, поки відбувалося об'єднання. Підказка: спробуйте знову. no_eol.tooltip = У цьому файлі відсутній символ закінчення рядка (EOL) у кінці. -settings.trust_model.committer.desc = Допустимі підписи будуть позначатися як «довірені», тільки якщо вони відповідають автору коміта, в іншому випадку вони позначатимуться як «невідповідні». Це змусить Forgejo бути автором підписаних комітів, а фактичного автора зазначати в трейлерах «Co-authored-by» і «Co-committed-by» в описі коміта. Типовий ключ Forgejo повинен відповідати користувачу в базі даних. +settings.trust_model.committer.desc = Допустимі підписи будуть позначатися як «довірені», тільки якщо вони відповідають авторові коміту, в іншому випадку вони позначатимуться як «невідповідні». Це змусить Forgejo бути автором підписаних комітів, а фактичного автора зазначати в трейлерах «Co-authored-by» і «Co-committed-by» в описі коміту. Типовий ключ Forgejo повинен відповідати користувачу в базі даних. pulls.clear_merge_message_hint = Очищення повідомлення про об'єднання видалить лише вміст повідомлення коміту і збереже згенеровані git-трейлери, такі як «Co-Authored-By…». branch.delete_branch_has_new_commits = Гілку «%s» не можна видалити, оскільки після об'єднання було додано нові коміти. settings.graphql_url = Посилання GraphQL @@ -2514,7 +2514,7 @@ projects.column.set_default = Установити за замовчування settings.federation_following_repos = URL-адреси відстежуваних репозиторіїв. Через «;», без пробілів. settings.federation_not_enabled = Федерацію вимкнено у вашому екземплярі. settings.federation_settings = Налаштування федерації -signing.wont_sign.nokey = Цей екземпляр не має ключа для підписання цього коміта. +signing.wont_sign.nokey = Цей екземпляр не має ключа для підписання цього коміту. settings.federation_apapiurl = URL федерації цього репозиторію. Скопіюйте її та вставте в налаштування федерації іншого репозиторію як URL-адресу відстежуваного репозиторію. fork_branch = Гілка, яку буде клоновано у форк already_forked = Ви вже створили форк %s @@ -2554,7 +2554,7 @@ activity.navbar.pulse = Пульс open_with_editor = Відкрити в %s commits.view_single_diff = Переглянути зміни до цього файлу, внесені у цьому коміті pulls.editable = Редаговане -pulls.editable_explanation = Цей запит на злиття дозволяє редагування від розробників. Ви можете зробити свій внесок безпосередньо до нього. +pulls.editable_explanation = Цей запит на злиття дозволено редагувати супроводжувачам. Ви можете зробити свій внесок безпосередньо до нього. admin.failed_to_replace_flags = Не вдалося замінити прапорці репозиторія admin.enabled_flags = Для репозиторія ввімкнено прапорці: admin.flags_replaced = Прапорці репозиторія замінено @@ -2773,6 +2773,58 @@ issues.closed_by = від %[3]s закрито %[1]s issues.action_check = Поставити/зняти позначку issues.action_check_all = Поставити/зняти позначку з усіх елементів vendored = Сторонній +blame.ignore_revs.failed = Не вдалося проігнорувати зміни в .git-blame-ignore-revs. +blame.ignore_revs = Зміни в .git-blame-ignore-revs ігноруються. Натисніть тут, щоб обійти це і переглянути авторство у звичайному вигляді. +editor.new_branch_name = Укажіть назву нової гілки для цього коміту +projects.column.deletion_desc = Видалення стовпчика проєкту призведе до переміщення всіх пов'язаних із ним задач до стовпчика за замовчуванням. Продовжити? +signing.wont_sign.approved = Злиття не буде підписано, оскільки запит на злиття не схвалено. +pulls.allow_edits_from_maintainers = Дозволити редагування від супроводжувачів +pulls.showing_only_single_commit = Показано тільки зміни коміту %[1]s +pulls.showing_specified_commit_range = Показано тільки зміни між %[1]s..%[2]s +pulls.blocked_by_outdated_branch = Цей запит на злиття заблоковано, оскільки він застарів. +pulls.blocked_by_changed_protected_files_n = Цей запит на злиття заблоковано, оскільки він змінює захищені файли: +pulls.auto_merge_newly_scheduled = Заплановано об'єднати запит на злиття після успішного завершення всіх перевірок. +pulls.auto_merge_newly_scheduled_comment = `планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[1]s` +comments.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст коментаря. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +pulls.auto_merge_canceled_schedule_comment = `скасовує автоматичне об'єднання цього запиту на злиття після успішного завершення всіх перевірок %[1]s` +signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає публічного ключа, пов'язаного з вашим обліковим записом. +signing.wont_sign.basesigned = Злиття не буде підписано, оскільки не підписано базовий коміт. +signing.wont_sign.headsigned = Злиття не буде підписано, оскільки не підписано головний коміт. +projects.column.new_submit = Створити стовпчик +settings.authorization_header_desc = За наявності буде включено як заголовок авторизації для запитів. Приклади: %s. +template.git_hooks_tooltip = Наразі ви не можете змінювати або видаляти додані Git-хуки. Вибирайте лише якщо ви довіряєте репозиторію шаблону. +projects.column.edit = Редагувати стовпчик +projects.column.set_default_desc = Призначити цей стовпчик за замовчуванням для задач і запитів на злиття без категорії +projects.column.new = Новий стовпчик +projects.column.delete = Видалити стовпчик +pulls.blocked_by_official_review_requests = Цей запит на злиття заблоковано, оскільки йому бракує схвалення від одного або кількох офіційних рецензентів. +pulls.auto_merge_has_pending_schedule = %[1]s планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[2]s. +signing.wont_sign.commitssigned = Злиття не буде підписано, оскільки всі пов'язані з ним коміти не підписані. +settings.authorization_header = Заголовок авторизації +issues.archived_label_description = (Архівна) %s +issues.reaction.add = Додати реакцію +issues.reaction.alt_add = Додати реакцію %[1]s до коментаря. +issues.reaction.alt_remove = Прибрати реакцію %[1] з коментаря. +migrate.github_token_desc = Ви можете ввести тут один або кілька токенів через кому, щоб пришвидшити міграцію в обхід обмеження частоти звернень до API GitHub. ОБЕРЕЖНО: зловживання цією функцією може порушити політику постачальника послуг і призвести до блокування облікового запису. +issues.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст задачі. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +pulls.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст запиту на злиття. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +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 = Цей запит на злиття містить зміни, запропоновані офіційним рецензентом. +signing.wont_sign.parentsigned = Цей коміт не буде підписано, оскільки не підписано батьківський коміт. +settings.ignore_stale_approvals = Ігнорувати застарілі схвалення +pulls.blocked_by_approvals = Цей запит на злиття ще не має достатньої кількості схвалень. Отримано %d з %d схвалень. +issues.delete.text = Ви дійсно хочете видалити цю задачу? (Весь її вміст буде остаточно видалено. Можливо, варто її закрити і зберегти в архіві) +signing.wont_sign.twofa = Щоб підписувати коміти, у вас повинна бути ввімкнена двофакторна автентифікація. +settings.mirror_settings.docs = Налаштуйте свій репозиторій на автоматичну синхронізацію комітів, тегів і гілок з іншим репозиторієм. +settings.mirror_settings.docs.disabled_push_mirror.instructions = Налаштуйте свій проєкт на автоматичне отримання комітів, тегів і гілок з іншого репозиторію. +settings.mirror_settings.docs.disabled_push_mirror.info = Push-дзеркала вимкнено адміністратором сайту. +settings.mirror_settings.docs.disabled_pull_mirror.instructions = Налаштуйте свій проєкт на автоматичне надсилання комітів, тегів і гілок до іншого репозиторію. Pull-дзеркала вимкнено адміністратором сайту. +issues.label_templates.fail_to_load_file = Не вдалося завантажити файл шаблону міток «%s»: %v +migrate.github.description = Перенесіть дані з github.com або сервера GitHub Enterprise. +migrate.cancel_migrating_confirm = Бажаєте скасувати перенесення? [graphs] contributors.what = внески @@ -3016,7 +3068,7 @@ users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора users.is_restricted=Обмежений users.allow_git_hook=Може створювати Git-хуки -users.allow_git_hook_tooltip=Git хуки виконуються від імені користувача OS сервісу Forgejo і мають однаковий рівень доступу до хоста. Як результат, користувачі з доступом до Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних, що використовуються в Forgejo. Отже, вони також здатні отримати права адміністратора Forgejo. +users.allow_git_hook_tooltip=Git-хуки виконуються від імені користувача ОС, від якого запущено Forgejo, і мають той самий рівень доступу до хоста. Таким чином, користувачі зі спеціальними правами Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних Forgejo. Вони також здатні отримати права адміністратора Forgejo. users.allow_import_local=Може імпортувати локальні репозиторії users.allow_create_organization=Може створювати організації users.update_profile=Оновити обліковий запис @@ -3170,7 +3222,7 @@ auths.tip.discord=Зареєструйте новий додаток на %s auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) auths.edit=Редагувати джерело автентифікації -auths.activated=Це джерело авторизації активоване +auths.activated=Це джерело автентифікація активоване auths.update_success=Параметри аутентифікації оновлені. auths.update=Оновити джерело автентифікації auths.delete=Видалити джерело автентифікації @@ -3196,7 +3248,7 @@ config.repo_root_path=Шлях до кореня репозиторію config.lfs_root_path=Кореневий шлях LFS config.log_file_root_path=Шлях до лог файлу config.script_type=Тип скрипта -config.reverse_auth_user=Ім'я користувача для авторизації на reverse proxy +config.reverse_auth_user=Користувач для авторизації на зворотному проксі config.ssh_config=Конфігурація SSH config.ssh_enabled=Увімкнено @@ -3423,6 +3475,12 @@ config.mailer_smtp_addr = Адреса SMTP dashboard.update_checker = Перевірка оновлень auths.map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи LDAP auths.enable_ldap_groups = Увімкнути групи LDAP +auths.unable_to_initialize_openid = Не вдалося ініціалізувати постачальника OpenID Connect: %s +auths.new_success = Метод автентифікації «%s» додано. +config.cache_test_slow = Перевірку кешу завершено успішно, але відповідь повільна: %s. +config.cache_test_succeeded = Перевірку кешу завершено успішно, відповідь отримано через %s. +config.cache_test = Перевірити кеш +config.cache_test_failed = Не вдалося перевірити кеш: %v. [action] @@ -3704,7 +3762,7 @@ variables.management = Керування змінними variables.id_not_exist = Змінної з ідентифікатором %d не існує. variables.edit = Редагувати змінну runs.expire_log_message = Журнали очищено, тому що вони були занадто старі. -runs.empty_commit_message = (порожнє повідомлення коміта) +runs.empty_commit_message = (порожнє повідомлення коміту) runners.status.unspecified = Невідомо runs.status_no_select = Усі стани runs.status = Стан @@ -3826,6 +3884,7 @@ wiki.write = Писати: створювати, оновлювати т issues.read = Читати: дивитись і створювати задачі та коментарі. wiki.read = Читати: переглядати вбудовану вікі та її історію. actions.write = Писати: вручну запускати, перезапускати, скасовувати або схвалювати конвеєри CI/CD в очікуванні. +projects.write = Писати: створювати проєкти і стовпчики та редагувати їх. [munits.data] pib = ПіБ diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json index cef0f06e23..eca76a5ba7 100644 --- a/options/locale_next/locale_ar.json +++ b/options/locale_next/locale_ar.json @@ -4,6 +4,138 @@ "home.explore_repos": "اكتشف المستودعات", "home.explore_users": "اكتشف المستخدمين", "home.explore_orgs": "اكتشف المنظمات", - "moderation.abuse_category.illegal_content": "المحتوى غير المشروع", - "moderation.abuse_category.malware": "برمجية خبيثة" + "moderation.abuse_category.illegal_content": "محتوى غير مشروع", + "moderation.abuse_category.malware": "برمجية خبيثة", + "relativetime.now": "الآن", + "relativetime.1month": "الشهر الفائت", + "relativetime.2weeks": "منذ أسبوعين", + "search.milestone_kind": "معالم البحث…", + "moderation.abuse_category.other_violations": "انتهاكات أخرى لقواعد المنصة", + "repo.issue_indexer.title": "مفهرس الإبلاغات", + "incorrect_root_url": "تم تكوين هذه النسخة من Forgejo لتعمل على العنوان \"%s\". أنت تقوم حاليًا بتصفّح Forgejo عبر رابط مختلف، مما قد يتسبب في تعطل بعض أجزاء التطبيق. يتم تحديد الرابط الرسمي (canonical URL) من قِبل مسؤولي Forgejo من خلال إعداد `ROOT_URL` في ملف `app.ini`.", + "error.not_found.title": "الصفحة غير موجودة", + "themes.names.forgejo-auto": "فورجيو (اتبع سمة النظام)", + "themes.names.forgejo-light": "فورجيجو المضيء", + "themes.names.forgejo-dark": "فورجيجو الداكن", + "stars.list.none": "لم يقم أحد بتمييز هذا المستودع بنجمة.", + "watch.list.none": "لا أحد يشاهد هذا المستودع.", + "followers.incoming.list.self.none": "لا أحد يتابع ملفك الشخصي.", + "followers.incoming.list.none": "لا أحد يتابع هذا المستخدم.", + "followers.outgoing.list.self.none": "أنت لا تتبع أي شخص.", + "relativetime.1day": "الأمس", + "followers.outgoing.list.none": "لا يتابع %s أي شخص.", + "relativetime.1week": "أخر أسبوع", + "relativetime.2days": "منذ يومين", + "relativetime.2months": "منذ شهرين", + "relativetime.1year": "السنة الفائتة", + "relativetime.2years": "منذ سنتين", + "repo.form.cannot_create": "بلغت جميع المساحات التي يمكنك إنشاء مستودعات بها حدها.", + "alert.asset_load_failed": "تعذّر تحميل ملفات الأصول من {path}. تأكد من أن الملفات متاحة للوصول.", + "settings.visibility.description": "رؤية ملفك الشخصي تؤثر في قدرة الآخرين على الوصول إلى مستودعاتك غير الخاصة. اعرف المزيد", + "relativetime.mins": { + "zero": "الآن", + "one": "منذ دقيقة مضت", + "two": "منذ دقيقتين مضت", + "few": "منذ %d دقائق مضت", + "many": "منذ %d دقيقة مضت", + "other": "منذ %d دقيقة مضت" + }, + "relativetime.hours": { + "zero": "الآن", + "one": "منذ ساعة", + "two": "منذ ساعتين", + "few": "منذ %d ساعات", + "many": "منذ %d ساعة", + "other": "منذ %d ساعة" + }, + "moderation.report_remarks": "الملاحظات", + "repo.diff.commit.next-short": "التالي", + "repo.diff.commit.previous-short": "السابق", + "admin.dashboard.cleanup_offline_runners": "تنظيف وحدات التشغيل غير المتصلة", + "relativetime.days": { + "zero": "اليوم", + "one": "منذ يوم واحد", + "two": "منذ يومين", + "few": "منذ %d أيام", + "many": "منذ %d يوماً", + "other": "منذ %d يوماً" + }, + "relativetime.weeks": { + "zero": "هذا الأسبوع", + "one": "منذ أسبوع واحد", + "two": "منذ أسبوعين", + "few": "منذ %d أسابيع", + "many": "منذ %d أسبوعاً", + "other": "منذ %d أسبوعاً" + }, + "relativetime.years": { + "zero": "هذه السنة", + "one": "منذ سنة واحدة", + "two": "منذ سنتين", + "few": "منذ %d سنوات", + "many": "منذ %d سنة", + "other": "منذ %d سنة" + }, + "repo.settings.push_mirror.branch_filter.label": "تصفية الفرع (اختياري)", + "repo.settings.push_mirror.branch_filter.description": "الفروع المطلوب عكسها. اترك الحقل فارغًا لعكس جميع الفروع. راجع توثيق %[2]s للاطلاع على الصيغة. أمثلة: main, release/*", + "og.repo.summary_card.alt_description": "بطاقة تلخيصية للمستودع %[1]، موصوفة بـ %[1]: %[2]s", + "meta.last_line": "شكرًا لك على ترجمة Forgejo! هذا السطر لا يراه المستخدمون ولكنه يخدم أغراضًا أخرى في إدارة الترجمة. يمكنك وضع حقيقة ممتعة في الترجمة بدلاً من ترجمتها.", + "relativetime.future": "في المستقبل", + "avatar.constraints_hint": "لا يمكن أن يتجاوز حجم الصورة الشخصية المخصصة %[1]s، ولا أبعادها عن %[2]d×%[3]d بكسل", + "repo.pulls.merged_title_desc": { + "zero": "لم دمج أي إيداع من %[2]s إلى %[3]s %[4]s", + "one": "تم دمج إيداع واحد من %[2]s إلى %[3]s %[4]s", + "two": "تم دمج إيداعين اثنين من %[2]s إلى %[3]s %[4]s", + "few": "تم دمج %[1]d إيداعات من %[2]s إلى %[3]s %[4]s", + "many": "تم دمج %[1]d إيداعاً من %[2]s إلى %[3]s %[4]s", + "other": "تم دمج %[1]d إيداعاً من %[2]s إلى %[3]s %[4]s" + }, + "relativetime.months": { + "zero": "هذا الشهر", + "one": "منذ شهر", + "two": "منذ شهرين", + "few": "منذ %d أشهر", + "many": "منذ %d شهراً", + "other": "منذ %d شهراً" + }, + "repo.pulls.title_desc": { + "zero": "لا يريد دمج أي إيداع من %[2]s إلى %[3]s", + "one": "يريد دمج إيداع واحد من %[2]s إلى %[3]s", + "two": "يريد دمج إيداعين من %[2]s إلى %[3]s", + "few": "يريد دمج %[1]d إيداعات من %[2]s إلى %[3]s", + "many": "يريد دمج %[1]d إيداعًا من %[2]s إلى %[3]s", + "other": "يريد دمج %[1]d إيداعًا من %[2]s إلى %[3]s" + }, + "mail.actions.run_info_sha": "إيداع: %[1]s", + "keys.gpg.link": "مفاتيح GPG", + "moderation.abuse_category.spam": "غير مرغوب به", + "keys.ssh.link": "مفاتيح SSH", + "profile.edit.link": "عدِّل ملف التعريف", + "moderation.abuse_category": "الفئة", + "moderation.abuse_category.placeholder": "حدد الفئة", + "moderation.report_remarks.placeholder": "يُرجى تقديم بعض التفاصيل المتعلقة بالإساءة التي أبلغت عنها.", + "moderation.submit_report": "إرسال التقرير", + "moderation.reporting_failed": "تعذر إرسال تقرير إساءة الاستخدام الجديد: %v", + "moderation.reported_thank_you": "شكراُ لك على تقريرك. وقد تم إبلاغ الإدارة به.", + "mail.actions.run_info_trigger": "تم تشغيله بسبب: %[1]s عبر: %[2]s", + "install.invalid_lfs_path": "غير قادر على إنشاء جذر LFS في المسار المحدد: %[1]s", + "alert.range_error": " يجب أن يكون رقمًا بين %[1]s و %[2]s.", + "mail.actions.not_successful_run_subject": "سير العمل %[1]s فشل في المستودت %[2]s", + "mail.actions.successful_run_after_failure": "تم استعادة سير العمل %[1]s في المستودع %[2]s", + "mail.actions.not_successful_run": "فشل سير العمل %[1]s في المستودع %[2]s", + "mail.actions.run_info_cur_status": "حالة هذا التشغيل: %[1]s (تم تحديثها من %[2]s للتو)", + "mail.actions.run_info_previous_status": "حالة التشغيل السابقة: %[1]s", + "discussion.locked": "تم إغلاق هذه المناقشة. يقتصر التعليق على المساهمين فقط.", + "editor.textarea.tab_hint": "السطر مُزاح بالفعل. اضغط Tab مرة أخرى أو Escape لمغادرة المحرر.", + "editor.textarea.shift_tab_hint": "لا توجد مسافة بادئة في هذا السطر. اضغط Shift + Tab مرة أخرى أو Escape لمغادرة المحرر.", + "profile.actions.tooltip": "إجراءات إضافية", + "moderation.report_content": "محتوى التقارير", + "moderation.report_abuse_form.header": "الإبلاغ عن الإساءة إلى المسؤول", + "moderation.report_abuse_form.details": "يتعين استخدام هذا النموذج للإبلاغ عن المستخدمين الذين ينشئون ملفات تعريف ، أو مستودعات ، أو إبلاغات ، أو تعليقات ، أو يتصرفون بشكل غير لائق.", + "moderation.report_abuse_form.invalid": "معاملا غير صالحة", + "moderation.report_abuse_form.already_reported": "‍لقد قمت بالفعل بالإبلاغ عن هذا المحتوى", + "moderation.report_abuse": "الإبلاغ عن إساءة الاستخدام", + "feed.atom.link": "موجز Atom", + "admin.config.moderation_config": "تهيئة الإشراف", + "mail.actions.successful_run_after_failure_subject": "تم استعادة سير العمل %[1]s في المستودع %[2]s" } diff --git a/options/locale_next/locale_be.json b/options/locale_next/locale_be.json index 40dfec0b41..c8ff00bd87 100644 --- a/options/locale_next/locale_be.json +++ b/options/locale_next/locale_be.json @@ -1,3 +1,8 @@ { - "moderation.abuse_category.malware": "Шкодная праграма" + "moderation.abuse_category.malware": "Шкодная праграма", + "home.welcome.no_activity": "Няма падзей", + "home.welcome.activity_hint": "У вашай стужцы пакуль што нічога няма. Вашыя дзеяньні ды падзеі з рэпазыторыяў зьявяцца тут.", + "home.explore_repos": "Агляд рэпазыторыяў", + "home.explore_users": "Агляд карыстальнікаў", + "home.explore_orgs": "Агляд арганізацый" } diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 3ce42e6bb0..b47c594348 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -111,5 +111,7 @@ "feed.atom.link": "Zdroj Atom", "keys.ssh.link": "Klíče SSH", "og.repo.summary_card.alt_description": "Karta se souhrnem repozitáře %[1]s, popsaným jako: %[2]s", - "mail.actions.run_info_sha": "Revize: %[1]s" + "mail.actions.run_info_sha": "Revize: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Filtr větve (nepovinný)", + "repo.settings.push_mirror.branch_filter.description": "Větve, které mají být zrcadleny. Ponechte prázdné pro zrcadlení všech větví. Syntaxi naleznete v dokumentaci %[2]s. Příklady: main, release/*" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 5d670aeedf..5a3ceef7e5 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -90,11 +90,11 @@ "followers.outgoing.list.none": "%s folgt niemanden.", "stars.list.none": "Niemand hat dieses Repo favorisiert.", "followers.incoming.list.none": "Niemand folgt diesem Benutzer.", - "editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals Tab oder Escape um den Editor zu verlassen.", - "editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals Shift + Tab oder Escape um den Editor zu verlassen.", - "admin.dashboard.cleanup_offline_runners": "Aufräumen der offline Runner", + "editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals Tab oder Escape, um den Editor zu verlassen.", + "editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals Shift + Tab oder Escape, um den Editor zu verlassen.", + "admin.dashboard.cleanup_offline_runners": "Aufräumen der Offline-Runner", "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr", - "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]dx%[3]d Pixel sein", + "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]d×%[3]d Pixel sein", "repo.diff.commit.next-short": "Nächste", "repo.diff.commit.previous-short": "Vorherige", "profile.edit.link": "Profil bearbeiten", @@ -103,5 +103,7 @@ "keys.gpg.link": "GPG-Schlüssel", "profile.actions.tooltip": "Mehr Aktionen", "og.repo.summary_card.alt_description": "Zusammenfassungskarte des Repositorys %[1]s, beschrieben als %[2]s", - "mail.actions.run_info_sha": "Commit: %[1]s" + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Branch-Filter (optional)", + "repo.settings.push_mirror.branch_filter.description": "Zu spiegelnde Branches. Leer lassen, um alle Branches zu spiegeln. Siehe die „%[2]s“-Dokumentation für die Syntax. Beispiele: main, release/*" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 2c62514648..6ec601c538 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -1,6 +1,6 @@ { "repo.pulls.merged_title_desc": { - "one": "isinali ang %[1]d commit mula%[2]s patungong %[3]s %[4]s", + "one": "isinali ang %[1]d commit mula %[2]s patungong %[3]s %[4]s", "other": "isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s" }, "repo.pulls.title_desc": { @@ -21,7 +21,7 @@ "alert.asset_load_failed": "Nabigong i-load ang mga asset file mula sa {path}. Siguraduhin na maa-access ang mga asset file.", "install.invalid_lfs_path": "Nabigong gawin ang LFS root sa tinakdang path: %[1]s", "alert.range_error": " dapat ay numero sa pagitan ng %[1]s at %[2]s.", - "meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices. What will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings. What good are words when a smile says it all? And if this world won't write me an ending... What will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat What do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.", + "meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices, what will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings, what good are words when a smile says it all? And if this world won't write me an ending, what will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat, what do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.", "mail.actions.successful_run_after_failure": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.run_info_previous_status": "Nakaraang Status ng Run: %[1]s", @@ -102,5 +102,8 @@ "keys.ssh.link": "Mga SSH key", "keys.gpg.link": "Mga GPG key", "profile.actions.tooltip": "Higit pang mga aksyon", - "og.repo.summary_card.alt_description": "Card ng pangkalahatang ideya ng repositoryong %[1]s, inilalarawan bilang: %[2]s" + "og.repo.summary_card.alt_description": "Card ng pangkalahatang ideya ng repositoryong %[1]s, inilalarawan bilang: %[2]s", + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Filter ng branch (opsyonal)", + "repo.settings.push_mirror.branch_filter.description": "Mga branch na imi-mirror. Iwanang walang laman para i-mirror ang lahat ng mga branch. Tignan ang dokumentasyon ng %[2]s para sa syntax. Halimbawa: main, your-reality, release/*" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 22b80f2fad..5503763dd3 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -111,5 +111,7 @@ "keys.ssh.link": "SSH atslēgas", "keys.gpg.link": "GPG atslēgas", "og.repo.summary_card.alt_description": "Glabātavas %[1]s kopsavilkuma kartīte, aprakstīta kā: %[2]s", - "mail.actions.run_info_sha": "Iesūtījums: %[1]s" + "mail.actions.run_info_sha": "Iesūtījums: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Zarus, kurus spoguļot. Atstāt tukšu, lai spoguļotu visus zarus. Pierakstu skatīt %[2]s dokumentācijā. Piemēri: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Zaru atlasītājs (izvēles)" } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 20c1208c42..4a796aaee9 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -103,5 +103,7 @@ "profile.actions.tooltip": "Mehr Aktioonen", "profile.edit.link": "Profil bewarken", "og.repo.summary_card.alt_description": "Tosamenfatens-Kaart vun de Repositorium %[1]s, beschrieven as: %[2]s", - "mail.actions.run_info_sha": "Kommitteren: %[1]s" + "mail.actions.run_info_sha": "Kommitteren: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Twiegen tum Spegeln. Laat dat leeg, um all Twiegen to spegeln. Lees de %[2]s-Dokumenteren för de Syntax. Bispölen: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Twieg-Filter (wenn du willst)" } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index e049db3d41..7491458957 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -103,5 +103,7 @@ "repo.diff.commit.previous-short": "Vorige", "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels", "og.repo.summary_card.alt_description": "Samenvattingsoverzicht van repositorie %[1]s, omschreven als: %[2]s", - "mail.actions.run_info_sha": "Commit: %[1]s" + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Branch filter (optioneel)", + "repo.settings.push_mirror.branch_filter.description": "Branches die gespiegeld moeten worden. Laat het laag om alle branches te spiegelen. Zie %[2]s documentatie voor de syntax. Voorbeeld: main, release/*" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 6ed6c5defd..80395b020b 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -111,5 +111,7 @@ "og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s", "profile.actions.tooltip": "Mais Actions", "keys.ssh.link": "Chaves SSH", - "mail.actions.run_info_sha": "Commit: %[1]s" + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.settings.push_mirror.branch_filter.label": "Filtro de branches (opcional)", + "repo.settings.push_mirror.branch_filter.description": "Branches para espelhar. Deixe em branco para espelhar todos os branches. Veja %[2]s documentação sobre a sintaxe. Exemplos: main, release/*" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 352e948966..4710d3d868 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -83,7 +83,7 @@ "moderation.abuse_category": "Categoria", "moderation.abuse_category.placeholder": "Escolha uma categoria", "moderation.abuse_category.spam": "Spam", - "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.malware": "Software malicioso", "moderation.abuse_category.illegal_content": "Conteúdo ilegal", "moderation.abuse_category.other_violations": "Outras violações das regras da plataforma", "moderation.report_remarks": "Observações", @@ -109,5 +109,9 @@ "feed.atom.link": "Feed Atom", "keys.ssh.link": "Chaves SSH", "keys.gpg.link": "Chaves GPG", - "repo.diff.commit.previous-short": "Ant." + "repo.diff.commit.previous-short": "Ant.", + "og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s", + "repo.settings.push_mirror.branch_filter.label": "Filtro de ramos (opcional)", + "repo.settings.push_mirror.branch_filter.description": "Ramos a serem espelhados. Deixe em branco para espelhar todos os ramos. Veja a %[2]s documentação sobre a sintaxe. Exemplos: main, release/*", + "mail.actions.run_info_sha": "Cometimento: %[1]s" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 19167d7177..ecd58b8b62 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -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": "Unskip..", + "meta.last_line": "...ъъ", "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", @@ -111,5 +111,7 @@ "keys.gpg.link": "Ключи GPG", "profile.edit.link": "Изменить профиль", "og.repo.summary_card.alt_description": "Карточка со сводкой о репозитории %s. Описание: %[2]s", - "mail.actions.run_info_sha": "Коммит: %[1]s" + "mail.actions.run_info_sha": "Коммит: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Синхронизируемые ветви. Оставьте пустым, чтобы синхронизировать все. Ознакомьтесь с синтаксисом в документации %[2]s. Примеры: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Выбор ветвей (опционально)" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 8e99505e57..f014a8dc66 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -111,5 +111,7 @@ "feed.atom.link": "Стрічка Atom", "profile.actions.tooltip": "Більше дій", "og.repo.summary_card.alt_description": "Підсумкова картка репозиторію %[1]s з описом: %[2]s", - "mail.actions.run_info_sha": "Коміт: %[1]s" + "mail.actions.run_info_sha": "Коміт: %[1]s", + "repo.settings.push_mirror.branch_filter.description": "Гілки для дзеркалювання. Залиште порожнім, щоб віддзеркалити всі гілки. Дивіться синтаксис у документації %[2]s. Приклади: main, release/*", + "repo.settings.push_mirror.branch_filter.label": "Фільтр гілок (необов'язково)" } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index c235ebd0ad..467c990d3a 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -78,5 +78,8 @@ "repo.diff.commit.previous-short": "上一个", "feed.atom.link": "Atom 订阅源", "profile.edit.link": "编辑个人资料", - "og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s" + "og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s", + "repo.settings.push_mirror.branch_filter.label": "分支过滤器(可选)", + "repo.settings.push_mirror.branch_filter.description": "欲镜像的分支。留空以镜像所有分支。关于语法的更多信息,请参见 %[2]s 文档。例如:main, release/*", + "mail.actions.run_info_sha": "提交:%[1]s" } From dd3f24deef1da119c5030750e1a508d7d389fb4a Mon Sep 17 00:00:00 2001 From: oliverpool Date: Thu, 17 Jul 2025 12:31:38 +0200 Subject: [PATCH 127/297] fix: storage(minio): prevent io.Reader close (#8541) Fixes #8529, reverts #8527. I was able to reproduce the problem in a test: - it triggered only when the reader was an io.Reader - and the size was provided (-1 takes another code path in minio) https://codeberg.org/oliverpool/forgejo/commit/287b1f21e1e5fc3513fcca4eb21ee4607520813c should fail when running: ``` docker run --rm -e MINIO_DOMAIN=minio -e MINIO_ROOT_USER=123456 -e MINIO_ROOT_PASSWORD=12345678 -p 9000:9000 data.forgejo.org/oci/bitnami/minio:2024.8.17 ``` and ``` TEST_MINIO_ENDPOINT=localhost:9000 go test -v -run ^TestMinioStorageIterator$ ./modules/storage ``` ### 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. ### 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8541 Reviewed-by: Michael Kriese Reviewed-by: Earl Warren Co-authored-by: oliverpool Co-committed-by: oliverpool --- modules/storage/minio.go | 2 +- modules/storage/storage_test.go | 36 +++++++++++++++------ routers/api/packages/container/container.go | 16 ++++++--- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/modules/storage/minio.go b/modules/storage/minio.go index bf51a1642a..424000a0d8 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -209,7 +209,7 @@ func (m *MinioStorage) Save(path string, r io.Reader, size int64) (int64, error) m.ctx, m.bucket, m.buildMinioPath(path), - r, + io.NopCloser(r), // prevent minio from closing the reader size, minio.PutObjectOptions{ ContentType: "application/octet-stream", diff --git a/modules/storage/storage_test.go b/modules/storage/storage_test.go index af3dd9520e..76589d941a 100644 --- a/modules/storage/storage_test.go +++ b/modules/storage/storage_test.go @@ -5,6 +5,7 @@ package storage import ( "bytes" + "io" "testing" "forgejo.org/modules/setting" @@ -13,22 +14,39 @@ import ( "github.com/stretchr/testify/require" ) +type spyCloser struct { + io.Reader + closed int +} + +func (s *spyCloser) Close() error { + s.closed++ + return nil +} + +var _ io.ReadCloser = &spyCloser{} + func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) { l, err := NewStorage(typStr, cfg) require.NoError(t, err) - testFiles := [][]string{ - {"a/1.txt", "a1"}, - {"/a/1.txt", "aa1"}, // same as above, but with leading slash that will be trim - {"ab/1.txt", "ab1"}, - {"b/1.txt", "b1"}, - {"b/2.txt", "b2"}, - {"b/3.txt", "b3"}, - {"b/x 4.txt", "bx4"}, + testFiles := []struct { + path, content string + size int64 + }{ + {"a/1.txt", "a1", -1}, + {"/a/1.txt", "aa1", -1}, // same as above, but with leading slash that will be trim + {"ab/1.txt", "ab1", 3}, + {"b/1.txt", "b1", 2}, // minio closes when the size is set + {"b/2.txt", "b2", -1}, + {"b/3.txt", "b3", -1}, + {"b/x 4.txt", "bx4", -1}, } for _, f := range testFiles { - _, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1) + sc := &spyCloser{bytes.NewBufferString(f.content), 0} + _, err = l.Save(f.path, sc, f.size) require.NoError(t, err) + assert.Equal(t, 0, sc.closed) } expectedList := map[string][]string{ diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index 9a7fd03aa8..117fc6ab88 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -399,7 +399,12 @@ func EndUploadBlob(ctx *context.Context) { } return } - defer uploader.Close() + doClose := true + defer func() { + if doClose { + uploader.Close() + } + }() if ctx.Req.Body != nil { if err := uploader.Append(ctx, ctx.Req.Body); err != nil { @@ -432,10 +437,11 @@ func EndUploadBlob(ctx *context.Context) { return } - // There was a strange bug: the "Close" fails with error "close .../tmp/package-upload/....: file already closed" - // AFAIK there should be no other "Close" call to the uploader between NewBlobUploader and this line. - // At least it's safe to call Close twice, so ignore the error. - _ = uploader.Close() + doClose = false + if err := uploader.Close(); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil { apiError(ctx, http.StatusInternalServerError, err) From 5645456cac9860036579ba0f242ba42a5cfdfcca Mon Sep 17 00:00:00 2001 From: forgejo-release-manager Date: Thu, 17 Jul 2025 16:25:09 +0200 Subject: [PATCH 128/297] chore(release-notes): Forgejo v12.0.0 (#8540) https://codeberg.org/forgejo/forgejo/milestone/12836 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8540 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: forgejo-release-manager Co-committed-by: forgejo-release-manager --- release-notes-published/12.0.0.md | 476 ++++++++++++++++++++++++++++++ 1 file changed, 476 insertions(+) diff --git a/release-notes-published/12.0.0.md b/release-notes-published/12.0.0.md index e69de29bb2..6d83eef3c5 100644 --- a/release-notes-published/12.0.0.md +++ b/release-notes-published/12.0.0.md @@ -0,0 +1,476 @@ + + + + +## Release notes + +- Breaking security features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7924): remove API authentication methods that uses the URL query. They are disabled by default and this only has an impact if `[security].DISABLE_QUERY_AUTH_TOKEN=false` is explicitly set. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#removing-deprecated-api-authentication-methods). +- Security features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7829): relax email requirements. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#relaxing-the-requirements-on-email-addresses). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7693): consider WebAuthn & SSH for instance signing. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6897): add SSH signing support for instances. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#instance-signing-with-ssh). +- Breaking features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8035): The `forgejo docs` command is deprecated and CLI errors are now displayed on stderr instead of stdout. These breaking changes happened because the package used to parse the command line arguments was [upgraded from v2 to v3](https://cli.urfave.org/migrate-v2-to-v3/). A [separate project was initiated](https://github.com/urfave/cli-docs) to re-implement the `docs` command, but it is not yet production ready. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7745): remove the legacy `TEST_CONFLICTING_PATCHES_WITH_GIT_APPLY` setting +- Breaking bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8139): fail if `sha` is not provided to the `POST /repos/{owner}/{repo}/contents` API endpoint. Although it was documented to be required, it was not enforced and clients that do not set the `sha` will no longer succeed. +- User Interface features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8185): transform fediverse handles (ex. @forgejo@floss.social and !forgejo@programming.dev) into links to https://fedirect.toolforge.org. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#redirecting-fediverse-handles). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8121): add user visibility description in the settings page. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8111): add model viewer for `.glb` (GLTF) model in file view. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#gltf-viewer). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7998): show size constraints of custom avatar. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7992): add links to milestones and projects in issue comments. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7958): global styling for the kbd tag. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7947): hints in empty usercards lists. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7906): the user profile has been redesigned. The most notable change is that actions have been moved to a dropdown and several new actions were added. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7531): improve the description in the packages settings. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7516): inline public ssh key in verification command. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7481): use switch element for markdown editor modes. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7388): make JS asset load error message translatable. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7385): improve performances by using `git switch -c` instead of `git checkout -b`. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7377): clarify the desired autocompletion type for the clone address in migrations. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7373): improve the clarity of the migration description textarea. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7361): automatically refresh workflows in the "Actions" list. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#automatically-refreshing-workflows). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7274): improve error pages. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7155): improve the user experience to review individual commits in a pull request. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#improved-ux-for-per-commit-reviews). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7128): use the available screen width when displaying Forgejo Actions logs. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6933): show if a commit is verified in the activity feed of a user or an organization. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6813): reimplemented editor Tab key handling with accessibility safeguards. Balance having the editor work as expected by developers (with Tab key affecting indentation) while also not impeding keyboard navigation. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#tabs-indentations-in-the-comment-editor) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6795): redesign the migration selection page. +- User Interface bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8417) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8514)): multiple ComboMarkdownEditors on one page interfere with each other. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7749) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8362)): pasting images into the comment editor will now show that image in the ‘dropzone’. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8296) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8317)): add missing trust status to pull review commits. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8246) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8282)): add missing lazy load attribute to images. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8067): retain sort type when viewing issue or pull requests. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7893): include enough activity for the entire heatmap. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7726): show warning in locked issue discussion. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7492): ensure consistent switch position in the markdown editor. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7420): display user-friendly message for range error. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7402): make limits clearer in the create repository form. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7307): don't put trailing slash in autogenerated name in the migration form. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7245): allow user with actions write permission to run a workflow from the web UI. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6799): ensure usercards in grid have the same width. +- Localization + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8238) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8294)): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8178): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8077): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7969): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7895): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7819): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7742): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7637): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7572): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7507): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7438): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7363): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7275): i18n: update of translations from Codeberg Translate +- Features + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8179): new `GET /repos/{owner}/{repo}/git/blobs` API endpoint to retrieve multiple blobs at once. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8177): always publish the link to the commit status. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8143): improve the performances of the generation of bundled assets. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8115): enable `mlkem768x25519-sha256` by default for builtin ssh. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8070): support artifact uploads for OCI container packages. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8047): add `admin user reset-mfa` CLI command. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7988): update the list of ambigious characters. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7986): make Forgejo Actions server logs less noisy. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7968): allow searching issues by number, prioritize title matches when sorted by relevance. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7953): replace go-rpmutils library with our own. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#forgejo-build-time-optimization). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7902): configurable default units for mirrors. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7896): a repository administrator has control over reindexing the issues. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7803): auto cleanup of offline runners. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7727): improved performances when checking for conflicts on pull requests. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#faster-conflict-checking). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7716): allow access to publicly available `/api/v1/packages/{username}` without a token. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7699): implement the `GET /repos/{owner}/{repo}/actions/runs` and `GET /repos/{owner}/{repo}/actions/runs/{run_id}` API endpoints. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7527): use git-replay for rebasing for better performances. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7509): send mail on failed or recovered Forgejo Actions run. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#forgejo-actions-email-notifications-on-failure). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7508): Forgejo Actions failure, success, recover webhooks. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7418): add `last_commit_when` to API contents responses. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7387): include a default robots.txt to reduce the impact of crawlers. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#default-robotstxt). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7212): use XORM EngineGroup instead of single Engine connection. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#xorm-enginegroup-connections-for-optimized-database-query-routing-and-load-balancing). + - [PR](https://codeberg.org/forgejo/forgejo/pulls/2364): sync forks. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#keeping-forks-in-sync). +- Bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8511) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8516)): pull requests were not blocked by review request for a whitelisted team. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8475) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8480)): several fixes of the ALT RPM package registry. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8391) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8459)): allow lowercase as well as uppercase token keyword in the auth header. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8450) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8454)): correctly mark reviews as stale for AGit pull requests. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8367) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8386)): user activation failed when an email address contained uppercase letters. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8330) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8349)): fix: load OldMilestone based on OldMilestoneID, not MilestoneID + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8037): omit Content-Length on 307 redirects when serving direct manifest for containers. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8034): fix a bug causing the PASCAL-modifier to return camel-case. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8028): remove the trailing slash from the issuer in OAuth claims. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8025): return the correct AGit type in ssh_info. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7932): fix url validation in the webhook add/edit API. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7930): add error reporting to pull requests with invalid Forgejo Actions workflow files. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7644): allow instance API URLs in release assets. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7604): improve the dashboard loading performances. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7454): fix a border case where it was not possible to cancel a pull request review. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7409): fix acme renewal. + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6352): migrate Maven packages to "groupId:artifactId" name concatenation, regenerate metadata and fix missing groupId. +- Included for completeness but not worth a release note + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8534): i18n: update of translations from Codeberg Translate + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8530) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8532)): fix(packages): skip another stack frame from logging + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8527) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8528)): fix: ignore "Close" error when uploading container blob + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8524) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8526)): chore: failed authentication attempts are not errors and are displayed at the log info level + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8519) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8523)): fix: expanding exactly 20 lines between diff sections leaves visual artifact + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8301) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8518)): chore: use eventually for mysql collation test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8492) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8498)): fix(code-search): HighlightSearchResultCode should count the number of bytes and not the number of runes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8464) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8474)): fix: use parent context for new transactions + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8460) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8466)): chore: disable mismatched root URL e2e test for safari + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8461) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8463)): chore: do not navigate to same URL in E2E test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8448) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8455)): fix(email): actions notification template confuses branch with PR + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8258) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8439)): fix: corrupted wiki unit default permission (#8234 follow-up) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8366) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8404)): fix: cancelled or skipped runs are not failures for notifications + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8400) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8402)): chore: improve reliability of webauthn e2e test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8261) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8398)): fix: skip empty tokens in SearchOptions.Tokens() + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8374) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8390)): fix: disable Forgejo Actions email notifications on recovery + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8326) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8331)): fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8320) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8321)): chore: sort blocked users list for determistic results + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8267) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8319)): fix: abuse reports string data types + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8304) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8318)): fix: pass doer's ID for CRUD instance signing + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8002) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8290)): fix(ui): release: name is overridden with tag name on edit + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8286) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8288)): Revert "fix(api): document `is_system_webhook` field (#7784)" + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8271) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8277)): CI debug: testSleep: show actual times on failures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8268): chore: update security option in issue templates + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8260): Token.ParseIssueReference crashing on empty string + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8256): Update renovate to v41.1.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8253): Update renovate to v41 (forgejo) (major) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8252): add an index to the ActionRun.stopped column + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8250): bug: unify RepoActionRun and ActionRun structs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8248): Update module github.com/go-chi/chi/v5 to v5.2.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8245): Downgrade playwright temporarily and allow running all e2e tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8243): git/blob use NewTruncatedReader for profile and codeowners + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8242): bug: Forgejo Actions email notifications are opt-in + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8236): prevent 500 message on invalid username + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8227): only send Forgejo Actions notifications to one user + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8226): chore: sort mailer messages in test assertion + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8224): chore(release-notes): Forgejo v11.0.2 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8223): blob: GetBlobContent: reduce allocations + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8220): fix(tests): TestInitInstructions must use forEachObjectFormat + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8219): Update module code.forgejo.org/forgejo/act to v1.28.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8218): Update data.forgejo.org/oci/alpine Docker tag to v3.22 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8217): Update module github.com/minio/minio-go/v7 to v7.0.94 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8216): chore: migrate to `@stylistic/eslint-plugin` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8215): Update dependency eslint-plugin-wc to v3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8214): fix(ui): issue comment anchor on time stamp + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8209): git_model.CommitStatusesHideActionsURL is obsolete + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8207): Remove 1ms delay before inserting list prefix, fix race condition in tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8205): chore: remove gopls in Makefile + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8202): do not check for `object_format_name` field + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8199): feat(ui): use kbd in label selector hint, remove enter + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8198): [gitea] week 2025-22 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8197): Fix sentence structure mentioning cooldown period + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8195): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8194): Update renovate to v40.57.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8190): Update dependency chart.js to v4.5.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8188): make test suite run on older git version + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8186): Update module github.com/go-sql-driver/mysql to v1.9.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8183): Update environment-to-ini README + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8181): Update https://data.forgejo.org/forgejo/forgejo-build-publish action to v5.3.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8176): Dockerfile should re-use bindata files when possible + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8174): Update dependency minimatch to v10.0.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8172): use zstd.WithLowerEncoderMem for generate-bindata + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8168): do not mix urfave v2 with urfave v3 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8166): chore(ci): skip tests if TEST_{MINIO_ENDPOINT,ELASTICSEARCH_URL} is not set + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8163): Update module gitlab.com/gitlab-org/api/client-go to v0.130.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8162): Update module github.com/minio/minio-go/v7 to v7.0.93 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8160): Update dependency postcss to v8.5.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8159): Update dependency minimatch to v10.0.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8157): chore(ci): use code.forgejo.org/forgejo/migration-test for migration tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8150): Update dependency happy-dom to v18 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8149): Update dependency typescript-eslint to v8.34.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8148): Update dependency @playwright/test to v1.53.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8147): Update ghcr.io/devcontainers/features/git-lfs Docker tag to v1.2.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8146): Update dependency postcss-nesting to v13.0.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8145): Update dependency @vitest/eslint-plugin to v1.2.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8144): chore: fix tests for old git versions + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8140): chore(ci): run additional tests in integration + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8137): Remove shebang from bash autocompletion + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8134): Make relative-time a self-maintaining custom element + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8129): create shell.nix; update flake.* + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8128): Update module connectrpc.com/connect to v1.18.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8127): Update dependency markdownlint-cli to v0.45.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8126): Update dependency eslint-plugin-regexp to v2.9.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8125): Update vitest monorepo to v3.2.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8118): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8117): Update renovate to v40.48.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8112): remove download attribute from external assets + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8108): always render detailed team permissions table in sidebar + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8103): Update x/tools to v0.34.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8102): Update vitest monorepo to v3.2.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8101): Update module github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker to v3.3.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8099): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8098): Update dependency happy-dom to v17.6.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8097): Update dependency @vitejs/plugin-vue to v5.2.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8096): Update dependency @axe-core/playwright to v4.10.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8094): show membership of limited orgs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8091): Update module golang.org/x/crypto to v0.39.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8090): Update module github.com/sergi/go-diff to v1.4.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8086): Update dependency go to v1.24.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8078): Federated user activity following: Isolated model changes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8066): rename api.{List,}ActionRun to api.{List,}RepoActionRun + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8065): Update module go.uber.org/mock/mockgen to v0.5.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8064): Update module github.com/blevesearch/bleve/v2 to v2.5.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8063): Update dependency vue to v3.5.16 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8062): Update dependency postcss to v8.5.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8061): chore: extract commit header template + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8058): chore: drop unused `misspell` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8056): Update module code.forgejo.org/f3/gof3/v3 to v3.11.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8055): chore(renovate): disable indirect digest updates for stable + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8054): chore: drop unused `@typescript-eslint/parser` package + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8052): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8051): Update dependency happy-dom to v17.6.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8050): chore(renovate): bump to v40.40.0 and disable v7 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8049): CLI is forgejo not Forgejo + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8044): add missing bottom margin for verification-button in release view + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8041): [gitea] Always use an empty line to separate the commit message and trailer + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8040): [gitea] week 2025-21 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8038): Add a GNU Guix manifest + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8021): chore(cleanup): suppress non actionable XORM warnings + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8019): migrate repository.topics column for SQLite + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7994): chore(cleanup): replaces unnecessary calls to formatting functions by non-formatting equivalents + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7987): aggregate deleted team as ghost team + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7981): add validating user password as trace region + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7978): chore(cleanup): fix and simplify API comparison helper + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7975): fix(i18n): use correct base capitalization style + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7961): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7959): Update renovate to v40.31.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7954): Update forgejo go-chi packages (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7952): Update module github.com/ProtonMail/go-crypto to v1.3.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7950): fix(ui): relative time elements were reset on htmx swap + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7945): `overflow-wrap` strategy in `.markup` CSS class + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7944): fix(ui): change escaping button bg on selected lines + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7933): do not unconditionally append $(GITEA_COMPATIBILITY) in version + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7925): fix(ui): center footer links + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7922): Update go-openapi packages (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7920): unify repository topics field by replacing JSON null with empty array + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7919): Update dependency webpack to v5.99.9 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7918): Update dependency sharp to v0.34.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7917): chore: QoL improvements to tests + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7914): chore(renovate): disable indirect major updates for stable branches + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7910): Update dependency clippie to v4.1.7 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7908): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7907): Update renovate to v40.26.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7901): Update module github.com/yuin/goldmark to v1.7.12 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7898): chore(ui): cleanup unused color CSS + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7894): fix(ui): fix force-push compare line layout + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7890): Update module github.com/alecthomas/chroma/v2 to v2.18.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7884): parse `change-id` in the git commit header + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7881): Update module github.com/blevesearch/bleve/v2 to v2.5.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7879): Update dependency vue to v3.5.14 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7877): Migrate renovate config + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7872): fix(ui): disable autocapitalization/autocorrect for username inputs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7868): fix(ui): disable spellcheck on TOTP form fields + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7866): chore: remove unused update-locales.sh + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7857): Update module github.com/msteinert/pam/v2 to v2.1.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7856): Update module code.forgejo.org/forgejo/act to v1.26.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7852): Fix TestSSHPushMirror/Normal/Check_mirrored_content test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7849): Update github.com/golang-jwt/jwt/v4 (indirect) to v4.5.2 [SECURITY] (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7848): ci(renovate): only fail on error + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7847): Update module github.com/niklasfasching/go-org to v1.8.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7840): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7835): remove redundant permission check in RemoveLabel + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7833): test(ui-e2e): fix flaky repo wiki test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7830): Update module github.com/go-webauthn/webauthn to v0.13.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7822): chore(ui): clean up hashbox CSS, small design changes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7817): replace ß with ss in normalizeUserName + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7814): Update dependency globals to v16.1.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7809): Update module golang.org/x/oauth2 to v0.30.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7807): Update dependency go to v1.24.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7799): add missing loadbalancing policies for EngineGroup connections + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7798): Update module golang.org/x/net to v0.40.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7797): Update module golang.org/x/image to v0.27.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7792): [skip ci] chore: remove backport script + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7789): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7785): [SKIP CI] chore: update CODEOWNERS + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7784): fix(api): document `is_system_webhook` field + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7778): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7777): Update renovate to v40 (forgejo) (major) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7776): Update renovate to v39.264.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7773): remove artificial delay for PR update + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7772): chore: Fix outdated usage of unittest.OverrideFixtures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7770): Update module github.com/42wim/httpsig to v1.2.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7769): Update dependency @github/relative-time-element to v4.4.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7768): Update github.com/42wim/sshsig digest to 5100632 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7765): chore(release-notes): Forgejo v7.0.15 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7764): chore(release-notes): Forgejo v11.0.1 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7762): document that /repos/{owner}/{repo}/pulls may contain nulls + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7760): Update dependency forgejo/release-notes-assistant to v1.2.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7759): chore(renovate): use `gitea-releases` datasource for rna + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7758): chore(renovate): allow updating release notes assistant + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7746): fix(ui): improve force-push compare line layout + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7744): Update dependency eslint-plugin-unicorn to v59 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7740): fix(ui): multiple fixes of sync fork UI + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7739): Update module github.com/redis/go-redis/v9 to v9.8.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7738): Update module github.com/alecthomas/chroma/v2 to v2.17.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7736): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7735): Update dependency happy-dom to v17.4.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7732): ci: add clarification regarding test label + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7731): chore: fix test to avoid data race + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7728): refactor & enhance AP elements used + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7720): chore(release): next-digest moved to invisible.forgejo.org + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7715): chore: replace `github.com/go-testfixtures/testfixtures` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7714): enhance validateable interface + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7713): [gitea] week 2025-17 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7707): do not set GOPROXY=direct in Dockerfile* + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7705): chore(ui): remove unused fomantic font size classes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7697): better comments and variable names for ActionRunNowDone + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7696): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7695): Update renovate to v39.261.4 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7690): fix various typos + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7689): Update module github.com/yuin/goldmark to v1.7.11 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7687): chore: tune down remote user promotion debug message shown as error + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7685): use `linguist-generated` for language stats + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7683): set default restricted for OAuth2 user + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7682): chore: simplify `GetDiff` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7680): chore: remove unused linters + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7678): add label for avatar settings + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7667): i18n(en): fix typo in archive notice + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7665): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7663): Update dependency @playwright/test to v1.52.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7662): Update module github.com/PuerkitoBio/goquery to v1.10.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7661): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7659): Update dependency webpack to v5.99.7 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7648): chore: merge tests.AddFixtures and unittest.OverrideFixtures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7640): Remove "create branch" button on mirrored repos + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7616): fix(ui): make pagination labels always visible to screenreader + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7613): Update module mvdan.cc/gofumpt to v0.8.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7612): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7611): Update vitest monorepo to v3.1.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7610): Update dependency @vitest/eslint-plugin to v1.1.43 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7607): Update module github.com/alecthomas/chroma/v2 to v2.17.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7606): Update module github.com/minio/minio-go/v7 to v7.0.91 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7602): [gitea] week 2025-16 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7597): delay-write trace.dat for forgejo diagnosis + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7588): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7587): Update renovate to v39.252.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7580): feat(ui): enlarge metadata line gaps in issue list and refactor + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7579): i18n(en): add positional hints to sync_fork placeholders + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7578): fix(ui): overflow tabular menu CSS fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7577): feat(ui): make fork related banners more consistent + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7575): fix(ui): use correct branch name in branch tag selector + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7573): Update dependency webpack to v5.99.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7571): fix(ui): show commit icon in branch dropdown button when viewing a commit + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7565): grammar in a release API error message + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7561): Update module github.com/caddyserver/certmagic to v0.23.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7560): Update module github.com/mattn/go-sqlite3 to v1.14.28 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7558): fix(ui): make tag dropdown clickable again + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7557): Update dependency python to v3.13.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7555): chore(release): v10.0 is EOL [skip ci] + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7554): chore(renovate): fix package name matching for go majors [skip ci] + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7552): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7551): Update https://data.forgejo.org/actions/git-backporting action to v4.8.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7550): Update dependency chart.js to v4.4.9 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7545): Update Node.js to v22 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7543): match PackageBlob.HashBlake2b definition and migration + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7539): fix(UI): i18n: improve naming + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7538): [gitea] week 2025-15 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7534): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7532): Update renovate to v39.240.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7528): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7520): package_blob.has_blake2b may be null + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7512): chore: use `sharp` to generate images + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7510): chore: refactor for Actions Done Notification + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7505): Update module golang.org/x/tools/cmd/deadcode to v0.32.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7504): Update module golang.org/x/net to v0.39.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7503): Update dependency katex to v0.16.22 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7499): chore(i18n): update contributing documentation with JSON format + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7497): Update module golang.org/x/image to v0.26.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7495): Update module github.com/minio/minio-go/v7 to v7.0.90 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7494): Update dependency webpack to v5.99.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7491): Actions Done Notification + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7490): Update dependency webpack to v5.99.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7488): Update module go.uber.org/mock to v0.5.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7486): [gitea] week 2025-14 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7485): Update module golang.org/x/crypto to v0.37.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7484): Update renovate to v39.233.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7479): Minor grammatical fix to runner deletion message + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7475): Update module golang.org/x/oauth2 to v0.29.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7473): Update module github.com/fsnotify/fsnotify to v1.9.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7472): Update dependency clippie to v4.1.6 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7471): fix(ui): ensure dimmer always covers whole page + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7470): fix(ui): only run auth_name code on new and edit page + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7469): chore(release-notes): Forgejo v11.0.0 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7468): Update module github.com/blevesearch/bleve/v2 to v2.5.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7467): Update dependency typescript to v5.8.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7465): Update dependency ansi_up to v6.0.5 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7463): chore: always enable webpack progress + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7462): chore(i18n): add a meta line to the base json translation + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7459): Update module github.com/alecthomas/chroma/v2 to v2.16.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7458): Update module code.gitea.io/sdk/gitea to v0.21.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7456): Update dependency ansi_up to v6.0.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7455): Makefile & BSDmakefile changes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7453): chore(sec): unify usage of `crypto/rand.Read` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7452): make installing Forgejo work again + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7451): chore(i18n): cleanup `settings.adopt` string + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7434): Update module github.com/mattn/go-sqlite3 to v1.14.27 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7433): feat(build): run lint-locale-usage w/o --allow-missing-msgids + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7431): make repo clone https/ssh listener conditional + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7429): feat(build): uniform ini parsing + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7425): feat(locale Iter): properly support trPluralString + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7424): Update module github.com/mattn/go-sqlite3 to v1.14.25 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7423): Update module github.com/go-webauthn/webauthn to v0.12.3 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7422): fix(i18n): fix several usages of i18n + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7421): fix(migrations): transfer PR flow information + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7419): chore: add empty `action_variable` fixture + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7417): Update dependency go to v1.24.2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7416): chore(ui): remove fomantic's dimmer module + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7414): chore(ui): remove ineffective class small in inline code preview + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7408): chore: enable several no-jquery rules + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7407): Update vitest monorepo to v3.1.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7406): Update linters to v8.29.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7400): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7399): Update renovate to v39.222.1 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7397): [gitea] week 2025-13 cherry pick (gitea/main -> forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7396): chore: use dynamic id + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7394): use correct init instruction for sha256 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7392): Lock file maintenance (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7391): Update github.com/google/pprof digest to a4b03ec (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7390): Accessibility: fix unreadable captcha with dark themes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7384): Update linters (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7383): Update dependency yamllint to v1.37.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7382): Update vitest monorepo to v3.0.9 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7381): Update dependency @vitest/eslint-plugin to v1.1.38 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7379): prepend AppSubURL to visibility hint URLs + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7378): chore: improve repo migrate e2e test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7376): chore(tests): fix testing failure caused by dep update + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7374): chore(tests): refactor migration form test + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7372): Update dependency eslint-plugin-unicorn to v58 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7371): chore: use correct import + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7367): Update module github.com/golangci/golangci-lint/cmd/golangci-lint to v2 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7366): Update https://data.forgejo.org/tj-actions/changed-files action to v46 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7362): Update module golang.org/x/net to v0.38.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7356): fix(ui): improve vertical alignment of icons with text in the overflow menu (#7314) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7355): 4108-empty-slice-encoded-to-null + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7350): refactor(cli): improve dump's temporary file handling + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7348): introduce gitNeeded bool in setup + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7344): fix(ui): Do not check for `vertical-align` + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7339): Update dependency mermaid to v11.6.0 (forgejo) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7337): chore: branding import path + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7314): fix(ui): improve vertical alignment of icons with text in the overflow menu + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7278): feat(build): lint-locale-usage should detect more Tr functions + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7271): feat(ui): improve button gap consistency, make it variable, larger on touchscreens + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7261): redirect to submodule instead of throwing 500 error when viewing submodule entry + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7203): add port and schema to federation host + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7092): feat(repo,locale): merge PR/issues cases for some repo/issue strings + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7035): enable HTTP signatures on all ActivityPub endpoints + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6977): feat!: Abusive content reporting + - [PR](https://codeberg.org/forgejo/forgejo/pulls/6154): Replace the 'relative-time' element scripting with custom, translatable rewrite +- Already announced in the release notes of an older stable release + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8234): collaborator can edit wiki with write access + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8189): do not ignore automerge while a PR is checking for conflicts + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8170): erroneous list continuation on Cmd+Enter (#8153) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8155): do not fail when release or wiki is set in `/repos/migrate` API + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7979): pull request cross references + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7976): ignore expired artifacts for quota calculation + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7883): quote reply in Chromium + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7775): make hash pattern more strict + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7755): fix(sec): add tests for OAuth2 signup + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7752): fix(sec): only degrade permission check for git push + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7650): display the list of tasks in the runner edit page + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7594): fix(i18n): prevent incorrect logging on strings missing in JSON locales + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7584): fix(ui/pr): use eye icon for reviews + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7581): fix(ui): use gap in switch items + - [PR](https://codeberg.org/forgejo/forgejo/pulls/7395): validate input for default_{merge,update}_style + From 27e853454da6b9f2dc510a22a890924dbb7045ac Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 17 Jul 2025 20:40:09 +0200 Subject: [PATCH 129/297] fix: use correct ACME default (#8550) - The `ACME_URL` setting is documented to default to Let's encrypt production server if left empty, so do precisely that. - Use a HTTP proxy to communicate with ACME if Forgejo is configured to use that. - Regression of forgejo/forgejo#7409 (previously certmagic took care of setting these defaults). - Resolves forgejo/forgejo#8548 ## Testing 1. Configure Forgejo's root URL to a public facing domain (that can pass a ACME challenge) 2. Configure Forgejo to use ACME by setting `[server].ENABLE_ACME = true` and `[server].ACME_ACCEPTTOS = true`. 3. Start Forgejo. 4. Observe that it's available via https. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8550 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- cmd/web_acme.go | 7 +++++++ modules/setting/server.go | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd/web_acme.go b/cmd/web_acme.go index 03b3b9f0da..be6314addb 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -15,6 +15,7 @@ import ( "forgejo.org/modules/graceful" "forgejo.org/modules/log" "forgejo.org/modules/process" + "forgejo.org/modules/proxy" "forgejo.org/modules/setting" "github.com/caddyserver/certmagic" @@ -76,6 +77,12 @@ func runACME(listenAddr string, m http.Handler) error { ListenHost: setting.HTTPAddr, AltTLSALPNPort: altTLSALPNPort, AltHTTPPort: altHTTPPort, + HTTPProxy: proxy.Proxy(), + } + + // Preserve behavior to use Let's encrypt test CA when Let's encrypt is CA. + if certmagic.DefaultACME.CA == certmagic.LetsEncryptProductionCA { + certmagic.DefaultACME.TestCA = certmagic.LetsEncryptStagingCA } magic := certmagic.NewDefault() diff --git a/modules/setting/server.go b/modules/setting/server.go index bff51f787d..3ff91d2cde 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -16,6 +16,8 @@ import ( "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/util" + + "github.com/caddyserver/certmagic" ) // Scheme describes protocol types @@ -206,7 +208,7 @@ func loadServerFrom(rootCfg ConfigProvider) { EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) } if EnableAcme { - AcmeURL = sec.Key("ACME_URL").MustString("") + AcmeURL = sec.Key("ACME_URL").MustString(certmagic.LetsEncryptProductionCA) AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") if sec.HasKey("ACME_ACCEPTTOS") { From c9949fbc6408907c426a3e8db5fd1e5460cf18fd Mon Sep 17 00:00:00 2001 From: Kumi Date: Fri, 18 Jul 2025 07:19:15 +0200 Subject: [PATCH 130/297] chore: improve the wording of the "not worth a release note" category (#8542) Hi all, I believe that open source projects in general are under-appreciated. And no matter how big or small, every contribution matters, whether it's the implementation of a shiny new feature or just taking the time to make sure that a dependency is updated to its latest patch release. I don't usually contribute to Forgejo, but we run an instance, and highly appreciate all of the work everyone involved is doing. As such, it kinda pains me to see the "Included for completeness but __not worth__ a release note" line (emphasis mine) in the release notes every time. More often than not, when clicking on one of those PRs, you'll see that people actually do put some thought into the changes hidden down there, there may even be more or less heated discussions about them, and even though these changes may not directly influence the user experience, they are an important part of running a project like this. As such, I propose changing that line to "Included for completeness but not user facing (chores, etc.)" (courtesy of @earl-warren on Matrix). It's a really really small change, but I think it matters. ## 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 Not applicable since there are no Go or JavaScript changes. - 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 Since this is only a change to the wording of the release notes, no user documentation is required for this. - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes This PR is certainly not worthy. - [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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8542 Reviewed-by: Earl Warren Co-authored-by: Kumi Co-committed-by: Kumi --- release-notes-assistant.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/release-notes-assistant.sh b/release-notes-assistant.sh index 89fd0833f5..4963edf286 100755 --- a/release-notes-assistant.sh +++ b/release-notes-assistant.sh @@ -64,7 +64,7 @@ function test_main() { test "$(categorize)" = 'ZE Other changes without a feature or bug label' test_payload_labels - test "$(categorize)" = 'ZF Included for completeness but not worth a release note' + test "$(categorize)" = 'ZF Included for completeness but not user-facing (chores, etc.)' test_payload_draft "fix(security)!: breaking security bug fix" test "$(categorize)" = 'AA Breaking security bug fixes' @@ -117,11 +117,12 @@ function categorize() { # # If there was no release-notes/N.md file and it is not - # worth a release note, just forget about it. + # directly user-facing, we include it in a separate section + # for completeness. # if test -z "$(jq --raw-output .Draft <$payload)"; then if ! $worth; then - echo -n ZF Included for completeness but not worth a release note + echo -n ZF Included for completeness but not user-facing \(chores, etc.\) exit 0 fi fi From 6e9b97e3776dc8fc73ffdeca879b95c46ed8be85 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Fri, 18 Jul 2025 13:13:45 +0200 Subject: [PATCH 131/297] fix(ui): prevent render failure on faulty org settings post (#8553) Fix regression of https://codeberg.org/forgejo/forgejo/pulls/7998 Same as https://codeberg.org/forgejo/forgejo/pulls/8236 but for orgs Amended existing tests to verify which error messages show up and not show up. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8553 Reviewed-by: Earl Warren Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- routers/web/org/setting.go | 3 +++ tests/integration/org_settings_test.go | 14 +++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index c83242754b..9b4e01597b 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -71,6 +71,9 @@ func SettingsPost(ctx *context.Context) { ctx.Data["PageIsSettingsOptions"] = true ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight if ctx.HasError() { ctx.HTML(http.StatusOK, tplSettingsOptions) diff --git a/tests/integration/org_settings_test.go b/tests/integration/org_settings_test.go index fde57e2e26..3ea6f10bf6 100644 --- a/tests/integration/org_settings_test.go +++ b/tests/integration/org_settings_test.go @@ -57,7 +57,9 @@ func TestOrgSettingsChangeEmail(t *testing.T) { settings := getOrgSettingsFormData(t, session, orgName) settings["email"] = "invalid" - session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusOK) + doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusOK).Body) + doc.AssertElement(t, ".status-page-500", false) + doc.AssertElement(t, ".flash-error", true) org := getOrgSettings(t, token, orgName) assert.Equal(t, "org3@example.com", org.Email) @@ -69,7 +71,10 @@ func TestOrgSettingsChangeEmail(t *testing.T) { settings := getOrgSettingsFormData(t, session, orgName) settings["email"] = "example@example.com" - session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther) + doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther).Body) + doc.AssertElement(t, "body", true) + doc.AssertElement(t, ".status-page-500", false) + doc.AssertElement(t, ".flash-error", false) org := getOrgSettings(t, token, orgName) assert.Equal(t, "example@example.com", org.Email) @@ -81,7 +86,10 @@ func TestOrgSettingsChangeEmail(t *testing.T) { settings := getOrgSettingsFormData(t, session, orgName) settings["email"] = "" - session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther) + doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther).Body) + doc.AssertElement(t, "body", true) + doc.AssertElement(t, ".status-page-500", false) + doc.AssertElement(t, ".flash-error", false) org := getOrgSettings(t, token, orgName) assert.Empty(t, org.Email) From 88c3b6deada7debbb7557f66fa83dd826161ec83 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Fri, 18 Jul 2025 14:11:14 +0200 Subject: [PATCH 132/297] chore(release notes): squash repeating localization updates (#8547) Followup to https://codeberg.org/forgejo/forgejo/pulls/8540 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8547 Reviewed-by: Beowulf Reviewed-by: Gusted --- release-notes-published/12.0.0.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/release-notes-published/12.0.0.md b/release-notes-published/12.0.0.md index 6d83eef3c5..f52480cc88 100644 --- a/release-notes-published/12.0.0.md +++ b/release-notes-published/12.0.0.md @@ -53,19 +53,7 @@ - [PR](https://codeberg.org/forgejo/forgejo/pulls/7245): allow user with actions write permission to run a workflow from the web UI. - [PR](https://codeberg.org/forgejo/forgejo/pulls/6799): ensure usercards in grid have the same width. - Localization - - [PR](https://codeberg.org/forgejo/forgejo/pulls/8238) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8294)): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/8178): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/8077): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7969): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7895): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7819): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7742): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7637): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7572): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7507): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7438): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7363): i18n: update of translations from Codeberg Translate - - [PR](https://codeberg.org/forgejo/forgejo/pulls/7275): i18n: update of translations from Codeberg Translate + - Updates from Codeberg Translate: [#7275](https://codeberg.org/forgejo/forgejo/pulls/7275), [#7363](https://codeberg.org/forgejo/forgejo/pulls/7363), [#7438](https://codeberg.org/forgejo/forgejo/pulls/7438), [#7507](https://codeberg.org/forgejo/forgejo/pulls/7507), [#7572](https://codeberg.org/forgejo/forgejo/pulls/7572), [#7637](https://codeberg.org/forgejo/forgejo/pulls/7637), [#7742](https://codeberg.org/forgejo/forgejo/pulls/7742), [#7819](https://codeberg.org/forgejo/forgejo/pulls/7819), [#7895](https://codeberg.org/forgejo/forgejo/pulls/7895), [#7969](https://codeberg.org/forgejo/forgejo/pulls/7969), [#8077](https://codeberg.org/forgejo/forgejo/pulls/8077), [#8178](https://codeberg.org/forgejo/forgejo/pulls/8178), [#8294](https://codeberg.org/forgejo/forgejo/pulls/8294) (backport of [#8238](https://codeberg.org/forgejo/forgejo/pulls/8238)), [#8534](https://codeberg.org/forgejo/forgejo/pulls/8534) (backport of [#8295](https://codeberg.org/forgejo/forgejo/issues/8295), [#8410](https://codeberg.org/forgejo/forgejo/issues/8410), [#8490](https://codeberg.org/forgejo/forgejo/issues/8490)) - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/8179): new `GET /repos/{owner}/{repo}/git/blobs` API endpoint to retrieve multiple blobs at once. - [PR](https://codeberg.org/forgejo/forgejo/pulls/8177): always publish the link to the commit status. From eaea89b7f04f433b0c631d30f45cfe0277d7eeff Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 18 Jul 2025 16:09:25 +0200 Subject: [PATCH 133/297] chore(ci): cache release-notes-assistant working directory [skip ci] (#8556) to reduce the number of requests to the API to obtain each pull request to the minimum. - cache the working directory - upgrade to [v1.3.0](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.0) to get optimizations - use the oci:1 image to reduce the network load re-installing every time ## Testing - Pushed to https://codeberg.org/forgejo/forgejo/src/branch/wip-rna so that it gets access to the secrets - Manually triggered [to populate the cache](https://codeberg.org/forgejo/forgejo/actions/runs/89735) cutting the run time from ~2h to ~45min - Manually triggered [to verify the cache is reused](https://codeberg.org/forgejo/forgejo/actions/runs/89759) cutting the run time from ~45min to ~15min ![image](/attachments/3ae48d6d-0d38-4559-8544-f0bc71eb2040) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8556 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- .../release-notes-assistant-milestones.yml | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index 7f77098357..0b8a416a02 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -5,32 +5,34 @@ on: - cron: '@daily' env: - RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_WORKDIR: /srv/rna + RNA_VERSION: v1.3.0 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: if: vars.ROLE == 'forgejo-coding' runs-on: docker container: - image: 'data.forgejo.org/oci/node:22-bookworm' + image: 'data.forgejo.org/oci/ci:1' steps: - uses: https://data.forgejo.org/actions/checkout@v4 - - uses: https://data.forgejo.org/actions/setup-go@v5 + - uses: https://data.forgejo.org/actions/cache@v4 with: - go-version-file: "go.mod" - cache: false + key: rna-${{ env.RNA_VERSION }} + path: ${{ env.RNA_WORKDIR }} - - name: apt install jq + - name: install release-notes-assistant run: | - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq - apt-get -q install -y -qq jq + set -x + wget -O /usr/local/bin/rna https://code.forgejo.org/forgejo/release-notes-assistant/releases/download/${{ env.RNA_VERSION}}/release-notes-assistant + chmod +x /usr/local/bin/rna - name: update open milestones run: | set -x - curl -sS $GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do + mkdir -p ${{ env.RNA_WORKDIR }} + curl -sS $FORGEJO_SERVER_URL/api/v1/repos/$FORGEJO_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do milestone="$forgejo $version" - go run code.forgejo.org/forgejo/release-notes-assistant@$RNA_VERSION --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} release $version + rna --workdir ${{ env.RNA_WORKDIR }} --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $FORGEJO_SERVER_URL --repository $FORGEJO_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} release $version done From f27b46436cad293c7d03f64c71fdc88bd3a51ba3 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 18 Jul 2025 17:25:58 +0200 Subject: [PATCH 134/297] Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.2.2 (forgejo) (#8557) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1db44e6f26..df23ec6fd2 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.2.1 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.2.2 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest From fe6ba0c7c9da7ae295d3c87b38b4363084e9a928 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 18 Jul 2025 17:28:16 +0200 Subject: [PATCH 135/297] Update dependency vite-string-plugin to v1.4.6 (forgejo) (#8559) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index c41b1f6b7e..b379db27c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,7 +99,7 @@ "svgo": "4.0.0", "typescript": "5.8.3", "typescript-eslint": "8.35.1", - "vite-string-plugin": "1.3.4", + "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, "engines": { @@ -15255,9 +15255,9 @@ } }, "node_modules/vite-string-plugin": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.3.4.tgz", - "integrity": "sha512-mHvcooHgZ0nVbHtj9o+c5dzD2/nclr/SOG023EFYF/zRnO8bxB63bV9WUA9X+njlgLpOwCJ3LI2IdihKoi0gZQ==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.4.6.tgz", + "integrity": "sha512-Csjtny8/uVIynzlaRRj4RpHrPAakNwlH9jw6kgQ8tQhc2f0zzA6bCbAgWD0y84EgB8aLNrz7pZFUqSt3LOtk+w==", "dev": true, "license": "BSD-2-Clause" }, diff --git a/package.json b/package.json index f6e998a570..0b65f75026 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "svgo": "4.0.0", "typescript": "5.8.3", "typescript-eslint": "8.35.1", - "vite-string-plugin": "1.3.4", + "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, "browserslist": [ From bc4c1d64cbb31611752cc855cb9905d324287c7f Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 18 Jul 2025 17:41:12 +0200 Subject: [PATCH 136/297] Update x/tools to v0.35.0 (forgejo) (#8561) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index df23ec6fd2..213d7f4b0b 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renova 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 -DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go +DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.35.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go RENOVATE_NPM_PACKAGE ?= renovate@41.32.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate diff --git a/go.mod b/go.mod index 1421f503fe..9e0f47f968 100644 --- a/go.mod +++ b/go.mod @@ -235,9 +235,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - golang.org/x/mod v0.25.0 // indirect + golang.org/x/mod v0.26.0 // indirect golang.org/x/time v0.11.0 // indirect - golang.org/x/tools v0.34.0 // indirect + golang.org/x/tools v0.35.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index cf10843262..db2a186d2c 100644 --- a/go.sum +++ b/go.sum @@ -597,8 +597,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -688,8 +688,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From fce430c6ba02f241a9b63b9fe4c91ab9ed921b03 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 18 Jul 2025 23:20:15 +0200 Subject: [PATCH 137/297] Update dependency @playwright/test to v1.54.1 (forgejo) (#8558) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8558 Reviewed-by: Otto Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 24 ++++++++++++------------ package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index b379db27c7..fdcc21c71a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.53.2", + "@playwright/test": "1.54.1", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.1.0", "@stylistic/stylelint-plugin": "3.1.3", @@ -2213,13 +2213,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", - "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", + "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.53.2" + "playwright": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -12002,13 +12002,13 @@ } }, "node_modules/playwright": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", - "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", + "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.2" + "playwright-core": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -12021,9 +12021,9 @@ } }, "node_modules/playwright-core": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", - "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", + "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 0b65f75026..b0ce447b2b 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.53.2", + "@playwright/test": "1.54.1", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.1.0", "@stylistic/stylelint-plugin": "3.1.3", From b705cfcdd8041e2d38c6b4a2d95043029ec3c97d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 19 Jul 2025 02:03:07 +0200 Subject: [PATCH 138/297] Update linters (forgejo) (#8560) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 185 ++++++++++++++++++++++------------------------ package.json | 8 +- 2 files changed, 91 insertions(+), 102 deletions(-) diff --git a/package-lock.json b/package-lock.json index fdcc21c71a..dbf23af045 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,13 +65,13 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.54.1", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "5.1.0", + "@stylistic/eslint-plugin": "5.2.0", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", - "eslint": "9.30.1", + "eslint": "9.31.0", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", "eslint-plugin-import-x": "4.16.1", @@ -92,13 +92,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.3", - "stylelint": "16.21.1", + "stylelint": "16.22.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.35.1", + "typescript-eslint": "8.37.0", "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, @@ -1075,9 +1075,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1162,9 +1162,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1198,19 +1198,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@github/combobox-nav": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.3.1.tgz", @@ -3089,18 +3076,18 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.1.0.tgz", - "integrity": "sha512-TJRJul4u/lmry5N/kyCU+7RWWOk0wyXN+BncRlDYBqpLFnzXkd7QGVfN7KewarFIXv0IX0jSF/Ksu7aHWEDeuw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.0.tgz", + "integrity": "sha512-RCEdbREv9EBiToUBQTlRhVYKG093I6ZnnQ990j08eJ6uRZh71DXkOnoxtTLfDQ6utVCVQzrhZFHZP0zfrfOIjA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/types": "^8.34.1", + "@typescript-eslint/types": "^8.37.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", - "picomatch": "^4.0.2" + "picomatch": "^4.0.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3110,9 +3097,9 @@ } }, "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -3585,17 +3572,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", - "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/type-utils": "8.35.1", - "@typescript-eslint/utils": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3609,7 +3596,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.35.1", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -3625,16 +3612,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz", - "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/typescript-estree": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -3650,14 +3637,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", - "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.35.1", - "@typescript-eslint/types": "^8.35.1", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -3672,14 +3659,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", - "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3690,9 +3677,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", - "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "license": "MIT", "engines": { @@ -3707,14 +3694,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz", - "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.35.1", - "@typescript-eslint/utils": "8.35.1", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3731,9 +3719,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", - "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "license": "MIT", "engines": { @@ -3745,16 +3733,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", - "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.35.1", - "@typescript-eslint/tsconfig-utils": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3807,16 +3795,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", - "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/typescript-estree": "8.35.1" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3831,13 +3819,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", - "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -7302,9 +7290,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7312,9 +7300,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -13850,9 +13838,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.21.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.21.1.tgz", - "integrity": "sha512-WCXdXnYK2tpCbebgMF0Bme3YZH/Rh/UXerj75twYo4uLULlcrLwFVdZTvTEF8idFnAcW21YUDJFyKOfaf6xJRw==", + "version": "16.22.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.22.0.tgz", + "integrity": "sha512-SVEMTdjKNV4ollUrIY9ordZ36zHv2/PHzPjfPMau370MlL2VYXeLgSNMMiEbLGRO8RmD2R8/BVUeF2DfnfkC0w==", "dev": true, "funding": [ { @@ -14936,15 +14924,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz", - "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", + "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.35.1", - "@typescript-eslint/parser": "8.35.1", - "@typescript-eslint/utils": "8.35.1" + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index b0ce447b2b..4f4f9d48b8 100644 --- a/package.json +++ b/package.json @@ -64,13 +64,13 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.54.1", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "5.1.0", + "@stylistic/eslint-plugin": "5.2.0", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", - "eslint": "9.30.1", + "eslint": "9.31.0", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", "eslint-plugin-import-x": "4.16.1", @@ -91,13 +91,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.3", - "stylelint": "16.21.1", + "stylelint": "16.22.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.35.1", + "typescript-eslint": "8.37.0", "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, From 8c8d6460994a9897c9df3cbfacc61c623c55e714 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 19 Jul 2025 15:03:10 +0200 Subject: [PATCH 139/297] fix: correct image source for quoted reply (#8565) - Since v10 replies are generated on the fly to handle quoted reply (forgejo/forgejo#5677), this means that we have to do some work to construct markdown that is equivalent to the HTML of the comment. - Images are slightly strange in the context of issues and pull requests, as Forgejo will render them in the context of the repository and as such links such as `/attachments` become `/user/repo/attachments`, the quoted reply did not take into account and would use `/user/repo/attachments` as link which means it gets transformed to `/user/repo//user/repo/attachments`. - Instead of fixing this on the backend (and maybe break some existing links), teach the quoted reply about this context and remove it from the image source before generating the markdown. Reported-by: mrwusel (via Matrix) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8565 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Beowulf Co-authored-by: Gusted Co-committed-by: Gusted --- .../repo/issue/view_content/context_menu.tmpl | 2 +- tests/e2e/fixtures/comment.yml | 4 +- tests/e2e/issue-comment.test.e2e.ts | 6 ++- web_src/js/features/repo-legacy.js | 53 +++++++++++-------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/templates/repo/issue/view_content/context_menu.tmpl b/templates/repo/issue/view_content/context_menu.tmpl index 6f43f768b3..8cd4a056f0 100644 --- a/templates/repo/issue/view_content/context_menu.tmpl +++ b/templates/repo/issue/view_content/context_menu.tmpl @@ -11,7 +11,7 @@ {{end}}
{{ctx.Locale.Tr "repo.issues.context.copy_link"}}
{{if and .ctxData.IsSigned (not .ctxData.Repository.IsArchived)}} -
{{ctx.Locale.Tr "repo.issues.context.quote_reply"}}
+
{{ctx.Locale.Tr "repo.issues.context.quote_reply"}}
{{if not .ctxData.UnitIssuesGlobalDisabled}}
{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}
{{end}} diff --git a/tests/e2e/fixtures/comment.yml b/tests/e2e/fixtures/comment.yml index f3f8a2d8e8..ae7c850036 100644 --- a/tests/e2e/fixtures/comment.yml +++ b/tests/e2e/fixtures/comment.yml @@ -3,7 +3,7 @@ type: 0 # comment poster_id: 2 issue_id: 1 # in repo_id 1 - content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:" + content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:\n![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n\nsomething something" created_unix: 946684811 updated_unix: 946684811 content_version: 1 @@ -13,7 +13,7 @@ type: 21 # code comment poster_id: 2 issue_id: 19 - content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:" + content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:\n![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n\nsomething something" review_id: 1001 line: 1 tree_path: "test1.txt" diff --git a/tests/e2e/issue-comment.test.e2e.ts b/tests/e2e/issue-comment.test.e2e.ts index d2dbafafd0..2017e4563e 100644 --- a/tests/e2e/issue-comment.test.e2e.ts +++ b/tests/e2e/issue-comment.test.e2e.ts @@ -123,7 +123,8 @@ test('Quote reply', async ({page}, workerInfo) => { "> alert('evil')\n" + '> ```\n' + '> \n' + - '> :+1: :100:\n\n'); + '> :+1: :100: [![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)](/user2/repo1/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n' + + '> something something\n\n'); await editorTextarea.fill(''); @@ -197,7 +198,8 @@ test('Pull quote reply', async ({page}, workerInfo) => { "> alert('evil')\n" + '> ```\n' + '> \n' + - '> :+1: :100:\n\n'); + '> :+1: :100: [![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)](/user2/commitsonpr/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n' + + '> something something\n\n'); await editorTextarea.fill(''); }); diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index e6af4cbf04..66ee945d02 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -531,6 +531,13 @@ const filters = { } return el; }, + IMG(el, context) { + const src = el.getAttribute('src'); + if (src?.startsWith(context)) { + el.src = src.slice(context.length); + } + return el; + }, }; function hasContent(node) { @@ -538,32 +545,34 @@ function hasContent(node) { } // This code matches that of what is done by @github/quote-selection -function preprocessFragment(fragment) { - const nodeIterator = document.createNodeIterator(fragment, NodeFilter.SHOW_ELEMENT, { - acceptNode(node) { - if (node.nodeName in filters && hasContent(node)) { - return NodeFilter.FILTER_ACCEPT; +function preprocessFragment(context) { + return function(fragment) { + const nodeIterator = document.createNodeIterator(fragment, NodeFilter.SHOW_ELEMENT, { + acceptNode(node) { + if (node.nodeName in filters && hasContent(node)) { + return NodeFilter.FILTER_ACCEPT; + } + + return NodeFilter.FILTER_SKIP; + }, + }); + const results = []; + let node = nodeIterator.nextNode(); + + while (node) { + if (node instanceof HTMLElement) { + results.push(node); } - - return NodeFilter.FILTER_SKIP; - }, - }); - const results = []; - let node = nodeIterator.nextNode(); - - while (node) { - if (node instanceof HTMLElement) { - results.push(node); + node = nodeIterator.nextNode(); } - node = nodeIterator.nextNode(); - } // process deepest matches first - results.reverse(); + results.reverse(); - for (const el of results) { - el.replaceWith(filters[el.nodeName](el)); - } + for (const el of results) { + el.replaceWith(filters[el.nodeName](el, context)); + } + }; } function initRepoIssueCommentEdit() { @@ -573,7 +582,7 @@ function initRepoIssueCommentEdit() { // Quote reply $(document).on('click', '.quote-reply', async (event) => { event.preventDefault(); - const quote = new MarkdownQuote('', preprocessFragment); + const quote = new MarkdownQuote('', preprocessFragment(event.target.getAttribute('data-context'))); let editorTextArea; if (event.target.classList.contains('quote-reply-diff')) { From 1b217198978746a5e5d625cb6aaab43af1702930 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sat, 19 Jul 2025 15:49:48 +0200 Subject: [PATCH 140/297] fix(ui): remove unused rule with unused class .tight (#8573) There's no usage of `tight` across the codebase. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8573 Reviewed-by: Gusted --- web_src/css/base.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/web_src/css/base.css b/web_src/css/base.css index cbb21951b4..af23ecf2c2 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -546,11 +546,6 @@ a.label, background: var(--color-active); } -.ui.secondary.menu.tight .item { - padding-left: 0.85714286em; - padding-right: 0.85714286em; -} - /* remove the menu clearfix so that it won't add undesired gaps when using "gap" */ .ui.menu::after { content: normal; From a5f7acdd2e83f4e62369c71009483f9f71a0c564 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 19 Jul 2025 16:34:21 +0200 Subject: [PATCH 141/297] fix: allow for tracked time to be removed again (#8575) - `getElementById` requires a id to be passed and not a query selector, change it to `querySelector`. - Regression of forgejo/forgejo#7408 - Resolves forgejo/forgejo#8571 - Add E2E tests for adding manual tracked time and removing it. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8575 Reviewed-by: Otto Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- tests/e2e/issue-timetracking.test.e2e.ts | 48 ++++++++++++++++++++++++ web_src/js/features/repo-issue.js | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/issue-timetracking.test.e2e.ts diff --git a/tests/e2e/issue-timetracking.test.e2e.ts b/tests/e2e/issue-timetracking.test.e2e.ts new file mode 100644 index 0000000000..901cbe793f --- /dev/null +++ b/tests/e2e/issue-timetracking.test.e2e.ts @@ -0,0 +1,48 @@ +// @watch start +// web_src/js/features/comp/** +// web_src/js/features/repo-** +// templates/repo/issue/view_content/* +// @watch end + +import {expect} from '@playwright/test'; +import {test, save_visual} from './utils_e2e.ts'; + +test.use({user: 'user2'}); + +test('Issue timetracking', async ({page}) => { + await page.goto('/user2/repo1/issues/new'); + + // Create temporary issue. + await page.getByPlaceholder('Title').fill('Just a title'); + await page.getByPlaceholder('Leave a comment').fill('Hi, have you considered using a rotating fish as logo?'); + await page.getByRole('button', {name: 'Create issue'}).click(); + await expect(page).toHaveURL(/\/user2\/repo1\/issues\/\d+$/); + + // Manually add time to the time tracker. + await page.getByRole('button', {name: 'Add time'}).click(); + await page.getByPlaceholder('Hours').fill('5'); + await page.getByPlaceholder('Minutes').fill('32'); + await page.getByRole('button', {name: 'Add time', exact: true}).click(); + + // Verify this was added in the timeline. + await expect(page.locator('.ui.timeline')).toContainText('added spent time'); + await expect(page.locator('.ui.timeline')).toContainText('5 hours 32 minutes'); + + // Verify it is shown in the issue sidebar + await expect(page.locator('.issue-content-right .comments')).toContainText('Total time spent: 5 hours 32 minutes'); + + await save_visual(page); + + // Delete the added time. + await page.getByRole('button', {name: 'Delete this time log'}).click(); + await page.getByRole('button', {name: 'Yes'}).click(); + + // Verify this was removed in the timeline. + await expect(page.locator('.ui.timeline')).toContainText('deleted spent time'); + await expect(page.locator('.ui.timeline')).toContainText('- 5 hours 32 minutes'); + + // Delete the issue. + await page.getByRole('button', {name: 'Delete'}).click(); + await page.getByRole('button', {name: 'Yes'}).click(); + await expect(page).toHaveURL('/user2/repo1/issues'); +}); diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index d678d9195b..84b8e42a61 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -57,7 +57,7 @@ export function initRepoIssueTimeTracking() { $(sel).modal({ duration: 200, onApprove() { - document.getElementById(`${sel} form`).requestSubmit(); + document.querySelector(`${sel} form`).requestSubmit(); }, }).modal('show'); }); From a16204d99f7498f7a85a9fd7bcdc8f7986a5ca67 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 19 Jul 2025 23:01:40 +0200 Subject: [PATCH 142/297] Update https://data.forgejo.org/actions/setup-forgejo action to v3 (forgejo) (#8562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [https://data.forgejo.org/actions/setup-forgejo](https://code.forgejo.org/actions/setup-forgejo) | action | major | `v2.0.4` -> `v3.0.1` | --- ### Release Notes
actions/setup-forgejo (https://data.forgejo.org/actions/setup-forgejo) ### [`v3.0.1`](https://code.forgejo.org/actions/setup-forgejo/compare/v3.0.0...v3.0.1) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v3.0.0...v3.0.1) ### [`v3.0.0`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.13...v3.0.0) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.13...v3.0.0) ### [`v2.0.13`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.12...v2.0.13) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.12...v2.0.13) ### [`v2.0.12`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.11...v2.0.12) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.11...v2.0.12) ### [`v2.0.11`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.10...v2.0.11) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.10...v2.0.11) ### [`v2.0.10`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.9...v2.0.10) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.9...v2.0.10) ### [`v2.0.9`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.8...v2.0.9) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.8...v2.0.9) ### [`v2.0.8`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.7...v2.0.8) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.7...v2.0.8) ### [`v2.0.7`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.6...v2.0.7) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.6...v2.0.7) ### [`v2.0.6`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.5...v2.0.6) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.5...v2.0.6) ### [`v2.0.5`](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.4...v2.0.5) [Compare Source](https://code.forgejo.org/actions/setup-forgejo/compare/v2.0.4...v2.0.5)
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8562 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/build-release-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index 1af6d567dd..a5f9991d45 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -28,7 +28,7 @@ jobs: - uses: https://data.forgejo.org/actions/checkout@v4 - id: forgejo - uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4 + uses: https://data.forgejo.org/actions/setup-forgejo@v3.0.1 with: user: root password: admin1234 From 4c34615e4eeb2e46e021c87d5f93bd7d6ce6de29 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 19 Jul 2025 23:08:04 +0200 Subject: [PATCH 143/297] Update dependency forgejo/release-notes-assistant to v1.3.1 (forgejo) (#8568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [forgejo/release-notes-assistant](https://code.forgejo.org/forgejo/release-notes-assistant) | minor | `v1.2.5` -> `v1.3.1` | | [forgejo/release-notes-assistant](https://code.forgejo.org/forgejo/release-notes-assistant) | patch | `v1.3.0` -> `v1.3.1` | --- ### Release Notes
forgejo/release-notes-assistant (forgejo/release-notes-assistant) ### [`v1.3.1`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.1) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.0...v1.3.1) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/91): fix: pull requests need sort=recentupdate not just in debug output ### [`v1.3.0`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.0) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.2.5...v1.3.0) - features including breaking changes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/86): feat!: remove maximum-age option - features - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/89): feat: get pull requests modified since the last cache update - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/88): feat: add last update time to the pull request cache - other - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/90): chore: improve verbosity when obtaining pull requests - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/87): chore(cleanup): pull request handler no longer needs a bool return value - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/85): Update module golang.org/x/tools/cmd/deadcode to v0.35.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/84): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.2.2 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/83): chore: s/GITHUB\_*/FORGEJO\_*/ & s/github./forge./ - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/82): Update dependency go to v1.24.5 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/81): Update module mvdan.cc/sh/v3/cmd/shfmt to v3.12.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/80): Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.2.1 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/79): Update module github.com/urfave/cli/v2 to v2.27.7 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/78): Update module golang.org/x/tools/cmd/deadcode to v0.34.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/77): Update dependency go to v1.24.4 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/76): Update module mvdan.cc/sh/v3/cmd/shfmt to v3.11.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/75): Update module mvdan.cc/gofumpt to v0.8.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/74): Update module golang.org/x/tools/cmd/deadcode to v0.33.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/73): Update module github.com/golangci/misspell/cmd/misspell to v0.7.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/72): Update dependency go to v1.23 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/70): Update module github.com/stretchr/testify to v1.10.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/69): Update module github.com/go-resty/resty/v2 to v2.16.5 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/68): Update https://code.forgejo.org/actions/forgejo-release action to v2.6.0 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/67): Update module github.com/urfave/cli/v2 to v2.27.6 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/66): ci: use `data.forgejo.org` - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/65): Update Node.js to v22 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/64): Update https://code.forgejo.org/actions/checkout action to v4 - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/61): Configure Renovate
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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 these updates again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8568 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/release-notes-assistant-milestones.yml | 2 +- .forgejo/workflows/release-notes-assistant.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index 0b8a416a02..2c278c7eb5 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -6,7 +6,7 @@ on: env: RNA_WORKDIR: /srv/rna - RNA_VERSION: v1.3.0 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index cdcd2e6fe4..b5eb9cd2b0 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,7 +8,7 @@ on: - labeled env: - RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: From b83a55492117ed108e9421b92d94133b9b26d1f1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 20 Jul 2025 02:48:29 +0200 Subject: [PATCH 144/297] Update module github.com/niklasfasching/go-org to v1.9.1 (forgejo) (#8579) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8579 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9e0f47f968..3dab99daf8 100644 --- a/go.mod +++ b/go.mod @@ -78,7 +78,7 @@ require ( github.com/minio/minio-go/v7 v7.0.94 github.com/msteinert/pam/v2 v2.1.0 github.com/nektos/act v0.2.52 - github.com/niklasfasching/go-org v1.9.0 + github.com/niklasfasching/go-org v1.9.1 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 diff --git a/go.sum b/go.sum index db2a186d2c..243dd6154d 100644 --- a/go.sum +++ b/go.sum @@ -426,8 +426,8 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/niklasfasching/go-org v1.9.0 h1:4/Sr68Qx06hjC9MVDB/4etGP67JionLHGscLMOClpnk= -github.com/niklasfasching/go-org v1.9.0/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48= +github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0= +github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= From 95e8bbd5f0434c5c377666524a9a496ac06f783f Mon Sep 17 00:00:00 2001 From: floss4good Date: Sun, 20 Jul 2025 23:52:58 +0200 Subject: [PATCH 145/297] fix: make sure to use unaltered fields when saving a shadow copy for updated profiles or comments (#8533) Follow-up of !6977 ### Manual testing - User **S** creates an organization **O** and posts a comment **C** (on a random issue); - User **R** report as abuse the comment **C**, the organization **O** as well as the user **S**; - User **S** changes the content of comment **C** and the description of organization **O** as well as the description of their own profile; - Check (within DB) that shadow copies are being created (and linked to corresponding abuse reports) for comment **C**, organization **O** and user **S** and the content is the one from the moment when the reports were submitted (therefore before the updates made by **S**). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8533 Reviewed-by: Gusted Co-authored-by: floss4good Co-committed-by: floss4good --- .../ModerationFeatures/abuse_report.yml | 21 +++++++ .../fixtures/ModerationFeatures/comment.yml | 7 +++ models/fixtures/ModerationFeatures/user.yml | 22 ++++++++ models/issues/comment.go | 4 +- models/issues/moderation.go | 8 ++- models/user/moderation.go | 19 +++++-- models/user/user.go | 4 +- services/issue/comments_test.go | 39 +++++++++++++ services/user/delete.go | 2 +- services/user/email.go | 2 +- services/user/user_test.go | 56 +++++++++++++++++++ 11 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 models/fixtures/ModerationFeatures/abuse_report.yml create mode 100644 models/fixtures/ModerationFeatures/comment.yml create mode 100644 models/fixtures/ModerationFeatures/user.yml diff --git a/models/fixtures/ModerationFeatures/abuse_report.yml b/models/fixtures/ModerationFeatures/abuse_report.yml new file mode 100644 index 0000000000..f2e371ee35 --- /dev/null +++ b/models/fixtures/ModerationFeatures/abuse_report.yml @@ -0,0 +1,21 @@ +- + id: 1 + status: 1 + reporter_id: 2 # @user2 + content_type: 4 # Comment + content_id: 18 # user2/repo2/issues/2#issuecomment-18 + category: 2 # Spam + remarks: The comment I'm reporting is pure SPAM. + shadow_copy_id: null + created_unix: 1752697980 # 2025-07-16 20:33:00 + +- + id: 2 + status: 1 # Open + reporter_id: 2 # @user2 + content_type: 1 # User (users or organizations) + content_id: 1002 # @alexsmith + category: 2 # Spam + remarks: This user just posted a spammy comment on my issue. + shadow_copy_id: null + created_unix: 1752698010 # 2025-07-16 20:33:30 diff --git a/models/fixtures/ModerationFeatures/comment.yml b/models/fixtures/ModerationFeatures/comment.yml new file mode 100644 index 0000000000..a4d41ad997 --- /dev/null +++ b/models/fixtures/ModerationFeatures/comment.yml @@ -0,0 +1,7 @@ +- # This is a spam comment (abusive content), created for testing moderation functionalities. + id: 18 + type: 0 # Standard comment + poster_id: 1002 # @alexsmith + issue_id: 7 # user2/repo2#2 + content: If anyone needs help for promoting their business online using SEO, just contact me (check my profile page). + created_unix: 1752697860 # 2025-07-16 20:31:00 diff --git a/models/fixtures/ModerationFeatures/user.yml b/models/fixtures/ModerationFeatures/user.yml new file mode 100644 index 0000000000..662c61a3e9 --- /dev/null +++ b/models/fixtures/ModerationFeatures/user.yml @@ -0,0 +1,22 @@ +- # This user is a spammer and will create abusive content (for testing moderation functionalities). + id: 1002 + lower_name: alexsmith + name: alexsmith + full_name: Alex Smith + email: alexsmith@example.org + keep_email_private: false + passwd: passwdSalt:password + passwd_hash_algo: dummy + type: 0 + location: '@master@seo.net' + website: http://promote-your-business.biz + pronouns: SEO + salt: passwdSalt + description: I can help you promote your business online using SEO. + created_unix: 1752697800 # 2025-07-16 20:30:00 + is_active: true + is_admin: false + is_restricted: false + avatar: avatar-hash-1002 + avatar_email: alexsmith@example.org + use_custom_avatar: false diff --git a/models/issues/comment.go b/models/issues/comment.go index a81221caf4..523a6ba9b9 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -1156,7 +1156,7 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us defer committer.Close() // If the comment was reported as abusive, a shadow copy should be created before first update. - if err := IfNeededCreateShadowCopyForComment(ctx, c); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, c, true); err != nil { return err } @@ -1197,7 +1197,7 @@ func DeleteComment(ctx context.Context, comment *Comment) error { e := db.GetEngine(ctx) // If the comment was reported as abusive, a shadow copy should be created before deletion. - if err := IfNeededCreateShadowCopyForComment(ctx, comment); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, comment, false); err != nil { return err } diff --git a/models/issues/moderation.go b/models/issues/moderation.go index 635d295db0..921f770d4d 100644 --- a/models/issues/moderation.go +++ b/models/issues/moderation.go @@ -87,13 +87,19 @@ func IfNeededCreateShadowCopyForIssue(ctx context.Context, issue *Issue) error { // IfNeededCreateShadowCopyForComment checks if for the given comment there are any reports of abusive content submitted // and if found a shadow copy of relevant comment fields will be stored into DB and linked to the above report(s). // This function should be called before a comment is deleted or updated. -func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) error { +func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment, forUpdates bool) error { shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeComment, comment.ID) if err != nil { return err } if shadowCopyNeeded { + if forUpdates { + // get the unaltered comment fields (for updates the provided variable is already altered but not yet saved) + if comment, err = GetCommentByID(ctx, comment.ID); err != nil { + return err + } + } commentData := newCommentData(comment) content, err := json.Marshal(commentData) if err != nil { diff --git a/models/user/moderation.go b/models/user/moderation.go index afda497f02..f9c16a17b3 100644 --- a/models/user/moderation.go +++ b/models/user/moderation.go @@ -73,16 +73,20 @@ var userDataColumnNames = sync.OnceValue(func() []string { // and if found a shadow copy of relevant user fields will be stored into DB and linked to the above report(s). // This function should be called before a user is deleted or updated. // +// In case the User object was already altered before calling this method, just provide the userID and +// nil for unalteredUser; when it is decided that a shadow copy should be created and unalteredUser is nil, +// the user will be retrieved from DB based on the provided userID. +// // For deletions alteredCols argument must be omitted. // // In case of updates it will first checks whether any of the columns being updated (alteredCols argument) // is relevant for moderation purposes (i.e. included in the UserData struct). -func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCols ...string) error { +func IfNeededCreateShadowCopyForUser(ctx context.Context, userID int64, unalteredUser *User, alteredCols ...string) error { // TODO: this can be triggered quite often (e.g. by routers/web/repo/middlewares.go SetDiffViewStyle()) shouldCheckIfNeeded := len(alteredCols) == 0 // no columns being updated, therefore a deletion if !shouldCheckIfNeeded { - // for updates we need to go further only if certain column are being changed + // for updates we need to go further only if certain columns are being changed for _, colName := range userDataColumnNames() { if shouldCheckIfNeeded = slices.Contains(alteredCols, colName); shouldCheckIfNeeded { break @@ -94,18 +98,23 @@ func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCol return nil } - shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, user.ID) + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, userID) if err != nil { return err } if shadowCopyNeeded { - userData := newUserData(user) + if unalteredUser == nil { + if unalteredUser, err = GetUserByID(ctx, userID); err != nil { + return err + } + } + userData := newUserData(unalteredUser) content, err := json.Marshal(userData) if err != nil { return err } - return moderation.CreateShadowCopyForUser(ctx, user.ID, string(content)) + return moderation.CreateShadowCopyForUser(ctx, userID, string(content)) } return nil diff --git a/models/user/user.go b/models/user/user.go index b124572bb6..6b54776adf 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -927,7 +927,9 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { // If the user was reported as abusive and any of the columns being updated is relevant // for moderation purposes a shadow copy should be created before first update. - if err := IfNeededCreateShadowCopyForUser(ctx, u, cols...); err != nil { + // Since u is already altered at this point we are sending nil instead as an argument + // so that the unaltered version will be retrieved from DB. + if err := IfNeededCreateShadowCopyForUser(ctx, u.ID, nil, cols...); err != nil { return err } diff --git a/services/issue/comments_test.go b/services/issue/comments_test.go index 8fa410c0f0..fcf06d9ec8 100644 --- a/services/issue/comments_test.go +++ b/services/issue/comments_test.go @@ -8,9 +8,11 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" + "forgejo.org/models/moderation" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" webhook_model "forgejo.org/models/webhook" + "forgejo.org/modules/json" "forgejo.org/modules/setting" "forgejo.org/modules/test" issue_service "forgejo.org/services/issue" @@ -148,3 +150,40 @@ func TestUpdateComment(t *testing.T) { unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID}) }) } + +func TestCreateShadowCopyOnCommentUpdate(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")() + require.NoError(t, unittest.PrepareTestDatabase()) + + userAlexSmithID := int64(1002) + spamCommentID := int64(18) // posted by @alexsmith + abuseReportID := int64(1) // submitted for above comment + newCommentContent := "If anyone needs help, just contact me." + + // Retrieve the abusive user (@alexsmith), their SPAM comment and the abuse report already created for this comment. + poster := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID}) + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: spamCommentID, PosterID: poster.ID}) + report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ + ID: abuseReportID, + ContentType: moderation.ReportedContentTypeComment, + ContentID: comment.ID, + }) + // The report should not already have a shadow copy linked. + assert.False(t, report.ShadowCopyID.Valid) + + // The abusive user is updating their comment. + oldContent := comment.Content + comment.Content = newCommentContent + require.NoError(t, issue_service.UpdateComment(t.Context(), comment, 0, poster, oldContent)) + + // Reload the report. + report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID}) + // A shadow copy should have been created and linked to our report. + assert.True(t, report.ShadowCopyID.Valid) + // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values. + shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64}) + shadowCopyCommentData := new(issues_model.CommentData) + require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyCommentData)) + // Check to see if the initial content of the comment was stored within the shadow copy. + assert.Equal(t, oldContent, shadowCopyCommentData.Content) +} diff --git a/services/user/delete.go b/services/user/delete.go index 9caa24c373..bed7abde07 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -218,7 +218,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) // ***** END: ExternalLoginUser ***** // If the user was reported as abusive, a shadow copy should be created before deletion. - if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u); err != nil { + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u); err != nil { return err } diff --git a/services/user/email.go b/services/user/email.go index 7a01fa77b3..36a1145aec 100644 --- a/services/user/email.go +++ b/services/user/email.go @@ -205,7 +205,7 @@ func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimary oldPrimaryEmail := u.Email // If the user was reported as abusive, a shadow copy should be created before first update (of certain columns). - if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u, "email"); err != nil { + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u, "email"); err != nil { return err } diff --git a/services/user/user_test.go b/services/user/user_test.go index 4678d3bc9a..36f2776ad8 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -15,10 +15,13 @@ import ( asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/auth" "forgejo.org/models/db" + "forgejo.org/models/moderation" "forgejo.org/models/organization" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/test" "forgejo.org/modules/timeutil" @@ -277,3 +280,56 @@ func TestDeleteInactiveUsers(t *testing.T) { unittest.AssertExistsIf(t, true, newUser) unittest.AssertExistsIf(t, true, newEmail) } + +func TestCreateShadowCopyOnUserUpdate(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")() + require.NoError(t, unittest.PrepareTestDatabase()) + + userAlexSmithID := int64(1002) + abuseReportID := int64(2) // submitted for @alexsmith + newDummyValue := "[REDACTED]" // used for updating profile text fields + + // Retrieve the abusive user (@alexsmith) and the abuse report already created for this user. + abuser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID}) + report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ + ID: abuseReportID, + ContentType: moderation.ReportedContentTypeUser, + ContentID: abuser.ID, + }) + // The report should not already have a shadow copy linked. + assert.False(t, report.ShadowCopyID.Valid) + + // Keep a copy of old field values before updating them. + oldUserData := user_model.UserData{ + FullName: abuser.FullName, + Location: abuser.Location, + Website: abuser.Website, + Pronouns: abuser.Pronouns, + Description: abuser.Description, + } + + // The abusive user is updating their profile. + opts := &UpdateOptions{ + FullName: optional.Some(newDummyValue), + Location: optional.Some(newDummyValue), + Website: optional.Some(newDummyValue), + Pronouns: optional.Some(newDummyValue), + Description: optional.Some(newDummyValue), + } + require.NoError(t, UpdateUser(t.Context(), abuser, opts)) + + // Reload the report. + report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID}) + // A shadow copy should have been created and linked to our report. + assert.True(t, report.ShadowCopyID.Valid) + // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values. + shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64}) + shadowCopyUserData := new(user_model.UserData) + require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyUserData)) + // Check to see if the initial field values of the user were stored within the shadow copy. + assert.Equal(t, oldUserData.FullName, shadowCopyUserData.FullName) + assert.Equal(t, oldUserData.Location, shadowCopyUserData.Location) + assert.Equal(t, oldUserData.Website, shadowCopyUserData.Website) + assert.Equal(t, oldUserData.Pronouns, shadowCopyUserData.Pronouns) + assert.Equal(t, oldUserData.Description, shadowCopyUserData.Description) +} From 9b470d27096a912ca09a3b57d322231abf7ad355 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Mon, 21 Jul 2025 09:20:24 +0200 Subject: [PATCH 146/297] feat(ui): use simplified visibility label in dashboard orgs list (#8582) Followup to https://codeberg.org/forgejo/forgejo/pulls/6201 Label height making entry height inconsistent isn't a concern because this change makes them 2px shorter. _And they are already inconsistent._ Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8582 Reviewed-by: Gusted Reviewed-by: Otto Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- web_src/js/components/DashboardRepoList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index 35f1082a93..e8c1b76a7f 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -457,7 +457,7 @@ export default sfc; // activate the IDE's Vue plugin
{{ org.name }}
- + {{ org.org_visibility === 'limited' ? textOrgVisibilityLimited: textOrgVisibilityPrivate }}
From 6ee4dd753a717e9acd145b8b846798f820755c5d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 21 Jul 2025 09:32:04 +0200 Subject: [PATCH 147/297] Update renovate to v41.40.0 (forgejo) (#8585) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index c127dd2eba..1be3cc0b17 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:41.32.1 + image: data.forgejo.org/renovate/renovate:41.40.0 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 213d7f4b0b..ea8ba4b192 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.35.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.32.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@41.40.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 8fc7295869e911fa83b7efae64efd78b510c441a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 21 Jul 2025 09:40:20 +0200 Subject: [PATCH 148/297] Update https://data.forgejo.org/actions/cascading-pr action to v2.2.1 (forgejo) (#8586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [https://data.forgejo.org/actions/cascading-pr](https://code.forgejo.org/actions/cascading-pr) | action | patch | `v2.2.0` -> `v2.2.1` | --- ### Release Notes
actions/cascading-pr (https://data.forgejo.org/actions/cascading-pr) ### [`v2.2.1`](https://code.forgejo.org/actions/cascading-pr/compare/v2.2.0...v2.2.1) [Compare Source](https://code.forgejo.org/actions/cascading-pr/compare/v2.2.0...v2.2.1)
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8586 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/build-release.yml | 2 +- .forgejo/workflows/cascade-setup-end-to-end.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index 3ab63b0589..944fd68fb7 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -201,7 +201,7 @@ jobs: - name: end-to-end tests if: ${{ secrets.TOKEN != '' && vars.ROLE == 'forgejo-integration' && vars.SKIP_END_TO_END != 'true' }} - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 + uses: https://data.forgejo.org/actions/cascading-pr@v2.2.1 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index 7c8c56de13..9d613189a4 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -41,7 +41,7 @@ jobs: with: fetch-depth: '0' show-progress: 'false' - - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 + - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.1 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} From 199d9cf65cbcaee8eb17afdbc0d45212e1d4b309 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 21 Jul 2025 12:32:49 +0200 Subject: [PATCH 149/297] Migrate renovate config (#8588) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8588 Reviewed-by: Michael Kriese Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- renovate.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renovate.json b/renovate.json index 135df58c07..e0d7bd5456 100644 --- a/renovate.json +++ b/renovate.json @@ -6,7 +6,7 @@ "docker:pinDigests", "helpers:pinGitHubActionDigests" ], - "baseBranches": [ + "baseBranchPatterns": [ "$default", "/^v11\\.\\d+/forgejo$/", "/^v12\\.\\d+/forgejo$/" From 8e4f50a909b74157cfc4dc81144ecd68ee5a224f Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 21 Jul 2025 22:33:17 +0200 Subject: [PATCH 150/297] feat: remove fomantic's tab module (#8587) In similar vein of forgejo/forgejo#7416 - Fomantic's tab module is responsible for showing the right content when a tab is clicked upon. Most notably the Write/Preview tabs on the comment editor. - Remove it and replace the javascript with our own function that is able to provide everything Forgejo needs. - Replace the CSS with our own bare minimum CSS. - No functionality or visual is affected by this replacement. - E2E test added. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8587 Reviewed-by: Otto Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- tests/e2e/image-diff.test.e2e.ts | 66 ++ tests/e2e/markdown-editor.test.e2e.ts | 2 + web_src/css/index.css | 1 + web_src/css/modules/tab.css | 7 + web_src/fomantic/build/semantic.css | 82 -- web_src/fomantic/build/semantic.js | 1002 ----------------- web_src/fomantic/semantic.json | 3 +- web_src/js/features/common-global.js | 5 +- .../js/features/comp/ComboMarkdownEditor.js | 6 +- web_src/js/features/repo-editor.js | 3 +- web_src/js/modules/fomantic.js | 3 - web_src/js/modules/tab.ts | 36 + 12 files changed, 123 insertions(+), 1093 deletions(-) create mode 100644 tests/e2e/image-diff.test.e2e.ts create mode 100644 web_src/css/modules/tab.css create mode 100644 web_src/js/modules/tab.ts diff --git a/tests/e2e/image-diff.test.e2e.ts b/tests/e2e/image-diff.test.e2e.ts new file mode 100644 index 0000000000..f7d4f7bd69 --- /dev/null +++ b/tests/e2e/image-diff.test.e2e.ts @@ -0,0 +1,66 @@ +// @watch start +// templates/repo/diff/** +// web_src/css/features/imagediff.css +// web_src/css/modules/tab.css +// web_src/js/modules/tab.ts +// @watch end + +import {expect} from '@playwright/test'; +import {save_visual, test, dynamic_id} from './utils_e2e.ts'; + +test.use({user: 'user2'}); + +test('Repository image diff', async ({page}) => { + // Generate a temporary SVG and edit it. + let response = await page.goto('/user2/repo1/_new/master', {waitUntil: 'domcontentloaded'}); + expect(response?.status()).toBe(200); + + const filename = `${dynamic_id()}.svg`; + + await page.getByPlaceholder('Name your file…').fill(filename); + await page.locator('.monaco-editor').click(); + await page.keyboard.type('\n'); + + await page.locator('.quick-pull-choice input[value="direct"]').click(); + await page.getByRole('button', {name: 'Commit changes'}).click(); + + response = await page.goto(`/user2/repo1/_edit/master/${filename}`, {waitUntil: 'domcontentloaded'}); + expect(response?.status()).toBe(200); + + await page.locator('.monaco-editor').click(); + await page.keyboard.press('Meta+KeyA'); + await page.keyboard.type('\n'); + + await page.locator('.quick-pull-choice input[value="direct"]').click(); + await page.getByRole('button', {name: 'Commit changes'}).click(); + + // Go to the commit page, where a image diff is shown. + await page.locator('.commit-summary a.default-link').click(); + + // Exhaustively test tabs works as expected + await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).toContainClass('active'); + await expect(page.locator('.item[data-tab="diff-swipe-1"]')).not.toContainClass('active'); + await expect(page.locator('.item[data-tab="diff-overlay-1"]')).not.toContainClass('active'); + await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeVisible(); + await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeHidden(); + await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeHidden(); + await save_visual(page); + + await page.getByText('Swipe').click(); + await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).not.toContainClass('active'); + await expect(page.locator('.item[data-tab="diff-swipe-1"]')).toContainClass('active'); + await expect(page.locator('.item[data-tab="diff-overlay-1"]')).not.toContainClass('active'); + await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeHidden(); + await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeVisible(); + await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeHidden(); + await save_visual(page); + + await page.getByText('Overlay').click(); + await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).not.toContainClass('active'); + await expect(page.locator('.item[data-tab="diff-swipe-1"]')).not.toContainClass('active'); + await expect(page.locator('.item[data-tab="diff-overlay-1"]')).toContainClass('active'); + await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeHidden(); + await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeHidden(); + await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeVisible(); + await save_visual(page); +}); diff --git a/tests/e2e/markdown-editor.test.e2e.ts b/tests/e2e/markdown-editor.test.e2e.ts index 7983907343..d0ab88fe26 100644 --- a/tests/e2e/markdown-editor.test.e2e.ts +++ b/tests/e2e/markdown-editor.test.e2e.ts @@ -1,4 +1,6 @@ // @watch start +// web_src/js/modules/tab.ts +// web_src/css/modules/tab.css // web_src/js/features/comp/ComboMarkdownEditor.js // web_src/css/editor/combomarkdowneditor.css // templates/shared/combomarkdowneditor.tmpl diff --git a/web_src/css/index.css b/web_src/css/index.css index e7e5dda2d5..d86a784bcc 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -12,6 +12,7 @@ @import "./modules/segment.css"; @import "./modules/grid.css"; @import "./modules/message.css"; +@import "./modules/tab.css"; @import "./modules/table.css"; @import "./modules/card.css"; @import "./modules/checkbox.css"; diff --git a/web_src/css/modules/tab.css b/web_src/css/modules/tab.css new file mode 100644 index 0000000000..63c83179b2 --- /dev/null +++ b/web_src/css/modules/tab.css @@ -0,0 +1,7 @@ +.ui.tab { + display: none; +} + +.ui.tab.active { + display: block; +} diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index 6e9958faa8..bd1777db36 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -9277,86 +9277,4 @@ Floated Menu / Item /******************************* Site Overrides -*******************************/ -/*! - * # Fomantic-UI - Tab - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -/******************************* - UI Tabs -*******************************/ - -.ui.tab { - display: none; -} - -/******************************* - States -*******************************/ - -/*-------------------- - Active ----------------------*/ - -.ui.tab.active, -.ui.tab.open { - display: block; -} - -/*-------------------- - Loading - ---------------------*/ - -.ui.tab.loading { - position: relative; - overflow: hidden; - display: block; - min-height: 250px; -} - -.ui.tab.loading * { - position: relative !important; - left: -10000px !important; -} - -.ui.tab.loading:before, -.ui.tab.loading.segment:before { - position: absolute; - content: ''; - top: 50%; - left: 50%; - margin: -1.25em 0 0 -1.25em; - width: 2.5em; - height: 2.5em; - border-radius: 500rem; - border: 0.2em solid rgba(0, 0, 0, 0.1); -} - -.ui.tab.loading:after, -.ui.tab.loading.segment:after { - position: absolute; - content: ''; - top: 50%; - left: 50%; - margin: -1.25em 0 0 -1.25em; - width: 2.5em; - height: 2.5em; - animation: loader 0.6s infinite linear; - border: 0.2em solid #767676; - border-radius: 500rem; - box-shadow: 0 0 0 1px transparent; -} - -/******************************* - Tab Overrides -*******************************/ - -/******************************* - User Overrides *******************************/ \ No newline at end of file diff --git a/web_src/fomantic/build/semantic.js b/web_src/fomantic/build/semantic.js index 393d9d8142..80ddb88eda 100644 --- a/web_src/fomantic/build/semantic.js +++ b/web_src/fomantic/build/semantic.js @@ -10234,1005 +10234,3 @@ $.fn.search.settings = { }; })( jQuery, window, document ); - -/*! - * # Fomantic-UI - Tab - * http://github.com/fomantic/Fomantic-UI/ - * - * - * Released under the MIT license - * http://opensource.org/licenses/MIT - * - */ - -;(function ($, window, document, undefined) { - -'use strict'; - -$.isWindow = $.isWindow || function(obj) { - return obj != null && obj === obj.window; -}; -$.isFunction = $.isFunction || function(obj) { - return typeof obj === "function" && typeof obj.nodeType !== "number"; -}; - -window = (typeof window != 'undefined' && window.Math == Math) - ? window - : (typeof self != 'undefined' && self.Math == Math) - ? self - : Function('return this')() -; - -$.fn.tab = function(parameters) { - - var - // use window context if none specified - $allModules = $.isFunction(this) - ? $(window) - : $(this), - - moduleSelector = $allModules.selector || '', - time = new Date().getTime(), - performance = [], - - query = arguments[0], - methodInvoked = (typeof query == 'string'), - queryArguments = [].slice.call(arguments, 1), - - initializedHistory = false, - returnedValue - ; - - $allModules - .each(function() { - var - - settings = ( $.isPlainObject(parameters) ) - ? $.extend(true, {}, $.fn.tab.settings, parameters) - : $.extend({}, $.fn.tab.settings), - - className = settings.className, - metadata = settings.metadata, - selector = settings.selector, - error = settings.error, - regExp = settings.regExp, - - eventNamespace = '.' + settings.namespace, - moduleNamespace = 'module-' + settings.namespace, - - $module = $(this), - $context, - $tabs, - - cache = {}, - firstLoad = true, - recursionDepth = 0, - element = this, - instance = $module.data(moduleNamespace), - - activeTabPath, - parameterArray, - module, - - historyEvent - - ; - - module = { - - initialize: function() { - module.debug('Initializing tab menu item', $module); - module.fix.callbacks(); - module.determineTabs(); - - module.debug('Determining tabs', settings.context, $tabs); - // set up automatic routing - if(settings.auto) { - module.set.auto(); - } - module.bind.events(); - - if(settings.history && !initializedHistory) { - module.initializeHistory(); - initializedHistory = true; - } - - if(settings.autoTabActivation && instance === undefined && module.determine.activeTab() == null) { - module.debug('No active tab detected, setting first tab active', module.get.initialPath()); - module.changeTab(settings.autoTabActivation === true ? module.get.initialPath() : settings.autoTabActivation); - }; - - module.instantiate(); - }, - - instantiate: function () { - module.verbose('Storing instance of module', module); - instance = module; - $module - .data(moduleNamespace, module) - ; - }, - - destroy: function() { - module.debug('Destroying tabs', $module); - $module - .removeData(moduleNamespace) - .off(eventNamespace) - ; - }, - - bind: { - events: function() { - // if using $.tab don't add events - if( !$.isWindow( element ) ) { - module.debug('Attaching tab activation events to element', $module); - $module - .on('click' + eventNamespace, module.event.click) - ; - } - } - }, - - determineTabs: function() { - var - $reference - ; - - // determine tab context - if(settings.context === 'parent') { - if($module.closest(selector.ui).length > 0) { - $reference = $module.closest(selector.ui); - module.verbose('Using closest UI element as parent', $reference); - } - else { - $reference = $module; - } - $context = $reference.parent(); - module.verbose('Determined parent element for creating context', $context); - } - else if(settings.context) { - $context = $(settings.context); - module.verbose('Using selector for tab context', settings.context, $context); - } - else { - $context = $('body'); - } - // find tabs - if(settings.childrenOnly) { - $tabs = $context.children(selector.tabs); - module.debug('Searching tab context children for tabs', $context, $tabs); - } - else { - $tabs = $context.find(selector.tabs); - module.debug('Searching tab context for tabs', $context, $tabs); - } - }, - - fix: { - callbacks: function() { - if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) { - if(parameters.onTabLoad) { - parameters.onLoad = parameters.onTabLoad; - delete parameters.onTabLoad; - module.error(error.legacyLoad, parameters.onLoad); - } - if(parameters.onTabInit) { - parameters.onFirstLoad = parameters.onTabInit; - delete parameters.onTabInit; - module.error(error.legacyInit, parameters.onFirstLoad); - } - settings = $.extend(true, {}, $.fn.tab.settings, parameters); - } - } - }, - - initializeHistory: function() { - module.debug('Initializing page state'); - if( $.address === undefined ) { - module.error(error.state); - return false; - } - else { - if(settings.historyType == 'state') { - module.debug('Using HTML5 to manage state'); - if(settings.path !== false) { - $.address - .history(true) - .state(settings.path) - ; - } - else { - module.error(error.path); - return false; - } - } - $.address - .bind('change', module.event.history.change) - ; - } - }, - - event: { - click: function(event) { - var - tabPath = $(this).data(metadata.tab) - ; - if(tabPath !== undefined) { - if(settings.history) { - module.verbose('Updating page state', event); - $.address.value(tabPath); - } - else { - module.verbose('Changing tab', event); - module.changeTab(tabPath); - } - event.preventDefault(); - } - else { - module.debug('No tab specified'); - } - }, - history: { - change: function(event) { - var - tabPath = event.pathNames.join('/') || module.get.initialPath(), - pageTitle = settings.templates.determineTitle(tabPath) || false - ; - module.performance.display(); - module.debug('History change event', tabPath, event); - historyEvent = event; - if(tabPath !== undefined) { - module.changeTab(tabPath); - } - if(pageTitle) { - $.address.title(pageTitle); - } - } - } - }, - - refresh: function() { - if(activeTabPath) { - module.debug('Refreshing tab', activeTabPath); - module.changeTab(activeTabPath); - } - }, - - cache: { - - read: function(cacheKey) { - return (cacheKey !== undefined) - ? cache[cacheKey] - : false - ; - }, - add: function(cacheKey, content) { - cacheKey = cacheKey || activeTabPath; - module.debug('Adding cached content for', cacheKey); - cache[cacheKey] = content; - }, - remove: function(cacheKey) { - cacheKey = cacheKey || activeTabPath; - module.debug('Removing cached content for', cacheKey); - delete cache[cacheKey]; - } - }, - - escape: { - string: function(text) { - text = String(text); - return text.replace(regExp.escape, '\\$&'); - } - }, - - set: { - auto: function() { - var - url = (typeof settings.path == 'string') - ? settings.path.replace(/\/$/, '') + '/{$tab}' - : '/{$tab}' - ; - module.verbose('Setting up automatic tab retrieval from server', url); - if($.isPlainObject(settings.apiSettings)) { - settings.apiSettings.url = url; - } - else { - settings.apiSettings = { - url: url - }; - } - }, - loading: function(tabPath) { - var - $tab = module.get.tabElement(tabPath), - isLoading = $tab.hasClass(className.loading) - ; - if(!isLoading) { - module.verbose('Setting loading state for', $tab); - $tab - .addClass(className.loading) - .siblings($tabs) - .removeClass(className.active + ' ' + className.loading) - ; - if($tab.length > 0) { - settings.onRequest.call($tab[0], tabPath); - } - } - }, - state: function(state) { - $.address.value(state); - } - }, - - changeTab: function(tabPath) { - var - pushStateAvailable = (window.history && window.history.pushState), - shouldIgnoreLoad = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad), - remoteContent = (settings.auto || $.isPlainObject(settings.apiSettings) ), - // only add default path if not remote content - pathArray = (remoteContent && !shouldIgnoreLoad) - ? module.utilities.pathToArray(tabPath) - : module.get.defaultPathArray(tabPath) - ; - tabPath = module.utilities.arrayToPath(pathArray); - $.each(pathArray, function(index, tab) { - var - currentPathArray = pathArray.slice(0, index + 1), - currentPath = module.utilities.arrayToPath(currentPathArray), - - isTab = module.is.tab(currentPath), - isLastIndex = (index + 1 == pathArray.length), - - $tab = module.get.tabElement(currentPath), - $anchor, - nextPathArray, - nextPath, - isLastTab - ; - module.verbose('Looking for tab', tab); - if(isTab) { - module.verbose('Tab was found', tab); - // scope up - activeTabPath = currentPath; - parameterArray = module.utilities.filterArray(pathArray, currentPathArray); - - if(isLastIndex) { - isLastTab = true; - } - else { - nextPathArray = pathArray.slice(0, index + 2); - nextPath = module.utilities.arrayToPath(nextPathArray); - isLastTab = ( !module.is.tab(nextPath) ); - if(isLastTab) { - module.verbose('Tab parameters found', nextPathArray); - } - } - if(isLastTab && remoteContent) { - if(!shouldIgnoreLoad) { - module.activate.navigation(currentPath); - module.fetch.content(currentPath, tabPath); - } - else { - module.debug('Ignoring remote content on first tab load', currentPath); - firstLoad = false; - module.cache.add(tabPath, $tab.html()); - module.activate.all(currentPath); - settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent); - settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent); - } - return false; - } - else { - module.debug('Opened local tab', currentPath); - module.activate.all(currentPath); - if( !module.cache.read(currentPath) ) { - module.cache.add(currentPath, true); - module.debug('First time tab loaded calling tab init'); - settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent); - } - settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent); - } - - } - else if(tabPath.search('/') == -1 && tabPath !== '') { - // look for in page anchor - tabPath = module.escape.string(tabPath); - $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]'); - currentPath = $anchor.closest('[data-tab]').data(metadata.tab); - $tab = module.get.tabElement(currentPath); - // if anchor exists use parent tab - if($anchor && $anchor.length > 0 && currentPath) { - module.debug('Anchor link used, opening parent tab', $tab, $anchor); - if( !$tab.hasClass(className.active) ) { - setTimeout(function() { - module.scrollTo($anchor); - }, 0); - } - module.activate.all(currentPath); - if( !module.cache.read(currentPath) ) { - module.cache.add(currentPath, true); - module.debug('First time tab loaded calling tab init'); - settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent); - } - settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent); - return false; - } - } - else { - module.error(error.missingTab, $module, $context, currentPath); - return false; - } - }); - }, - - scrollTo: function($element) { - var - scrollOffset = ($element && $element.length > 0) - ? $element.offset().top - : false - ; - if(scrollOffset !== false) { - module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element); - $(document).scrollTop(scrollOffset); - } - }, - - update: { - content: function(tabPath, html, evaluateScripts) { - var - $tab = module.get.tabElement(tabPath), - tab = $tab[0] - ; - evaluateScripts = (evaluateScripts !== undefined) - ? evaluateScripts - : settings.evaluateScripts - ; - if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') { - $tab - .empty() - .append($(html).clone(true)) - ; - } - else { - if(evaluateScripts) { - module.debug('Updating HTML and evaluating inline scripts', tabPath, html); - $tab.html(html); - } - else { - module.debug('Updating HTML', tabPath, html); - tab.innerHTML = html; - } - } - } - }, - - fetch: { - - content: function(tabPath, fullTabPath) { - var - $tab = module.get.tabElement(tabPath), - apiSettings = { - dataType : 'html', - encodeParameters : false, - on : 'now', - cache : settings.alwaysRefresh, - headers : { - 'X-Remote': true - }, - onSuccess : function(response) { - if(settings.cacheType == 'response') { - module.cache.add(fullTabPath, response); - } - module.update.content(tabPath, response); - if(tabPath == activeTabPath) { - module.debug('Content loaded', tabPath); - module.activate.tab(tabPath); - } - else { - module.debug('Content loaded in background', tabPath); - } - settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent); - settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent); - - if(settings.loadOnce) { - module.cache.add(fullTabPath, true); - } - else if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) { - setTimeout(function() { - var - $clone = $tab.children().clone(true) - ; - $clone = $clone.not('script'); - module.cache.add(fullTabPath, $clone); - }, 0); - } - else { - module.cache.add(fullTabPath, $tab.html()); - } - }, - urlData: { - tab: fullTabPath - } - }, - request = $tab.api('get request') || false, - existingRequest = ( request && request.state() === 'pending' ), - requestSettings, - cachedContent - ; - - fullTabPath = fullTabPath || tabPath; - cachedContent = module.cache.read(fullTabPath); - - - if(settings.cache && cachedContent) { - module.activate.tab(tabPath); - module.debug('Adding cached content', fullTabPath); - if(!settings.loadOnce) { - if(settings.evaluateScripts == 'once') { - module.update.content(tabPath, cachedContent, false); - } - else { - module.update.content(tabPath, cachedContent); - } - } - settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent); - } - else if(existingRequest) { - module.set.loading(tabPath); - module.debug('Content is already loading', fullTabPath); - } - else if($.api !== undefined) { - requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings); - module.debug('Retrieving remote content', fullTabPath, requestSettings); - module.set.loading(tabPath); - $tab.api(requestSettings); - } - else { - module.error(error.api); - } - } - }, - - activate: { - all: function(tabPath) { - module.activate.tab(tabPath); - module.activate.navigation(tabPath); - }, - tab: function(tabPath) { - var - $tab = module.get.tabElement(tabPath), - $deactiveTabs = (settings.deactivate == 'siblings') - ? $tab.siblings($tabs) - : $tabs.not($tab), - isActive = $tab.hasClass(className.active) - ; - module.verbose('Showing tab content for', $tab); - if(!isActive) { - $tab - .addClass(className.active) - ; - $deactiveTabs - .removeClass(className.active + ' ' + className.loading) - ; - if($tab.length > 0) { - settings.onVisible.call($tab[0], tabPath); - } - } - }, - navigation: function(tabPath) { - var - $navigation = module.get.navElement(tabPath), - $deactiveNavigation = (settings.deactivate == 'siblings') - ? $navigation.siblings($allModules) - : $allModules.not($navigation), - isActive = $navigation.hasClass(className.active) - ; - module.verbose('Activating tab navigation for', $navigation, tabPath); - if(!isActive) { - $navigation - .addClass(className.active) - ; - $deactiveNavigation - .removeClass(className.active + ' ' + className.loading) - ; - } - } - }, - - deactivate: { - all: function() { - module.deactivate.navigation(); - module.deactivate.tabs(); - }, - navigation: function() { - $allModules - .removeClass(className.active) - ; - }, - tabs: function() { - $tabs - .removeClass(className.active + ' ' + className.loading) - ; - } - }, - - is: { - tab: function(tabName) { - return (tabName !== undefined) - ? ( module.get.tabElement(tabName).length > 0 ) - : false - ; - } - }, - - get: { - initialPath: function() { - return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab); - }, - path: function() { - return $.address.value(); - }, - // adds default tabs to tab path - defaultPathArray: function(tabPath) { - return module.utilities.pathToArray( module.get.defaultPath(tabPath) ); - }, - defaultPath: function(tabPath) { - var - $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + module.escape.string(tabPath) + '/"]').eq(0), - defaultTab = $defaultNav.data(metadata.tab) || false - ; - if( defaultTab ) { - module.debug('Found default tab', defaultTab); - if(recursionDepth < settings.maxDepth) { - recursionDepth++; - return module.get.defaultPath(defaultTab); - } - module.error(error.recursion); - } - else { - module.debug('No default tabs found for', tabPath, $tabs); - } - recursionDepth = 0; - return tabPath; - }, - navElement: function(tabPath) { - tabPath = tabPath || activeTabPath; - return $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]'); - }, - tabElement: function(tabPath) { - var - $fullPathTab, - $simplePathTab, - tabPathArray, - lastTab - ; - tabPath = tabPath || activeTabPath; - tabPathArray = module.utilities.pathToArray(tabPath); - lastTab = module.utilities.last(tabPathArray); - $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]'); - $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + module.escape.string(lastTab) + '"]'); - return ($fullPathTab.length > 0) - ? $fullPathTab - : $simplePathTab - ; - }, - tab: function() { - return activeTabPath; - } - }, - - determine: { - activeTab: function() { - var activeTab = null; - - $tabs.each(function(_index, tab) { - var $tab = $(tab); - - if( $tab.hasClass(className.active) ) { - var - tabPath = $(this).data(metadata.tab), - $anchor = $allModules.filter('[data-' + metadata.tab + '="' + module.escape.string(tabPath) + '"]') - ; - - if( $anchor.hasClass(className.active) ) { - activeTab = tabPath; - } - } - }); - - return activeTab; - } - }, - - utilities: { - filterArray: function(keepArray, removeArray) { - return $.grep(keepArray, function(keepValue) { - return ( $.inArray(keepValue, removeArray) == -1); - }); - }, - last: function(array) { - return Array.isArray(array) - ? array[ array.length - 1] - : false - ; - }, - pathToArray: function(pathName) { - if(pathName === undefined) { - pathName = activeTabPath; - } - return typeof pathName == 'string' - ? pathName.split('/') - : [pathName] - ; - }, - arrayToPath: function(pathArray) { - return Array.isArray(pathArray) - ? pathArray.join('/') - : false - ; - } - }, - - setting: function(name, value) { - module.debug('Changing setting', name, value); - if( $.isPlainObject(name) ) { - $.extend(true, settings, name); - } - else if(value !== undefined) { - if($.isPlainObject(settings[name])) { - $.extend(true, settings[name], value); - } - else { - settings[name] = value; - } - } - else { - return settings[name]; - } - }, - internal: function(name, value) { - if( $.isPlainObject(name) ) { - $.extend(true, module, name); - } - else if(value !== undefined) { - module[name] = value; - } - else { - return module[name]; - } - }, - debug: function() { - if(!settings.silent && settings.debug) { - if(settings.performance) { - module.performance.log(arguments); - } - else { - module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); - module.debug.apply(console, arguments); - } - } - }, - verbose: function() { - if(!settings.silent && settings.verbose && settings.debug) { - if(settings.performance) { - module.performance.log(arguments); - } - else { - module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); - module.verbose.apply(console, arguments); - } - } - }, - error: function() { - if(!settings.silent) { - module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); - module.error.apply(console, arguments); - } - }, - performance: { - log: function(message) { - var - currentTime, - executionTime, - previousTime - ; - if(settings.performance) { - currentTime = new Date().getTime(); - previousTime = time || currentTime; - executionTime = currentTime - previousTime; - time = currentTime; - performance.push({ - 'Name' : message[0], - 'Arguments' : [].slice.call(message, 1) || '', - 'Element' : element, - 'Execution Time' : executionTime - }); - } - clearTimeout(module.performance.timer); - module.performance.timer = setTimeout(module.performance.display, 500); - }, - display: function() { - var - title = settings.name + ':', - totalTime = 0 - ; - time = false; - clearTimeout(module.performance.timer); - $.each(performance, function(index, data) { - totalTime += data['Execution Time']; - }); - title += ' ' + totalTime + 'ms'; - if(moduleSelector) { - title += ' \'' + moduleSelector + '\''; - } - if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { - console.groupCollapsed(title); - if(console.table) { - console.table(performance); - } - else { - $.each(performance, function(index, data) { - console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); - }); - } - console.groupEnd(); - } - performance = []; - } - }, - invoke: function(query, passedArguments, context) { - var - object = instance, - maxDepth, - found, - response - ; - passedArguments = passedArguments || queryArguments; - context = element || context; - if(typeof query == 'string' && object !== undefined) { - query = query.split(/[\. ]/); - maxDepth = query.length - 1; - $.each(query, function(depth, value) { - var camelCaseValue = (depth != maxDepth) - ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) - : query - ; - if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { - object = object[camelCaseValue]; - } - else if( object[camelCaseValue] !== undefined ) { - found = object[camelCaseValue]; - return false; - } - else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { - object = object[value]; - } - else if( object[value] !== undefined ) { - found = object[value]; - return false; - } - else { - module.error(error.method, query); - return false; - } - }); - } - if ( $.isFunction( found ) ) { - response = found.apply(context, passedArguments); - } - else if(found !== undefined) { - response = found; - } - if(Array.isArray(returnedValue)) { - returnedValue.push(response); - } - else if(returnedValue !== undefined) { - returnedValue = [returnedValue, response]; - } - else if(response !== undefined) { - returnedValue = response; - } - return found; - } - }; - if(methodInvoked) { - if(instance === undefined) { - module.initialize(); - } - module.invoke(query); - } - else { - if(instance !== undefined) { - instance.invoke('destroy'); - } - module.initialize(); - } - }) - ; - return (returnedValue !== undefined) - ? returnedValue - : this - ; - -}; - -// shortcut for tabbed content with no defined navigation -$.tab = function() { - $(window).tab.apply(this, arguments); -}; - -$.fn.tab.settings = { - - name : 'Tab', - namespace : 'tab', - - silent : false, - debug : false, - verbose : false, - performance : true, - - auto : false, // uses pjax style endpoints fetching content from same url with remote-content headers - history : false, // use browser history - historyType : 'hash', // #/ or html5 state - path : false, // base path of url - - context : false, // specify a context that tabs must appear inside - childrenOnly : false, // use only tabs that are children of context - maxDepth : 25, // max depth a tab can be nested - - deactivate : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together - - alwaysRefresh : false, // load tab content new every tab click - cache : true, // cache the content requests to pull locally - loadOnce : false, // Whether tab data should only be loaded once when using remote content - cacheType : 'response', // Whether to cache exact response, or to html cache contents after scripts execute - ignoreFirstLoad : false, // don't load remote content on first load - - apiSettings : false, // settings for api call - evaluateScripts : 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content - autoTabActivation: true, // whether a non existing active tab will auto activate the first available tab - - onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded - onLoad : function(tabPath, parameterArray, historyEvent) {}, // called on every load - onVisible : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible - onRequest : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content - - templates : { - determineTitle: function(tabArray) {} // returns page title for path - }, - - error: { - api : 'You attempted to load content without API module', - method : 'The method you called is not defined', - missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.', - noContent : 'The tab you specified is missing a content url.', - path : 'History enabled, but no path was specified', - recursion : 'Max recursive depth reached', - legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.', - legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code', - state : 'History requires Asual\'s Address library ' - }, - - regExp : { - escape : /[-[\]{}()*+?.,\\^$|#\s:=@]/g - }, - - metadata : { - tab : 'tab', - loaded : 'loaded', - promise: 'promise' - }, - - className : { - loading : 'loading', - active : 'active' - }, - - selector : { - tabs : '.ui.tab', - ui : '.ui' - } - -}; - -})( jQuery, window, document ); diff --git a/web_src/fomantic/semantic.json b/web_src/fomantic/semantic.json index 69474b7539..14afba2193 100644 --- a/web_src/fomantic/semantic.json +++ b/web_src/fomantic/semantic.json @@ -27,7 +27,6 @@ "form", "menu", "modal", - "search", - "tab" + "search" ] } diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 2102e995d8..a99af0ef25 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -12,6 +12,7 @@ import {confirmModal} from './comp/ConfirmModal.js'; import {showErrorToast} from '../modules/toast.js'; import {request, POST, GET} from '../modules/fetch.js'; import '../htmx.js'; +import {initTab} from '../modules/tab.ts'; const {appUrl, appSubUrl, csrfToken, i18n} = window.config; @@ -195,7 +196,9 @@ export function initGlobalCommon() { $uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward'); $uiDropdowns.filter('.downward').dropdown('setting', 'direction', 'downward'); - $('.tabular.menu .item').tab(); + for (const el of document.querySelectorAll('.tabular.menu')) { + initTab(el); + } initSubmitEventPolyfill(); document.addEventListener('submit', formFetchAction); diff --git a/web_src/js/features/comp/ComboMarkdownEditor.js b/web_src/js/features/comp/ComboMarkdownEditor.js index 4edd13bfc2..d96628c644 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.js +++ b/web_src/js/features/comp/ComboMarkdownEditor.js @@ -10,6 +10,7 @@ import {easyMDEToolbarActions} from './EasyMDEToolbarActions.js'; import {initTextExpander} from './TextExpander.js'; import {showErrorToast, showHintToast} from '../../modules/toast.js'; import {POST} from '../../modules/fetch.js'; +import {initTab} from '../../modules/tab.ts'; /** * validate if the given textarea is non-empty. @@ -200,7 +201,8 @@ class ComboMarkdownEditor { setupTab() { const $container = $(this.container); - const tabs = $container[0].querySelectorAll('.switch > .item'); + const switchEl = $container[0].querySelector('.switch'); + const tabs = switchEl.querySelectorAll('.item'); // Fomantic Tab requires the "data-tab" to be globally unique. // So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic. @@ -221,7 +223,7 @@ class ComboMarkdownEditor { }); }); - $(tabs).tab(); + initTab(switchEl); this.previewUrl = tabPreviewer.getAttribute('data-preview-url'); this.previewContext = tabPreviewer.getAttribute('data-preview-context'); diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js index d3f5f2f8a9..ac4fc8a75e 100644 --- a/web_src/js/features/repo-editor.js +++ b/web_src/js/features/repo-editor.js @@ -5,10 +5,11 @@ import {hideElem, showElem, createElementFromHTML} from '../utils/dom.js'; import {initMarkupContent} from '../markup/content.js'; import {attachRefIssueContextPopup} from './contextpopup.js'; import {POST} from '../modules/fetch.js'; +import {initTab} from '../modules/tab.ts'; function initEditPreviewTab($form) { const $tabMenu = $form.find('.tabular.menu'); - $tabMenu.find('.item').tab(); + initTab($tabMenu[0]); const $previewTab = $tabMenu.find( `.item[data-tab="${$tabMenu.data('preview')}"]`, ); diff --git a/web_src/js/modules/fomantic.js b/web_src/js/modules/fomantic.js index c736026c6c..9d27a9bb47 100644 --- a/web_src/js/modules/fomantic.js +++ b/web_src/js/modules/fomantic.js @@ -11,9 +11,6 @@ import {initDimmer} from './dimmer.ts'; export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)'); export function initGiteaFomantic() { - // Silence fomantic's error logging when tabs are used without a target content element - $.fn.tab.settings.silent = true; - // By default, use "exact match" for full text search $.fn.dropdown.settings.fullTextSearch = 'exact'; // Do not use "cursor: pointer" for dropdown labels diff --git a/web_src/js/modules/tab.ts b/web_src/js/modules/tab.ts new file mode 100644 index 0000000000..a19281793f --- /dev/null +++ b/web_src/js/modules/tab.ts @@ -0,0 +1,36 @@ +export function initTab(parentEl: Element) { + if (!parentEl) { + return; + } + + // Keep track of which tab is active for this element. + let activeTabPath = parentEl.querySelector('.item.active')?.getAttribute('data-tab'); + if (!activeTabPath) { + return; + } + + for (const el of parentEl.querySelectorAll('.item')) { + el.addEventListener('click', (ev) => { + // There's no data-tab attribute we can't do anything, ignore. + const tabPath = el.getAttribute('data-tab'); + if (!tabPath) { + return; + } + + // The item is already active, ignore. + if (el.classList.contains('active')) { + return; + } + + // Make the current item active and the previous item inactive. + parentEl.querySelector('.item.active').classList.remove('active'); + document.querySelector(`.tab.active[data-tab=${activeTabPath}]`).classList.remove('active'); + el.classList.add('active'); + document.querySelector(`.tab[data-tab=${tabPath}]`).classList.add('active'); + activeTabPath = tabPath; + + // Not really sure if this is useful, it is kept from how Fomantic did it. + ev.preventDefault(); + }, {passive: false}); + } +} From ccc33dd2dfcd8e9d81c7e91f74acf92114a61ea0 Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 21 Jul 2025 22:34:54 +0200 Subject: [PATCH 151/297] chore: tweak fomantic config (#8583) Disable many unneeded variants in fomantic's config. Reduces the amount of CSS is generated. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8583 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- Makefile | 1 + web_src/fomantic/_site/globals/site.variables | 20 + web_src/fomantic/build/semantic.css | 849 ------------------ 3 files changed, 21 insertions(+), 849 deletions(-) diff --git a/Makefile b/Makefile index ea8ba4b192..17ed23662e 100644 --- a/Makefile +++ b/Makefile @@ -940,6 +940,7 @@ fomantic: cd $(FOMANTIC_WORK_DIR) && npm install --no-save cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/ + rm -rf $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/themes/default/modules/dropdown.overrides $(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build # fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event diff --git a/web_src/fomantic/_site/globals/site.variables b/web_src/fomantic/_site/globals/site.variables index 283b9e18eb..19375cf047 100644 --- a/web_src/fomantic/_site/globals/site.variables +++ b/web_src/fomantic/_site/globals/site.variables @@ -14,9 +14,14 @@ @variationButtonAnimated: false; @variationButtonAnimatedFade: false; @variationButtonAttached: false; +@variationButtonCircular: false; +@variationButtonFloated: false; @variationButtonInverted: false; +@variationButtonToggle: false; @variationButtonSocial: false; @variationButtonTertiary: false; +@variationButtonOr: false; +@variationButtonSizes: mini, tiny, small; @variationCalendarInverted: false; @variationCardInverted: false; @variationCheckboxInverted: false; @@ -24,14 +29,18 @@ @variationDimmerInverted: false; @variationDividerInverted: false; @variationDropdownInverted: false; +@variationDropdownScrollhint: false; +@variationDropdownSimple: false; @variationFeedInverted: false; @variationFlyoutInverted: false; @variationFormInverted: false; @variationFormTransparent: false; +@variationFormSizes: false; @variationGridDoubling: false; @variationGridInverted: false; @variationHeaderInverted: false; @variationIconInverted: false; +@variationInputAction: false; @variationInputInverted: false; @variationItemInverted: false; @variationLabelCorner: false; @@ -40,14 +49,25 @@ @variationLabelRibbon: false; @variationLabelTag: false; @variationListInverted: false; +@variationMenuText: false; +@variationMenuFitted: false; +@variationMenuFixed: false; @variationMenuInverted: false; @variationMessageInverted: false; +@variationModalBasic: false; @variationModalInverted: false; +@variationModalLegacy: false; +@variationModalOverlay: false; +@variationModalSizes: mini, tiny, small; @variationNagInverted: false; @variationPlaceholderInverted: false; @variationPopupInverted: false; @variationPopupTooltip: false; @variationProgressInverted: false; +@variationSearchAligned: false; +@variationSearchCategory: false; +@variationSearchShort: false; +@variationSearchLong: false; @variationSegmentInverted: false; @variationSegmentPiled: false; @variationSegmentStacked: false; diff --git a/web_src/fomantic/build/semantic.css b/web_src/fomantic/build/semantic.css index bd1777db36..2212ed2dd5 100644 --- a/web_src/fomantic/build/semantic.css +++ b/web_src/fomantic/build/semantic.css @@ -287,24 +287,6 @@ Variations *******************************/ -/*------------------- - Floated - --------------------*/ - -.ui[class*="left floated"].buttons, -.ui[class*="left floated"].button { - float: left; - margin-left: 0; - margin-right: 0.25em; -} - -.ui[class*="right floated"].buttons, -.ui[class*="right floated"].button { - float: right; - margin-right: 0; - margin-left: 0.25em; -} - /*------------------- Compact --------------------*/ @@ -363,14 +345,6 @@ font-size: 0.92857143rem; } -.ui.large.buttons .dropdown, -.ui.large.buttons .dropdown .menu > .item, -.ui.large.buttons .button, -.ui.large.buttons .or, -.ui.ui.ui.ui.large.button { - font-size: 1.14285714rem; -} - /*-------------- Icon Only ---------------*/ @@ -571,85 +545,6 @@ animation: loader 2s linear infinite; } -/*-------------- - Toggle - ---------------*/ - -/* Toggle (Modifies active state to give affordances) */ - -.ui.toggle.buttons .active.button, -.ui.buttons .button.toggle.active, -.ui.button.toggle.active { - background-color: #21BA45; - box-shadow: none; - text-shadow: none; - color: #FFFFFF; -} - -.ui.button.toggle.active:hover { - background-color: #16ab39; - text-shadow: none; - color: #FFFFFF; -} - -/*-------------- - Circular - ---------------*/ - -.ui.circular.button { - border-radius: 10em; -} - -.ui.circular.button > .icon { - width: 1em; - vertical-align: baseline; -} - -/*------------------- - Or Buttons - --------------------*/ - -.ui.buttons .or { - position: relative; - width: 0.3em; - height: 2.57142857em; - z-index: 3; -} - -.ui.buttons .or:before { - position: absolute; - text-align: center; - border-radius: 500rem; - content: 'or'; - top: 50%; - left: 50%; - background-color: #FFFFFF; - text-shadow: none; - margin-top: -0.89285714em; - margin-left: -0.89285714em; - width: 1.78571429em; - height: 1.78571429em; - line-height: 1.78571429em; - color: rgba(0, 0, 0, 0.4); - font-style: normal; - font-weight: 500; - box-shadow: 0 0 0 1px transparent inset; -} - -.ui.buttons .or[data-text]:before { - content: attr(data-text); -} - -/* Fluid Or */ - -.ui.fluid.buttons .or { - width: 0 !important; -} - -.ui.fluid.buttons .or:after { - display: none; -} - /*------------------- Fluid --------------------*/ @@ -2944,45 +2839,6 @@ select.ui.dropdown { box-shadow: none !important; } -/* CSS specific to iOS devices or firefox mobile only */ - -@supports (-webkit-touch-callout: none) or (-webkit-overflow-scrolling: touch) or (-moz-appearance:none) { -@media (-moz-touch-enabled), (pointer: coarse) { - .ui.dropdown .scrollhint.menu:not(.hidden):before { - animation: scrollhint 2s ease 2; - content: ''; - z-index: 15; - display: block; - position: absolute; - opacity: 0; - right: 0.25em; - top: 0; - height: 100%; - border-right: 0.25em solid; - border-left: 0; - -o-border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%; - border-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)) 1 100%; - } - - .ui.inverted.dropdown .scrollhint.menu:not(.hidden):before { - -o-border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%; - border-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.75), rgba(255, 255, 255, 0)) 1 100%; - } - -@keyframes scrollhint { - 0% { - opacity: 1; - top: 100%; - } - - 100% { - opacity: 0; - top: 0; - } -} -} -} - /*-------------- Searchable ---------------*/ @@ -3820,85 +3676,6 @@ select.ui.dropdown { width: 20%; } -/*-------------- - Simple - ---------------*/ - -/* Displays without javascript */ - -.ui.simple.dropdown .menu:before, -.ui.simple.dropdown .menu:after { - display: none; -} - -.ui.simple.dropdown .menu { - position: absolute; - /* IE hack to make dropdown icons appear inline */ - display: -ms-inline-flexbox !important; - display: block; - overflow: hidden; - top: -9999px; - opacity: 0; - width: 0; - height: 0; - transition: opacity 0.1s ease; - margin-top: 0 !important; -} - -.ui.simple.active.dropdown, -.ui.simple.dropdown:hover { - border-bottom-left-radius: 0 !important; - border-bottom-right-radius: 0 !important; -} - -.ui.simple.active.dropdown > .menu, -.ui.simple.dropdown:hover > .menu { - overflow: visible; - width: auto; - height: auto; - top: 100%; - opacity: 1; -} - -.ui.simple.dropdown > .menu > .item:active > .menu, -.ui.simple.dropdown .menu .item:hover > .menu { - overflow: visible; - width: auto; - height: auto; - top: 0 !important; - left: 100%; - opacity: 1; -} - -.ui.simple.dropdown > .menu > .item:active > .left.menu, -.ui.simple.dropdown .menu .item:hover > .left.menu, -.right.menu .ui.simple.dropdown > .menu > .item:active > .menu:not(.right), -.right.menu .ui.simple.dropdown > .menu .item:hover > .menu:not(.right) { - left: auto; - right: 100%; -} - -.ui.simple.disabled.dropdown:hover .menu { - display: none; - height: 0; - width: 0; - overflow: hidden; -} - -/* Visible */ - -.ui.simple.visible.dropdown > .menu { - display: block; -} - -/* Scrolling */ - -.ui.simple.scrolling.active.dropdown > .menu, -.ui.simple.scrolling.dropdown:hover > .menu { - overflow-x: hidden; - overflow-y: auto; -} - /*-------------- Fluid ---------------*/ @@ -4184,70 +3961,6 @@ select.ui.dropdown { font-size: 1.14285714rem; } -/******************************* - Theme Overrides -*******************************/ - -/* Dropdown Carets */ - -@font-face { - font-family: 'Dropdown'; - src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMggjB5AAAAC8AAAAYGNtYXAPfuIIAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5Zjo82LgAAAFwAAABVGhlYWQAQ88bAAACxAAAADZoaGVhAwcB6QAAAvwAAAAkaG10eAS4ABIAAAMgAAAAIGxvY2EBNgDeAAADQAAAABJtYXhwAAoAFgAAA1QAAAAgbmFtZVcZpu4AAAN0AAABRXBvc3QAAwAAAAAEvAAAACAAAwIAAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADw2gHg/+D/4AHgACAAAAABAAAAAAAAAAAAAAAgAAAAAAACAAAAAwAAABQAAwABAAAAFAAEADgAAAAKAAgAAgACAAEAIPDa//3//wAAAAAAIPDX//3//wAB/+MPLQADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAIABJQElABMAABM0NzY3BTYXFhUUDwEGJwYvASY1AAUGBwEACAUGBoAFCAcGgAUBEgcGBQEBAQcECQYHfwYBAQZ/BwYAAQAAAG4BJQESABMAADc0PwE2MzIfARYVFAcGIyEiJyY1AAWABgcIBYAGBgUI/wAHBgWABwaABQWABgcHBgUFBgcAAAABABIASQC3AW4AEwAANzQ/ATYXNhcWHQEUBwYnBi8BJjUSBoAFCAcFBgYFBwgFgAbbBwZ/BwEBBwQJ/wgEBwEBB38GBgAAAAABAAAASQClAW4AEwAANxE0NzYzMh8BFhUUDwEGIyInJjUABQYHCAWABgaABQgHBgVbAQAIBQYGgAUIBwWABgYFBwAAAAEAAAABAADZuaKOXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAAAAACgAUAB4AQgBkAIgAqgAAAAEAAAAIABQAAQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQAOAAAAAQAAAAAAAgAOAEcAAQAAAAAAAwAOACQAAQAAAAAABAAOAFUAAQAAAAAABQAWAA4AAQAAAAAABgAHADIAAQAAAAAACgA0AGMAAwABBAkAAQAOAAAAAwABBAkAAgAOAEcAAwABBAkAAwAOACQAAwABBAkABAAOAFUAAwABBAkABQAWAA4AAwABBAkABgAOADkAAwABBAkACgA0AGMAaQBjAG8AbQBvAG8AbgBWAGUAcgBzAGkAbwBuACAAMQAuADAAaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AbgBSAGUAZwB1AGwAYQByAGkAYwBvAG0AbwBvAG4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=) format('truetype'), url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAAVwAAoAAAAABSgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAAdkAAAHZLDXE/09TLzIAAALQAAAAYAAAAGAIIweQY21hcAAAAzAAAABMAAAATA9+4ghnYXNwAAADfAAAAAgAAAAIAAAAEGhlYWQAAAOEAAAANgAAADYAQ88baGhlYQAAA7wAAAAkAAAAJAMHAelobXR4AAAD4AAAACAAAAAgBLgAEm1heHAAAAQAAAAABgAAAAYACFAAbmFtZQAABAgAAAFFAAABRVcZpu5wb3N0AAAFUAAAACAAAAAgAAMAAAEABAQAAQEBCGljb21vb24AAQIAAQA6+BwC+BsD+BgEHgoAGVP/i4seCgAZU/+LiwwHi2v4lPh0BR0AAACIDx0AAACNER0AAAAJHQAAAdASAAkBAQgPERMWGyAlKmljb21vb25pY29tb29udTB1MXUyMHVGMEQ3dUYwRDh1RjBEOXVGMERBAAACAYkABgAIAgABAAQABwAKAA0AVgCfAOgBL/yUDvyUDvyUDvuUDvtvi/emFYuQjZCOjo+Pj42Qiwj3lIsFkIuQiY6Hj4iNhouGi4aJh4eHCPsU+xQFiIiGiYaLhouHjYeOCPsU9xQFiI+Jj4uQCA77b4v3FBWLkI2Pjo8I9xT3FAWPjo+NkIuQi5CJjogI9xT7FAWPh42Hi4aLhomHh4eIiIaJhosI+5SLBYaLh42HjoiPiY+LkAgO+92d928Vi5CNkI+OCPcU9xQFjo+QjZCLkIuPiY6Hj4iNhouGCIv7lAWLhomHh4iIh4eJhouGi4aNiI8I+xT3FAWHjomPi5AIDvvdi+YVi/eUBYuQjZCOjo+Pj42Qi5CLkImOhwj3FPsUBY+IjYaLhouGiYeHiAj7FPsUBYiHhomGi4aLh42Hj4iOiY+LkAgO+JQU+JQViwwKAAAAAAMCAAGQAAUAAAFMAWYAAABHAUwBZgAAAPUAGQCEAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA8NoB4P/g/+AB4AAgAAAAAQAAAAAAAAAAAAAAIAAAAAAAAgAAAAMAAAAUAAMAAQAAABQABAA4AAAACgAIAAIAAgABACDw2v/9//8AAAAAACDw1//9//8AAf/jDy0AAwABAAAAAAAAAAAAAAABAAH//wAPAAEAAAABAAA5emozXw889QALAgAAAAAA0ABHWAAAAADQAEdYAAAAAAElAW4AAAAIAAIAAAAAAAAAAQAAAeD/4AAAAgAAAAAAASUAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAABAAAAASUAAAElAAAAtwASALcAAAAAUAAACAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIADgBHAAEAAAAAAAMADgAkAAEAAAAAAAQADgBVAAEAAAAAAAUAFgAOAAEAAAAAAAYABwAyAAEAAAAAAAoANABjAAMAAQQJAAEADgAAAAMAAQQJAAIADgBHAAMAAQQJAAMADgAkAAMAAQQJAAQADgBVAAMAAQQJAAUAFgAOAAMAAQQJAAYADgA5AAMAAQQJAAoANABjAGkAYwBvAG0AbwBvAG4AVgBlAHIAcwBpAG8AbgAgADEALgAwAGkAYwBvAG0AbwBvAG5pY29tb29uAGkAYwBvAG0AbwBvAG4AUgBlAGcAdQBsAGEAcgBpAGMAbwBtAG8AbwBuAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('woff'); - font-weight: normal; - font-style: normal; -} - -.ui.dropdown > .dropdown.icon { - font-family: 'Dropdown'; - line-height: 1; - height: 1em; - width: 1.23em; - backface-visibility: hidden; - font-weight: normal; - font-style: normal; - text-align: center; -} - -.ui.dropdown > .dropdown.icon { - width: auto; -} - -.ui.dropdown > .dropdown.icon:before { - content: '\f0d7'; -} - -/* Sub Menu */ - -.ui.dropdown .menu .item .dropdown.icon:before { - content: '\f0da' ; -} - -.ui.dropdown .item .left.dropdown.icon:before, -.ui.dropdown .left.menu .item .dropdown.icon:before { - content: "\f0d9" ; -} - -/* Vertical Menu Dropdown */ - -.ui.vertical.menu .dropdown.item > .dropdown.icon:before { - content: "\f0da" ; -} - -/* Icons for Reference -.dropdown.down.icon { - content: "\f0d7"; -} -.dropdown.up.icon { - content: "\f0d8"; -} -.dropdown.left.icon { - content: "\f0d9"; -} -.dropdown.icon.icon { - content: "\f0da"; -} -*/ - /******************************* User Overrides *******************************/ @@ -4659,38 +4372,6 @@ select.ui.dropdown { box-shadow: 0 0 0 0 rgba(34, 36, 38, 0.35) inset; } -.ui.form .ui.action.input:not([class*="left action"]) input:not([type]):focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="date"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="datetime-local"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="email"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="number"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="password"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="search"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="tel"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="time"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="text"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="file"]:focus, -.ui.form .ui.action.input:not([class*="left action"]) input[type="url"]:focus { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.ui.form .ui[class*="left action"].input input:not([type]), -.ui.form .ui[class*="left action"].input input[type="date"], -.ui.form .ui[class*="left action"].input input[type="datetime-local"], -.ui.form .ui[class*="left action"].input input[type="email"], -.ui.form .ui[class*="left action"].input input[type="number"], -.ui.form .ui[class*="left action"].input input[type="password"], -.ui.form .ui[class*="left action"].input input[type="search"], -.ui.form .ui[class*="left action"].input input[type="tel"], -.ui.form .ui[class*="left action"].input input[type="time"], -.ui.form .ui[class*="left action"].input input[type="text"], -.ui.form .ui[class*="left action"].input input[type="file"], -.ui.form .ui[class*="left action"].input input[type="url"] { - border-bottom-left-radius: 0; - border-top-left-radius: 0; -} - .ui.form textarea:focus { color: rgba(0, 0, 0, 0.95); border-color: #85B7D9; @@ -6032,30 +5713,6 @@ select.ui.dropdown { font-size: 1rem; } -.ui.mini.form, -.ui.mini.form .field .dropdown, -.ui.mini.form .field .dropdown .menu > .item { - font-size: 0.78571429rem; -} - -.ui.tiny.form, -.ui.tiny.form .field .dropdown, -.ui.tiny.form .field .dropdown .menu > .item { - font-size: 0.85714286rem; -} - -.ui.small.form, -.ui.small.form .field .dropdown, -.ui.small.form .field .dropdown .menu > .item { - font-size: 0.92857143rem; -} - -.ui.large.form, -.ui.large.form .field .dropdown, -.ui.large.form .field .dropdown .menu > .item { - font-size: 1.14285714rem; -} - /******************************* Theme Overrides *******************************/ @@ -7147,139 +6804,6 @@ Floated Menu / Item border-color: currentColor; } -/*-------------- - Text Menu - ---------------*/ - -.ui.text.menu { - background: none transparent; - border-radius: 0; - box-shadow: none; - border: none; - margin: 1em -0.5em; -} - -.ui.text.menu .item { - border-radius: 0; - box-shadow: none; - align-self: center; - margin: 0 0; - padding: 0.35714286em 0.5em; - font-weight: normal; - color: rgba(0, 0, 0, 0.6); - transition: opacity 0.1s ease; -} - -/* Border */ - -.ui.text.menu .item:before, -.ui.text.menu .menu .item:before { - display: none !important; -} - -/* Header */ - -.ui.text.menu .header.item { - background-color: transparent; - opacity: 1; - color: rgba(0, 0, 0, 0.85); - font-size: 0.92857143em; - text-transform: uppercase; - font-weight: 500; -} - -/* Image */ - -.ui.text.menu .item > img:not(.ui) { - margin: 0; -} - -/*--- fluid text ---*/ - -.ui.text.item.menu .item { - margin: 0; -} - -/*--- vertical text ---*/ - -.ui.vertical.text.menu { - margin: 1em 0; -} - -.ui.vertical.text.menu:first-child { - margin-top: 0; -} - -.ui.vertical.text.menu:last-child { - margin-bottom: 0; -} - -.ui.vertical.text.menu .item { - margin: 0.57142857em 0; - padding-left: 0; - padding-right: 0; -} - -.ui.vertical.text.menu .item > i.icon { - float: none; - margin: 0 0.35714286em 0 0; -} - -.ui.vertical.text.menu .header.item { - margin: 0.57142857em 0 0.71428571em; -} - -/* Vertical Sub Menu */ - -.ui.vertical.text.menu .item:not(.dropdown) > .menu { - margin: 0; -} - -.ui.vertical.text.menu .item:not(.dropdown) > .menu > .item { - margin: 0; - padding: 0.5em 0; -} - -/*--- hover ---*/ - -.ui.text.menu .item:hover { - opacity: 1; - background-color: transparent; -} - -/*--- active ---*/ - -.ui.text.menu .active.item { - background-color: transparent; - border: none; - box-shadow: none; - font-weight: normal; - color: rgba(0, 0, 0, 0.95); -} - -/*--- active hover ---*/ - -.ui.text.menu .active.item:hover { - background-color: transparent; -} - -/* Disable Bariations */ - -.ui.text.pointing.menu .active.item:after { - box-shadow: none; -} - -.ui.text.attached.menu { - box-shadow: none; -} - -/* Fluid */ - -.ui.fluid.text.menu { - margin-left: 0; - margin-right: 0; -} - /*-------------- Icon Only ---------------*/ @@ -7523,30 +7047,6 @@ Floated Menu / Item margin: 0 0 0 0.5rem; } -/*-------------- - Fitted - ---------------*/ - -.ui.fitted.menu .item, -.ui.fitted.menu .item .menu .item, -.ui.menu .fitted.item { - padding: 0; -} - -.ui.horizontally.fitted.menu .item, -.ui.horizontally.fitted.menu .item .menu .item, -.ui.menu .horizontally.fitted.item { - padding-top: 0.92857143em; - padding-bottom: 0.92857143em; -} - -.ui.vertically.fitted.menu .item, -.ui.vertically.fitted.menu .item .menu .item, -.ui.menu .vertically.fitted.item { - padding-left: 1.14285714em; - padding-right: 1.14285714em; -} - /*-------------- Borderless ---------------*/ @@ -7665,77 +7165,6 @@ Floated Menu / Item width: 8.333%; } -/*-------------- - Fixed - ---------------*/ - -.ui.menu.fixed { - position: fixed; - z-index: 101; - margin: 0; - width: 100%; -} - -.ui.menu.fixed, -.ui.menu.fixed .item:first-child, -.ui.menu.fixed .item:last-child { - border-radius: 0 !important; -} - -.ui.fixed.menu, -.ui[class*="top fixed"].menu { - top: 0; - left: 0; - right: auto; - bottom: auto; -} - -.ui[class*="top fixed"].menu { - border-top: none; - border-left: none; - border-right: none; -} - -.ui[class*="right fixed"].menu { - border-top: none; - border-bottom: none; - border-right: none; - top: 0; - right: 0; - left: auto; - bottom: auto; - width: auto; - height: 100%; -} - -.ui[class*="bottom fixed"].menu { - border-bottom: none; - border-left: none; - border-right: none; - bottom: 0; - left: 0; - top: auto; - right: auto; -} - -.ui[class*="left fixed"].menu { - border-top: none; - border-bottom: none; - border-left: none; - top: 0; - left: 0; - right: auto; - bottom: auto; - width: auto; - height: 100%; -} - -/* Coupling with Grid */ - -.ui.fixed.menu + .ui.grid { - padding-top: 2.75rem; -} - /*------------------- Pointing --------------------*/ @@ -8358,62 +7787,6 @@ Floated Menu / Item Types *******************************/ -.ui.basic.modal { - background-color: transparent; - border: none; - border-radius: 0; - box-shadow: none !important; - color: #FFFFFF; -} - -.ui.basic.modal > .header, -.ui.basic.modal > .content, -.ui.basic.modal > .actions { - background-color: transparent; -} - -.ui.basic.modal > .header { - color: #FFFFFF; - border-bottom: none; -} - -.ui.basic.modal > .close { - top: 1rem; - right: 1.5rem; - color: #FFFFFF; -} - -.ui.inverted.dimmer > .basic.modal { - color: rgba(0, 0, 0, 0.87); -} - -.ui.inverted.dimmer > .ui.basic.modal > .header { - color: rgba(0, 0, 0, 0.85); -} - -/* Resort to margin positioning if legacy */ - -.ui.legacy.legacy.modal, -.ui.legacy.legacy.page.dimmer > .ui.modal { - left: 50% !important; -} - -.ui.legacy.legacy.modal:not(.aligned), -.ui.legacy.legacy.page.dimmer > .ui.modal:not(.aligned) { - top: 50%; -} - -.ui.legacy.legacy.page.dimmer > .ui.scrolling.modal:not(.aligned), -.ui.page.dimmer > .ui.scrolling.legacy.legacy.modal:not(.aligned), -.ui.top.aligned.legacy.legacy.page.dimmer > .ui.modal:not(.aligned), -.ui.top.aligned.dimmer > .ui.legacy.legacy.modal:not(.aligned) { - top: auto; -} - -.ui.legacy.overlay.fullscreen.modal { - margin-top: -2rem !important; -} - /******************************* States *******************************/ @@ -8509,14 +7882,6 @@ Floated Menu / Item overflow: auto; } -.ui.overlay.fullscreen.modal > .content { - min-height: calc(100vh - 9.1rem); -} - -.ui.overlay.fullscreen.modal > .scrolling.content { - max-height: calc(100vh - 9.1rem); -} - /*-------------- Full Screen ---------------*/ @@ -8527,14 +7892,6 @@ Floated Menu / Item margin: 1em auto; } -.ui.overlay.fullscreen.modal { - width: 100%; - left: 0; - margin: 0 auto; - top: 0; - border-radius: 0; -} - .ui.modal > .close.inside + .header, .ui.fullscreen.modal > .header { padding-right: 2.25rem; @@ -8547,10 +7904,6 @@ Floated Menu / Item color: rgba(0, 0, 0, 0.87); } -.ui.basic.fullscreen.modal > .close { - color: #FFFFFF; -} - /*-------------- Size ---------------*/ @@ -8676,45 +8029,6 @@ Floated Menu / Item } } -.ui.large.modal > .header:not(.ui) { - font-size: 1.6em; -} - -@media only screen and (max-width: 767.98px) { - .ui.large.modal { - width: 95%; - margin: 0 0 0 0; - } -} - -@media only screen and (min-width: 768px) { - .ui.large.modal { - width: 88%; - margin: 0 0 0 0; - } -} - -@media only screen and (min-width: 992px) { - .ui.large.modal { - width: 1020px; - margin: 0 0 0 0; - } -} - -@media only screen and (min-width: 1200px) { - .ui.large.modal { - width: 1080px; - margin: 0 0 0 0; - } -} - -@media only screen and (min-width: 1920px) { - .ui.large.modal { - width: 1140px; - margin: 0 0 0 0; - } -} - /******************************* Theme Overrides *******************************/ @@ -8957,14 +8271,6 @@ Floated Menu / Item Active ---------------*/ -.ui.category.search > .results .category.active { - background: #F3F4F5; -} - -.ui.category.search > .results .category.active > .name { - color: rgba(0, 0, 0, 0.87); -} - .ui.search > .results .result.active, .ui.category.search > .results .category .result.active { position: relative; @@ -9033,75 +8339,6 @@ Floated Menu / Item color: #DB2828; } -/*-------------- - Category - ---------------*/ - -.ui.category.search .results { - width: 28em; -} - -.ui.category.search .results.animating, -.ui.category.search .results.visible { - display: table; -} - -/* Category */ - -.ui.category.search > .results .category { - display: table-row; - background: #F3F4F5; - box-shadow: none; - transition: background 0.1s ease, border-color 0.1s ease; -} - -/* Last Category */ - -.ui.category.search > .results .category:last-child { - border-bottom: none; -} - -/* First / Last */ - -.ui.category.search > .results .category:first-child .name + .result { - border-radius: 0 0.28571429rem 0 0; -} - -.ui.category.search > .results .category:last-child .result:last-child { - border-radius: 0 0 0.28571429rem 0; -} - -/* Category Result Name */ - -.ui.category.search > .results .category > .name { - display: table-cell; - text-overflow: ellipsis; - width: 100px; - white-space: nowrap; - background: transparent; - font-family: var(--fonts-regular); - font-size: 1em; - padding: 0.4em 1em; - font-weight: 500; - color: rgba(0, 0, 0, 0.4); - border-bottom: 1px solid rgba(34, 36, 38, 0.1); -} - -/* Category Result */ - -.ui.category.search > .results .category .results { - display: table-cell; - background: #FFFFFF; - border-left: 1px solid rgba(34, 36, 38, 0.15); - border-bottom: 1px solid rgba(34, 36, 38, 0.1); -} - -.ui.category.search > .results .category .result { - border-bottom: 1px solid rgba(34, 36, 38, 0.1); - transition: background 0.1s ease, border-color 0.1s ease; - padding: 0.85714286em 1.14285714em; -} - /******************************* Variations *******************************/ @@ -9143,92 +8380,6 @@ Floated Menu / Item } } -@media only screen and (max-width: 767.98px) { - .ui.search.short > .results { - max-height: 12.17714286em; - } - - .ui.search[class*="very short"] > .results { - max-height: 9.13285714em; - } - - .ui.search.long > .results { - max-height: 24.35428571em; - } - - .ui.search[class*="very long"] > .results { - max-height: 36.53142857em; - } -} - -@media only screen and (min-width: 768px) { - .ui.search.short > .results { - max-height: 18.26571429em; - } - - .ui.search[class*="very short"] > .results { - max-height: 13.69928571em; - } - - .ui.search.long > .results { - max-height: 36.53142857em; - } - - .ui.search[class*="very long"] > .results { - max-height: 54.79714286em; - } -} - -@media only screen and (min-width: 992px) { - .ui.search.short > .results { - max-height: 24.35428571em; - } - - .ui.search[class*="very short"] > .results { - max-height: 18.26571429em; - } - - .ui.search.long > .results { - max-height: 48.70857143em; - } - - .ui.search[class*="very long"] > .results { - max-height: 73.06285714em; - } -} - -@media only screen and (min-width: 1920px) { - .ui.search.short > .results { - max-height: 36.53142857em; - } - - .ui.search[class*="very short"] > .results { - max-height: 27.39857143em; - } - - .ui.search.long > .results { - max-height: 73.06285714em; - } - - .ui.search[class*="very long"] > .results { - max-height: 109.59428571em; - } -} - -/*------------------- - Left / Right - --------------------*/ - -.ui[class*="left aligned"].search > .results { - right: auto; - left: 0; -} - -.ui[class*="right aligned"].search > .results { - right: 0; - left: auto; -} - /*-------------- Fluid ---------------*/ From 65a9c12a1fdc31555b386d1a159c77e55ec226b7 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 22 Jul 2025 14:10:22 +0200 Subject: [PATCH 152/297] Update renovate to v41.42.2 (forgejo) (#8605) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 1be3cc0b17..aa3d48d4a7 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:41.40.0 + image: data.forgejo.org/renovate/renovate:41.42.2 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 17ed23662e..1ab8c84646 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.35.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.40.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@41.42.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 0c7612bca430896d240a51ed231cb2922d579114 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 22 Jul 2025 15:02:47 +0200 Subject: [PATCH 153/297] fix: follow symlinks for local assets (#8596) - This reverts behavior that was partially unintentionally introduced in forgejo/forgejo#8143, symbolic links were no longer followed (if they escaped the asset folder) for local assets. - Having symbolic links for user-added files is, to my understanding, a ,common usecase for NixOS and would thus have symbolic links in the asset folders. Avoiding symbolic links is not easy. - The previous code used `http.Dir`, we cannot use that as it's not of the same type. The equivalent is `os.DirFS`. - Unit test to prevent this regression from happening again. Reported-by: bloxx12 (Matrix). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8596 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/assetfs/layered.go | 11 ++--------- modules/assetfs/layered_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index 48c6728f43..2041f28bb1 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -56,14 +56,7 @@ func Local(name, base string, sub ...string) *Layer { panic(fmt.Sprintf("Unable to get absolute path for %q: %v", base, err)) } root := util.FilePathJoinAbs(base, sub...) - fsRoot, err := os.OpenRoot(root) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - return nil - } - panic(fmt.Sprintf("Unable to open layer %q", err)) - } - return &Layer{name: name, fs: fsRoot.FS(), localPath: root} + return &Layer{name: name, fs: os.DirFS(root), localPath: root} } // Bindata returns a new Layer with the given name, it serves files from the given bindata asset. @@ -80,7 +73,7 @@ type LayeredFS struct { // Layered returns a new LayeredFS with the given layers. The first layer is the top layer. func Layered(layers ...*Layer) *LayeredFS { - return &LayeredFS{layers: slices.DeleteFunc(layers, func(layer *Layer) bool { return layer == nil })} + return &LayeredFS{layers: layers} } // Open opens the named file. The caller is responsible for closing the file. diff --git a/modules/assetfs/layered_test.go b/modules/assetfs/layered_test.go index 87d1f92b00..76eeb61d83 100644 --- a/modules/assetfs/layered_test.go +++ b/modules/assetfs/layered_test.go @@ -1,4 +1,5 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package assetfs @@ -108,3 +109,30 @@ func TestLayered(t *testing.T) { assert.Equal(t, "l1", assets.GetFileLayerName("f1")) assert.Equal(t, "l2", assets.GetFileLayerName("f2")) } + +// Allow layers to read symlink outside the layer root. +func TestLayeredSymlink(t *testing.T) { + dir := t.TempDir() + dirl1 := filepath.Join(dir, "l1") + require.NoError(t, os.MkdirAll(dirl1, 0o755)) + + // Open layer in dir/l1 + layer := Local("l1", dirl1) + + // Create a file in dir/outside + fileContents := []byte("I am outside the layer") + require.NoError(t, os.WriteFile(filepath.Join(dir, "outside"), fileContents, 0o600)) + // Symlink dir/l1/outside to dir/outside + require.NoError(t, os.Symlink(filepath.Join(dir, "outside"), filepath.Join(dirl1, "outside"))) + + // Open dir/l1/outside. + f, err := layer.Open("outside") + require.NoError(t, err) + defer f.Close() + + // Confirm it contains the output of dir/outside + contents, err := io.ReadAll(f) + require.NoError(t, err) + + assert.Equal(t, fileContents, contents) +} From 34caa374d611f9a06580f5117e3a8fa7d25fe095 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 22 Jul 2025 15:32:10 +0200 Subject: [PATCH 154/297] Update dependency forgejo/release-notes-assistant to v1.3.2 (forgejo) (#8606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [forgejo/release-notes-assistant](https://code.forgejo.org/forgejo/release-notes-assistant) | patch | `v1.3.1` -> `v1.3.2` | --- ### Release Notes
forgejo/release-notes-assistant (forgejo/release-notes-assistant) ### [`v1.3.2`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.2) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.1...v1.3.2) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/94): fix: use clone --mirror to ensure git fetch retreives all branches - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/92): fix(ci): ensure commits are one second appart
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8606 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/release-notes-assistant-milestones.yml | 2 +- .forgejo/workflows/release-notes-assistant.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index 2c278c7eb5..e16c5a5507 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -6,7 +6,7 @@ on: env: RNA_WORKDIR: /srv/rna - RNA_VERSION: v1.3.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.2 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index b5eb9cd2b0..9c97d21dff 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,7 +8,7 @@ on: - labeled env: - RNA_VERSION: v1.3.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.2 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: From cf46b22272efbac3c3981b239c85d9e5060ea79e Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 22 Jul 2025 18:16:32 +0200 Subject: [PATCH 155/297] fix: upgrade fails or hang at migration[32]: Migrate maven package name concatenation (#8609) - Some SQL queries were not being run in the transaction of v32, which could lead to the migration failing or hanging indefinitely. - Use `db.WithTx` to get a `context.Context` that will make sure to run SQL queries in the transaction. - Using `db.DefaultContext` is fine to be used as parent context for starting the transaction, in all cases of starting the migration `x` and `db.DefaultContext` will point to the same engine. - Resolves forgejo/forgejo#8580 ## Testing 1. Have a v11 Forgejo database with a maven package. 2. Run this migration. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8609): upgrade fails or hang at migration[32]: Migrate maven package name concatenation Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8609 Reviewed-by: Earl Warren Reviewed-by: JSchlarb Co-authored-by: Gusted Co-committed-by: Gusted --- models/forgejo_migrations/v32.go | 87 +++++++++++++++----------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/models/forgejo_migrations/v32.go b/models/forgejo_migrations/v32.go index 81b22c585c..ce3f855694 100644 --- a/models/forgejo_migrations/v32.go +++ b/models/forgejo_migrations/v32.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "forgejo.org/models/db" "forgejo.org/models/packages" "forgejo.org/modules/json" "forgejo.org/modules/log" @@ -52,55 +53,50 @@ type mavenPackageResult struct { // ChangeMavenArtifactConcatenation resolves old dash-concatenated Maven coordinates and regenerates metadata. // Note: runs per-owner in a single transaction; failures roll back all owners. func ChangeMavenArtifactConcatenation(x *xorm.Engine) error { - sess := x.NewSession() - defer sess.Close() - - if err := sess.Begin(); err != nil { - return err - } - - // get unique owner IDs of Maven packages - var ownerIDs []*int64 - if err := sess. - Table("package"). - Select("package.owner_id"). - Where("package.type = 'maven'"). - GroupBy("package.owner_id"). - OrderBy("package.owner_id DESC"). - Find(&ownerIDs); err != nil { - return err - } - - for _, id := range ownerIDs { - if err := fixMavenArtifactPerOwner(sess, id); err != nil { - log.Error("owner %d migration failed: %v", id, err) - return err // rollback all + return db.WithTx(db.DefaultContext, func(ctx context.Context) error { + // get unique owner IDs of Maven packages + var ownerIDs []*int64 + if err := db.GetEngine(ctx). + Table("package"). + Select("package.owner_id"). + Where("package.type = 'maven'"). + GroupBy("package.owner_id"). + OrderBy("package.owner_id DESC"). + Find(&ownerIDs); err != nil { + return err } - } - return sess.Commit() + for _, id := range ownerIDs { + if err := fixMavenArtifactPerOwner(ctx, id); err != nil { + log.Error("owner %d migration failed: %v", id, err) + return err // rollback all + } + } + + return nil + }) } -func fixMavenArtifactPerOwner(sess *xorm.Session, ownerID *int64) error { - results, err := getMavenPackageResultsToUpdate(sess, ownerID) +func fixMavenArtifactPerOwner(ctx context.Context, ownerID *int64) error { + results, err := getMavenPackageResultsToUpdate(ctx, ownerID) if err != nil { return err } - if err = resolvePackageCollisions(results, sess); err != nil { + if err = resolvePackageCollisions(ctx, results); err != nil { return err } - if err = processPackageVersions(results, sess); err != nil { + if err = processPackageVersions(ctx, results); err != nil { return err } - return processPackageFiles(results, sess) + return processPackageFiles(ctx, results) } // processPackageFiles updates Maven package files and versions in the database // Returns an error if any database or processing operation fails. -func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) error { +func processPackageFiles(ctx context.Context, results []*mavenPackageResult) error { processedVersion := make(map[string][]*mavenPackageResult) for _, r := range results { @@ -113,7 +109,7 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro if r.PackageVersion.ID != r.PackageFile.VersionID { pattern := strings.TrimSuffix(r.PackageFile.Name, ".pom") + "%" // Per routers/api/packages/maven/maven.go:338, POM files already have the `IsLead`, so no update needed for this prop - if _, err := sess.Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { + if _, err := db.GetEngine(ctx).Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { return err } } @@ -128,14 +124,14 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro rs := packageResults[0] - pf, md, err := parseMetadata(sess, rs) + pf, md, err := parseMetadata(ctx, rs) if err != nil { return err } if pf != nil && md != nil && md.GroupID == rs.GroupID && md.ArtifactID == rs.ArtifactID { if pf.VersionID != rs.PackageFile.VersionID { - if _, err := sess.ID(pf.ID).Cols("version_id").Update(pf); err != nil { + if _, err := db.GetEngine(ctx).ID(pf.ID).Cols("version_id").Update(pf); err != nil { return err } } @@ -150,11 +146,9 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro // parseMetadata retrieves metadata for a Maven package file from the database and decodes it into a Metadata object. // Returns the associated PackageFile, Metadata, and any error encountered during processing. -func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { - ctx := context.Background() - +func parseMetadata(ctx context.Context, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { var pf packages.PackageFile - found, err := sess.Table(pf). + found, err := db.GetEngine(ctx).Table(pf). Where("version_id = ?", snapshot.PackageFile.VersionID). // still the old id And("lower_name = ?", "maven-metadata.xml"). Get(&pf) @@ -183,7 +177,7 @@ func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages. // processPackageVersions processes Maven package versions by updating metadata or inserting new records as necessary. // It avoids redundant updates by tracking already processed versions using a map. Returns an error on failure. -func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) error { +func processPackageVersions(ctx context.Context, results []*mavenPackageResult) error { processedVersion := make(map[string]int64) for _, r := range results { @@ -196,14 +190,14 @@ func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) e // for non collisions, just update the metadata if r.PackageVersion.PackageID == r.Package.ID { - if _, err := sess.ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { + if _, err := db.GetEngine(ctx).ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { return err } } else { log.Info("Create new maven package version for %s:%s", r.PackageName, r.PackageVersion.Version) r.PackageVersion.ID = 0 r.PackageVersion.PackageID = r.Package.ID - if _, err := sess.Insert(r.PackageVersion); err != nil { + if _, err := db.GetEngine(ctx).Insert(r.PackageVersion); err != nil { return err } } @@ -216,10 +210,9 @@ func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) e // getMavenPackageResultsToUpdate retrieves Maven package results that need updates based on the owner ID. // It processes POM metadata, fixes package inconsistencies, and filters corrupted package versions. -func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mavenPackageResult, error) { - ctx := context.Background() +func getMavenPackageResultsToUpdate(ctx context.Context, ownerID *int64) ([]*mavenPackageResult, error) { var candidates []*mavenPackageResult - if err := sess. + if err := db.GetEngine(ctx). Table("package_file"). Select("package_file.*, package_version.*, package.*"). Join("INNER", "package_version", "package_version.id = package_file.version_id"). @@ -265,7 +258,7 @@ func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mave // resolvePackageCollisions handles name collisions by keeping the first existing record and inserting new Package records for subsequent collisions. // Returns a map from PackageName to its resolved Package.ID. -func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) error { +func resolvePackageCollisions(ctx context.Context, results []*mavenPackageResult) error { // Group new names by lowerName collisions := make(map[string][]string) for _, r := range results { @@ -292,7 +285,7 @@ func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) } else if list[0] == r.PackageName { pkgIDByName[r.PackageName] = r.Package.ID - if _, err = sess.ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { + if _, err = db.GetEngine(ctx).ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { return err } // create a new entry @@ -300,7 +293,7 @@ func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) log.Info("Create new maven package for %s", r.Package.Name) r.Package.ID = 0 - if _, err = sess.Insert(r.Package); err != nil { + if _, err = db.GetEngine(ctx).Insert(r.Package); err != nil { return err } From d74c9daa8af64f9165d62a10828aa10c297cbe13 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 22 Jul 2025 18:18:19 +0200 Subject: [PATCH 156/297] chore: disable E2E test for webkit (#8611) As far as I can see and tell, the newest webkit version contains a regression that makes this specific test fail. The screenshots that are uploaded upon failure do not seem to suggest that this test should fail. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8611 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Michael Kriese Co-authored-by: Gusted Co-committed-by: Gusted --- tests/e2e/markup.test.e2e.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/markup.test.e2e.ts b/tests/e2e/markup.test.e2e.ts index 398a0a6300..b26e83661b 100644 --- a/tests/e2e/markup.test.e2e.ts +++ b/tests/e2e/markup.test.e2e.ts @@ -5,7 +5,8 @@ import {expect} from '@playwright/test'; import {save_visual, test} from './utils_e2e.ts'; -test('markup with #xyz-mode-only', async ({page}) => { +test('markup with #xyz-mode-only', async ({page}, workerInfo) => { + test.skip(['webkit', 'Mobile Safari'].includes(workerInfo.project.name), 'Newest version contains a regression'); const response = await page.goto('/user2/repo1/issues/1'); expect(response?.status()).toBe(200); From ed3c5588ad2bbd8fccbc30092cd5b3e1032bccbb Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 22 Jul 2025 19:26:22 +0200 Subject: [PATCH 157/297] Update renovate to v41.42.5 (forgejo) (#8615) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index aa3d48d4a7..a4e438a879 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:41.42.2 + image: data.forgejo.org/renovate/renovate:41.42.5 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 1ab8c84646..06d71a51c1 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.35.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.42.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@41.42.5 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 8235a9752dfb327826b85311b7ddb4c3a5fcbf9d Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Tue, 22 Jul 2025 20:51:58 +0200 Subject: [PATCH 158/297] fix(i18n): improve en locale (#8593) This commit has the following: * partial port of changes from [gitea#35053](https://github.com/go-gitea/gitea/pull/35053) ([corresponding WCP](https://codeberg.org/forgejo/forgejo/pulls/8591)) * some changes were applied * some strings we've fixed ourself before * some strings we don't have * a few fixes I wanted to propose but didn't want to dedicate a PR just to add a full stop to one string Co-authored-by: DJ Phoenix Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8593 Reviewed-by: Robert Wolff Reviewed-by: Gusted --- options/locale/locale_en-US.ini | 24 ++++++++++---------- options/locale_next/locale_en-US.json | 2 +- tests/integration/api_packages_cargo_test.go | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c01d1464cb..391d3d06f0 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -372,7 +372,7 @@ no_reply_address = Hidden email domain no_reply_address_helper = Domain name for users with a hidden email address. For example, the username "joe" will be logged in Git as "joe@noreply.example.org" if the hidden email domain is set to "noreply.example.org". password_algorithm = Password hash algorithm invalid_password_algorithm = Invalid password hash algorithm -password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. +password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strengths. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. enable_update_checker = Enable update checker env_config_keys = Environment Configuration env_config_keys_prompt = The following environment variables will also be applied to your configuration file: @@ -479,7 +479,7 @@ email_domain_blacklisted = You cannot register with your email address. authorize_application = Authorize Application authorize_redirect_notice = You will be redirected to %s if you authorize this application. authorize_application_created_by = This application was created by %s. -authorize_application_description = If you grant the access, it will be able to access and write to all your account information, including private repos and organizations. +authorize_application_description = If you grant access, it will be able to access and write to all your account information, including private repos and organizations. authorize_title = Authorize "%s" to access your account? authorization_failed = Authorization failed authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you have tried to authorize. @@ -838,7 +838,7 @@ activations_pending = Activations pending can_not_add_email_activations_pending = There is a pending activation, try again in a few minutes if you want to add a new email. delete_email = Remove email_deletion = Remove email address -email_deletion_desc = The email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue? +email_deletion_desc = This email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue? email_deletion_success = The email address has been removed. theme_update_success = Your theme was updated. theme_update_error = The selected theme does not exist. @@ -1056,7 +1056,7 @@ user_block_success = The user has been blocked successfully. user_block_yourself = You cannot block yourself. quota.applies_to_user = The following quota rules apply to your account -quota.applies_to_org = The following quota rules apply to this organisation +quota.applies_to_org = The following quota rules apply to this organization quota.rule.exceeded = Exceeded quota.rule.exceeded.helper = The total size of objects for this rule has exceeded the quota. quota.rule.no_limit = Unlimited @@ -1153,7 +1153,7 @@ mirror_sync = synced mirror_sync_on_commit = Sync when commits are pushed mirror_address = Clone from URL mirror_address_desc = Put any required credentials in the Authorization section. -mirror_address_url_invalid = The provided URL is invalid. You must escape all components of the URL correctly. +mirror_address_url_invalid = The provided URL is invalid. Make sure that components of the URL are escaped correctly. mirror_address_protocol_invalid = The provided URL is invalid. Only http(s):// or git:// locations can be used for mirroring. mirror_lfs = Large File Storage (LFS) mirror_lfs_desc = Activate mirroring of LFS data. @@ -1251,7 +1251,7 @@ migrate_repo = Migrate repository migrate.repo_desc_helper = Leave empty to import existing description migrate.clone_address = Migrate / Clone from URL migrate.clone_address_desc = The HTTP(S) or Git "clone" URL of an existing repository -migrate.github_token_desc = You can put one or more tokens with comma separated here to make migrating faster because of GitHub API rate limit. WARN: Abusing this feature may violate the service provider's policy and lead to account blocking. +migrate.github_token_desc = You can put one or more tokens here separated by commas to make migrating faster by circumventing the GitHub API rate limit. WARNING: Abusing this feature may violate the service provider's policy and may lead to getting your account(s) blocked. migrate.clone_local_path = or a local server path migrate.permission_denied = You are not allowed to import local repositories. migrate.permission_denied_blocked = You cannot import from disallowed hosts, please ask the admin to check ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS settings. @@ -1919,7 +1919,7 @@ pulls.select_commit_hold_shift_for_range = Select commit. Hold shift + click to pulls.review_only_possible_for_full_diff = Review is only possible when viewing the full diff pulls.filter_changes_by_commit = Filter by commit pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request. -pulls.nothing_to_compare_have_tag = The selected branch/tag are equal. +pulls.nothing_to_compare_have_tag = The selected branches/tags are equal. pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty. pulls.has_pull_request = `A pull request between these branches already exists: %[2]s#%[3]d` pulls.create = Create pull request @@ -2088,7 +2088,7 @@ milestones.filter_sort.most_issues = Most issues milestones.filter_sort.least_issues = Least issues signing.will_sign = This commit will be signed with key "%s". -signing.wont_sign.error = There was an error whilst checking if the commit could be signed. +signing.wont_sign.error = There was an error while checking if the commit could be signed. signing.wont_sign.nokey = This instance has no key to sign this commit with. signing.wont_sign.never = Commits are never signed. signing.wont_sign.always = Commits are always signed. @@ -3065,9 +3065,9 @@ dashboard.resync_all_sshprincipals = Update the ".ssh/authorized_principals" fil dashboard.resync_all_hooks = Resynchronize pre-receive, update and post-receive hooks of all repositories dashboard.reinit_missing_repos = Reinitialize all missing Git repositories for which records exist dashboard.sync_external_users = Synchronize external user data -dashboard.cleanup_hook_task_table = Cleanup hook_task table -dashboard.cleanup_packages = Cleanup expired packages -dashboard.cleanup_actions = Cleanup expired logs and artifacts from actions +dashboard.cleanup_hook_task_table = Clean up hook_task table +dashboard.cleanup_packages = Clean up expired packages +dashboard.cleanup_actions = Clean up expired logs and artifacts from actions dashboard.server_uptime = Server uptime dashboard.current_goroutine = Current goroutines dashboard.current_memory_usage = Current memory usage @@ -3797,7 +3797,7 @@ owner.settings.cargo.initialize.success = The Cargo index was successfully creat owner.settings.cargo.rebuild = Rebuild index owner.settings.cargo.rebuild.description = Rebuilding can be useful if the index is not synchronized with the stored Cargo packages. owner.settings.cargo.rebuild.error = Failed to rebuild Cargo index: %v -owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuild. +owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuilt. owner.settings.cargo.rebuild.no_index = Cannot rebuild, no index is initialized. owner.settings.cleanuprules.title = Cleanup rules owner.settings.cleanuprules.add = Add cleanup rule diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 3c877ff627..be3d7f92c4 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -103,7 +103,7 @@ "editor.textarea.tab_hint": "Line already indented. Press Tab again or Escape to leave the editor.", "editor.textarea.shift_tab_hint": "No indentation on this line. Press Shift + Tab again or Escape to leave the editor.", "admin.dashboard.cleanup_offline_runners": "Cleanup offline runners", - "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more", + "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more.", "avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels", "og.repo.summary_card.alt_description": "Summary card of repository %[1]s, described as: %[2]s", "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." diff --git a/tests/integration/api_packages_cargo_test.go b/tests/integration/api_packages_cargo_test.go index d42dc61c62..9e03d9e8a8 100644 --- a/tests/integration/api_packages_cargo_test.go +++ b/tests/integration/api_packages_cargo_test.go @@ -441,7 +441,7 @@ func TestRebuildCargo(t *testing.T) { flashCookie := session.GetCookie(gitea_context.CookieNameFlash) assert.NotNil(t, flashCookie) - assert.Equal(t, "success%3DThe%2BCargo%2Bindex%2Bwas%2Bsuccessfully%2Brebuild.", flashCookie.Value) + assert.Equal(t, "success%3DThe%2BCargo%2Bindex%2Bwas%2Bsuccessfully%2Brebuilt.", flashCookie.Value) }) }) } From 6007f2d3d51ae0af19d947469923177947715acf Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 22 Jul 2025 23:40:30 +0200 Subject: [PATCH 159/297] fix: make the action feed resilient to database inconsistencies (#8617) This reverts commit 7380eac5a2a2c04e6e8948f74d1b71dee2ffb61e. Resolves forgejo/forgejo#8612 It is possible for the action feed to reference deleted repositories the `INNER JOIN` will make sure that these are filtered out. We cannot filter these out after the fact, because the value of `count` will still be incorrect. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8617 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- models/activities/action.go | 5 ++++- models/activities/action_test.go | 18 ++++++++++++++++++ models/fixtures/action.yml | 8 ++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/models/activities/action.go b/models/activities/action.go index 8592f81414..f928ad6784 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -473,8 +473,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, err } + sess := db.GetEngine(ctx).Where(cond). + Select("`action`.*"). // this line will avoid select other joined table's columns + Join("INNER", "repository", "`repository`.id = `action`.repo_id") + opts.SetDefaultValues() - sess := db.GetEngine(ctx).Where(cond) sess = db.SetSessionPagination(sess, &opts) actions := make([]*Action, 0, opts.PageSize) diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 47dbd8ac2d..161d05bbfa 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -227,6 +227,24 @@ func TestNotifyWatchers(t *testing.T) { }) } +func TestGetFeedsCorrupted(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ + ID: 8, + RepoID: 1700, + }) + + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + RequestedUser: user, + Actor: user, + IncludePrivate: true, + }) + require.NoError(t, err) + assert.Empty(t, actions) + assert.Equal(t, int64(0), count) +} + func TestConsistencyUpdateAction(t *testing.T) { if !setting.Database.Type.IsSQLite3() { t.Skip("Test is only for SQLite database.") diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index a97e94fbf4..f1592d4569 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -59,6 +59,14 @@ created_unix: 1603011540 # grouped with id:7 - id: 8 + user_id: 1 + op_type: 12 # close issue + act_user_id: 1 + repo_id: 1700 # dangling intentional + is_private: false + created_unix: 1603011541 + +- id: 9 user_id: 34 op_type: 12 # close issue act_user_id: 34 From c17dd6c5b3552e3ac5e4d460edaf5743b574f53e Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 22 Jul 2025 23:55:46 +0200 Subject: [PATCH 160/297] Update module github.com/go-webauthn/webauthn to v0.13.4 (forgejo) (#8578) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8578 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 3dab99daf8..8a7c8b6faa 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-openapi/spec v0.21.0 github.com/go-sql-driver/mysql v1.9.3 - github.com/go-webauthn/webauthn v0.13.3 + github.com/go-webauthn/webauthn v0.13.4 github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 @@ -158,7 +158,7 @@ require ( 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 - github.com/fxamacker/cbor/v2 v2.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-enry/go-oniguruma v1.2.1 // indirect diff --git a/go.sum b/go.sum index 243dd6154d..a291e6c4b7 100644 --- a/go.sum +++ b/go.sum @@ -202,8 +202,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= -github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 h1:j2TrkUG/NATGi/EQS+MvEoF79CxiRUmT16ErFroNcKI= github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9/go.mod h1:cJ9Ye0ZNSMN7RzZDBRY3E+8M3Bpf/R1JX22Ir9yX6WI= github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 h1:I2nuhyVI/48VXoRCCZR2hYBgnSXa+EuDJf/VyX06TC0= @@ -250,8 +250,8 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/go-webauthn/webauthn v0.13.3 h1:rvX539Gy9U4xAuFQRFJtkgoH5E1GEUyIVbHUDC89Mo4= -github.com/go-webauthn/webauthn v0.13.3/go.mod h1:H9EdVnxXFMMJyx8Nd/OL3aFFEop3Rb+Af1naR0IbuUQ= +github.com/go-webauthn/webauthn v0.13.4 h1:q68qusWPcqHbg9STSxBLBHnsKaLxNO0RnVKaAqMuAuQ= +github.com/go-webauthn/webauthn v0.13.4/go.mod h1:MglN6OH9ECxvhDqoq1wMoF6P6JRYDiQpC9nc5OomQmI= github.com/go-webauthn/x v0.1.23 h1:9lEO0s+g8iTyz5Vszlg/rXTGrx3CjcD0RZQ1GPZCaxI= github.com/go-webauthn/x v0.1.23/go.mod h1:AJd3hI7NfEp/4fI6T4CHD753u91l510lglU7/NMN6+E= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= From d87e2e7e409f74e03d17e0efa285f6487ed41b24 Mon Sep 17 00:00:00 2001 From: floss4good Date: Wed, 23 Jul 2025 00:20:15 +0200 Subject: [PATCH 161/297] feat: Admin interface for abuse reports (#7905) - Implementation of milestone 5. from **Task F. Moderation features: Reporting** (part of [amendment of the workplan](https://codeberg.org/forgejo/sustainability/src/branch/main/2022-12-01-nlnet/2025-02-07-extended-workplan.md#task-f-moderation-features-reporting) for NLnet 2022-12-035): `5. Forgejo admins can see a list of reports` There is a lot of room for improvements, but it was decided to start with a basic version so that feedback can be collected from real-life usages (based on which the UI might change a lot). - Also covers milestone 2. from same **Task F. Moderation features: Reporting**: `2. Reports from multiple users are combined in the database and don't create additional reports.` But instead of combining the reports when stored, they are grouped when retrieved (it was concluded _that it might be preferable to take care of the deduplication while implementing the admin interface_; see https://codeberg.org/forgejo/forgejo/pulls/7939#issuecomment-4841754 for more details). --- Follow-up of !6977 ### See also: - forgejo/design#30 --- This adds a new _Moderation reports_ section (/admin/moderation/reports) within the _Site administration_ page, where administrators can see an overview with the submitted abuse reports that are still open (not yet handled in any way). When multiple reports exist for the same content (submitted by distinct users) only the first one will be shown in the list and a counter can be seen on the right side (indicating the number of open reports for the same content type and ID). Clicking on the counter or the icon from the right side will open the details page where a list with all the reports (when multiple) linked to the reported content is available, as well as any shadow copy saved for the current report(s). The new section is available only when moderation in enabled ([moderation] ENABLED config is set as true within app.ini). Discussions regarding the UI/UX started with https://codeberg.org/forgejo/design/issues/30#issuecomment-2908849 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7905 Reviewed-by: Otto Reviewed-by: jerger Co-authored-by: floss4good Co-committed-by: floss4good --- models/issues/moderation.go | 31 +++- models/issues/moderation_test.go | 70 ++++++++ models/moderation/abuse_report.go | 15 +- models/moderation/abuse_report_detailed.go | 135 +++++++++++++++ models/moderation/shadow_copy.go | 16 ++ models/repo/moderation.go | 18 ++ models/repo/moderation_test.go | 51 ++++++ models/user/moderation.go | 20 +++ models/user/moderation_test.go | 60 +++++++ options/locale_next/locale_en-US.json | 4 + routers/web/admin/reports.go | 157 +++++++++++++++++ routers/web/web.go | 9 +- services/moderation/moderating.go | 41 +++++ .../admin/moderation/report_details.tmpl | 65 +++++++ templates/admin/moderation/reports.tmpl | 66 ++++++++ templates/admin/navbar.tmpl | 5 + tests/integration/admin_moderation_test.go | 158 ++++++++++++++++++ .../abuse_report.yml | 111 ++++++++++++ .../comment.yml | 21 +++ .../TestAdminModerationViewReports/issue.yml | 23 +++ .../issue_index.yml | 7 + .../repository.yml | 45 +++++ .../TestAdminModerationViewReports/user.yml | 108 ++++++++++++ 23 files changed, 1230 insertions(+), 6 deletions(-) create mode 100644 models/issues/moderation_test.go create mode 100644 models/moderation/abuse_report_detailed.go create mode 100644 models/repo/moderation_test.go create mode 100644 models/user/moderation_test.go create mode 100644 routers/web/admin/reports.go create mode 100644 services/moderation/moderating.go create mode 100644 templates/admin/moderation/report_details.tmpl create mode 100644 templates/admin/moderation/reports.tmpl create mode 100644 tests/integration/admin_moderation_test.go create mode 100644 tests/integration/fixtures/TestAdminModerationViewReports/abuse_report.yml create mode 100644 tests/integration/fixtures/TestAdminModerationViewReports/comment.yml create mode 100644 tests/integration/fixtures/TestAdminModerationViewReports/issue.yml create mode 100644 tests/integration/fixtures/TestAdminModerationViewReports/issue_index.yml create mode 100644 tests/integration/fixtures/TestAdminModerationViewReports/repository.yml create mode 100644 tests/integration/fixtures/TestAdminModerationViewReports/user.yml diff --git a/models/issues/moderation.go b/models/issues/moderation.go index 921f770d4d..9afb711d65 100644 --- a/models/issues/moderation.go +++ b/models/issues/moderation.go @@ -5,6 +5,7 @@ package issues import ( "context" + "strconv" "forgejo.org/models/moderation" "forgejo.org/modules/json" @@ -24,6 +25,21 @@ type IssueData struct { UpdatedUnix timeutil.TimeStamp } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (cd IssueData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "RepoID", Value: strconv.FormatInt(cd.RepoID, 10)}, + {Key: "Index", Value: strconv.FormatInt(cd.Index, 10)}, + {Key: "PosterID", Value: strconv.FormatInt(cd.PosterID, 10)}, + {Key: "Title", Value: cd.Title}, + {Key: "Content", Value: cd.Content}, + {Key: "ContentVersion", Value: strconv.Itoa(cd.ContentVersion)}, + {Key: "CreatedUnix", Value: cd.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: cd.UpdatedUnix.AsLocalTime().String()}, + } +} + // newIssueData creates a trimmed down issue to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newIssueData(issue *Issue) IssueData { @@ -31,8 +47,8 @@ func newIssueData(issue *Issue) IssueData { RepoID: issue.RepoID, Index: issue.Index, PosterID: issue.PosterID, - Content: issue.Content, Title: issue.Title, + Content: issue.Content, ContentVersion: issue.ContentVersion, CreatedUnix: issue.CreatedUnix, UpdatedUnix: issue.UpdatedUnix, @@ -50,6 +66,19 @@ type CommentData struct { UpdatedUnix timeutil.TimeStamp } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (cd CommentData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "PosterID", Value: strconv.FormatInt(cd.PosterID, 10)}, + {Key: "IssueID", Value: strconv.FormatInt(cd.IssueID, 10)}, + {Key: "Content", Value: cd.Content}, + {Key: "ContentVersion", Value: strconv.Itoa(cd.ContentVersion)}, + {Key: "CreatedUnix", Value: cd.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: cd.UpdatedUnix.AsLocalTime().String()}, + } +} + // newCommentData creates a trimmed down comment to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newCommentData(comment *Comment) CommentData { diff --git a/models/issues/moderation_test.go b/models/issues/moderation_test.go new file mode 100644 index 0000000000..adb07bd63a --- /dev/null +++ b/models/issues/moderation_test.go @@ -0,0 +1,70 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package issues_test + +import ( + "testing" + + "forgejo.org/models/issues" + "forgejo.org/models/moderation" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +const ( + tsCreated timeutil.TimeStamp = timeutil.TimeStamp(1753093500) // 2025-07-21 10:25:00 UTC + tsUpdated timeutil.TimeStamp = timeutil.TimeStamp(1753093525) // 2025-07-21 10:25:25 UTC +) + +func testShadowCopyField(t *testing.T, scField moderation.ShadowCopyField, key, value string) { + assert.Equal(t, key, scField.Key) + assert.Equal(t, value, scField.Value) +} + +func TestIssueDataGetFieldsMap(t *testing.T) { + id := issues.IssueData{ + RepoID: 2001, + Index: 2, + PosterID: 1002, + Title: "Professional marketing services", + Content: "Visit my website at promote-your-business.biz for a list of available services.", + ContentVersion: 0, + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + } + scFields := id.GetFieldsMap() + + if assert.Len(t, scFields, 8) { + testShadowCopyField(t, scFields[0], "RepoID", "2001") + testShadowCopyField(t, scFields[1], "Index", "2") + testShadowCopyField(t, scFields[2], "PosterID", "1002") + testShadowCopyField(t, scFields[3], "Title", "Professional marketing services") + testShadowCopyField(t, scFields[4], "Content", "Visit my website at promote-your-business.biz for a list of available services.") + testShadowCopyField(t, scFields[5], "ContentVersion", "0") + testShadowCopyField(t, scFields[6], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[7], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + } +} + +func TestCommentDataGetFieldsMap(t *testing.T) { + cd := issues.CommentData{ + PosterID: 1002, + IssueID: 3001, + Content: "Check out [alexsmith/website](/alexsmith/website)", + ContentVersion: 0, + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + } + scFields := cd.GetFieldsMap() + + if assert.Len(t, scFields, 6) { + testShadowCopyField(t, scFields[0], "PosterID", "1002") + testShadowCopyField(t, scFields[1], "IssueID", "3001") + testShadowCopyField(t, scFields[2], "Content", "Check out [alexsmith/website](/alexsmith/website)") + testShadowCopyField(t, scFields[3], "ContentVersion", "0") + testShadowCopyField(t, scFields[4], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[5], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + } +} diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go index 3a6244ef4c..9852268910 100644 --- a/models/moderation/abuse_report.go +++ b/models/moderation/abuse_report.go @@ -47,14 +47,21 @@ const ( AbuseCategoryTypeIllegalContent // 4 ) +var AbuseCategoriesTranslationKeys = map[AbuseCategoryType]string{ + AbuseCategoryTypeSpam: "moderation.abuse_category.spam", + AbuseCategoryTypeMalware: "moderation.abuse_category.malware", + AbuseCategoryTypeIllegalContent: "moderation.abuse_category.illegal_content", + AbuseCategoryTypeOther: "moderation.abuse_category.other_violations", +} + // GetAbuseCategoriesList returns a list of pairs with the available abuse category types // and their corresponding translation keys func GetAbuseCategoriesList() []AbuseCategoryItem { return []AbuseCategoryItem{ - {AbuseCategoryTypeSpam, "moderation.abuse_category.spam"}, - {AbuseCategoryTypeMalware, "moderation.abuse_category.malware"}, - {AbuseCategoryTypeIllegalContent, "moderation.abuse_category.illegal_content"}, - {AbuseCategoryTypeOther, "moderation.abuse_category.other_violations"}, + {AbuseCategoryTypeSpam, AbuseCategoriesTranslationKeys[AbuseCategoryTypeSpam]}, + {AbuseCategoryTypeMalware, AbuseCategoriesTranslationKeys[AbuseCategoryTypeMalware]}, + {AbuseCategoryTypeIllegalContent, AbuseCategoriesTranslationKeys[AbuseCategoryTypeIllegalContent]}, + {AbuseCategoryTypeOther, AbuseCategoriesTranslationKeys[AbuseCategoryTypeOther]}, } } diff --git a/models/moderation/abuse_report_detailed.go b/models/moderation/abuse_report_detailed.go new file mode 100644 index 0000000000..265d143709 --- /dev/null +++ b/models/moderation/abuse_report_detailed.go @@ -0,0 +1,135 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "context" + "fmt" + "strings" + + "forgejo.org/models/db" + "forgejo.org/modules/setting" + "forgejo.org/modules/timeutil" + + "xorm.io/builder" +) + +type AbuseReportDetailed struct { + AbuseReport `xorm:"extends"` + ReportedTimes int // only for overview + ReporterName string + ContentReference string + ShadowCopyDate timeutil.TimeStamp // only for details + ShadowCopyRawValue string // only for details +} + +func (ard AbuseReportDetailed) ContentTypeIconName() string { + switch ard.ContentType { + case ReportedContentTypeUser: + return "octicon-person" + case ReportedContentTypeRepository: + return "octicon-repo" + case ReportedContentTypeIssue: + return "octicon-issue-opened" + case ReportedContentTypeComment: + return "octicon-comment" + default: + return "octicon-question" + } +} + +func (ard AbuseReportDetailed) ContentURL() string { + switch ard.ContentType { + case ReportedContentTypeUser: + return strings.TrimLeft(ard.ContentReference, "@") + case ReportedContentTypeIssue: + return strings.ReplaceAll(ard.ContentReference, "#", "/issues/") + default: + return ard.ContentReference + } +} + +func GetOpenReports(ctx context.Context) ([]*AbuseReportDetailed, error) { + var reports []*AbuseReportDetailed + + // - For PostgreSQL user table name should be escaped. + // - Escaping can be done with double quotes (") but this doesn't work for MariaDB. + // - For SQLite index column name should be escaped. + // - Escaping can be done with double quotes (") or backticks (`). + // - For MariaDB/MySQL there is no need to escape the above. + // - Therefore we will use double quotes (") but only for PostgreSQL and SQLite. + identifierEscapeChar := `` + if setting.Database.Type.IsPostgreSQL() || setting.Database.Type.IsSQLite3() { + identifierEscapeChar = `"` + } + + err := db.GetEngine(ctx).SQL(fmt.Sprintf(`SELECT AR.*, ARD.reported_times, U.name AS reporter_name, REFS.ref AS content_reference + FROM abuse_report AR + INNER JOIN ( + SELECT min(id) AS id, count(id) AS reported_times + FROM abuse_report + WHERE status = %[2]d + GROUP BY content_type, content_id + ) ARD ON ARD.id = AR.id + LEFT JOIN %[1]suser%[1]s U ON U.id = AR.reporter_id + LEFT JOIN ( + SELECT %[3]d AS type, id, concat('@', name) AS "ref" + FROM %[1]suser%[1]s WHERE id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[3]d + ) + UNION + SELECT %[4]d AS "type", id, concat(owner_name, '/', name) AS "ref" + FROM repository WHERE id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[4]d + ) + UNION + SELECT %[5]d AS "type", I.id, concat(IR.owner_name, '/', IR.name, '#', I.%[1]sindex%[1]s) AS "ref" + FROM issue I + LEFT JOIN repository IR ON IR.id = I.repo_id + WHERE I.id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[5]d + ) + UNION + SELECT %[6]d AS "type", C.id, concat(CIR.owner_name, '/', CIR.name, '/issues/', CI.%[1]sindex%[1]s, '#issuecomment-', C.id) AS "ref" + FROM comment C + LEFT JOIN issue CI ON CI.id = C.issue_id + LEFT JOIN repository CIR ON CIR.id = CI.repo_id + WHERE C.id IN ( + SELECT content_id FROM abuse_report WHERE status = %[2]d AND content_type = %[6]d + ) + ) REFS ON REFS.type = AR.content_type AND REFS.id = AR.content_id + ORDER BY AR.created_unix ASC`, identifierEscapeChar, ReportStatusTypeOpen, + ReportedContentTypeUser, ReportedContentTypeRepository, ReportedContentTypeIssue, ReportedContentTypeComment)). + Find(&reports) + if err != nil { + return nil, err + } + return reports, nil +} + +func GetOpenReportsByTypeAndContentID(ctx context.Context, contentType ReportedContentType, contentID int64) ([]*AbuseReportDetailed, error) { + var reports []*AbuseReportDetailed + + // Some remarks concerning PostgreSQL: + // - user table should be escaped (e.g. `user`); + // - tried to use aliases for table names but errors like 'invalid reference to FROM-clause entry' + // or 'missing FROM-clause entry' were returned; + err := db.GetEngine(ctx). + Select("abuse_report.*, `user`.name AS reporter_name, abuse_report_shadow_copy.created_unix AS shadow_copy_date, abuse_report_shadow_copy.raw_value AS shadow_copy_raw_value"). + Table("abuse_report"). + Join("LEFT", "user", "`user`.id = abuse_report.reporter_id"). + Join("LEFT", "abuse_report_shadow_copy", "abuse_report_shadow_copy.id = abuse_report.shadow_copy_id"). + Where(builder.Eq{ + "content_type": contentType, + "content_id": contentID, + "status": ReportStatusTypeOpen, + }). + Asc("abuse_report.created_unix"). + Find(&reports) + if err != nil { + return nil, err + } + + return reports, nil +} diff --git a/models/moderation/shadow_copy.go b/models/moderation/shadow_copy.go index d363610a48..8abb32e8ec 100644 --- a/models/moderation/shadow_copy.go +++ b/models/moderation/shadow_copy.go @@ -26,6 +26,22 @@ func (sc AbuseReportShadowCopy) NullableID() sql.NullInt64 { return sql.NullInt64{Int64: sc.ID, Valid: sc.ID > 0} } +// ShadowCopyField defines a pair of a value stored within the shadow copy +// (of some content reported as abusive) and a corresponding key (caption). +// A list of such pairs is used when rendering shadow copies for admins reviewing abuse reports. +type ShadowCopyField struct { + Key string + Value string +} + +// ShadowCopyData interface should be implemented by the type structs used for marshaling/unmarshaling the fields +// preserved as shadow copies for abusive content reports (i.e. UserData, RepositoryData, IssueData, CommentData). +type ShadowCopyData interface { + // GetFieldsMap returns a list of pairs with the fields stored within shadow copies + // of content reported as abusive, to be used when rendering a shadow copy in the admin UI. + GetFieldsMap() []ShadowCopyField +} + func init() { // RegisterModel will create the table if does not already exist // or any missing columns if the table was previously created. diff --git a/models/repo/moderation.go b/models/repo/moderation.go index d7b87dffa0..0d2672227b 100644 --- a/models/repo/moderation.go +++ b/models/repo/moderation.go @@ -5,6 +5,8 @@ package repo import ( "context" + "strconv" + "strings" "forgejo.org/models/moderation" "forgejo.org/modules/json" @@ -25,6 +27,22 @@ type RepositoryData struct { UpdatedUnix timeutil.TimeStamp } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (rd RepositoryData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "OwnerID", Value: strconv.FormatInt(rd.OwnerID, 10)}, + {Key: "OwnerName", Value: rd.OwnerName}, + {Key: "Name", Value: rd.Name}, + {Key: "Description", Value: rd.Description}, + {Key: "Website", Value: rd.Website}, + {Key: "Topics", Value: strings.Join(rd.Topics, ", ")}, + {Key: "Avatar", Value: rd.Avatar}, + {Key: "CreatedUnix", Value: rd.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: rd.UpdatedUnix.AsLocalTime().String()}, + } +} + // newRepositoryData creates a trimmed down repository to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newRepositoryData(repo *Repository) RepositoryData { diff --git a/models/repo/moderation_test.go b/models/repo/moderation_test.go new file mode 100644 index 0000000000..9852db1b51 --- /dev/null +++ b/models/repo/moderation_test.go @@ -0,0 +1,51 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package repo_test + +import ( + "testing" + + "forgejo.org/models/moderation" + "forgejo.org/models/repo" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +const ( + tsCreated timeutil.TimeStamp = timeutil.TimeStamp(1753093500) // 2025-07-21 10:25:00 UTC + tsUpdated timeutil.TimeStamp = timeutil.TimeStamp(1753093525) // 2025-07-21 10:25:25 UTC +) + +func testShadowCopyField(t *testing.T, scField moderation.ShadowCopyField, key, value string) { + assert.Equal(t, key, scField.Key) + assert.Equal(t, value, scField.Value) +} + +func TestRepositoryDataGetFieldsMap(t *testing.T) { + rd := repo.RepositoryData{ + OwnerID: 1002, + OwnerName: "alexsmith", + Name: "website", + Description: "My static website.", + Website: "http://promote-your-business.biz", + Topics: []string{"bulk-email", "email-services"}, + Avatar: "avatar-hash-repo-2002", + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + } + scFields := rd.GetFieldsMap() + + if assert.Len(t, scFields, 9) { + testShadowCopyField(t, scFields[0], "OwnerID", "1002") + testShadowCopyField(t, scFields[1], "OwnerName", "alexsmith") + testShadowCopyField(t, scFields[2], "Name", "website") + testShadowCopyField(t, scFields[3], "Description", "My static website.") + testShadowCopyField(t, scFields[4], "Website", "http://promote-your-business.biz") + testShadowCopyField(t, scFields[5], "Topics", "bulk-email, email-services") + testShadowCopyField(t, scFields[6], "Avatar", "avatar-hash-repo-2002") + testShadowCopyField(t, scFields[7], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[8], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + } +} diff --git a/models/user/moderation.go b/models/user/moderation.go index f9c16a17b3..17901f84ec 100644 --- a/models/user/moderation.go +++ b/models/user/moderation.go @@ -37,6 +37,26 @@ type UserData struct { //revive:disable-line:exported AvatarEmail string } +// Implements GetFieldsMap() from ShadowCopyData interface, returning a list of pairs +// to be used when rendering the shadow copy for admins reviewing the corresponding abuse report(s). +func (ud UserData) GetFieldsMap() []moderation.ShadowCopyField { + return []moderation.ShadowCopyField{ + {Key: "Name", Value: ud.Name}, + {Key: "FullName", Value: ud.FullName}, + {Key: "Email", Value: ud.Email}, + {Key: "LoginName", Value: ud.LoginName}, + {Key: "Location", Value: ud.Location}, + {Key: "Website", Value: ud.Website}, + {Key: "Pronouns", Value: ud.Pronouns}, + {Key: "Description", Value: ud.Description}, + {Key: "CreatedUnix", Value: ud.CreatedUnix.AsLocalTime().String()}, + {Key: "UpdatedUnix", Value: ud.UpdatedUnix.AsLocalTime().String()}, + {Key: "LastLogin", Value: ud.LastLogin.AsLocalTime().String()}, + {Key: "Avatar", Value: ud.Avatar}, + {Key: "AvatarEmail", Value: ud.AvatarEmail}, + } +} + // newUserData creates a trimmed down user to be used just to create a JSON structure // (keeping only the fields relevant for moderation purposes) func newUserData(user *User) UserData { diff --git a/models/user/moderation_test.go b/models/user/moderation_test.go new file mode 100644 index 0000000000..f951e41e11 --- /dev/null +++ b/models/user/moderation_test.go @@ -0,0 +1,60 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package user_test + +import ( + "testing" + + "forgejo.org/models/moderation" + "forgejo.org/models/user" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +const ( + tsCreated timeutil.TimeStamp = timeutil.TimeStamp(1753093200) // 2025-07-21 10:20:00 UTC + tsUpdated timeutil.TimeStamp = timeutil.TimeStamp(1753093320) // 2025-07-21 10:22:00 UTC + tsLastLogin timeutil.TimeStamp = timeutil.TimeStamp(1753093800) // 2025-07-21 10:30:00 UTC +) + +func testShadowCopyField(t *testing.T, scField moderation.ShadowCopyField, key, value string) { + assert.Equal(t, key, scField.Key) + assert.Equal(t, value, scField.Value) +} + +func TestUserDataGetFieldsMap(t *testing.T) { + ud := user.UserData{ + Name: "alexsmith", + FullName: "Alex Smith", + Email: "alexsmith@example.org", + LoginName: "", + Location: "@master@seo.net", + Website: "http://promote-your-business.biz", + Pronouns: "SEO", + Description: "I can help you promote your business online using SEO.", + CreatedUnix: tsCreated, + UpdatedUnix: tsUpdated, + LastLogin: tsLastLogin, + Avatar: "avatar-hash-user-1002", + AvatarEmail: "alexsmith@example.org", + } + scFields := ud.GetFieldsMap() + + if assert.Len(t, scFields, 13) { + testShadowCopyField(t, scFields[0], "Name", "alexsmith") + testShadowCopyField(t, scFields[1], "FullName", "Alex Smith") + testShadowCopyField(t, scFields[2], "Email", "alexsmith@example.org") + testShadowCopyField(t, scFields[3], "LoginName", "") + testShadowCopyField(t, scFields[4], "Location", "@master@seo.net") + testShadowCopyField(t, scFields[5], "Website", "http://promote-your-business.biz") + testShadowCopyField(t, scFields[6], "Pronouns", "SEO") + testShadowCopyField(t, scFields[7], "Description", "I can help you promote your business online using SEO.") + testShadowCopyField(t, scFields[8], "CreatedUnix", tsCreated.AsLocalTime().String()) + testShadowCopyField(t, scFields[9], "UpdatedUnix", tsUpdated.AsLocalTime().String()) + testShadowCopyField(t, scFields[10], "LastLogin", tsLastLogin.AsLocalTime().String()) + testShadowCopyField(t, scFields[11], "Avatar", "avatar-hash-user-1002") + testShadowCopyField(t, scFields[12], "AvatarEmail", "alexsmith@example.org") + } +} diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index be3d7f92c4..c2c682a4db 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -71,6 +71,10 @@ "keys.ssh.link": "SSH keys", "keys.gpg.link": "GPG keys", "admin.config.moderation_config": "Moderation configuration", + "admin.moderation.moderation_reports": "Moderation reports", + "admin.moderation.reports": "Reports", + "admin.moderation.no_open_reports": "There are currently no open reports.", + "admin.moderation.deleted_content_ref": "Reported content with type %[1]v and id %[2]d no longer exists", "moderation.report_abuse": "Report abuse", "moderation.report_content": "Report content", "moderation.report_abuse_form.header": "Report abuse to administrator", diff --git a/routers/web/admin/reports.go b/routers/web/admin/reports.go new file mode 100644 index 0000000000..ac43d1296f --- /dev/null +++ b/routers/web/admin/reports.go @@ -0,0 +1,157 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package admin + +import ( + "fmt" + "net/http" + + "forgejo.org/models/issues" + "forgejo.org/models/moderation" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/user" + "forgejo.org/modules/base" + "forgejo.org/services/context" + moderation_service "forgejo.org/services/moderation" +) + +const ( + tplModerationReports base.TplName = "admin/moderation/reports" + tplModerationReportDetails base.TplName = "admin/moderation/report_details" +) + +// AbuseReports renders the reports overview page from admin moderation section. +func AbuseReports(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("admin.moderation.reports") + ctx.Data["PageIsAdminModerationReports"] = true + + reports, err := moderation.GetOpenReports(ctx) + if err != nil { + ctx.ServerError("Failed to load abuse reports", err) + return + } + + ctx.Data["Reports"] = reports + ctx.Data["AbuseCategories"] = moderation.AbuseCategoriesTranslationKeys + ctx.Data["GhostUserName"] = user.GhostUserName + + ctx.HTML(http.StatusOK, tplModerationReports) +} + +// AbuseReportDetails renders a report details page opened from the reports overview from admin moderation section. +func AbuseReportDetails(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("admin.moderation.reports") + ctx.Data["PageIsAdminModerationReports"] = true + + ctx.Data["Type"] = ctx.ParamsInt64(":type") + ctx.Data["ID"] = ctx.ParamsInt64(":id") + + contentType := moderation.ReportedContentType(ctx.ParamsInt64(":type")) + + if !contentType.IsValid() { + ctx.Flash.Error("Invalid content type") + return + } + + reports, err := moderation.GetOpenReportsByTypeAndContentID(ctx, contentType, ctx.ParamsInt64(":id")) + if err != nil { + ctx.ServerError("Failed to load reports", err) + return + } + if len(reports) == 0 { + // something is wrong + ctx.HTML(http.StatusOK, tplModerationReportDetails) + return + } + + ctx.Data["Reports"] = reports + ctx.Data["AbuseCategories"] = moderation.AbuseCategoriesTranslationKeys + ctx.Data["GhostUserName"] = user.GhostUserName + + ctx.Data["GetShadowCopyMap"] = moderation_service.GetShadowCopyMap + + if err = setReportedContentDetails(ctx, reports[0]); err != nil { + if user.IsErrUserNotExist(err) || issues.IsErrCommentNotExist(err) || issues.IsErrIssueNotExist(err) || repo_model.IsErrRepoNotExist(err) { + ctx.Data["ContentReference"] = ctx.Tr("admin.moderation.deleted_content_ref", reports[0].ContentType, reports[0].ContentID) + } else { + ctx.ServerError("Failed to load reported content details", err) + return + } + } + + ctx.HTML(http.StatusOK, tplModerationReportDetails) +} + +// setReportedContentDetails adds some values into context data for the given report +// (icon name, a reference, the URL and in case of issues and comments also the poster name). +func setReportedContentDetails(ctx *context.Context, report *moderation.AbuseReportDetailed) error { + contentReference := "" + var contentURL string + var poster string + contentType := report.ContentType + contentID := report.ContentID + + ctx.Data["ContentTypeIconName"] = report.ContentTypeIconName() + + switch contentType { + case moderation.ReportedContentTypeUser: + reportedUser, err := user.GetUserByID(ctx, contentID) + if err != nil { + return err + } + + contentReference = reportedUser.Name + contentURL = reportedUser.HomeLink() + case moderation.ReportedContentTypeRepository: + repo, err := repo_model.GetRepositoryByID(ctx, contentID) + if err != nil { + return err + } + + contentReference = repo.FullName() + contentURL = repo.Link() + case moderation.ReportedContentTypeIssue: + issue, err := issues.GetIssueByID(ctx, contentID) + if err != nil { + return err + } + if err = issue.LoadRepo(ctx); err != nil { + return err + } + if err = issue.LoadPoster(ctx); err != nil { + return err + } + if issue.Poster != nil { + poster = issue.Poster.Name + } + + contentReference = fmt.Sprintf("%s#%d", issue.Repo.FullName(), issue.Index) + contentURL = issue.Link() + case moderation.ReportedContentTypeComment: + comment, err := issues.GetCommentByID(ctx, contentID) + if err != nil { + return err + } + if err = comment.LoadIssue(ctx); err != nil { + return err + } + if err = comment.Issue.LoadRepo(ctx); err != nil { + return err + } + if err = comment.LoadPoster(ctx); err != nil { + return err + } + if comment.Poster != nil { + poster = comment.Poster.Name + } + + contentURL = comment.Link(ctx) + contentReference = contentURL + } + + ctx.Data["ContentReference"] = contentReference + ctx.Data["ContentURL"] = contentURL + ctx.Data["Poster"] = poster + return nil +} diff --git a/routers/web/web.go b/routers/web/web.go index 6cca2a9f2e..497352cdc7 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -781,7 +781,14 @@ func registerRoutes(m *web.Route) { addSettingsRunnersRoutes() addSettingsVariablesRoutes() }) - }, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled)) + + if setting.Moderation.Enabled { + m.Group("/moderation/reports", func() { + m.Get("", admin.AbuseReports) + m.Get("/type/{type:1|2|3|4}/id/{id}", admin.AbuseReportDetails) + }) + } + }, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled, "EnableModeration", setting.Moderation.Enabled)) // ***** END: Admin ***** m.Group("", func() { diff --git a/services/moderation/moderating.go b/services/moderation/moderating.go new file mode 100644 index 0000000000..f329070963 --- /dev/null +++ b/services/moderation/moderating.go @@ -0,0 +1,41 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "forgejo.org/models/issues" + "forgejo.org/models/moderation" + "forgejo.org/models/repo" + "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/services/context" +) + +// GetShadowCopyMap unmarshals the shadow copy raw value of the given abuse report and returns a list of pairs +// (to be rendered when the report is reviewed by an admin). +// If the report does not have a shadow copy ID or the raw value is empty, returns nil. +// If the unmarshal fails a warning is added in the logs and returns nil. +func GetShadowCopyMap(ctx *context.Context, ard *moderation.AbuseReportDetailed) []moderation.ShadowCopyField { + if ard.ShadowCopyID.Valid && len(ard.ShadowCopyRawValue) > 0 { + var data moderation.ShadowCopyData + + switch ard.ContentType { + case moderation.ReportedContentTypeUser: + data = new(user.UserData) + case moderation.ReportedContentTypeRepository: + data = new(repo.RepositoryData) + case moderation.ReportedContentTypeIssue: + data = new(issues.IssueData) + case moderation.ReportedContentTypeComment: + data = new(issues.CommentData) + } + if err := json.Unmarshal([]byte(ard.ShadowCopyRawValue), &data); err != nil { + log.Warn("Unmarshal failed for shadow copy #%d. %v", ard.ShadowCopyID.Int64, err) + return nil + } + return data.GetFieldsMap() + } + return nil +} diff --git a/templates/admin/moderation/report_details.tmpl b/templates/admin/moderation/report_details.tmpl new file mode 100644 index 0000000000..26a8b5964b --- /dev/null +++ b/templates/admin/moderation/report_details.tmpl @@ -0,0 +1,65 @@ +{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin")}} +
+

+ {{ctx.Locale.Tr "admin.moderation.reports"}} +

+ {{if .Reports}} +
+
+
+ {{svg .ContentTypeIconName 24}} +
+
+
+ {{if .ContentURL}}{{.ContentReference}}{{else}}{{.ContentReference}}{{end}} + {{if .Poster}} — {{.Poster}}{{end}} +
+
+
+
+ {{end}} +
+ {{if .Reports}} +
+ {{range .Reports}} +
+
+
+ + {{svg "octicon-calendar"}} + {{DateUtils.AbsoluteShort .CreatedUnix}} + + + {{svg "octicon-report"}} + {{if .ReporterName}}{{.ReporterName}}{{else}}{{$.GhostUserName}}{{end}} + + + {{svg "octicon-tag" 12}} + {{ctx.Locale.Tr (index $.AbuseCategories .Category)}} + +
+ +
{{.Remarks}}
+ + {{if .ShadowCopyID.Valid}} +
{{DateUtils.FullTime .ShadowCopyDate}} shadow copy + + {{range $scField := (call $.GetShadowCopyMap $.Context .)}} + + + + + {{end}} +
{{$scField.Key}}{{$scField.Value}}
+
+ {{end}} +
+
+ {{end}} +
+ {{else}} +

{{ctx.Locale.Tr "admin.moderation.no_open_reports"}}

+ {{end}} +
+
+{{template "admin/layout_footer" .}} diff --git a/templates/admin/moderation/reports.tmpl b/templates/admin/moderation/reports.tmpl new file mode 100644 index 0000000000..151a079673 --- /dev/null +++ b/templates/admin/moderation/reports.tmpl @@ -0,0 +1,66 @@ +{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin")}} +
+

+ {{ctx.Locale.Tr "admin.moderation.reports"}} +

+ +
+ {{if .Reports}} +
+
+
+ Type +
+
+ Summary +
+
+ {{ctx.Locale.Tr "admin.moderation.reports"}} +
+
+ {{range .Reports}} +
+
+ {{svg .ContentTypeIconName 24}} +
+
+
+ {{if .ContentReference}} + {{.ContentReference}} + {{else}} + {{ctx.Locale.Tr "admin.moderation.deleted_content_ref" .ContentType .ContentID}} + {{end}} +
+
+ + {{svg "octicon-calendar"}} + {{DateUtils.TimeSince .CreatedUnix}} + + + {{svg "octicon-report"}} + {{if .ReporterName}}{{.ReporterName}}{{else}}{{$.GhostUserName}}{{end}} + + + {{svg "octicon-tag" 12}} + {{ctx.Locale.Tr (index $.AbuseCategories .Category)}} + +
+ +
+ {{ctx.Locale.Tr "moderation.report_remarks"}}: {{.Remarks}} +
+
+ +
+ {{.ReportedTimes}}{{svg "octicon-report" "tw-ml-2"}} +
+
+
+ {{end}} +
+ {{else}} +

{{ctx.Locale.Tr "admin.moderation.no_open_reports"}}

+ {{end}} +
+
+{{template "admin/layout_footer" .}} diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl index 1ec703b296..7ca8538bce 100644 --- a/templates/admin/navbar.tmpl +++ b/templates/admin/navbar.tmpl @@ -108,5 +108,10 @@
+ {{if .EnableModeration}} + + {{ctx.Locale.Tr "admin.moderation.moderation_reports"}} + + {{end}}
diff --git a/tests/integration/admin_moderation_test.go b/tests/integration/admin_moderation_test.go new file mode 100644 index 0000000000..de0daebe86 --- /dev/null +++ b/tests/integration/admin_moderation_test.go @@ -0,0 +1,158 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package integration + +import ( + "net/http" + "strings" + "testing" + + "forgejo.org/models/unittest" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/tests" + + "github.com/stretchr/testify/assert" +) + +func testReportDetails(t *testing.T, htmlDoc *HTMLDoc, reportID, contentIcon, contentRef, contentURL, category, reportsNo string) { + // Check icon octicon + icon := htmlDoc.Find("#report-" + reportID + " svg." + contentIcon) + assert.Equal(t, 1, icon.Length()) + + // Check content reference and URL + title := htmlDoc.Find("#report-" + reportID + " .flex-item-main .flex-item-title a") + if len(contentURL) == 0 { + // No URL means that the content was already deleted, so we should not find the anchor element. + assert.Zero(t, title.Length()) + // Instead we should find an emphasis element. + title = htmlDoc.Find("#report-" + reportID + " .flex-item-main .flex-item-title em") + assert.Equal(t, 1, title.Length()) + assert.Equal(t, contentRef, title.Text()) + } else { + assert.Equal(t, 1, title.Length()) + assert.Equal(t, contentRef, title.Text()) + + href, exists := title.Attr("href") + assert.True(t, exists) + assert.Equal(t, contentURL, href) + } + + // Check category + cat := htmlDoc.Find("#report-" + reportID + " .flex-item-main .flex-items-inline .item:nth-child(3)") + assert.Equal(t, 1, cat.Length()) + assert.Equal(t, category, strings.TrimSpace(cat.Text())) + + // Check number of reports for the same content + count := htmlDoc.Find("#report-" + reportID + " a span") + assert.Equal(t, 1, count.Length()) + assert.Equal(t, reportsNo, count.Text()) +} + +func TestAdminModerationViewReports(t *testing.T) { + defer unittest.OverrideFixtures("tests/integration/fixtures/TestAdminModerationViewReports")() + defer tests.PrepareTestEnv(t)() + + t.Run("Moderation enabled", func(t *testing.T) { + defer test.MockVariableValue(&setting.Moderation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + t.Run("Anonymous user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/admin/moderation/reports") + MakeRequest(t, req, http.StatusSeeOther) + req = NewRequest(t, "GET", "/admin/moderation/reports/type/1/id/1002") + MakeRequest(t, req, http.StatusSeeOther) + }) + + t.Run("Normal user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/admin/moderation/reports") + session.MakeRequest(t, req, http.StatusForbidden) + req = NewRequest(t, "GET", "/admin/moderation/reports/type/1/id/1002") + session.MakeRequest(t, req, http.StatusForbidden) + }) + + t.Run("Admin user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user1") + req := NewRequest(t, "GET", "/admin/moderation/reports") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + // Check how many reports are being displayed. + // Reports linked to the same content (type and id) should be grouped; therefore we should see only 6 instead of 9. + reports := htmlDoc.Find(".admin-setting-content .flex-list .flex-item.report") + assert.Equal(t, 7, reports.Length()) + + // Check details for shown reports. + testReportDetails(t, htmlDoc, "1", "octicon-person", "@SPAM-services", "/SPAM-services", "Illegal content", "1") + testReportDetails(t, htmlDoc, "2", "octicon-repo", "SPAM-services/spammer-Tools", "/SPAM-services/spammer-Tools", "Illegal content", "1") + testReportDetails(t, htmlDoc, "3", "octicon-issue-opened", "SPAM-services/spammer-Tools#1", "/SPAM-services/spammer-Tools/issues/1", "Spam", "1") + // #4 is combined with #7 and #9 + testReportDetails(t, htmlDoc, "4", "octicon-person", "@spammer01", "/spammer01", "Spam", "3") + // #5 is combined with #6 + testReportDetails(t, htmlDoc, "5", "octicon-comment", "contributor/first/issues/1#issuecomment-1001", "/contributor/first/issues/1#issuecomment-1001", "Malware", "2") + testReportDetails(t, htmlDoc, "8", "octicon-issue-opened", "contributor/first#1", "/contributor/first/issues/1", "Other violations of platform rules", "1") + // #10 is for a Ghost user + testReportDetails(t, htmlDoc, "10", "octicon-person", "Reported content with type 1 and id 9999 no longer exists", "", "Other violations of platform rules", "1") + + t.Run("reports details page", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req = NewRequest(t, "GET", "/admin/moderation/reports/type/1/id/1002") + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc = NewHTMLParser(t, resp.Body) + + // Check the title (content reference) and corresponding URL + title := htmlDoc.Find(".admin-setting-content .flex-item-main .flex-item-title a") + assert.Equal(t, 1, title.Length()) + assert.Equal(t, "spammer01", title.Text()) + href, exists := title.Attr("href") + assert.True(t, exists) + assert.Equal(t, "/spammer01", href) + + // Check how many reports are being displayed for user 1002. + reports = htmlDoc.Find(".admin-setting-content .flex-list .flex-item") + assert.Equal(t, 3, reports.Length()) + }) + }) + }) + + t.Run("Moderation disabled", func(t *testing.T) { + t.Run("Anonymous user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/admin/moderation/reports") + MakeRequest(t, req, http.StatusNotFound) + req = NewRequest(t, "GET", "/admin/moderation/reports/type/1/id/1002") + MakeRequest(t, req, http.StatusNotFound) + }) + + t.Run("Normal user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/admin/moderation/reports") + session.MakeRequest(t, req, http.StatusNotFound) + req = NewRequest(t, "GET", "/admin/moderation/reports/type/1/id/1002") + session.MakeRequest(t, req, http.StatusNotFound) + }) + + t.Run("Admin user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user1") + req := NewRequest(t, "GET", "/admin/moderation/reports") + session.MakeRequest(t, req, http.StatusNotFound) + req = NewRequest(t, "GET", "/admin/moderation/reports/type/1/id/1002") + session.MakeRequest(t, req, http.StatusNotFound) + }) + }) +} diff --git a/tests/integration/fixtures/TestAdminModerationViewReports/abuse_report.yml b/tests/integration/fixtures/TestAdminModerationViewReports/abuse_report.yml new file mode 100644 index 0000000000..8bc3c0834b --- /dev/null +++ b/tests/integration/fixtures/TestAdminModerationViewReports/abuse_report.yml @@ -0,0 +1,111 @@ +- + id: 1 + status: 1 # Open + reporter_id: 1004 # @reporter1 + content_type: 1 # User (users or organizations) + content_id: 1003 # @SPAM-services + category: 4 # IllegalContent + remarks: This organization was created for spamming. + shadow_copy_id: null + created_unix: 1121423460 # 2005-07-15 10:31:00 + +- + id: 2 + status: 1 + reporter_id: 1004 # @reporter1 + content_type: 2 # Repository + content_id: 1002 # SPAM-services/spammer-tools + category: 4 # IllegalContent + remarks: This repository was created for building spamming tools. + shadow_copy_id: null + created_unix: 1121423520 # 2005-07-15 10:32:00 + +- + id: 3 + status: 1 + reporter_id: 1004 # @reporter1 + content_type: 3 # Issue (issues or pull requests) + content_id: 1002 # SPAM-services/spammer-tools#1 + category: 2 # Spam + remarks: This issue advertises spam services. + shadow_copy_id: null + created_unix: 1121423580 # 2005-07-15 10:33:00 + +- + id: 4 + status: 1 + reporter_id: 1004 # @reporter1 + content_type: 1 # User (users or organizations) + content_id: 1002 # @spammer01 + category: 2 # Spam + remarks: | + This profile advertises spam services and the user already created spam content. + I have reported some of them. + shadow_copy_id: null + created_unix: 1121423640 # 2005-07-15 10:34:00 + +- + id: 5 + status: 1 + reporter_id: 1004 # @reporter1 + content_type: 4 # Comment + content_id: 1001 # contributor/first/issues/1#issuecomment-1001 + category: 3 # Malware + remarks: This comment references a spammy issue from a spammy repository of a spammy organization created by a spammer. + shadow_copy_id: null + created_unix: 1121423700 # 2005-07-15 10:35:00 + +- + id: 6 + status: 1 + reporter_id: 1001 # @contributor + content_type: 4 # Comment + content_id: 1001 # contributor/first/issues/1#issuecomment-1001 + category: 2 # Spam + remarks: I should delete this, since I can; but first I want to test the reporting functionality. + shadow_copy_id: null + created_unix: 1121423730 # 2005-07-15 10:35:30 + +- + id: 7 + status: 1 + reporter_id: 1001 # @contributor + content_type: 1 # User (users or organizations) + content_id: 1002 # @spammer01 + category: 1 # Other + remarks: Should investigate the origin of this abuser. + shadow_copy_id: null + created_unix: 1121423760 # 2005-07-15 10:36:00 + +- + id: 8 + status: 1 + reporter_id: 1002 # @spammer01 + content_type: 3 # Issue (issues or pull requests) + content_id: 1001 # contributor/first#1 + category: 1 # Other + remarks: Just because you are the administrator of this Forgejo instance this doesn't mean that you should be more privileged compared to the rest of average users. I believe it is my right to post links to external websites where users can find more about myself and my own work, even if they are professional services. I strongly believe you should reconsider your totalitarian behaviour. The users of this instance deserve better, more inclusive rules that should prevent abuses from administrators or mod. + shadow_copy_id: null + created_unix: 1121424000 # 2005-07-15 10:40:00 + +- + id: 9 + status: 1 + reporter_id: 1005 # @reporter2 + content_type: 1 # User (users or organizations) + content_id: 1002 # @spammer01 + category: 2 # Spam + remarks: This user is just spamming wherever they can. + shadow_copy_id: null + created_unix: 1121424030 # 2005-07-15 10:40:30 + +- + id: 10 + status: 1 + reporter_id: 1005 # @reporter2 + content_type: 1 # User (users or organizations) + content_id: 9999 # Ghost user + category: 1 # Other + remarks: Check this spammer as soon as possible, before they delete their account. + shadow_copy_id: null + created_unix: 1121424150 # 2005-07-15 10:42:30 diff --git a/tests/integration/fixtures/TestAdminModerationViewReports/comment.yml b/tests/integration/fixtures/TestAdminModerationViewReports/comment.yml new file mode 100644 index 0000000000..a7ee83d53e --- /dev/null +++ b/tests/integration/fixtures/TestAdminModerationViewReports/comment.yml @@ -0,0 +1,21 @@ +- + id: 1001 + type: 0 # Standard comment + poster_id: 1002 # @spammer01 + issue_id: 1001 # contributor/first#1 + content: And the first spammer; check SPAM-services/spammer-Tools#1 + created_unix: 1121422990 # 2005-07-15 10:23:10 + +- + id: 1002 + type: 5 # Reference from a comment + poster_id: 1002 # @spammer01 + issue_id: 1002 # SPAM-services/spammer-tools#1 + content: '' + content_version: 0 + created_unix: 1121422990 + ref_repo_id: 1001 + ref_issue_id: 1001 + ref_comment_id: 1001 + ref_action: 0 + ref_is_pull: false diff --git a/tests/integration/fixtures/TestAdminModerationViewReports/issue.yml b/tests/integration/fixtures/TestAdminModerationViewReports/issue.yml new file mode 100644 index 0000000000..74af88b3b6 --- /dev/null +++ b/tests/integration/fixtures/TestAdminModerationViewReports/issue.yml @@ -0,0 +1,23 @@ +- + id: 1001 + repo_id: 1001 # contributor/first + index: 1 + poster_id: 1001 + name: first repo should have a first issue + content: so here we go + is_closed: false + is_pull: false + num_comments: 1 + created_unix: 1121422320 # 2005-07-15 10:12:00 + +- + id: 1002 + repo_id: 1002 # SPAM-services/spammer-tools + index: 1 + poster_id: 1002 + name: Professional marketing services + content: Visit my website at spammer.xyz/services for a list of available services. + is_closed: false + is_pull: false + num_comments: 0 + created_unix: 1121422980 # 2005-07-15 10:23:00 diff --git a/tests/integration/fixtures/TestAdminModerationViewReports/issue_index.yml b/tests/integration/fixtures/TestAdminModerationViewReports/issue_index.yml new file mode 100644 index 0000000000..cecf437282 --- /dev/null +++ b/tests/integration/fixtures/TestAdminModerationViewReports/issue_index.yml @@ -0,0 +1,7 @@ +- + group_id: 1001 + max_index: 1 + +- + group_id: 1002 + max_index: 1 diff --git a/tests/integration/fixtures/TestAdminModerationViewReports/repository.yml b/tests/integration/fixtures/TestAdminModerationViewReports/repository.yml new file mode 100644 index 0000000000..cee5d6a74c --- /dev/null +++ b/tests/integration/fixtures/TestAdminModerationViewReports/repository.yml @@ -0,0 +1,45 @@ +- + id: 1001 + owner_id: 1001 + owner_name: contributor + lower_name: first + name: first + description: '' + website: '' + default_branch: main + num_watches: 1 + num_stars: 0 + num_forks: 0 + num_issues: 1 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_private: false + is_empty: false + is_archived: false + size: 0 + topics: '[]' + created_unix: 1121422260 # 2005-07-15 10:11:00 + +- + id: 1002 + owner_id: 1003 + owner_name: SPAM-services + lower_name: spammer-tools + name: spammer-Tools + description: Another _place_ for abusive content. + website: '' + default_branch: main + num_watches: 1 + num_stars: 0 + num_forks: 0 + num_issues: 1 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + is_private: false + is_empty: false + is_archived: false + size: 0 + topics: "[\"bulk-email\",\"email-services\",\"spam\",\"spamservices.co\"]" + created_unix: 1121422920 # 2005-07-15 10:22:00 diff --git a/tests/integration/fixtures/TestAdminModerationViewReports/user.yml b/tests/integration/fixtures/TestAdminModerationViewReports/user.yml new file mode 100644 index 0000000000..f00f3de338 --- /dev/null +++ b/tests/integration/fixtures/TestAdminModerationViewReports/user.yml @@ -0,0 +1,108 @@ +- + id: 1001 + lower_name: contributor + name: contributor + full_name: The Contributor + email: contributor@example.org + keep_email_private: true + passwd: passwdSalt:password + passwd_hash_algo: dummy + type: 0 + salt: passwdSalt + description: '' + created_unix: 1121422200 # 2005-07-15 10:10:00 + is_active: true + is_admin: false + is_restricted: false + avatar: avatar-hash-1001 + avatar_email: contributor@example.org + use_custom_avatar: false + num_repos: 1 + +- + id: 1002 + lower_name: spammer01 + name: spammer01 + full_name: King of SPAM + email: spammer01@example.org + keep_email_private: false + passwd: passwdSalt:password + passwd_hash_algo: dummy + type: 0 + location: '@master@smap.net' + website: http://spammer.xyz + pronouns: http://spam.me + salt: passwdSalt + description: I can help you abuse others inboxes. Updated prices on spammer.xyz/services + created_unix: 1121422800 # 2005-07-15 10:20:00 + is_active: true + is_admin: false + is_restricted: false + avatar: avatar-hash-1002 + avatar_email: spammer01@example.org + use_custom_avatar: false + +- + id: 1003 + lower_name: spam-services + name: SPAM-services + full_name: SPAM services + email: get@spamservices.co + keep_email_private: false + email_notifications_preference: '' + passwd: '' + passwd_hash_algo: '' + type: 1 + location: www.spamservices.co + website: https://spamservices.co + salt: 1888c34e04642082a791b49cf147cc88 + description: Contact us for **bulk emails** sending. + created_unix: 1121422860 # 2005-07-15 10:21:00 + is_active: true + is_admin: false + is_restricted: false + allow_create_organization: false + avatar: avatar-hash-1003 + avatar_email: '' + use_custom_avatar: true + num_repos: 1 + +- + id: 1004 + lower_name: reporter1 + name: reporter1 + full_name: Reporter One + email: reporter1@example.org + keep_email_private: true + passwd: passwdSalt:password + passwd_hash_algo: dummy + type: 0 + salt: passwdSalt + description: '' + created_unix: 1121423400 # 2005-07-15 10:30:00 + is_active: true + is_admin: false + is_restricted: false + avatar: avatar-hash-1004 + avatar_email: reporter1@example.org + use_custom_avatar: false + +- + id: 1005 + lower_name: reporter2 + name: reporter2 + full_name: Reporter Two + email: reporter2@example.org + keep_email_private: true + passwd: passwdSalt:password + passwd_hash_algo: dummy + type: 0 + salt: passwdSalt + description: '' + created_unix: 1121424000 # 2005-07-15 10:40:00 + is_active: true + is_admin: false + is_restricted: false + avatar: avatar-hash-1005 + avatar_email: reporter2@example.org + use_custom_avatar: false From 0fb9fc752b37bb5a4aa14c122ccf2d651aa9d00f Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 23 Jul 2025 01:01:31 +0200 Subject: [PATCH 162/297] Update module code.forgejo.org/forgejo/act to v1.32.0 (forgejo) (#8502) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [code.forgejo.org/forgejo/act](https://code.forgejo.org/forgejo/act) | `v1.29.0` -> `v1.32.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/code.forgejo.org%2fforgejo%2fact/v1.32.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/code.forgejo.org%2fforgejo%2fact/v1.29.0/v1.32.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
forgejo/act (code.forgejo.org/forgejo/act) ### [`v1.32.0`](https://code.forgejo.org/forgejo/act/compare/v1.31.0...v1.32.0) [Compare Source](https://code.forgejo.org/forgejo/act/compare/v1.31.0...v1.32.0) ### [`v1.31.0`](https://code.forgejo.org/forgejo/act/compare/v1.30.0...v1.31.0) [Compare Source](https://code.forgejo.org/forgejo/act/compare/v1.30.0...v1.31.0) ### [`v1.30.0`](https://code.forgejo.org/forgejo/act/compare/v1.29.0...v1.30.0) [Compare Source](https://code.forgejo.org/forgejo/act/compare/v1.29.0...v1.30.0)
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). ## Release notes - Breaking features - [PR](https://codeberg.org/forgejo/forgejo/pulls/8502): Forgejo Actions workflows are verified with a YAML schema and common errors such as using an incorrect context (e.g. `${{ badcontext.FORGEJO_REPOSITORY }}`) or a typo in a required keyword (e.g. `ruins-on:` instead of `runs-on:`) will be reported in the action page and the web page that displays the file in the repository. It is recommended to verify existing workflows are successfully verified prior to upgrading, [as explained in the Forgejo runner release notes](https://code.forgejo.org/forgejo/runner/src/branch/main/RELEASE-NOTES.md#8-0-0). Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8502 Reviewed-by: Michael Kriese Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- models/actions/task.go | 2 +- modules/actions/workflows.go | 2 +- release-notes/8502.md | 1 + routers/web/repo/actions/actions.go | 2 +- routers/web/repo/view.go | 2 +- services/actions/commit_status.go | 2 +- services/actions/job_emitter.go | 2 +- services/actions/notifier_helper.go | 6 +++--- services/actions/schedule_tasks.go | 4 ++-- services/actions/workflows.go | 4 ++-- 12 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 release-notes/8502.md diff --git a/go.mod b/go.mod index 8a7c8b6faa..3afb624188 100644 --- a/go.mod +++ b/go.mod @@ -244,7 +244,7 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.29.0 +replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.32.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 diff --git a/go.sum b/go.sum index a291e6c4b7..35d1d09dde 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFs code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/act v1.29.0 h1:CPiI0LRPU0f6gUdQj1ZVax0ySc8CfegY4hiRsymdZU0= -code.forgejo.org/forgejo/act v1.29.0/go.mod h1:RPqtuaI2FkC1SVOaYCRODo5jIfoMTBVgEOOP3Sdiuh4= +code.forgejo.org/forgejo/act v1.32.0 h1:hns2WvrJs6qWCmvzoSllNGNzSvcDMcSvJvVtQj3FaQc= +code.forgejo.org/forgejo/act v1.32.0/go.mod h1:WkmxVBteC4zoyQGYp8ZFZY7Xb+jat+b7ChvqW6TxqF8= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M= diff --git a/models/actions/task.go b/models/actions/task.go index 93369db7e8..7c85ffc232 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -275,7 +275,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask } var workflowJob *jobparser.Job - if gots, err := jobparser.Parse(job.WorkflowPayload); err != nil { + if gots, err := jobparser.Parse(job.WorkflowPayload, false); err != nil { return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err) } else if len(gots) != 1 { return nil, false, fmt.Errorf("workflow of job %d: not single workflow", job.ID) diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index 7ae4557ed6..b04f97df0f 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -86,7 +86,7 @@ func GetContentFromEntry(entry *git.TreeEntry) ([]byte, error) { } func GetEventsFromContent(content []byte) ([]*jobparser.Event, error) { - workflow, err := model.ReadWorkflow(bytes.NewReader(content)) + workflow, err := model.ReadWorkflow(bytes.NewReader(content), false) if err != nil { return nil, err } diff --git a/release-notes/8502.md b/release-notes/8502.md new file mode 100644 index 0000000000..b7fa0fd892 --- /dev/null +++ b/release-notes/8502.md @@ -0,0 +1 @@ +Forgejo Actions workflows are verified with a YAML schema and common errors such as using an incorrect context (e.g. `${{ badcontext.FORGEJO_REPOSITORY }}`) or a typo in a required keyword (e.g. `ruins-on:` instead of `runs-on:`) will be reported in the action page and the web page that displays the file in the repository. It is recommended to verify existing workflows are successfully verified prior to upgrading, [as explained in the Forgejo runner release notes](https://code.forgejo.org/forgejo/runner/src/branch/main/RELEASE-NOTES.md#8-0-0). diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 7aa52ddd4c..6f1f19b107 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -111,7 +111,7 @@ func List(ctx *context.Context) { ctx.ServerError("GetContentFromEntry", err) return } - wf, err := model.ReadWorkflow(bytes.NewReader(content)) + wf, err := model.ReadWorkflow(bytes.NewReader(content), true) if err != nil { workflow.ErrMsg = ctx.Locale.TrString("actions.runs.invalid_workflow_helper", err.Error()) workflows = append(workflows, workflow) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index e61059da64..8e1028968e 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -434,7 +434,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { if err != nil { log.Error("actions.GetContentFromEntry: %v", err) } - _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content)) + _, workFlowErr := model.ReadWorkflow(bytes.NewReader(content), true) if workFlowErr != nil { ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) } diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 755fa648dc..b054f2036c 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -80,7 +80,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er repo := run.Repo // TODO: store workflow name as a field in ActionRun to avoid parsing runName := path.Base(run.WorkflowID) - if wfs, err := jobparser.Parse(job.WorkflowPayload); err == nil && len(wfs) > 0 { + if wfs, err := jobparser.Parse(job.WorkflowPayload, false); err == nil && len(wfs) > 0 { runName = wfs[0].Name } ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event) diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go index 942c698e73..8178d210ce 100644 --- a/services/actions/job_emitter.go +++ b/services/actions/job_emitter.go @@ -142,7 +142,7 @@ func (r *jobStatusResolver) resolve() map[int64]actions_model.Status { } else { // Check if the job has an "if" condition hasIf := false - if wfJobs, _ := jobparser.Parse(r.jobMap[id].WorkflowPayload); len(wfJobs) == 1 { + if wfJobs, _ := jobparser.Parse(r.jobMap[id].WorkflowPayload, false); len(wfJobs) == 1 { _, wfJob := wfJobs[0].Job() hasIf = len(wfJob.If.Value) > 0 } diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index e240c996b5..e31817a5e2 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -345,7 +345,7 @@ func handleWorkflows( Status: actions_model.StatusWaiting, } - if workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)); err == nil { + if workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content), false); err == nil { notifications, err := workflow.Notifications() if err != nil { log.Error("Notifications: %w", err) @@ -372,7 +372,7 @@ func handleWorkflows( continue } - jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars)) + jobs, err := jobparser.Parse(dwf.Content, false, jobparser.WithVars(vars)) if err != nil { run.Status = actions_model.StatusFailure log.Info("jobparser.Parse: invalid workflow, setting job status to failed: %v", err) @@ -537,7 +537,7 @@ func handleSchedules( crons := make([]*actions_model.ActionSchedule, 0, len(detectedWorkflows)) for _, dwf := range detectedWorkflows { // Check cron job condition. Only working in default branch - workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)) + workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content), false) if err != nil { log.Error("ReadWorkflow: %v", err) continue diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index cf8b29ead7..d275837cbb 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -142,7 +142,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) return err } - workflow, err := act_model.ReadWorkflow(bytes.NewReader(cron.Content)) + workflow, err := act_model.ReadWorkflow(bytes.NewReader(cron.Content), false) if err != nil { return err } @@ -153,7 +153,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) run.NotifyEmail = notifications // Parse the workflow specification from the cron schedule - workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars)) + workflows, err := jobparser.Parse(cron.Content, false, jobparser.WithVars(vars)) if err != nil { return err } diff --git a/services/actions/workflows.go b/services/actions/workflows.go index fbba3fd667..22417d4e32 100644 --- a/services/actions/workflows.go +++ b/services/actions/workflows.go @@ -56,7 +56,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette return nil, nil, err } - wf, err := act_model.ReadWorkflow(bytes.NewReader(content)) + wf, err := act_model.ReadWorkflow(bytes.NewReader(content), false) if err != nil { return nil, nil, err } @@ -138,7 +138,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette return nil, nil, err } - jobs, err := jobparser.Parse(content, jobparser.WithVars(vars)) + jobs, err := jobparser.Parse(content, false, jobparser.WithVars(vars)) if err != nil { return nil, nil, err } From b06f4fdd631ed10704b453257e50f2a42b12c77c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 23 Jul 2025 01:12:08 +0200 Subject: [PATCH 163/297] Update dependency forgejo/release-notes-assistant to v1.3.3 (forgejo) (#8620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [forgejo/release-notes-assistant](https://code.forgejo.org/forgejo/release-notes-assistant) | patch | `v1.3.2` -> `v1.3.3` | --- ### Release Notes
forgejo/release-notes-assistant (forgejo/release-notes-assistant) ### [`v1.3.3`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.3) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.2...v1.3.3) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/95): fix: git fetch on a mirror must explicitly prune tags
--- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8620 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/release-notes-assistant-milestones.yml | 2 +- .forgejo/workflows/release-notes-assistant.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index e16c5a5507..6c61774325 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -6,7 +6,7 @@ on: env: RNA_WORKDIR: /srv/rna - RNA_VERSION: v1.3.2 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.3 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index 9c97d21dff..b1acde6030 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,7 +8,7 @@ on: - labeled env: - RNA_VERSION: v1.3.2 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.3 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: From c11dd3fb46d2c21be8f609484d9e0619b569306a Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Wed, 23 Jul 2025 02:06:13 +0200 Subject: [PATCH 164/297] feat(ui): improve org header with new noJS dropdown and more options (#8572) Related: https://codeberg.org/forgejo/forgejo/pulls/6977/files#diff-fd05eba523810d46c7763db938ad5839372a074a, https://codeberg.org/forgejo/forgejo/pulls/3949, https://codeberg.org/forgejo/forgejo/pulls/7906 * use the new noJS dropdown for extra actions in org view (currently only includes report button) * this required some refactoring of the area because the said dropdown was not built to be placed in an area where `font-size:36px` is forced onto everything * this greatly improves consistently with user profiles which now use this type of dropdown * I decided against making the opener button mimicrate an actual button because it looks ok as is and is consitent with menu in user profiles and because I don't think this is a good design language to make a kebab menu opener look this way * add icon to the entry * add atom entry Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8572 Reviewed-by: Gusted Reviewed-by: Beowulf Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- templates/org/follow_unfollow.tmpl | 2 +- templates/org/header.tmpl | 42 ++++++++++++++---------- tests/integration/org_profile_test.go | 46 +++++++++++++++++++++++++++ web_src/css/modules/dropdown.css | 8 ++++- web_src/css/org.css | 17 +++++----- 5 files changed, 88 insertions(+), 27 deletions(-) diff --git a/templates/org/follow_unfollow.tmpl b/templates/org/follow_unfollow.tmpl index ba0bd01efe..9175088da4 100644 --- a/templates/org/follow_unfollow.tmpl +++ b/templates/org/follow_unfollow.tmpl @@ -1,4 +1,4 @@ - + {{$moderationEntryNeeded := and .IsModerationEnabled .IsSigned (not .IsOrganizationOwner)}} + {{if or .EnableFeed $moderationEntryNeeded}} + {{end}}
diff --git a/tests/integration/org_profile_test.go b/tests/integration/org_profile_test.go index 2211a8b3d2..8125c3e6ba 100644 --- a/tests/integration/org_profile_test.go +++ b/tests/integration/org_profile_test.go @@ -104,5 +104,51 @@ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequa assert.NotContains(t, resp.Body.String(), "veniam") }) }) + + t.Run("More actions - feeds only", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer test.MockVariableValue(&setting.Other.EnableFeed, true)() + defer test.MockVariableValue(&setting.Moderation.Enabled, false)() + + // Both guests and logged in users should see the feed option + doc := NewHTMLParser(t, MakeRequest(t, NewRequest(t, "GET", "/org3"), http.StatusOK).Body) + doc.AssertElement(t, "details.dropdown a[href='/org3.rss']", true) + doc.AssertElement(t, "details.dropdown a[href^='/report_abuse']", false) + + doc = NewHTMLParser(t, loginUser(t, "user10").MakeRequest(t, NewRequest(t, "GET", "/org3"), http.StatusOK).Body) + doc.AssertElement(t, "details.dropdown a[href='/org3.rss']", true) + doc.AssertElement(t, "details.dropdown a[href^='/report_abuse']", false) + }) + + t.Run("More actions - none", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer test.MockVariableValue(&setting.Other.EnableFeed, false)() + defer test.MockVariableValue(&setting.Moderation.Enabled, false)() + + // The dropdown won't appear if no entries are available, for both guests and logged in users + doc := NewHTMLParser(t, MakeRequest(t, NewRequest(t, "GET", "/org3"), http.StatusOK).Body) + doc.AssertElement(t, "details.dropdown", false) + + doc = NewHTMLParser(t, loginUser(t, "user10").MakeRequest(t, NewRequest(t, "GET", "/org3"), http.StatusOK).Body) + doc.AssertElement(t, "details.dropdown", false) + }) + + t.Run("More actions - moderation", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer test.MockVariableValue(&setting.Other.EnableFeed, false)() + defer test.MockVariableValue(&setting.Moderation.Enabled, true)() + + // The report option shouldn't be available to a guest + doc := NewHTMLParser(t, MakeRequest(t, NewRequest(t, "GET", "/org3"), http.StatusOK).Body) + doc.AssertElement(t, "details.dropdown", false) + + // But should be available to a logged in user + doc = NewHTMLParser(t, loginUser(t, "user10").MakeRequest(t, NewRequest(t, "GET", "/org3"), http.StatusOK).Body) + doc.AssertElement(t, "details.dropdown a[href^='/report_abuse']", true) + + // But the org owner shouldn't see the report option + doc = NewHTMLParser(t, loginUser(t, "user1").MakeRequest(t, NewRequest(t, "GET", "/org3"), http.StatusOK).Body) + doc.AssertElement(t, "details.dropdown", false) + }) }) } diff --git a/web_src/css/modules/dropdown.css b/web_src/css/modules/dropdown.css index 66762ac45c..22920c9f67 100644 --- a/web_src/css/modules/dropdown.css +++ b/web_src/css/modules/dropdown.css @@ -99,7 +99,13 @@ details.dropdown > summary + ul > li:last-child { /* Note: https://css-tricks.com/css-anchor-positioning-guide/ * looks like a great thing but FF still doesn't support it. */ -/* Note: dropdown.dir-rtl can be implemented when needed, e.g. for navbar profile dropdown on desktop layout. */ +details.dropdown.dir-rtl > summary + ul { + inset-inline: 0 auto; + direction: rtl; +} +details.dropdown.dir-rtl > summary + ul > li { + direction: ltr; +} details.dropdown > summary + ul > li > .item { padding: var(--dropdown-item-padding); diff --git a/web_src/css/org.css b/web_src/css/org.css index 6853a26bf7..05eb0c5476 100644 --- a/web_src/css/org.css +++ b/web_src/css/org.css @@ -89,28 +89,29 @@ text-align: center; } -.page-content.organization .org-avatar { - margin-right: 15px; -} - .page-content.organization #org-info { overflow-wrap: anywhere; flex: 1; } -.page-content.organization #org-info .ui.header { +.page-content.organization #org-info .org-title { display: flex; + column-gap: 1rem; align-items: center; - font-size: 36px; - margin-bottom: 0; +} + +.page-content.organization #org-info .org-title h1 { + margin: 0; + font-size: 2.5rem; } @media (max-width: 767.98px) { - .page-content.organization #org-info .ui.header { + .page-content.organization #org-info .org-header { flex-direction: column; margin-bottom: 1rem; } .page-content.organization #org-info .org-title { + flex-wrap: wrap; width: 100%; margin-bottom: 0.5rem; } From c3b18a6debe08f63cc69eb43aff1e7b2bc91ac64 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 23 Jul 2025 02:58:57 +0200 Subject: [PATCH 165/297] Update module github.com/yuin/goldmark to v1.7.13 (forgejo) (#8621) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8621 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3afb624188..6007181a63 100644 --- a/go.mod +++ b/go.mod @@ -94,7 +94,7 @@ require ( github.com/urfave/cli/v3 v3.3.3 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 - github.com/yuin/goldmark v1.7.12 + github.com/yuin/goldmark v1.7.13 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc gitlab.com/gitlab-org/api/client-go v0.130.1 go.uber.org/mock v0.5.2 diff --git a/go.sum b/go.sum index 35d1d09dde..7725cfd6d0 100644 --- a/go.sum +++ b/go.sum @@ -548,8 +548,8 @@ github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBz github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= -github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= +github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= From 83ea43cf49c235b61531570d3719c1190a92ac67 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Wed, 23 Jul 2025 04:10:50 +0200 Subject: [PATCH 166/297] frontend: generic lazy loader for webcomponents (#8510) After seeing #8111 use a webcomponent, I think that they are a neat usecase for Forgejo where most of the frontend is backend-generated, with some "island of enhancements". I am considering using a webcomponent for the CITATION management (last occurrence of [`Blob.GetBlobContent`](https://codeberg.org/forgejo/forgejo/issues/8222)), however I noticed that the developer experience wasn't ideal. With this PR it would be very easy to declare a webcomponent, which will be loaded only if needed (I converted `model-viewer` and `pdf-object` to this technique). Some cleanup in the neighbor webcomponents. ## Testing 1) Create a new repository or use an existing one. 2) Upload a `.pdf` or `.glb` file (such as https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/testdata/data/viewer/Unicode%E2%9D%A4%E2%99%BBTest.glb) 3) Open the Network inspector and view the file in the repository. - After a short loading spinner, the PDF or 3D model should be rendered in a viewer - the related JS should have been loaded (e.g. http://localhost:3000/assets/js/model-viewer.494bf0cd.js) - visiting another page and check that this JS file isn't loaded Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8510 Reviewed-by: Gusted Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Beowulf Co-authored-by: oliverpool Co-committed-by: oliverpool --- modules/translation/plural_rules.go | 3 +- templates/repo/settings/lfs_file.tmpl | 6 +- templates/repo/view_file.tmpl | 6 +- web_src/css/modules/animations.css | 6 +- web_src/css/repo.css | 8 +-- web_src/js/index.js | 4 -- web_src/js/render/gltf.js | 6 -- web_src/js/render/pdf.js | 19 ------ web_src/js/webcomponents/i18n.js | 75 ----------------------- web_src/js/webcomponents/index.js | 3 +- web_src/js/webcomponents/lazy-webc.js | 66 ++++++++++++++++++++ web_src/js/webcomponents/pdf-object.js | 14 +++++ web_src/js/webcomponents/polyfills.js | 17 ----- web_src/js/webcomponents/relative-time.js | 70 ++++++++++++++++++++- 14 files changed, 166 insertions(+), 137 deletions(-) delete mode 100644 web_src/js/render/gltf.js delete mode 100644 web_src/js/render/pdf.js delete mode 100644 web_src/js/webcomponents/i18n.js create mode 100644 web_src/js/webcomponents/lazy-webc.js create mode 100644 web_src/js/webcomponents/pdf-object.js delete mode 100644 web_src/js/webcomponents/polyfills.js diff --git a/modules/translation/plural_rules.go b/modules/translation/plural_rules.go index 59665da255..587ee48850 100644 --- a/modules/translation/plural_rules.go +++ b/modules/translation/plural_rules.go @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT // Some useful links: +// https://codeberg.org/forgejo/forgejo/src/branch/forgejo/web_src/js/webcomponents/relative-time.js // https://www.unicode.org/cldr/charts/46/supplemental/language_plural_rules.html // https://translate.codeberg.org/languages/$LANGUAGE_CODE/#information // https://github.com/WeblateOrg/language-data/blob/main/languages.csv @@ -16,7 +17,7 @@ import ( "forgejo.org/modules/translation/i18n" ) -// The constants refer to indices below in `PluralRules` and also in i18n.js, keep them in sync! +// The constants refer to indices below in `PluralRules` and also in web_src/js/webcomponents/relative-time.js, keep them in sync! const ( PluralRuleDefault = 0 PluralRuleBengali = 1 diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl index 9864ed01d6..00bbae616a 100644 --- a/templates/repo/settings/lfs_file.tmpl +++ b/templates/repo/settings/lfs_file.tmpl @@ -31,10 +31,12 @@ {{ctx.Locale.Tr "repo.audio_not_supported_in_browser"}} {{else if .IsPDFFile}} -
+ + {{ctx.Locale.Tr "repo.diff.view_file"}} + {{else if .Is3DModelFile}} {{if .IsGLBFile}} - + {{else}} {{ctx.Locale.Tr "repo.file_view_raw"}}! {{end}} diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 36809b769e..65be791405 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -127,10 +127,12 @@ {{ctx.Locale.Tr "repo.audio_not_supported_in_browser"}} {{else if .IsPDFFile}} -
+ + {{ctx.Locale.Tr "repo.diff.view_file"}} + {{else if .Is3DModelFile}} {{if .IsGLBFile}} - + {{else}} {{ctx.Locale.Tr "repo.file_view_raw"}} {{end}} diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index a86c9234aa..88b128c643 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -3,11 +3,13 @@ 100% { transform: translate(-50%, -50%) rotate(360deg); } } +lazy-webc, .is-loading { pointer-events: none !important; position: relative !important; } +lazy-webc > *, .is-loading > * { opacity: 0.3; } @@ -17,6 +19,7 @@ opacity: 0; } +lazy-webc::after, .is-loading::after { content: ""; position: absolute; @@ -52,8 +55,7 @@ form.single-button-form.is-loading .button { } .markup pre.is-loading, -.editor-loading.is-loading, -.pdf-content.is-loading { +.editor-loading.is-loading { height: var(--height-loading); } diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 078ff7b4c4..a3713a4c3d 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -415,12 +415,14 @@ td .commit-summary { max-width: 600px !important; } +lazy-webc[tag="model-viewer"], model-viewer { width: 100%; height: 100vh; } -.pdf-content { +lazy-webc[tag="pdf-object"], +pdf-object { width: 100%; height: 100vh; border: none !important; @@ -429,10 +431,6 @@ model-viewer { justify-content: center; } -.pdf-content .pdf-fallback-button { - margin: 50px auto; -} - .repository.file.list .non-diff-file-content .plain-text { padding: 1em 2em; } diff --git a/web_src/js/index.js b/web_src/js/index.js index 1dab9ae292..98e57ef450 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -22,8 +22,6 @@ import {initRepoIssueContentHistory} from './features/repo-issue-content.js'; import {initStopwatch} from './features/stopwatch.js'; import {initFindFileInRepo} from './features/repo-findfile.js'; import {initCommentContent, initMarkupContent} from './markup/content.js'; -import {initPdfViewer} from './render/pdf.js'; -import {initGltfViewer} from './render/gltf.js'; import {initUserAuthOauth2, initUserAuth} from './features/user-auth.js'; import { @@ -189,8 +187,6 @@ onDomReady(() => { initUserAuthWebAuthnRegister(); initUserAuth(); initRepoDiffView(); - initPdfViewer(); - initGltfViewer(); initScopedAccessTokenCategories(); initColorPickers(); diff --git a/web_src/js/render/gltf.js b/web_src/js/render/gltf.js deleted file mode 100644 index 2d48e9f8e6..0000000000 --- a/web_src/js/render/gltf.js +++ /dev/null @@ -1,6 +0,0 @@ -export async function initGltfViewer() { - const els = document.querySelectorAll('model-viewer'); - if (!els.length) return; - - await import(/* webpackChunkName: "@google/model-viewer" */'@google/model-viewer'); -} diff --git a/web_src/js/render/pdf.js b/web_src/js/render/pdf.js deleted file mode 100644 index f31f161e6e..0000000000 --- a/web_src/js/render/pdf.js +++ /dev/null @@ -1,19 +0,0 @@ -import {htmlEscape} from 'escape-goat'; - -export async function initPdfViewer() { - const els = document.querySelectorAll('.pdf-content'); - if (!els.length) return; - - const pdfobject = await import(/* webpackChunkName: "pdfobject" */'pdfobject'); - - for (const el of els) { - const src = el.getAttribute('data-src'); - const fallbackText = el.getAttribute('data-fallback-button-text'); - pdfobject.embed(src, el, { - fallbackLink: htmlEscape` - ${fallbackText} - `, - }); - el.classList.remove('is-loading'); - } -} diff --git a/web_src/js/webcomponents/i18n.js b/web_src/js/webcomponents/i18n.js deleted file mode 100644 index c94b82b583..0000000000 --- a/web_src/js/webcomponents/i18n.js +++ /dev/null @@ -1,75 +0,0 @@ -const {pageData} = window.config; - -/** - * A list of plural rules for all languages. - * `plural_rules.go` defines the index for each of the 14 known plural rules. - * - * `pageData.PLURAL_RULE_LANG` is the index of the plural rule for the current language. - * `pageData.PLURAL_RULE_FALLBACK` is the index of the plural rule for the default language, - * to be used when a string is not translated in the current language. - * - * Each plural rule is a function that maps an amount `n` to the appropriate plural form index. - * Which index means which rule is specific for each language and also defined in `plural_rules.go`. - * The actual strings are in `pageData.PLURALSTRINGS_LANG` and `pageData.PLURALSTRINGS_FALLBACK` - * respectively, which is an array indexed by the plural form index. - * - * Links to the language plural rule and form definitions: - * https://codeberg.org/forgejo/forgejo/src/branch/forgejo/modules/translation/plural_rules.go - * https://www.unicode.org/cldr/charts/46/supplemental/language_plural_rules.html - * https://translate.codeberg.org/languages/$LANGUAGE_CODE/#information - * https://github.com/WeblateOrg/language-data/blob/main/languages.csv - */ -const PLURAL_RULES = [ - // [ 0] Common 2-form, e.g. English, German - function (n) { return n !== 1 ? 1 : 0 }, - - // [ 1] Bengali 2-form - function (n) { return n > 1 ? 1 : 0 }, - - // [ 2] Icelandic 2-form - function (n) { return n % 10 !== 1 || n % 100 === 11 ? 1 : 0 }, - - // [ 3] Filipino 2-form - function (n) { return n !== 1 && n !== 2 && n !== 3 && (n % 10 === 4 || n % 10 === 6 || n % 10 === 9) ? 1 : 0 }, - - // [ 4] One form - function (_) { return 0 }, - - // [ 5] Czech 3-form - function (n) { return (n === 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2 }, - - // [ 6] Russian 3-form - function (n) { return n % 10 === 1 && n % 100 !== 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2 }, - - // [ 7] Polish 3-form - function (n) { return n === 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2 }, - - // [ 8] Latvian 3-form - function (n) { return (n % 10 === 0 || n % 100 >= 11 && n % 100 <= 19) ? 0 : ((n % 10 === 1 && n % 100 !== 11) ? 1 : 2) }, - - // [ 9] Lithunian 3-form - function (n) { return (n % 10 === 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2) }, - - // [10] French 3-form - function (n) { return (n === 0 || n === 1) ? 0 : ((n !== 0 && n % 1000000 === 0) ? 1 : 2) }, - - // [11] Catalan 3-form - function (n) { return (n === 1) ? 0 : ((n !== 0 && n % 1000000 === 0) ? 1 : 2) }, - - // [12] Slovenian 4-form - function (n) { return n % 100 === 1 ? 0 : n % 100 === 2 ? 1 : n % 100 === 3 || n % 100 === 4 ? 2 : 3 }, - - // [13] Arabic 6-form - function (n) { return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5 }, -]; - -/** - * Look up the correct localized plural form for amount `n` for the string with the translation key `key`. - * If the current language does not contain a translation for this key, returns the text in the default language, - * or `null` if `suppress_fallback` is set to `true`. - */ -export function GetPluralizedString(key, n, suppress_fallback) { - const result = pageData.PLURALSTRINGS_LANG[key]?.[PLURAL_RULES[pageData.PLURAL_RULE_LANG](n)]; - if (result || suppress_fallback) return result; - return pageData.PLURALSTRINGS_FALLBACK[key][PLURAL_RULES[pageData.PLURAL_RULE_FALLBACK](n)]; -} diff --git a/web_src/js/webcomponents/index.js b/web_src/js/webcomponents/index.js index f8883fa47a..1572de262f 100644 --- a/web_src/js/webcomponents/index.js +++ b/web_src/js/webcomponents/index.js @@ -1,6 +1,5 @@ -import './polyfills.js'; -import './i18n.js'; import './relative-time.js'; import './origin-url.js'; import './overflow-menu.js'; import './absolute-date.js'; +import './lazy-webc.js'; // infrequently used components should be lazy-loaded with ... diff --git a/web_src/js/webcomponents/lazy-webc.js b/web_src/js/webcomponents/lazy-webc.js new file mode 100644 index 0000000000..3570df3b5d --- /dev/null +++ b/web_src/js/webcomponents/lazy-webc.js @@ -0,0 +1,66 @@ +import {onDomReady} from '../utils/dom.js'; + +/** + * Lazy-load the promise (making it a singleton). + * @param {()=>Promise} newPromise Promise factory. + * @returns {()=>Promise} Singleton promise + */ +function lazyPromise(newPromise) { + /** @type {Promise?} */ + let p; + return () => { + p ??= newPromise(); + return p; + }; +} + +// the following web components will only be loaded if present in the page (to reduce the bundle size for infrequently used components) +const loadableComponents = { + 'model-viewer': lazyPromise(() => { + return import(/* webpackChunkName: "model-viewer" */ '@google/model-viewer'); + }), + 'pdf-object': lazyPromise(() => { + return import(/* webpackChunkName: "pdf-object" */ './pdf-object.js'); + }), +}; + +/** + * Replace elt with an element having the given tag. + * @param {HTMLElement} elt The element to replace. + * @param {string} name The tagName of the new element. + */ +function replaceTag(elt, name) { + const successor = document.createElement(name); + // Move the children to the successor + while (elt.firstChild) { + successor.append(elt.firstChild); + } + // Copy the attributes to the successor + for (let index = elt.attributes.length - 1; index >= 0; --index) { + successor.attributes.setNamedItem(elt.attributes[index].cloneNode()); + } + // Replace elt with the successor + elt.parentNode.replaceChild(successor, elt); +} + +onDomReady(() => { + // The lazy-webc component will replace itself with an element of the type given in the attribute tag. + // This seems to be the best way without having to create a global mutationObserver. + // See https://codeberg.org/forgejo/forgejo/pulls/8510 for discussion. + window.customElements.define( + 'lazy-webc', + class extends HTMLElement { + connectedCallback() { + const name = this.getAttribute('tag'); + if (loadableComponents[name]) { + loadableComponents[name]().finally(() => { + replaceTag(this, name); + }); + } else { + console.error('lazy-webc: unknown webcomponent:', name); + replaceTag(this, name); // still replace it, maybe it was eagerly defined + } + } + }, + ); +}); diff --git a/web_src/js/webcomponents/pdf-object.js b/web_src/js/webcomponents/pdf-object.js new file mode 100644 index 0000000000..15334dafe7 --- /dev/null +++ b/web_src/js/webcomponents/pdf-object.js @@ -0,0 +1,14 @@ +import pdfobject from 'pdfobject'; + +window.customElements.define( + 'pdf-object', + class extends HTMLElement { + connectedCallback() { + // since the web-component is defined after the DOM is ready, it is safe to look at the children. + const fallbackLink = this.innerHTML; // eslint-disable-line wc/no-child-traversal-in-connectedcallback + pdfobject.embed(this.getAttribute('src'), this, { + fallbackLink, + }); + } + }, +); diff --git a/web_src/js/webcomponents/polyfills.js b/web_src/js/webcomponents/polyfills.js deleted file mode 100644 index 38f50fa02f..0000000000 --- a/web_src/js/webcomponents/polyfills.js +++ /dev/null @@ -1,17 +0,0 @@ -try { - // some browsers like PaleMoon don't have full support for Intl.NumberFormat, so do the minimum polyfill to support "relative-time-element" - // https://repo.palemoon.org/MoonchildProductions/UXP/issues/2289 - new Intl.NumberFormat('en', {style: 'unit', unit: 'minute'}).format(1); -} catch { - const intlNumberFormat = Intl.NumberFormat; - Intl.NumberFormat = function(locales, options) { - if (options.style === 'unit') { - return { - format(value) { - return ` ${value} ${options.unit}`; - }, - }; - } - return intlNumberFormat(locales, options); - }; -} diff --git a/web_src/js/webcomponents/relative-time.js b/web_src/js/webcomponents/relative-time.js index d247ced3ca..2ec87450c1 100644 --- a/web_src/js/webcomponents/relative-time.js +++ b/web_src/js/webcomponents/relative-time.js @@ -1,4 +1,3 @@ -import {GetPluralizedString} from './i18n.js'; import dayjs from 'dayjs'; const {pageData} = window.config; @@ -17,8 +16,75 @@ const ABSOLUTE_DATETIME_FORMAT = new Intl.DateTimeFormat(navigator.language, { }); const FALLBACK_DATETIME_FORMAT = new Intl.RelativeTimeFormat(navigator.language, {style: 'long'}); +/** + * A list of plural rules for all languages. + * `plural_rules.go` defines the index for each of the 14 known plural rules. + * + * `pageData.PLURAL_RULE_LANG` is the index of the plural rule for the current language. + * `pageData.PLURAL_RULE_FALLBACK` is the index of the plural rule for the default language, + * to be used when a string is not translated in the current language. + * + * Each plural rule is a function that maps an amount `n` to the appropriate plural form index. + * Which index means which rule is specific for each language and also defined in `plural_rules.go`. + * The actual strings are in `pageData.PLURALSTRINGS_LANG` and `pageData.PLURALSTRINGS_FALLBACK` + * respectively, which is an array indexed by the plural form index. + * + * Links to the language plural rule and form definitions: + * https://codeberg.org/forgejo/forgejo/src/branch/forgejo/modules/translation/plural_rules.go + * https://www.unicode.org/cldr/charts/46/supplemental/language_plural_rules.html + * https://translate.codeberg.org/languages/$LANGUAGE_CODE/#information + * https://github.com/WeblateOrg/language-data/blob/main/languages.csv + */ +const PLURAL_RULES = [ + // [ 0] Common 2-form, e.g. English, German + function (n) { return n !== 1 ? 1 : 0 }, + + // [ 1] Bengali 2-form + function (n) { return n > 1 ? 1 : 0 }, + + // [ 2] Icelandic 2-form + function (n) { return n % 10 !== 1 || n % 100 === 11 ? 1 : 0 }, + + // [ 3] Filipino 2-form + function (n) { return n !== 1 && n !== 2 && n !== 3 && (n % 10 === 4 || n % 10 === 6 || n % 10 === 9) ? 1 : 0 }, + + // [ 4] One form + function (_) { return 0 }, + + // [ 5] Czech 3-form + function (n) { return (n === 1) ? 0 : (n >= 2 && n <= 4) ? 1 : 2 }, + + // [ 6] Russian 3-form + function (n) { return n % 10 === 1 && n % 100 !== 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2 }, + + // [ 7] Polish 3-form + function (n) { return n === 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2 }, + + // [ 8] Latvian 3-form + function (n) { return (n % 10 === 0 || n % 100 >= 11 && n % 100 <= 19) ? 0 : ((n % 10 === 1 && n % 100 !== 11) ? 1 : 2) }, + + // [ 9] Lithunian 3-form + function (n) { return (n % 10 === 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2) }, + + // [10] French 3-form + function (n) { return (n === 0 || n === 1) ? 0 : ((n !== 0 && n % 1000000 === 0) ? 1 : 2) }, + + // [11] Catalan 3-form + function (n) { return (n === 1) ? 0 : ((n !== 0 && n % 1000000 === 0) ? 1 : 2) }, + + // [12] Slovenian 4-form + function (n) { return n % 100 === 1 ? 0 : n % 100 === 2 ? 1 : n % 100 === 3 || n % 100 === 4 ? 2 : 3 }, + + // [13] Arabic 6-form + function (n) { return n === 0 ? 0 : n === 1 ? 1 : n === 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5 }, +]; + +/** + * Look up the correct localized plural form for amount `n` for the string with the translation key `key`. + * If the current language does not contain a translation for this key, fallback to the browser's formatting. + */ function GetPluralizedStringOrFallback(key, n, unit) { - const translation = GetPluralizedString(key, n, true); + const translation = pageData.PLURALSTRINGS_LANG[key]?.[PLURAL_RULES[pageData.PLURAL_RULE_LANG](n)]; if (translation) return translation.replace('%d', n); return FALLBACK_DATETIME_FORMAT.format(-n, unit); } From 82daae4c7cccb8bfa90efcd6aea6da91565ecd3a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 23 Jul 2025 04:38:03 +0200 Subject: [PATCH 167/297] Update dependency vue to v3.5.18 (forgejo) (#8623) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 144 +++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 87 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index dbf23af045..d69bddd887 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,7 +52,7 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.5.16", + "vue": "3.5.18", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", @@ -4342,42 +4342,42 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz", - "integrity": "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", + "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.2", - "@vue/shared": "3.5.16", + "@babel/parser": "^7.28.0", + "@vue/shared": "3.5.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz", - "integrity": "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", + "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/compiler-core": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz", - "integrity": "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", + "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.2", - "@vue/compiler-core": "3.5.16", - "@vue/compiler-dom": "3.5.16", - "@vue/compiler-ssr": "3.5.16", - "@vue/shared": "3.5.16", + "@babel/parser": "^7.28.0", + "@vue/compiler-core": "3.5.18", + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", - "postcss": "^8.5.3", + "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, @@ -4390,64 +4390,92 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz", - "integrity": "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==", + "node_modules/@vue/compiler-sfc/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.16", - "@vue/shared": "3.5.16" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", + "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/reactivity": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.16.tgz", - "integrity": "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", + "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.16" + "@vue/shared": "3.5.18" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.16.tgz", - "integrity": "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", + "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/reactivity": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz", - "integrity": "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", + "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.16", - "@vue/runtime-core": "3.5.16", - "@vue/shared": "3.5.16", + "@vue/reactivity": "3.5.18", + "@vue/runtime-core": "3.5.18", + "@vue/shared": "3.5.18", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.16.tgz", - "integrity": "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", + "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18" }, "peerDependencies": { - "vue": "3.5.16" + "vue": "3.5.18" } }, "node_modules/@vue/shared": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz", - "integrity": "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", + "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -15522,16 +15550,16 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz", - "integrity": "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", + "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.16", - "@vue/compiler-sfc": "3.5.16", - "@vue/runtime-dom": "3.5.16", - "@vue/server-renderer": "3.5.16", - "@vue/shared": "3.5.16" + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-sfc": "3.5.18", + "@vue/runtime-dom": "3.5.18", + "@vue/server-renderer": "3.5.18", + "@vue/shared": "3.5.18" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index 4f4f9d48b8..466f8fbb01 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.5.16", + "vue": "3.5.18", "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", From 7643bdd2b503d3e141e2e9fc96afb7bccbad4e97 Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Wed, 23 Jul 2025 04:45:58 +0200 Subject: [PATCH 168/297] feat(ui): add links to review request targets in issue comments (#8239) - Add links to review request targets in issue comments - Fix links to ghost users/orgs/teams to be empty Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8239 Reviewed-by: Gusted Co-authored-by: Robert Wolff Co-committed-by: Robert Wolff --- models/issues/action_aggregator.go | 8 ++ models/issues/action_aggregator_test.go | 37 +++++++++ models/organization/org.go | 5 ++ models/organization/team.go | 50 +++++++++--- models/organization/team_test.go | 30 ++++++-- models/user/user.go | 12 +++ models/user/user_test.go | 22 ++++++ modules/templates/util_render.go | 12 ++- modules/templates/util_render_test.go | 17 ++++ .../repo/issue/view_content/comments.tmpl | 7 +- tests/integration/issue_comment_test.go | 77 +++++++++++++++++++ 11 files changed, 256 insertions(+), 21 deletions(-) create mode 100644 models/issues/action_aggregator_test.go diff --git a/models/issues/action_aggregator.go b/models/issues/action_aggregator.go index d3643adeef..c4632fd4dd 100644 --- a/models/issues/action_aggregator.go +++ b/models/issues/action_aggregator.go @@ -4,6 +4,7 @@ package issues import ( + "context" "slices" "forgejo.org/models/organization" @@ -374,3 +375,10 @@ func (t *RequestReviewTarget) Type() string { } return "team" } + +func (t *RequestReviewTarget) Link(ctx context.Context) string { + if t.User != nil { + return t.User.HomeLink() + } + return t.Team.Link(ctx) +} diff --git a/models/issues/action_aggregator_test.go b/models/issues/action_aggregator_test.go new file mode 100644 index 0000000000..1962596d2d --- /dev/null +++ b/models/issues/action_aggregator_test.go @@ -0,0 +1,37 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package issues + +import ( + "testing" + + "forgejo.org/models/db" + org_model "forgejo.org/models/organization" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + + "github.com/stretchr/testify/assert" +) + +func TestRequestReviewTarget(t *testing.T) { + unittest.PrepareTestEnv(t) + + target := RequestReviewTarget{User: &user_model.User{ID: 1, Name: "user1"}} + assert.Equal(t, int64(1), target.ID()) + assert.Equal(t, "user1", target.Name()) + assert.Equal(t, "user", target.Type()) + assert.Equal(t, "/user1", target.Link(db.DefaultContext)) + + target = RequestReviewTarget{Team: &org_model.Team{ID: 2, Name: "Collaborators", OrgID: 3}} + assert.Equal(t, int64(2), target.ID()) + assert.Equal(t, "Collaborators", target.Name()) + assert.Equal(t, "team", target.Type()) + assert.Equal(t, "/org/org3/teams/Collaborators", target.Link(db.DefaultContext)) + + target = RequestReviewTarget{Team: org_model.NewGhostTeam()} + assert.Equal(t, int64(-1), target.ID()) + assert.Equal(t, "Ghost team", target.Name()) + assert.Equal(t, "team", target.Type()) + assert.Empty(t, target.Link(db.DefaultContext)) +} diff --git a/models/organization/org.go b/models/organization/org.go index ff95261051..c4df5d4fe1 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -186,6 +186,11 @@ func (org *Organization) CanCreateRepo() bool { return org.AsUser().CanCreateRepo() } +// IsGhost returns if the organization is a ghost +func (org *Organization) IsGhost() bool { + return org.AsUser().IsGhost() +} + // FindOrgMembersOpts represensts find org members conditions type FindOrgMembersOpts struct { db.ListOptions diff --git a/models/organization/team.go b/models/organization/team.go index c78eff39fb..209471e013 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -1,5 +1,6 @@ -// Copyright 2018 The Gitea Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package organization @@ -7,6 +8,7 @@ package organization import ( "context" "fmt" + "net/url" "strings" "forgejo.org/models/db" @@ -20,13 +22,6 @@ import ( "xorm.io/builder" ) -// ___________ -// \__ ___/___ _____ _____ -// | |_/ __ \\__ \ / \ -// | |\ ___/ / __ \| Y Y \ -// |____| \___ >____ /__|_| / -// \/ \/ \/ - // ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error. type ErrTeamAlreadyExist struct { OrgID int64 @@ -193,6 +188,27 @@ func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode return perm.AccessModeNone } +// GetOrg returns the team's organization +func (t *Team) GetOrg(ctx context.Context) *Organization { + org, err := GetOrgByID(ctx, t.OrgID) + if err != nil { + return OrgFromUser(user_model.NewGhostUser()) + } + return org +} + +// Link returns the team's page link +func (t *Team) Link(ctx context.Context) string { + if t.IsGhost() { + return "" + } + org := t.GetOrg(ctx) + if org.IsGhost() { + return "" + } + return org.OrganisationLink() + "/teams/" + url.PathEscape(t.Name) +} + // IsUsableTeamName tests if a name could be as team name func IsUsableTeamName(name string) error { switch name { @@ -293,10 +309,22 @@ func FixInconsistentOwnerTeams(ctx context.Context) (int64, error) { return int64(len(teamIDs)), nil } +const ( + GhostTeamID = -1 + GhostTeamName = "Ghost team" + GhostTeamLowerName = "ghost team" +) + +// NewGhostTeam creates ghost team (for deleted team) func NewGhostTeam() *Team { return &Team{ - ID: -1, - Name: "Ghost team", - LowerName: "ghost team", + ID: GhostTeamID, + Name: GhostTeamName, + LowerName: GhostTeamLowerName, } } + +// IsGhost returns if a team is a ghost team +func (t *Team) IsGhost() bool { + return t.ID == GhostTeamID +} diff --git a/models/organization/team_test.go b/models/organization/team_test.go index 60c500e7ec..768ccdf5be 100644 --- a/models/organization/team_test.go +++ b/models/organization/team_test.go @@ -1,4 +1,5 @@ // Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package organization_test @@ -15,14 +16,33 @@ import ( "github.com/stretchr/testify/require" ) -func TestTeam_IsOwnerTeam(t *testing.T) { +func TestTeam(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}) - assert.True(t, team.IsOwnerTeam()) + owners := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}) + assert.Equal(t, int64(3), owners.GetOrg(db.DefaultContext).ID) + assert.Equal(t, "/org/org3/teams/Owners", owners.Link(db.DefaultContext)) + assert.False(t, owners.IsGhost()) + assert.True(t, owners.IsOwnerTeam()) - team = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}) - assert.False(t, team.IsOwnerTeam()) + team1 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}) + assert.Equal(t, int64(3), team1.GetOrg(db.DefaultContext).ID) + assert.Equal(t, "/org/org3/teams/team1", team1.Link(db.DefaultContext)) + assert.False(t, team1.IsGhost()) + assert.False(t, team1.IsOwnerTeam()) + + ghost := organization.NewGhostTeam() + assert.Equal(t, int64(-1), ghost.ID) + assert.Equal(t, int64(-1), ghost.GetOrg(db.DefaultContext).ID) + assert.Empty(t, ghost.Link(db.DefaultContext)) + assert.True(t, ghost.IsGhost()) + assert.False(t, ghost.IsOwnerTeam()) + + ghosted := organization.Team{ID: 10, Name: "Ghosted"} + assert.Equal(t, int64(-1), ghosted.GetOrg(db.DefaultContext).ID) + assert.Empty(t, ghosted.Link(db.DefaultContext)) + assert.False(t, ghosted.IsGhost()) + assert.False(t, ghosted.IsOwnerTeam()) } func TestTeam_IsMember(t *testing.T) { diff --git a/models/user/user.go b/models/user/user.go index 6b54776adf..e3d725677f 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -296,6 +296,9 @@ func (u *User) CanImportLocal() bool { // DashboardLink returns the user dashboard page link. func (u *User) DashboardLink() string { + if u.IsGhost() { + return "" + } if u.IsOrganization() { return u.OrganisationLink() + "/dashboard" } @@ -304,16 +307,25 @@ func (u *User) DashboardLink() string { // HomeLink returns the user or organization home page link. func (u *User) HomeLink() string { + if u.IsGhost() { + return "" + } return setting.AppSubURL + "/" + url.PathEscape(u.Name) } // HTMLURL returns the user or organization's full link. func (u *User) HTMLURL() string { + if u.IsGhost() { + return "" + } return setting.AppURL + url.PathEscape(u.Name) } // OrganisationLink returns the organization sub page link. func (u *User) OrganisationLink() string { + if u.IsGhost() || !u.IsOrganization() { + return "" + } return setting.AppSubURL + "/org/" + url.PathEscape(u.Name) } diff --git a/models/user/user_test.go b/models/user/user_test.go index f9a3aa6075..71190751da 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -46,6 +46,28 @@ func TestIsValidUserID(t *testing.T) { assert.True(t, user_model.IsValidUserID(200)) } +func TestUserLinks(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + assert.Equal(t, "/", user1.DashboardLink()) + assert.Equal(t, "/user1", user1.HomeLink()) + assert.Equal(t, "https://try.gitea.io/user1", user1.HTMLURL()) + assert.Empty(t, user1.OrganisationLink()) + + org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) + assert.Equal(t, "/org/org3/dashboard", org3.DashboardLink()) + assert.Equal(t, "/org3", org3.HomeLink()) + assert.Equal(t, "https://try.gitea.io/org3", org3.HTMLURL()) + assert.Equal(t, "/org/org3", org3.OrganisationLink()) + + ghost := user_model.NewGhostUser() + assert.Empty(t, ghost.DashboardLink()) + assert.Empty(t, ghost.HomeLink()) + assert.Empty(t, ghost.HTMLURL()) + assert.Empty(t, ghost.OrganisationLink()) +} + func TestGetUserFromMap(t *testing.T) { id := int64(200) idMap := map[int64]*user_model.User{ diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 48f4eb04a3..badff5f193 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -275,10 +275,18 @@ func RenderUser(ctx context.Context, user user_model.User) template.HTML { html.EscapeString(user.GetDisplayName()))) } -func RenderReviewRequest(users []issues_model.RequestReviewTarget) template.HTML { +func RenderReviewRequest(ctx context.Context, users []issues_model.RequestReviewTarget) template.HTML { usernames := make([]string, 0, len(users)) for _, user := range users { - usernames = append(usernames, html.EscapeString(user.Name())) + if user.ID() > 0 { + usernames = append(usernames, fmt.Sprintf( + "%s", + user.Link(ctx), html.EscapeString(user.Name()))) + } else { + usernames = append(usernames, fmt.Sprintf( + "%s", + html.EscapeString(user.Name()))) + } } htmlCode := `` diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index a5fb18642a..8d58d7d2d4 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -10,6 +10,7 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" + org_model "forgejo.org/models/organization" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/setting" @@ -266,3 +267,19 @@ func TestRenderUser(t *testing.T) { assert.Contains(t, RenderUser(db.DefaultContext, *ghost), "Ghost") } + +func TestRenderReviewRequest(t *testing.T) { + unittest.PrepareTestEnv(t) + + target1 := issues_model.RequestReviewTarget{User: &user_model.User{ID: 1, Name: "user1", FullName: "User "}} + target2 := issues_model.RequestReviewTarget{Team: &org_model.Team{ID: 2, Name: "Team2", OrgID: 3}} + target3 := issues_model.RequestReviewTarget{Team: org_model.NewGhostTeam()} + assert.Contains(t, RenderReviewRequest(db.DefaultContext, []issues_model.RequestReviewTarget{target1, target2, target3}), + "user1, "+ + "Team2, "+ + "Ghost team") + + defer test.MockVariableValue(&setting.UI.DefaultShowFullName, true)() + assert.Contains(t, RenderReviewRequest(db.DefaultContext, []issues_model.RequestReviewTarget{target1}), + "User <One>") +} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 4ab1fa7584..76b02f4755 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -548,14 +548,15 @@ {{svg "octicon-eye"}} {{template "shared/user/avatarlink" dict "user" .Poster}} + {{template "shared/user/authorlink" .Poster}} {{if and (eq (len .RemovedRequestReview) 1) (eq (len .AddedRequestReview) 0) (eq ((index .RemovedRequestReview 0).ID) .PosterID) (eq ((index .RemovedRequestReview 0).Type) "user")}} {{ctx.Locale.Tr "repo.issues.review.remove_review_request_self" $createdStr}} {{else if and .AddedRequestReview (not .RemovedRequestReview)}} - {{ctx.Locale.TrN (len .AddedRequestReview) "repo.issues.review.add_review_request" "repo.issues.review.add_review_requests" (RenderReviewRequest .AddedRequestReview) $createdStr}} + {{ctx.Locale.TrN (len .AddedRequestReview) "repo.issues.review.add_review_request" "repo.issues.review.add_review_requests" (RenderReviewRequest $.Context .AddedRequestReview) $createdStr}} {{else if and (not .AddedRequestReview) .RemovedRequestReview}} - {{ctx.Locale.TrN (len .RemovedRequestReview) "repo.issues.review.remove_review_request" "repo.issues.review.remove_review_requests" (RenderReviewRequest .RemovedRequestReview) $createdStr}} + {{ctx.Locale.TrN (len .RemovedRequestReview) "repo.issues.review.remove_review_request" "repo.issues.review.remove_review_requests" (RenderReviewRequest $.Context .RemovedRequestReview) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.review.add_remove_review_requests" (RenderReviewRequest .AddedRequestReview) (RenderReviewRequest .RemovedRequestReview) $createdStr}} + {{ctx.Locale.Tr "repo.issues.review.add_remove_review_requests" (RenderReviewRequest $.Context .AddedRequestReview) (RenderReviewRequest $.Context .RemovedRequestReview) $createdStr}} {{end}}
diff --git a/tests/integration/issue_comment_test.go b/tests/integration/issue_comment_test.go index 6c4a514eba..eda643fa79 100644 --- a/tests/integration/issue_comment_test.go +++ b/tests/integration/issue_comment_test.go @@ -5,13 +5,20 @@ package integration import ( "net/http" + "strconv" "strings" "testing" + "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + org_model "forgejo.org/models/organization" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" "forgejo.org/tests" "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func testIssueCommentChangeEvent(t *testing.T, htmlDoc *HTMLDoc, commentID, badgeOcticon, avatarTitle, avatarLink string, texts, links []string) { @@ -238,6 +245,76 @@ func TestIssueCommentChangeAssignee(t *testing.T) { []string{"/user2"}) } +func TestIssueCommentChangeReviewRequest(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 6}) + require.NoError(t, pull.LoadIssue(db.DefaultContext)) + issue := pull.Issue + require.NoError(t, issue.LoadRepo(db.DefaultContext)) + + user1, err := user_model.GetUserByID(db.DefaultContext, 1) + require.NoError(t, err) + user2, err := user_model.GetUserByID(db.DefaultContext, 2) + require.NoError(t, err) + team1, err := org_model.GetTeamByID(db.DefaultContext, 2) + require.NoError(t, err) + assert.NotNil(t, team1) + + // Request from other + comment1, err := issues_model.AddReviewRequest(db.DefaultContext, issue, user2, user1) + require.NoError(t, err) + + // Refuse review + comment2, err := issues_model.RemoveReviewRequest(db.DefaultContext, issue, user2, user2) + require.NoError(t, err) + + // Request from other + comment3, err := issues_model.AddReviewRequest(db.DefaultContext, issue, user2, user1) + require.NoError(t, err) + // Request from team + comment4, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team1, user1) + require.NoError(t, err) + + // Remove request from team + comment5, err := issues_model.RemoveTeamReviewRequest(db.DefaultContext, issue, team1, user2) + require.NoError(t, err) + // Request from other + comment6, err := issues_model.AddReviewRequest(db.DefaultContext, issue, user1, user2) + require.NoError(t, err) + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/org3/repo3/pulls/2") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + // Request from other + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment1.ID, 10), + "octicon-eye", "User One", "/user1", + []string{"user1 requested review from user2"}, + []string{"/user1", "/user2"}) + + // Refuse review + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment2.ID, 10), + "octicon-eye", "< Ur Tw ><", "/user2", + []string{"user2 refused to review"}, + []string{"/user2"}) + + // Request review from other and from team + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment3.ID, 10), + "octicon-eye", "User One", "/user1", + []string{"user1 requested reviews from user2, team1"}, + []string{"/user1", "/user2", "/org/org3/teams/team1"}) + assert.Empty(t, htmlDoc.Find("#issuecomment-"+strconv.FormatInt(comment4.ID, 10)+" .text").Text()) + + // Remove and add request + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment5.ID, 10), + "octicon-eye", "< Ur Tw ><", "/user2", + []string{"user2 requested reviews from user1 and removed review requests for team1"}, + []string{"/user2", "/user1", "/org/org3/teams/team1"}) + assert.Empty(t, htmlDoc.Find("#issuecomment-"+strconv.FormatInt(comment6.ID, 10)+" .text").Text()) +} + func TestIssueCommentChangeLock(t *testing.T) { defer tests.PrepareTestEnv(t)() From f3ccfc49697ffc762d1043d838fc13314ce98799 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 23 Jul 2025 07:30:30 +0200 Subject: [PATCH 169/297] fix: short-circuit to avoid rebasing (#8622) - Do not try to rebase a pull request when it is zero commits behind. We can trust this number as before merging a repository the status of the pull request is mergeable and thus not in a conflict checking stage (where this would be updated). - This resolves a issue where `git-replay` would rebase a pull request when this is not needed and causes to lose the signature of Git commits and commit IDs as shown in the pullrequest commits timeline. - Resolves forgejo/forgejo#8619 - Add a simple integration test that simply checks that after merging a up-to-date pull request via the rebase style that the commit ID didn't change. This demonstrates that it didn't do needlessly rebasing. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8622 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- services/pull/merge_prepare.go | 5 +++++ tests/integration/pull_merge_test.go | 29 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/services/pull/merge_prepare.go b/services/pull/merge_prepare.go index fc70da10a4..4598d57b7a 100644 --- a/services/pull/merge_prepare.go +++ b/services/pull/merge_prepare.go @@ -249,6 +249,11 @@ func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) ctx.outbuf.Reset() ctx.errbuf.Reset() + // If the pull request is zero commits behind, then no rebasing needs to be done. + if ctx.pr.CommitsBehind == 0 { + return nil + } + // Check git version for availability of git-replay. If it is available, we use // it for performance and to preserve unknown commit headers like the // "change-id" header used by Jujutsu and GitButler to track changes across diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index cca2381fd4..ab3d1604de 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -1135,3 +1135,32 @@ func TestPullDeleteBranchPerms(t *testing.T) { user4Session.MakeRequest(t, req, http.StatusOK) }) } + +// Test that rebasing only happens when its necessary. +func TestRebaseWhenNecessary(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + + resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") + pullLink := test.RedirectURL(resp) + + resp = session.MakeRequest(t, NewRequest(t, "GET", test.RedirectURL(resp)+"/commits"), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + commitLinkBefore, ok := htmlDoc.Find("a.sha").Attr("href") + assert.True(t, ok) + commitBefore := commitLinkBefore[strings.LastIndexByte(commitLinkBefore, '/'):] + + elem := strings.Split(pullLink, "/") + testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false) + + resp = session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1"), http.StatusOK) + htmlDoc = NewHTMLParser(t, resp.Body) + commitLinkAfter, ok := htmlDoc.Find(".latest-commit a.sha").Attr("href") + assert.True(t, ok) + commitAfter := commitLinkAfter[strings.LastIndexByte(commitLinkAfter, '/'):] + + assert.Equal(t, commitBefore, commitAfter) + }) +} From 4d06d62515a5e8d63f8714c67272159a7cc08ad2 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 23 Jul 2025 09:17:32 +0200 Subject: [PATCH 170/297] fix(ci): pull stylus from github:stylus/stylus#0.57.0 (#8625) npm error 404 Not Found - GET https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz - Not found Workaround to be reverted when the issue is fixed. Refs https://github.com/stylus/stylus/issues/2938 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8625 Reviewed-by: Michael Kriese Reviewed-by: Beowulf Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- package-lock.json | 3 +-- package.json | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d69bddd887..d5516ede3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14138,8 +14138,7 @@ }, "node_modules/stylus": { "version": "0.57.0", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz", - "integrity": "sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==", + "resolved": "git+ssh://git@github.com/stylus/stylus.git#bc1404aa1f6c03341bd76529c8cf4beb4f3d99f7", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 466f8fbb01..2a2928a699 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,9 @@ "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, + "overrides": { + "stylus": "github:stylus/stylus#0.57.0" + }, "browserslist": [ "defaults" ] From 4d20a74c0463c41f45976a4cd6858b43ffcb3926 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Wed, 23 Jul 2025 10:37:46 +0200 Subject: [PATCH 171/297] chore(renovate): use `forgejo` platform (#8629) Use the new `forgejo` platform instead of the `gitea` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8629 Reviewed-by: Earl Warren Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- .forgejo/workflows/renovate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index a4e438a879..ecc39e7c62 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -49,7 +49,7 @@ jobs: LOG_LEVEL: debug RENOVATE_BASE_DIR: ${{ github.workspace }}/.tmp RENOVATE_ENDPOINT: ${{ github.server_url }} - RENOVATE_PLATFORM: gitea + RENOVATE_PLATFORM: forgejo RENOVATE_REPOSITORY_CACHE: 'enabled' RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }} RENOVATE_GIT_AUTHOR: 'Renovate Bot ' From b52cec753fc35391b58399563ce696bea549f79e Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 23 Jul 2025 22:30:39 +0200 Subject: [PATCH 172/297] Revert "fix(ci): pull stylus from github:stylus/stylus#0.57.0 (#8625)" (#8638) This reverts commit 4d06d62515a5e8d63f8714c67272159a7cc08ad2. https://www.npmjs.com/package/stylus?activeTab=versions is back. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8638 Reviewed-by: Michael Kriese Reviewed-by: Beowulf Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- package-lock.json | 3 ++- package.json | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index d5516ede3e..d69bddd887 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14138,7 +14138,8 @@ }, "node_modules/stylus": { "version": "0.57.0", - "resolved": "git+ssh://git@github.com/stylus/stylus.git#bc1404aa1f6c03341bd76529c8cf4beb4f3d99f7", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz", + "integrity": "sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 2a2928a699..466f8fbb01 100644 --- a/package.json +++ b/package.json @@ -101,9 +101,6 @@ "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, - "overrides": { - "stylus": "github:stylus/stylus#0.57.0" - }, "browserslist": [ "defaults" ] From 2f708695192e4c3d6a14b7bb33b1476997b36414 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 23 Jul 2025 22:35:40 +0200 Subject: [PATCH 173/297] chore: integration test when rebase should happen (#8635) - Follow up to forgejo/forgejo#8622 - Now the converse is also tested, a rebase should have happened and the commit IDs should differ. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8635 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- tests/integration/pull_merge_test.go | 73 ++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index ab3d1604de..b8923dd6f4 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -1141,26 +1141,67 @@ func TestRebaseWhenNecessary(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { session := loginUser(t, "user1") testRepoFork(t, session, "user2", "repo1", "user1", "repo1") - testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") - pullLink := test.RedirectURL(resp) + t.Run("No rebase needed", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - resp = session.MakeRequest(t, NewRequest(t, "GET", test.RedirectURL(resp)+"/commits"), http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - commitLinkBefore, ok := htmlDoc.Find("a.sha").Attr("href") - assert.True(t, ok) - commitBefore := commitLinkBefore[strings.LastIndexByte(commitLinkBefore, '/'):] + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") - elem := strings.Split(pullLink, "/") - testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false) + resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") + pullLink := test.RedirectURL(resp) - resp = session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1"), http.StatusOK) - htmlDoc = NewHTMLParser(t, resp.Body) - commitLinkAfter, ok := htmlDoc.Find(".latest-commit a.sha").Attr("href") - assert.True(t, ok) - commitAfter := commitLinkAfter[strings.LastIndexByte(commitLinkAfter, '/'):] + resp = session.MakeRequest(t, NewRequest(t, "GET", test.RedirectURL(resp)+"/commits"), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + commitLinkBefore, ok := htmlDoc.Find("a.sha").Attr("href") + assert.True(t, ok) + commitBefore := commitLinkBefore[strings.LastIndexByte(commitLinkBefore, '/'):] - assert.Equal(t, commitBefore, commitAfter) + elem := strings.Split(pullLink, "/") + testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false) + + resp = session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1"), http.StatusOK) + htmlDoc = NewHTMLParser(t, resp.Body) + commitLinkAfter, ok := htmlDoc.Find(".latest-commit a.sha").Attr("href") + assert.True(t, ok) + commitAfter := commitLinkAfter[strings.LastIndexByte(commitLinkAfter, '/'):] + + assert.Equal(t, commitBefore, commitAfter) + }) + + t.Run("Rebase needed", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Make user2/repo1 ahead of user1/repo1 + testEditFile(t, session, "user2", "repo1", "master", "README.md", "Hello, World (Edited 2x)\n") + + // To avoid conflicts, create a new file on /user/repo1. + session.MakeRequest(t, NewRequestWithValues(t, "POST", "/user1/repo1/_new/master", map[string]string{ + "_csrf": GetCSRF(t, session, "/user/settings"), + "commit_choice": "direct", + "tree_path": "test-file.md", + "content": "newly-added-test-file", + "commit_mail_id": "-1", + }), http.StatusSeeOther) + + resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is another pull") + pullLink := test.RedirectURL(resp) + + resp = session.MakeRequest(t, NewRequest(t, "GET", test.RedirectURL(resp)+"/commits"), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + commitLinkBefore, ok := htmlDoc.Find("a.sha").Attr("href") + assert.True(t, ok) + commitBefore := commitLinkBefore[strings.LastIndexByte(commitLinkBefore, '/'):] + + elem := strings.Split(pullLink, "/") + testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false) + + resp = session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1"), http.StatusOK) + htmlDoc = NewHTMLParser(t, resp.Body) + commitLinkAfter, ok := htmlDoc.Find(".latest-commit a.sha").Attr("href") + assert.True(t, ok) + commitAfter := commitLinkAfter[strings.LastIndexByte(commitLinkAfter, '/'):] + + assert.NotEqual(t, commitBefore, commitAfter) + }) }) } From 87a7bf2436de7c14f782764fbea7cdc1d86af3c8 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Thu, 24 Jul 2025 07:17:11 +0200 Subject: [PATCH 174/297] fix(ui): update i18n usage in comments (#8644) Fix regression of https://codeberg.org/forgejo/forgejo/pulls/8214 (regressing v11 feature https://codeberg.org/forgejo/forgejo/pulls/6523) Reporeted by @Andre601. ## Preview ![bug](/attachments/0e0c4703-537f-4adc-95f7-4047710522b4) ![fixed](/attachments/07bc5824-87ae-43da-92a2-8e6e9b9cf567) ## Testing * go to https://v13.next.forgejo.org/, log in * create repo, add some issue labels (on `./labels`) * create issue * add some labels to it and then close it * observe that what you see looks more like the 2nd screenshot than the 1st screenshot ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8644): fix(ui): update i18n usage in comments Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8644 Reviewed-by: Earl Warren Reviewed-by: Robert Wolff Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- templates/repo/issue/view_content/comments.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 76b02f4755..3e833cbc5a 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -721,18 +721,18 @@
  • {{svg "octicon-dot-fill"}} {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.pulls.reopened_at" "" ""}} + {{ctx.Locale.Tr "repo.pulls.reopened_at" ""}} {{else}} - {{ctx.Locale.Tr "repo.issues.reopened_at" "" ""}} + {{ctx.Locale.Tr "repo.issues.reopened_at" ""}} {{end}}
  • {{else if and (not .Aggregator.PrevClosed) .Aggregator.IsClosed}} {{svg "octicon-circle-slash"}}
  • {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.pulls.closed_at" "" ""}} + {{ctx.Locale.Tr "repo.pulls.closed_at" ""}} {{else}} - {{ctx.Locale.Tr "repo.issues.closed_at" "" ""}} + {{ctx.Locale.Tr "repo.issues.closed_at" ""}} {{end}}
  • {{end}} From bfa9c89e6f1e8eef787d848a20c872820098f851 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 24 Jul 2025 17:19:24 +0200 Subject: [PATCH 175/297] Revert "feat: remove API authentication methods that uses the URL query (#7924)" (#8633) This reverts commit b2a3966e648fc72eddf1bbc9383b069b0c8d59e8. weblate etc. are using this method and need to be updated before the change is enforced. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8633 Reviewed-by: Michael Kriese Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- modules/setting/security.go | 11 +++++++++++ routers/api/shared/middleware.go | 8 ++++++++ routers/api/v1/api.go | 12 ++++++++++++ services/auth/oauth2.go | 12 ++++++++++++ templates/swagger/v1_json.tmpl | 18 ++++++++++++++++++ tests/mysql.ini.tmpl | 1 + tests/pgsql.ini.tmpl | 1 + tests/sqlite.ini.tmpl | 1 + 8 files changed, 64 insertions(+) diff --git a/modules/setting/security.go b/modules/setting/security.go index c38d8dae79..f3480d1056 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -35,6 +35,7 @@ var ( PasswordHashAlgo string PasswordCheckPwn bool SuccessfulTokensCacheSize int + DisableQueryAuthToken bool CSRFCookieName = "_csrf" CSRFCookieHTTPOnly = true ) @@ -159,4 +160,14 @@ func loadSecurityFrom(rootCfg ConfigProvider) { PasswordComplexity = append(PasswordComplexity, name) } } + + sectionHasDisableQueryAuthToken := sec.HasKey("DISABLE_QUERY_AUTH_TOKEN") + + // TODO: default value should be true in future releases + DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false) + + // warn if the setting is set to false explicitly + if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken { + log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.") + } } diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go index 7d537f1ef9..f56acbe1bf 100644 --- a/routers/api/shared/middleware.go +++ b/routers/api/shared/middleware.go @@ -30,6 +30,7 @@ func Middlewares() (stack []any) { return append(stack, context.APIContexter(), + checkDeprecatedAuthMethods, // Get user from session if logged in. apiAuth(buildAuthGroup()), verifyAuthWithOptions(&common.VerifyOptions{ @@ -126,6 +127,13 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC } } +// check for and warn against deprecated authentication options +func checkDeprecatedAuthMethods(ctx *context.APIContext) { + if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { + ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") + } +} + func securityHeaders() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 3b66d02fba..6a51f33bd8 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -22,6 +22,8 @@ // // Security: // - BasicAuth : +// - Token : +// - AccessToken : // - AuthorizationHeaderToken : // - SudoParam : // - SudoHeader : @@ -30,6 +32,16 @@ // SecurityDefinitions: // BasicAuth: // type: basic +// Token: +// type: apiKey +// name: token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. +// AccessToken: +// type: apiKey +// name: access_token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. // AuthorizationHeaderToken: // type: apiKey // name: Authorization diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 4fdd15d7ec..fa13c20a7f 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -122,6 +122,18 @@ func (o *OAuth2) Name() string { // representing whether the token exists or not func parseToken(req *http.Request) (string, bool) { _ = req.ParseForm() + if !setting.DisableQueryAuthToken { + // Check token. + if token := req.Form.Get("token"); token != "" { + return token, true + } + // Check access token. + if token := req.Form.Get("access_token"); token != "" { + return token, true + } + } else if req.Form.Get("token") != "" || req.Form.Get("access_token") != "" { + log.Warn("API token sent in query string but DISABLE_QUERY_AUTH_TOKEN=true") + } // check header token if auHead := req.Header.Get("Authorization"); auHead != "" { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index bba6825b72..7cf3d4301f 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -30084,6 +30084,12 @@ } }, "securityDefinitions": { + "AccessToken": { + "description": "This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead.", + "type": "apiKey", + "name": "access_token", + "in": "query" + }, "AuthorizationHeaderToken": { "description": "API tokens must be prepended with \"token\" followed by a space.", "type": "apiKey", @@ -30110,12 +30116,24 @@ "type": "apiKey", "name": "X-FORGEJO-OTP", "in": "header" + }, + "Token": { + "description": "This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead.", + "type": "apiKey", + "name": "token", + "in": "query" } }, "security": [ { "BasicAuth": [] }, + { + "Token": [] + }, + { + "AccessToken": [] + }, { "AuthorizationHeaderToken": [] }, diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index f44aff7594..3315d85a3f 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -92,6 +92,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ +DISABLE_QUERY_AUTH_TOKEN = true [lfs] PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/data/lfs diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl index 829fdc5b75..1e9b981800 100644 --- a/tests/pgsql.ini.tmpl +++ b/tests/pgsql.ini.tmpl @@ -97,6 +97,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ +DISABLE_QUERY_AUTH_TOKEN = true [lfs] MINIO_BASE_PATH = lfs/ diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl index d36388405b..df6cea44ca 100644 --- a/tests/sqlite.ini.tmpl +++ b/tests/sqlite.ini.tmpl @@ -94,6 +94,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8 +DISABLE_QUERY_AUTH_TOKEN = true [oauth2] JWT_SECRET = KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko From f4894b0edd1fceb1fade57b58941ac583f021447 Mon Sep 17 00:00:00 2001 From: DebuggerAndrzej Date: Thu, 24 Jul 2025 22:40:27 +0200 Subject: [PATCH 176/297] feat: add sort parameter for users/search api endpoint (#8594) Introduces a new sort parameter to the `/users/search` API endpoint, similar to the one that already exists for the `/admin/users` endpoint. Resolves #8499 Co-authored-by: DebuggerAndrzej Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8594 Reviewed-by: Lucas Reviewed-by: Gusted Co-authored-by: DebuggerAndrzej Co-committed-by: DebuggerAndrzej --- routers/api/v1/admin/user.go | 22 +------- routers/api/v1/user/user.go | 6 +++ routers/api/v1/utils/db_search_order.go | 28 ++++++++++ templates/swagger/v1_json.tmpl | 14 +++++ tests/integration/api_user_search_test.go | 65 +++++++++++++++++++++++ 5 files changed, 114 insertions(+), 21 deletions(-) create mode 100644 routers/api/v1/utils/db_search_order.go diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 8aa67b3b0a..de54f21d94 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -436,26 +436,6 @@ func SearchUsers(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - sort := ctx.FormString("sort") - var orderBy db.SearchOrderBy - - switch sort { - case "oldest": - orderBy = db.SearchOrderByOldest - case "newest": - orderBy = db.SearchOrderByNewest - case "alphabetically": - orderBy = db.SearchOrderByAlphabetically - case "reversealphabetically": - orderBy = db.SearchOrderByAlphabeticallyReverse - case "recentupdate": - orderBy = db.SearchOrderByRecentUpdated - case "leastupdate": - orderBy = db.SearchOrderByLeastUpdated - default: - orderBy = db.SearchOrderByAlphabetically - } - intSource, err := strconv.ParseInt(ctx.FormString("source_id"), 10, 64) var sourceID optional.Option[int64] if ctx.FormString("source_id") == "" || err != nil { @@ -469,7 +449,7 @@ func SearchUsers(ctx *context.APIContext) { Type: user_model.UserTypeIndividual, LoginName: ctx.FormTrim("login_name"), SourceID: sourceID, - OrderBy: orderBy, + OrderBy: utils.GetDbSearchOrder(ctx), ListOptions: listOptions, }) if err != nil { diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 5bdd56c892..19f7440047 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -33,6 +33,11 @@ func Search(ctx *context.APIContext) { // description: ID of the user to search for // type: integer // format: int64 + // - name: sort + // in: query + // description: sort order of results + // type: string + // enum: [oldest, newest, alphabetically, reversealphabetically, recentupdate, leastupdate] // - name: page // in: query // description: page number of results to return (1-based) @@ -81,6 +86,7 @@ func Search(ctx *context.APIContext) { SearchByEmail: true, Visible: visible, ListOptions: listOptions, + OrderBy: utils.GetDbSearchOrder(ctx), }) if err != nil { ctx.JSON(http.StatusInternalServerError, map[string]any{ diff --git a/routers/api/v1/utils/db_search_order.go b/routers/api/v1/utils/db_search_order.go new file mode 100644 index 0000000000..f089ba5f16 --- /dev/null +++ b/routers/api/v1/utils/db_search_order.go @@ -0,0 +1,28 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package utils + +import ( + "forgejo.org/models/db" + "forgejo.org/services/context" +) + +func GetDbSearchOrder(ctx *context.APIContext) db.SearchOrderBy { + switch ctx.FormString("sort") { + case "oldest": + return db.SearchOrderByOldest + case "newest": + return db.SearchOrderByNewest + case "alphabetically": + return db.SearchOrderByAlphabetically + case "reversealphabetically": + return db.SearchOrderByAlphabeticallyReverse + case "recentupdate": + return db.SearchOrderByRecentUpdated + case "leastupdate": + return db.SearchOrderByLeastUpdated + default: + return db.SearchOrderByAlphabetically + } +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 7cf3d4301f..e03f5d57c1 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -20284,6 +20284,20 @@ "name": "uid", "in": "query" }, + { + "enum": [ + "oldest", + "newest", + "alphabetically", + "reversealphabetically", + "recentupdate", + "leastupdate" + ], + "type": "string", + "description": "sort order of results", + "name": "sort", + "in": "query" + }, { "type": "integer", "description": "page number of results to return (1-based)", diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go index 6252c2ebde..0e8f7a123e 100644 --- a/tests/integration/api_user_search_test.go +++ b/tests/integration/api_user_search_test.go @@ -4,18 +4,25 @@ package integration import ( + "context" + "fmt" "net/http" + "strconv" "testing" + "time" auth_model "forgejo.org/models/auth" + "forgejo.org/models/db" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" "forgejo.org/modules/test" + "forgejo.org/modules/timeutil" "forgejo.org/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type SearchResults struct { @@ -179,3 +186,61 @@ func TestAPIUserSearchByEmail(t *testing.T) { assert.Len(t, results.Data, 1) assert.Equal(t, query, results.Data[0].Email) } + +func TestUsersSearchSorted(t *testing.T) { + defer tests.PrepareTestEnv(t)() + createTimestamp := time.Now().Unix() - 1000 + updateTimestamp := time.Now().Unix() - 500 + sess := db.GetEngine(context.Background()) + + for i := int64(1); i <= 10; i++ { + name := "sorttest" + strconv.Itoa(int(i)) + user := &user_model.User{ + Name: name, + LowerName: name, + LoginName: name, + Email: name + "@example.com", + Passwd: name + ".password", + Avatar: "xyz", + Type: user_model.UserTypeIndividual, + LoginType: auth_model.OAuth2, + CreatedUnix: timeutil.TimeStamp(createTimestamp - i), + UpdatedUnix: timeutil.TimeStamp(updateTimestamp - i), + } + _, err := sess.NoAutoTime().Insert(user) + require.NoError(t, err) + } + + session := loginUser(t, "user1") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser) + + testCases := []struct { + sortType string + expectedUsers []string + }{ + {"alphabetically", []string{"sorttest1", "sorttest10", "sorttest2", "sorttest3"}}, + {"reversealphabetically", []string{"sorttest9", "sorttest8", "sorttest7", "sorttest6"}}, + {"newest", []string{"sorttest1", "sorttest2", "sorttest3", "sorttest4"}}, + {"oldest", []string{"sorttest10", "sorttest9", "sorttest8", "sorttest7"}}, + {"recentupdate", []string{"sorttest1", "sorttest2", "sorttest3", "sorttest4"}}, + {"leastupdate", []string{"sorttest10", "sorttest9", "sorttest8", "sorttest7"}}, + } + + for _, testCase := range testCases { + req := NewRequest( + t, + "GET", + fmt.Sprintf("/api/v1/users/search?q=sorttest&sort=%s&limit=4", + testCase.sortType, + ), + ).AddTokenAuth(token) + resp := session.MakeRequest(t, req, http.StatusOK) + + var results SearchResults + DecodeJSON(t, resp, &results) + assert.Len(t, results.Data, 4) + for i, searchData := range results.Data { + assert.Equalf(t, testCase.expectedUsers[i], searchData.UserName, "Sort type: %s, index %d", testCase.sortType, i) + } + } +} From 38c83ae6e128a88b9f4b66df83fd2883e26a6cb7 Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Fri, 25 Jul 2025 02:39:16 +0200 Subject: [PATCH 177/297] tests(e2e): Silence heading level warning (#8659) The problem surfaced in and after https://codeberg.org/forgejo/forgejo/pulls/8572, but the actual problem is the underlying mess of forms. A better fix would be https://codeberg.org/forgejo/forgejo/issues/8658, but I want to make playwright tests pass again. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8659 Reviewed-by: Gusted Co-authored-by: Otto Richter Co-committed-by: Otto Richter --- tests/e2e/shared/forms.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/shared/forms.ts b/tests/e2e/shared/forms.ts index adb5b6e3cb..862a243600 100644 --- a/tests/e2e/shared/forms.ts +++ b/tests/e2e/shared/forms.ts @@ -10,6 +10,9 @@ export async function validate_form({page}: {page: Page}, scope: 'form' | 'field // legacy dropdowns don't use semantic HTML yet, // avoid using these where possible '.ui.dropdown', + // for some reason we use h1 to h5 for form sections, + // and it usually makes no sense semantically + '.ui.top.attached.header', ]; await accessibilityCheck({page}, [scope], excludedElements, []); From 1b32d7a8748d4ead9c4c1c86c9302cb656518e0c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 25 Jul 2025 06:36:01 +0200 Subject: [PATCH 178/297] Update module code.forgejo.org/forgejo/act to v1.33.0 (forgejo) (#8661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [code.forgejo.org/forgejo/act](https://code.forgejo.org/forgejo/act) | `v1.32.0` -> `v1.33.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/code.forgejo.org%2fforgejo%2fact/v1.33.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/code.forgejo.org%2fforgejo%2fact/v1.32.0/v1.33.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
    forgejo/act (code.forgejo.org/forgejo/act) ### [`v1.33.0`](https://code.forgejo.org/forgejo/act/compare/v1.32.0...v1.33.0) [Compare Source](https://code.forgejo.org/forgejo/act/compare/v1.32.0...v1.33.0)
    --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8661 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6007181a63..01a8be0c51 100644 --- a/go.mod +++ b/go.mod @@ -244,7 +244,7 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.32.0 +replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.33.0 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 diff --git a/go.sum b/go.sum index 7725cfd6d0..0bef4abf37 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFs code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/act v1.32.0 h1:hns2WvrJs6qWCmvzoSllNGNzSvcDMcSvJvVtQj3FaQc= -code.forgejo.org/forgejo/act v1.32.0/go.mod h1:WkmxVBteC4zoyQGYp8ZFZY7Xb+jat+b7ChvqW6TxqF8= +code.forgejo.org/forgejo/act v1.33.0 h1:ayQTXkpk+Vj5/yQMNZagA0xpQgGVeSbcrPXcIS3K1kY= +code.forgejo.org/forgejo/act v1.33.0/go.mod h1:WkmxVBteC4zoyQGYp8ZFZY7Xb+jat+b7ChvqW6TxqF8= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M= From f9c2c910d6a89f9a5af834288748e1849afc8c88 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 25 Jul 2025 06:36:59 +0200 Subject: [PATCH 179/297] Update module github.com/mattn/go-sqlite3 to v1.14.29 (forgejo) (#8660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) | `v1.14.28` -> `v1.14.29` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fmattn%2fgo-sqlite3/v1.14.29?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fmattn%2fgo-sqlite3/v1.14.28/v1.14.29?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
    mattn/go-sqlite3 (github.com/mattn/go-sqlite3) ### [`v1.14.29`](https://github.com/mattn/go-sqlite3/compare/v1.14.28...v1.14.29) [Compare Source](https://github.com/mattn/go-sqlite3/compare/v1.14.28...v1.14.29)
    --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8660 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 01a8be0c51..9cf9183b8e 100644 --- a/go.mod +++ b/go.mod @@ -71,7 +71,7 @@ require ( github.com/lib/pq v1.10.9 github.com/markbates/goth v1.80.0 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-sqlite3 v1.14.28 + github.com/mattn/go-sqlite3 v1.14.29 github.com/meilisearch/meilisearch-go v0.31.0 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 diff --git a/go.sum b/go.sum index 0bef4abf37..1367fc8d10 100644 --- a/go.sum +++ b/go.sum @@ -395,8 +395,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= -github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.29 h1:1O6nRLJKvsi1H2Sj0Hzdfojwt8GiGKm+LOfLaBFaouQ= +github.com/mattn/go-sqlite3 v1.14.29/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= From 61334f7982aff3e46e2bd9a4745ee932c068bcd6 Mon Sep 17 00:00:00 2001 From: forgejo-release-manager Date: Fri, 25 Jul 2025 11:05:03 +0200 Subject: [PATCH 180/297] chore(release-notes): Forgejo v12.0.1 [skip ci] (#8664) https://codeberg.org/forgejo/forgejo/milestone/22237 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8664 Reviewed-by: Beowulf Reviewed-by: Michael Kriese Co-authored-by: forgejo-release-manager Co-committed-by: forgejo-release-manager --- release-notes-published/12.0.1.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 release-notes-published/12.0.1.md diff --git a/release-notes-published/12.0.1.md b/release-notes-published/12.0.1.md new file mode 100644 index 0000000000..e3de6cae3a --- /dev/null +++ b/release-notes-published/12.0.1.md @@ -0,0 +1,24 @@ +Insecure authentication methods have been deprecated since 2023. They were [removed in v12.0.0](https://codeberg.org/forgejo/forgejo/pulls/7924) but they were [restored in v12.0.1](https://codeberg.org/forgejo/forgejo/pulls/8653). Certain OAuth2 clients and packages in the Forgejo ecosystem still rely on these methods and it was premature to remove them. + + + +## Release notes + +- User Interface bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8575) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8576)): allow for tracked time to be removed again + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8565) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8574)): correct image source for quoted reply + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8553) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8555)): prevent render failure on faulty org settings post +- Bug fixes + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8633) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8653)): Revert "feat: remove API authentication methods that uses the URL query (#7924)" + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8644) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8646)): update i18n usage in comments + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8609) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8613)): upgrade fails or hang at migration[31]: Migrate maven package name concatenation + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8622) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8624)): rebase and fast forward merge breaks commit signatures + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8617) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8618)): make the action feed resilient to database inconsistencies + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8533) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8584)): make sure to use unaltered fields when saving a shadow copy for updated profiles or comments + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8596) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8610)): follow symlinks for local assets + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8550) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8552)): use correct ACME default +- Included for completeness but not user-facing (chores, etc.) + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8638) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8641)): Revert "fix(ci): pull stylus from github:stylus/stylus#0.57.0 (#8625)" + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8625) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8627)): fix(ci): pull stylus from github:stylus/stylus#0.57.0 + - [PR](https://codeberg.org/forgejo/forgejo/pulls/8611) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8616)): chore: disable E2E test for webkit + From d4e4a2a1e3d90c77ae88381c189ba89b8508a93f Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 25 Jul 2025 11:55:15 +0200 Subject: [PATCH 181/297] chore: move template context (#8663) The template module now holds the **Template** context, this makes it possible for (render) function in the template module to access functions and share data between render functions. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8663 Reviewed-by: Earl Warren Reviewed-by: Lucas Co-authored-by: Gusted Co-committed-by: Gusted --- modules/templates/context.go | 23 ++++++++++++ modules/templates/context_test.go | 18 ++++++++++ modules/templates/util_render.go | 9 +++-- modules/templates/util_render_test.go | 13 ++++--- routers/common/errpage.go | 5 ++- services/context/context.go | 13 ++++--- services/context/context_template.go | 35 ------------------- templates/repo/issue/card.tmpl | 2 +- templates/repo/issue/filter_actions.tmpl | 2 +- templates/repo/issue/labels/label.tmpl | 2 +- templates/repo/issue/labels/label_list.tmpl | 4 +-- .../issue/labels/labels_selector_field.tmpl | 4 +-- .../repo/issue/view_content/comments.tmpl | 12 +++---- templates/shared/issuelist.tmpl | 2 +- templates/shared/label_filter.tmpl | 2 +- tests/integration/issue_test.go | 12 +++++++ 16 files changed, 88 insertions(+), 70 deletions(-) create mode 100644 modules/templates/context.go create mode 100644 modules/templates/context_test.go delete mode 100644 services/context/context_template.go diff --git a/modules/templates/context.go b/modules/templates/context.go new file mode 100644 index 0000000000..d2b896391b --- /dev/null +++ b/modules/templates/context.go @@ -0,0 +1,23 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package templates + +import ( + "context" + + "forgejo.org/modules/translation" +) + +type Context struct { + context.Context + Locale translation.Locale + AvatarUtils *AvatarUtils + Data map[string]any +} + +var _ context.Context = Context{} + +func NewContext(ctx context.Context) *Context { + return &Context{Context: ctx} +} diff --git a/modules/templates/context_test.go b/modules/templates/context_test.go new file mode 100644 index 0000000000..d854fbf0ff --- /dev/null +++ b/modules/templates/context_test.go @@ -0,0 +1,18 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later +package templates + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestContext(t *testing.T) { + type ctxKey struct{} + + // Test that the original context is used for its context functions. + ctx := NewContext(context.WithValue(t.Context(), ctxKey{}, "there")) + assert.Equal(t, "there", ctx.Value(ctxKey{})) +} diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index badff5f193..bec8d5f5e3 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -22,7 +22,6 @@ import ( "forgejo.org/modules/markup" "forgejo.org/modules/markup/markdown" "forgejo.org/modules/setting" - "forgejo.org/modules/translation" "forgejo.org/modules/util" ) @@ -145,7 +144,7 @@ func RenderRefIssueTitle(ctx context.Context, text string) template.HTML { // RenderLabel renders a label // locale is needed due to an import cycle with our context providing the `Tr` function -func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { +func RenderLabel(ctx *Context, label *issues_model.Label) template.HTML { var ( archivedCSSClass string textColor = util.ContrastColor(label.Color) @@ -156,7 +155,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m if label.IsArchived() { archivedCSSClass = "archived-label" - description = locale.TrString("repo.issues.archived_label_description", description) + description = ctx.Locale.TrString("repo.issues.archived_label_description", description) } if labelScope == "" { @@ -246,7 +245,7 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n return output } -func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string, isPull bool) template.HTML { +func RenderLabels(ctx *Context, labels []*issues_model.Label, repoLink string, isPull bool) template.HTML { htmlCode := `` for _, label := range labels { // Protect against nil value in labels - shouldn't happen but would cause a panic if so @@ -259,7 +258,7 @@ func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issu issuesOrPull = "pulls" } htmlCode += fmt.Sprintf("%s ", - repoLink, issuesOrPull, label.ID, RenderLabel(ctx, locale, label)) + repoLink, issuesOrPull, label.ID, RenderLabel(ctx, label)) } htmlCode += "" return template.HTML(htmlCode) diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 8d58d7d2d4..3cfd572491 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -223,23 +223,26 @@ func TestRenderLabels(t *testing.T) { labelMalicious := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 11}) labelArchived := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 12}) - rendered := RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", false) + ctx := NewContext(t.Context()) + ctx.Locale = tr + + rendered := RenderLabels(ctx, []*issues_model.Label{label}, "user2/repo1", false) assert.Contains(t, rendered, "user2/repo1/issues?labels=1") assert.Contains(t, rendered, ">label1<") assert.Contains(t, rendered, "title='First label'") - rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", true) + rendered = RenderLabels(ctx, []*issues_model.Label{label}, "user2/repo1", true) assert.Contains(t, rendered, "user2/repo1/pulls?labels=1") assert.Contains(t, rendered, ">label1<") - rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{labelScoped}, "user2/repo1", false) + rendered = RenderLabels(ctx, []*issues_model.Label{labelScoped}, "user2/repo1", false) assert.Contains(t, rendered, "user2/repo1/issues?labels=7") assert.Contains(t, rendered, ">scope<") assert.Contains(t, rendered, ">label1<") - rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{labelMalicious}, "user2/repo1", false) + rendered = RenderLabels(ctx, []*issues_model.Label{labelMalicious}, "user2/repo1", false) assert.Contains(t, rendered, "user2/repo1/issues?labels=11") assert.Contains(t, rendered, "> <script>malicious</script> <") assert.Contains(t, rendered, ">'?&<") assert.Contains(t, rendered, "title='Malicious label ' <script>malicious</script>'") - rendered = RenderLabels(db.DefaultContext, tr, []*issues_model.Label{labelArchived}, "user2/repo1", false) + rendered = RenderLabels(ctx, []*issues_model.Label{labelArchived}, "user2/repo1", false) assert.Contains(t, rendered, "user2/repo1/issues?labels=12") assert.Contains(t, rendered, ">archived label<><") assert.Contains(t, rendered, "title='repo.issues.archived_label_description'") diff --git a/routers/common/errpage.go b/routers/common/errpage.go index 907c278ab1..4dc5a58858 100644 --- a/routers/common/errpage.go +++ b/routers/common/errpage.go @@ -15,7 +15,6 @@ import ( "forgejo.org/modules/templates" "forgejo.org/modules/web/middleware" "forgejo.org/modules/web/routing" - "forgejo.org/services/context" ) const tplStatus500 base.TplName = "status/500" @@ -36,8 +35,8 @@ func RenderPanicErrorPage(w http.ResponseWriter, req *http.Request, err any) { httpcache.SetCacheControlInHeader(w.Header(), 0, "no-transform") w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) - tmplCtx := context.TemplateContext{} - tmplCtx["Locale"] = middleware.Locale(w, req) + tmplCtx := templates.NewContext(req.Context()) + tmplCtx.Locale = middleware.Locale(w, req) ctxData := middleware.GetContextData(req.Context()) // This recovery handler could be called without Gitea's web context, so we shouldn't touch that context too much. diff --git a/services/context/context.go b/services/context/context.go index 1a839773a8..68074964c8 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -41,7 +41,7 @@ type Render interface { type Context struct { *Base - TemplateContext TemplateContext + TemplateContext *templates.Context Render Render PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData` @@ -64,8 +64,6 @@ type Context struct { Package *Package } -type TemplateContext map[string]any - func init() { web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider { return req.Context().Value(WebContextKey).(*Context) @@ -98,10 +96,11 @@ func GetValidateContext(req *http.Request) (ctx *ValidateContext) { return ctx } -func NewTemplateContextForWeb(ctx *Context) TemplateContext { - tmplCtx := NewTemplateContext(ctx) - tmplCtx["Locale"] = ctx.Locale - tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) +func NewTemplateContextForWeb(ctx *Context) *templates.Context { + tmplCtx := templates.NewContext(ctx) + tmplCtx.Locale = ctx.Locale + tmplCtx.AvatarUtils = templates.NewAvatarUtils(ctx) + tmplCtx.Data = ctx.Data return tmplCtx } diff --git a/services/context/context_template.go b/services/context/context_template.go deleted file mode 100644 index 7878d409ca..0000000000 --- a/services/context/context_template.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package context - -import ( - "context" - "time" -) - -var _ context.Context = TemplateContext(nil) - -func NewTemplateContext(ctx context.Context) TemplateContext { - return TemplateContext{"_ctx": ctx} -} - -func (c TemplateContext) parentContext() context.Context { - return c["_ctx"].(context.Context) -} - -func (c TemplateContext) Deadline() (deadline time.Time, ok bool) { - return c.parentContext().Deadline() -} - -func (c TemplateContext) Done() <-chan struct{} { - return c.parentContext().Done() -} - -func (c TemplateContext) Err() error { - return c.parentContext().Err() -} - -func (c TemplateContext) Value(key any) any { - return c.parentContext().Value(key) -} diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index 6d2f441793..ba6022ae3a 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -65,7 +65,7 @@
    diff --git a/templates/repo/issue/filter_actions.tmpl b/templates/repo/issue/filter_actions.tmpl index 60237f225d..ea7878c47b 100644 --- a/templates/repo/issue/filter_actions.tmpl +++ b/templates/repo/issue/filter_actions.tmpl @@ -30,7 +30,7 @@ {{end}} {{$previousExclusiveScope = $exclusiveScope}}
    - {{if SliceUtils.Contains $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context ctx.Locale .}} + {{if SliceUtils.Contains $.SelLabelIDs .ID}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel ctx .}} {{template "repo/issue/labels/label_archived" .}}
    {{end}} diff --git a/templates/repo/issue/labels/label.tmpl b/templates/repo/issue/labels/label.tmpl index 7844362911..fe4c7494bb 100644 --- a/templates/repo/issue/labels/label.tmpl +++ b/templates/repo/issue/labels/label.tmpl @@ -4,5 +4,5 @@ href="{{.root.RepoLink}}/{{if or .root.IsPull .root.Issue.IsPull}}pulls{{else}}issues{{end}}?labels={{.label.ID}}"{{/* FIXME: use .root.Issue.Link or create .root.Link */}} rel="nofollow" > - {{- RenderLabel $.Context ctx.Locale .label -}} + {{- RenderLabel ctx .label -}} diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl index 8d7fc2c3db..0a7262bdd8 100644 --- a/templates/repo/issue/labels/label_list.tmpl +++ b/templates/repo/issue/labels/label_list.tmpl @@ -32,7 +32,7 @@ {{range .Labels}}
  • - {{RenderLabel $.Context ctx.Locale .}} + {{RenderLabel ctx .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}
    @@ -72,7 +72,7 @@ {{range .OrgLabels}}
  • - {{RenderLabel $.Context ctx.Locale .}} + {{RenderLabel ctx .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}
    diff --git a/templates/repo/issue/labels/labels_selector_field.tmpl b/templates/repo/issue/labels/labels_selector_field.tmpl index 9e54e7a649..8a931a9433 100644 --- a/templates/repo/issue/labels/labels_selector_field.tmpl +++ b/templates/repo/issue/labels/labels_selector_field.tmpl @@ -21,7 +21,7 @@
    {{end}} {{$previousExclusiveScope = $exclusiveScope}} - {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel $.Context ctx.Locale .}} + {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel ctx .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}

    {{template "repo/issue/labels/label_archived" .}}

    @@ -34,7 +34,7 @@
    {{end}} {{$previousExclusiveScope = $exclusiveScope}} - {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel $.Context ctx.Locale .}} + {{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}  {{RenderLabel ctx .}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}

    {{template "repo/issue/labels/label_archived" .}}

    diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 3e833cbc5a..454467a4d0 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -176,11 +176,11 @@ {{template "shared/user/authorlink" .Poster}} {{if and .AddedLabels (not .RemovedLabels)}} - {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) $createdStr}} + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels ctx .AddedLabels $.RepoLink .Issue.IsPull) $createdStr}} {{else if and (not .AddedLabels) .RemovedLabels}} - {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) $createdStr}} + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels ctx .RemovedLabels $.RepoLink .Issue.IsPull) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) $createdStr}} + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels ctx .AddedLabels $.RepoLink .Issue.IsPull) (RenderLabels ctx .RemovedLabels $.RepoLink .Issue.IsPull) $createdStr}} {{end}}
    @@ -742,11 +742,11 @@
  • {{svg "octicon-tag" 20}} {{if and .AddedLabels (not .RemovedLabels)}} - {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) ""}} + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels ctx .AddedLabels $.RepoLink .Issue.IsPull) ""}} {{else if and (not .AddedLabels) .RemovedLabels}} - {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) ""}} + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels ctx .RemovedLabels $.RepoLink .Issue.IsPull) ""}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) ""}} + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels ctx .AddedLabels $.RepoLink .Issue.IsPull) (RenderLabels ctx .RemovedLabels $.RepoLink .Issue.IsPull) ""}} {{end}}
  • {{end}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 6c0950caff..256b5e3e07 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -21,7 +21,7 @@ {{end}} {{range .Labels}} - {{RenderLabel $.Context ctx.Locale .}} + {{RenderLabel ctx .}} {{end}}
    diff --git a/templates/shared/label_filter.tmpl b/templates/shared/label_filter.tmpl index 50040c208d..0c8d537cc7 100644 --- a/templates/shared/label_filter.tmpl +++ b/templates/shared/label_filter.tmpl @@ -42,7 +42,7 @@ {{svg "octicon-check"}} {{end}} {{end}} - {{RenderLabel $.Context ctx.Locale .}} + {{RenderLabel ctx .}}

    {{template "repo/issue/labels/label_archived" .}}

    {{end}} diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 72fe2a4e49..f75c345e63 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -1524,3 +1524,15 @@ func TestIssuePostersSearch(t *testing.T) { assert.EqualValues(t, 1, data.Results[0].UserID) }) } + +func TestIssueTimelineLabels(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + req := NewRequest(t, "GET", "/user2/repo1/issues/1") + resp := MakeRequest(t, req, http.StatusOK) + assert.NotContains(t, resp.Body.String(), `status-page-500`) + + htmlDoc := NewHTMLParser(t, resp.Body) + filterLinks := htmlDoc.Find(".timeline .labels-list a") + assert.Equal(t, 9, filterLinks.Length()) +} From 57148eb1e89942821c27925e5c1da2ee1f7c4dc4 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 25 Jul 2025 12:24:26 +0200 Subject: [PATCH 182/297] fix: query token auth version mismatch (#8666) It's now scheduled for Forgejo v13 see #8633 for more context I used Github Copilot for some auto completion of code. ## 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8666 Reviewed-by: Earl Warren Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- modules/setting/security.go | 2 +- routers/api/shared/middleware.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/setting/security.go b/modules/setting/security.go index f3480d1056..1f38857af6 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -168,6 +168,6 @@ func loadSecurityFrom(rootCfg ConfigProvider) { // warn if the setting is set to false explicitly if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken { - log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.") + log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will be removed in Forgejo v13.0.0.") } } diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go index f56acbe1bf..b57fabac0e 100644 --- a/routers/api/shared/middleware.go +++ b/routers/api/shared/middleware.go @@ -130,7 +130,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC // check for and warn against deprecated authentication options func checkDeprecatedAuthMethods(ctx *context.APIContext) { if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { - ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") + ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") } } From e8acd8afd3a06726ad5c29a33d5d6ef7ae6b2dab Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Fri, 25 Jul 2025 14:11:36 +0000 Subject: [PATCH 183/297] i18n: update of translations from Codeberg Translate Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Juno Takano Co-authored-by: Lzebulon Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: Zalexanninev15 Co-authored-by: adf19 Co-authored-by: darkswordreams Co-authored-by: earl-warren Co-authored-by: justbispo Co-authored-by: oatbiscuits Co-authored-by: xtex Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/hi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_PT/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/zh_Hans/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_ar.ini | 352 +++++++++++++++++++++++--- options/locale/locale_cs-CZ.ini | 20 +- options/locale/locale_de-DE.ini | 10 +- options/locale/locale_fil.ini | 12 +- options/locale/locale_fr-FR.ini | 2 +- options/locale/locale_lv-LV.ini | 4 +- options/locale/locale_nds.ini | 10 +- options/locale/locale_pt-BR.ini | 12 +- options/locale/locale_ru-RU.ini | 10 +- options/locale/locale_uk-UA.ini | 99 ++++++-- options/locale_next/locale_ar.json | 6 +- options/locale_next/locale_cs-CZ.json | 45 ++-- options/locale_next/locale_de-DE.json | 9 +- options/locale_next/locale_fil.json | 9 +- options/locale_next/locale_fr-FR.json | 3 +- options/locale_next/locale_hi.json | 82 +++++- options/locale_next/locale_lv-LV.json | 9 +- options/locale_next/locale_nds.json | 9 +- options/locale_next/locale_pt-BR.json | 9 +- options/locale_next/locale_pt-PT.json | 3 +- options/locale_next/locale_ru-RU.json | 3 +- options/locale_next/locale_uk-UA.json | 8 +- options/locale_next/locale_zh-CN.json | 3 +- 23 files changed, 587 insertions(+), 142 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 638c2dc6ce..0b81b9cc03 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -309,31 +309,31 @@ blocked_since = محظور منذ %s comment_type_group_milestone = الأهداف ui = السمة email_notifications.disable = عطّل إشعارات البريد الإلكتروني -passcode_invalid = رمز الدخول خطأ. حاول مرة أخرى. +passcode_invalid = رمز الدخول خطأ. حاول مجدداً. openid_deletion = أزل عنوان OpenID activate_email = أرسل التفعيل uploaded_avatar_not_a_image = الملف المرفوع ليس صورة. theme_update_error = السمة المختارة غير موجودة. -twofa_disabled = عُطِّل الاستيثاق الثنائي. +twofa_disabled = تم تعطيل المصادقة الثنائية. theme_desc = ستكون هذه السمة المبدئية لك عبر الموقع. new_password = كلمة المرور الجديدة -twofa_disable_desc = تعطيل الاستيثاق الثنائي سيجعل حسابك أقل أمانًا. أتريد الاستمرار؟ +twofa_disable_desc = تعطيل المصادقة الثنائية سيجعل حسابك أقل أماناً. الاستمرار؟ manage_themes = الموضوع الافتراضي -delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عنها بعد ذلك. -cancel = ألغ -repos_none = ليس لديك أي مستودع. +delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عن ذلك. +cancel = إلغاء +repos_none = لا تملك أية مستودع. twofa_desc = لحماية حسابك من سرقة كلمة المرور، يمكنك استخدام هاتف ذكي أو جهاز آخر لاستلام كلمة مرور مؤقتة ذات استخدام واحد ("TOTP"). -email_notifications.submit = اضبط تفضيلات البريد الإلكتروني +email_notifications.submit = ضبط تفضيلات البريد الإلكتروني update_user_avatar_success = حُدِّثت صورة المستخدم الرمزية. activations_pending = في انتظار التفعيل -language = اللغة +language = اللّغة primary = الأساسي update_avatar_success = حُدِّثت صورتك الرمزية. keep_email_private = أخفِ عنوان البريد delete_current_avatar = احذف الصورة الرمزية الحالية avatar = صورة رمزية email_preference_set_success = حُدِّثت تفضيلات البريد الإلكتروني. -scan_this_image = امسح هذه الصورة بتطبيق الاستيثاق الذي تستخدمه: +scan_this_image = امسح هذه الصورة بتطبيق المصادقة الذي تستخدمه: orgs_none = لست عضوًا في أي منظمة. delete_email = أزله theme_update_success = حُدِّثت السمة. @@ -360,14 +360,14 @@ openid_deletion_success = أزيل عنوان OpenID. add_email_success = أضيف عنوان البريد الجديد. enable_custom_avatar = استخدم صورة رمزية مخصصة update_avatar = حدّث الصورة الرمزية -twofa_disable = تعطيل الاستيثاق الثنائي +twofa_disable = تعطيل المصادقة الثنائية retype_new_password = تأكيد كلمة المرور الجديدة manage_emails = أدر عناوين البريد الإلكتروني then_enter_passcode = وأدخل رمز الدخول الظاهر في التطبيق: update_password = حدّث كلمة المرور continue = استمر emails = عناوين البريد الإلكتروني -confirm_delete_account = أكُد الحذف +confirm_delete_account = تأكيد الحذف change_password_success = حُدِّثت كلمة مرورك. سجّل الدخول بكلمة مرورك الجديدة من الآن فصاعدا. email_deletion_desc = سيُزال عنوان البريد هذا مع كل المعلومات المرتطبة به من حسابك. لكن ستبقى إيداعات Git المودعة به بلا تغيير. أتريد الاستمرار؟ or_enter_secret = أو أدخل السر: %s @@ -386,14 +386,14 @@ saved_successfully = حُدِّثت إعداداتك بنجاح. update_theme = حدِّث السمة access_token_deletion_confirm_action = احذف website = الموقع الإلكتروني -delete_token = احذف +delete_token = حذف hidden_comment_types.ref_tooltip = التعليقات التي تقول أن هذه المسألة قد أشير إليها في مسألة أخرى أو إيداع أو غير ذلك… update_language_success = تم تحديث اللغة. privacy = الخصوصية comment_type_group_label = التصنيفات account_link = الحسابات المرتبطة comment_type_group_assignee = المكلفون -update_language = حدِّث اللغة +update_language = تغيير اللغة organization = المنظمات update_language_not_found = اللغة "%s" غير متاحة. update_profile_success = تم تحديث ملفك الشخصي. @@ -421,7 +421,7 @@ can_write_info = كتابة delete = احذف الحساب oauth2_application_name = اسم التطبيق key_state_desc = هذا المفتاح أستُعمل خلال آخر 7 أيام -webauthn_delete_key = أزِل مفتاح الأمان +webauthn_delete_key = إزالة مفتاح الأمان valid_forever = صالح للأبد can_read_info = قراءة create_oauth2_application_button = أنشئ تطبيقا @@ -433,14 +433,14 @@ select_permissions = أختر التصاريح added_on = مُضاف في %s show_openid = أظهر على الملف الشخصي hide_openid = أخفي من الملف الشخصي -webauthn_delete_key_desc = إذا أزلت مفتاح الأمان، فلن تتمكن من تسجيل الدخول باستخدامه. اكمل؟ +webauthn_delete_key_desc = إذا أزلت مفتاح الأمان، فلن تتمكن من تسجيل الدخول باستخدامه. المتابعة؟ permissions_list = التصاريح: webauthn_key_loss_warning = إذا فقدت مفاتيح الأمان الخاصة بك، فسوف تفقد الوصول إلى حسابك. -hooks.desc = أضف خطاطيف ويب تُطلق لكل مستودعاتك. +hooks.desc = إضافة خطاطيف ويب سيتم تشغيلها لـ جميع المستودعات التي تمتلكها. keep_activity_private_popup = يجعل النشاط مرأياً لك وللمديرين فقط keep_email_private_popup = سيؤدي هذا إلى إخفاء عنوان بريدك الإلكتروني من ملفك الشخصي، وكذلك عند تقديم طلب سحب أو تحرير ملف باستخدام واجهة الويب. لن يتم تعديل الالتزامات المدفوعة. استخدم %s في الإيداعات لربطها بحسابك. ssh_key_name_used = هناك مفتاح SSH بنفس الاسم موجود بالفعل على حسابك. -authorized_oauth2_applications = تطبيقات OAuth2 المأذونة +authorized_oauth2_applications = تطبيقات OAuth2 المأذون لها uid = المعرّف الرمزي manage_openid = عناوين OpenID webauthn = استيثاق ثنائي (مفاتيح الأمان) @@ -465,11 +465,11 @@ key_content_gpg_placeholder = يبدأ بـ '-----BEGIN PGP PUBLIC KEY BLOCK---- add_email_confirmation_sent = بريد تفعيل جديد تم إرساله إلى "%s". يُرجى التحقق من البريد الوارد خلال %s لتأكيد عنوان البريد الإلكتروني. ssh_desc = مفاتيح SSH العمومية هذه مرتبطة بحسابك. وتسمح المفاتيح الخصوصية المرافقة بالوصول الكامل إلى مستودعاتك. ويمكن استعمال مفاتيح SSH الموثَّقة لتوثيق إيداعات جت الموقَّعة بمفاتيح SSH. ssh_gpg_keys = مفاتيح SSH / GPG -authorized_oauth2_applications_description = لقد منحتَ إمكانية الوصول إلى حسابك الشخصي على فورجيو لهذه التطبيقات من تطبيقات خارجية. الرجاء إلغاء وصول التطبيقات التي لم تعد بحاجة إليها. +authorized_oauth2_applications_description = لقد منحت حق الوصول إلى حسابك الشخصي في Forgejo لهذه التطبيقات الخارجية. يرجى إلغاء الوصول للتطبيقات التي لم تعد قيد الاستخدام. ssh_key_been_used = هذا المفتاح الـSSH تم إضافته بالفعل إلى هذا الخادم. password_change_disabled = المستخدمين غير المحليين لا يمكنهم تغيير كلمة مرورهم عن طريق واجهة ويب فورجيو. token_state_desc = هذا الرمز استخدم خلال آخر 7 أيام -delete_key = أزله +delete_key = إزالة ssh_invalid_token_signature = مفتاح SSH المزود، والتوقيع والرمز لا يتطابقوا أو الرمز قديم. ssh_token_help = يمكنك توليد توقيع باستخدام: gpg_key_verify = تحقق @@ -483,10 +483,10 @@ unbind = الغ الربط verify_ssh_key_success = تم التحقق من مفتاح SSH "%s". gpg_token_required = يجب أن تقدم توقيعاً للرمز التالي ssh_key_verified_long = تم التحقق من المفتاح مع رمز ويمكن استخدامه للتحقق من الإيداعات المتطابقة مع أي عناوين البريد المفعلة لهذا المستخدم. -gpg_key_deletion = أزل مفتاح GPG +gpg_key_deletion = إزالة مفتاح GPG gpg_token_help = يمكنك توليد توقيع باستخدام: -ssh_key_deletion = أزل مفتاح SSH -ssh_token = رمز +ssh_key_deletion = إزالة مفتاح SSH +ssh_token = رمز فريد ssh_disabled = SSH مُعطل gpg_key_verified_long = تم التحقق من المفتاح بواسطة رمز ويمكن استخدامه للتحقق من إيداعات متطابقة لعناوين البريد المفعلة لهذا المستخدم بالإضافة إلى أي هويات متطابقة لهذا المفتاح. change_username_redirect_prompt = اسم المستخدم القديم سوف يعاد توجيهه حتى يطالب به شخص آخر. @@ -494,7 +494,7 @@ add_key_success = تم إضافة مفتاح SSH "%s". key_name = اسم المفتاح comment_type_group_time_tracking = تتبع الوقت gpg_invalid_token_signature = مفتاح GPG المزود، والتوقيع والرمز لا يتطابقوا أو الرمز قديم. -ssh_key_verified = مفتاح مُتحقق منه +ssh_key_verified = مفتاح تم التحقق منه ssh_key_deletion_success = تم إزالة مفتاح SSH. key_signature_ssh_placeholder = يبدأ بـ'-----BEGIN SSH SIGNATURE-----' gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig @@ -508,7 +508,7 @@ gpg_key_deletion_desc = إزالة مفتاح GPG يُلغي تحقق الإيد permission_read = القراءة ssh_token_signature = توقيع SSH مصفح ssh_key_deletion_desc = إزالة مفتاح SSH يلغي وصوله إلى حسابك. أكمل؟ -revoke_key = أسحب +revoke_key = سحب gpg_token = رمز gpg_key_matched_identities_long = الهويات المدمجة في هذا المفتاح تطابق عناوين البريد الإلكتروني المفعلة التالية لهذا المستخدم. ويمكن التحقق من صحة الإيداعات المطابقة لهذه العناوين البريدية مع هذا المفتاح. key_content = المحتوى @@ -522,8 +522,120 @@ remove_oauth2_application_success = أُزيل التطبيق. update_oauth2_application_success = لقد حدّثت بنجاح تطبيق OAuth2. oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع كل رابط في سطر وحده. remove_account_link = أزل الحساب المربوط -remove_account_link_success = أُزيل الحساب المربوط. +remove_account_link_success = أُزيل الحساب المرتبط. quota = كوتا +update_hints = حدِّث التلميحات +keep_activity_private.description = سيكون نشاطك العام مرئيًا لك ولمشرفي المثيل فقط. +manage_ssh_principals = إدارة مدراء شهادات SSH الرئيسية +pronouns = الضمائر +pronouns_unspecified = غير محدد +ssh_principal_been_used = تمت إضافة هذه الهوية الرئيسية إلى الخادم مسبقاً. +principal_desc = هذه الهُويات الرئيسية لشهادات SSH مرتبطة بحسابك وتتيح وصولاً كاملاً إلى مستودعاتك. +gpg_helper = تحتاج لمساعدة؟ اطّلع على الدليل حول GPG. +ssh_helper = هل تحتاج مساعدة؟ اطلع على الدليل لـ إنشاء مفاتيح SSH الخاصة بك أو لحل المشكلات الشائعة التي قد تواجهها عند استخدام SSH. +add_new_principal = إضافة هوية رئيسية +keep_pronouns_private.description = سيؤدي ذلك إلى إخفاء ضمائرك عن الزوار الذين لم يقوموا بتسجيل الدخول. +language.description = سيتم حفظ هذه اللغة في حسابك واستخدامها كلغة افتراضية بعد تسجيل الدخول. +storage_overview = نظرة عامة على التخزين +hints = تلميحات +language.title = اللغة الافتراضية +update_hints_success = تم تحديث التلميحات. +language.localization_project = "ساعدنا في ترجمة Forgejo إلى لغتك! المزيد من المعلومات. +change_username_redirect_prompt.with_cooldown.one = سيصبح اسم المستخدم القديم متاحًا للجميع بعد فترة تباطؤ تبلغ %[1]d يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +additional_repo_units_hint = اقتراح تفعيل وحدات المستودعات الإضافية +additional_repo_units_hint_description = اعرض تلميح "تفعيل المزيد" للمستودعات التي لم يتم تفعيل جميع الوحدات المتاحة بها. +change_password = غيّر كلمة المرور +keep_pronouns_private = إظهار الضمائر للمستخدمين الذين تمت مصادقتهم فقط +change_username_redirect_prompt.with_cooldown.few = سيصبح اسم المستخدم القديم متاحًا للجميع بعد فترة تباطؤ تبلغ يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +no_activity = لا يوجد نشاط حديث +generate_token_success = تم إنشاء الرمز الفريد الجديد الخاص بك. انسخه الآن لأنه لن يظهر مرة أخرى. +manage_access_token = رموز الوصول الفريدة +token_name = اسم الرمز الفريد +generate_token_name_duplicate = اسم التطبيق %s مُستخدم مسبقًا. يُرجى استخدام اسم جديد. +regenerate_token = إعادة التوليد +access_token_regeneration = إعادة توليد رمز وصول فريد +permission_write = قراءة وكتابة +delete_token_success = تم حذف الرمز الفريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك. +gpg_key_matched_identities = الهويات المتطابقة: +tokens_desc = تمنح هذه الرموز الفريدة إمكانية الوصول إلى حسابك باستخدام واجهة برمجة تطبيقات Forgejo. +generate_token = توليد رمز فريد +access_token_deletion = حذف رمز الوصول الفريد +generate_new_token = توليد رمز جديد +access_token_deletion_desc = حذف الرمز الفريد سيسحب صلاحية الوصول إلى حسابك من التطبيقات التي تستخدمه. لا يمكن التراجع عن هذا الإجراء. تريد المتابعة؟ +access_token_desc = تقتصر صلاحيات الرمز المحددة على مسارات واجهة البرمجة (API) المقابلة فقط. اطلع على الوثائق لمزيد من المعلومات. +oauth2_application_remove_description = ستؤدي إزالة تطبيق OAuth2 إلى منعه من الوصول إلى حسابات المستخدمين المصرح لهم على هذا المثيل. المتابعة؟ +ssh_principal_deletion = إزالة الهوية الرئيسية لشهادة SSH +at_least_one_permission = يجب عليك تحديد صلاحية واحدة على الأقل لإنشاء رمز فريد +subkeys = المفاتيح الفرعية +ssh_principal_deletion_desc = إزالة هوية رئيسية لشهادة SSH ستسحب صلاحية وصولها إلى حسابك. تريد المتابعة؟ +principal_state_desc = استخدمت هذه الهوية في آخر 7 أيام +ssh_signonly = SSH معطّل حاليًا، لذا تُستخدم هذه المفاتيح فقط للتحقق من توقيع الإيداع. +ssh_externally_managed = يتم إدارة مفتاح SSH هذا خارجيًا لهذا المستخدم +access_token_regeneration_desc = سيؤدي إعادة إنشاء رمز فريد إلى إبطال الوصول إلى حسابك للتطبيقات التي تستخدمه. لا يمكن التراجع عن ذلك. المتابعة؟ +regenerate_token_success = تم إعادة إنشاء الرمز الغريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك ويجب تحديثها بالرمز الجديد. +repo_and_org_access = الوصول إلى المستودع والمنظمة +add_principal_success = تمت إضافة الهوية الرئيسية لشهادة "SSH "%s. +ssh_principal_deletion_success = تم إزالة الهوية. +oauth2_applications_desc = تمكّن تطبيقات OAuth2 تطبيقات الطرف الثالث من مصادقة المستخدمين بأمان في مثيل Forgejo هذا. +oauth2_client_secret = سر العميل +oauth2_regenerate_secret = تجديد السر +oauth2_regenerate_secret_hint = فقدت سرك؟ +oauth2_client_id = معرف العميل +permission_no_access = لا وصول +remove_oauth2_application_desc = ستؤدي إزالة تطبيق OAuth2 إلى إبطال الوصول إلى جميع رموز الوصول الفريدة الموقعة. المتابعة؟ +oauth2_confidential_client = العميل السري. حدد للتطبيقات التي تحافظ على السرية، مثل تطبيقات الويب. لا تحدد للتطبيقات الأصلية بما في ذلك تطبيقات سطح المكتب وتطبيقات الأجهزة المحمولة. +oauth2_client_secret_hint = لن يظهر السر مرة أخرى بعد مغادرة هذه الصفحة أو تحديثها. يرجى التأكد من أنك قمت بحفظه. +oauth2_application_create_description = ‪تمنح تطبيقات OAuth2 تطبيقات الطرف الثالث حق الوصول إلى حسابات المستخدمين على هذا المثيل. +oauth2_application_locked = يقوم Forgejo بالتسجيل المسبق لبعض تطبيقات OAuth2 عند بدء التشغيل إذا تم تمكينها في التكوين. لمنع السلوك غير المتوقع، لا يمكن تحريرها أو إزالتها. يرجى الرجوع إلى وثائق OAuth2 لمزيد من المعلومات. +twofa_recovery_tip = إذا فقدت جهازك، ستتمكن من استخدام مفتاح الاسترداد للاستخدام مرة واحدة لاستعادة الوصول إلى حسابك. +twofa_not_enrolled = حسابك غير مسجّل حالياً في المصادقة الثنائية. +twofa_scratch_token_regenerate = إعادة إنشاء مفتاح الاسترداد للاستخدام مرة واحدة +regenerate_scratch_token_desc = إذا فقدت مفتاح الاسترداد الخاص بك في غير محله أو استخدمته بالفعل لتسجيل الدخول، يمكنك إعادة تعيينه هنا. +twofa_failed_get_secret = إخفاق في الحصول على سر. +manage_account_links_desc = هذه الحسابات الخارجية مرتبطة بحسابك في Forgejo. +revoke_oauth2_grant_description = سيؤدي إبطال الوصول لهذا التطبيق التابع لجهة خارجية إلى منع هذا التطبيق من الوصول إلى بياناتك. أنت متأكد؟ +revoke_oauth2_grant_success = تم سحب صلاحية الوصول بنجاح. +webauthn_alternative_tip = قد ترغب في تكوين أسلوب مصادقة إضافي. +webauthn_register_key = إضافة مفتاح تشفير +webauthn_nickname = الاسم المستعار +manage_account_links = الحسابات المرتبطة +revoke_oauth2_grant = سحب صلاحية الوصول +twofa_enroll = التسجيل في المصادقة الثنائية +twofa_is_enrolled = حسابك مسجّل حاليًا في المصادقة الثنائية. +twofa_scratch_token_regenerated = مفتاح الاسترداد للاستخدام مرة واحدة هو %s الآن. قم بتخزينه في مكان آمن، فلن يتم عرضه مجدداً. +twofa_enrolled = تم تسجيل حسابك بنجاح. قم بتخزين مفتاح الاسترداد للاستخدام لمرة واحدة (%s) في مكان آمن، فلن يتم عرضه مجدداً. +webauthn_desc = مفاتيح الأمان هي أجهزة فعلية تحوي على مفاتيح تشفير. يمكن استخدامها للتحقق بخطوتين. يجب أن تدعم مفاتيح الأمان معيار WebAuthn Authenticator. +remove_account_link_desc = ستؤدي إزالة حساب مرتبط إلى إلغاء وصوله إلى حساب Forgejo الخاص بك. المتابعة؟ +email_notifications.onmention = البريد الإلكتروني فقط عند الإشارة +email_notifications.andyourown = والإشعارات الخاصة بك +visibility = رؤية المستخدم +visibility.public_tooltip = مرئي للجميع +visibility.private_tooltip = مرئي فقط لأعضاء المؤسسات التي انضممت إليها +visibility.public = عام +delete_account_desc = هل أنت متأكد من رغبتك في حذف حساب المستخدم هذا نهائيًا؟ +user_block_yourself = لا يمكنك حظر نفسك. +delete_with_all_comments = حسابك أصغر من %s. لتجنب التعليقات الوهمية، سيتم حذف جميع تعليقات المشكلة/المسؤولية الشخصية معها. +visibility.limited_tooltip = مرئية فقط للمستخدمين الذين قاموا بتسجيل الدخول +visibility.limited = محدود +visibility.private = خاص +quota.applies_to_org = تنطبق قواعد الحصص التالية على هذه المنظمة +quota.rule.no_limit = غير محدود +quota.sizes.all = الكل +quota.sizes.repos.all = المستودعات +quota.sizes.repos.public = مستودعات عامة +quota.sizes.repos.private = مستودعات خاصة +quota.sizes.git.all = محتوى Git +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = الأصول +quota.sizes.assets.attachments.all = المرفقات +quota.sizes.assets.attachments.issues = إصدار المرفقات +quota.sizes.assets.attachments.releases = تحرير المرفقات +quota.sizes.assets.artifacts = التحف الفنية +quota.sizes.assets.packages.all = الحزم +quota.rule.exceeded.helper = لقد تجاوز الحجم الإجمالي للكائنات لهذه القاعدة الحصة النسبية. +quota.rule.exceeded = تم تجاوزه +quota.applies_to_user = تنطبق قواعد الحصص التالية على حسابك +quota.sizes.wiki = الموسوعة [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. @@ -632,7 +744,7 @@ pulls.blocked_by_user = لا يمكنك أن ترسل طلب سحب في هذا migrate.migrating_milestones = ترحيل الأهداف migrate_items_milestones = أهداف repo_size = حجم المستودع -object_format = صيغة الكائنات +object_format = تنسيق الكائنات use_template = استخدم هذا القالب migrate_items_merge_requests = طلبات الدمج repo_name = اسم المستودع @@ -640,11 +752,11 @@ template = القالب projects.modify = عدّل المشروع tree_path_not_found_commit = المسار %[1]s غير موجود في الإيداع %[2]s repo_lang = اللغة -fork_repo = اشتق المستودع +fork_repo = اشتقاق المستودع fork_no_valid_owners = لا يمكن اشتقاق هذا المستودع لعدم وجود مالك صالح. license = الترخيص fork_branch = الفرع الذي سيُستنسخ إلى الاشتقاق -template_helper = اجعل المستودع قالبا +template_helper = اجعل المستودع قالباً owner = المالك projects.deletion_success = تم حذف المشروع. projects.deletion = احذف المشروع @@ -654,14 +766,14 @@ projects.edit = عدّل المشروع template.avatar = الصورة الرمزية migrate_items_wiki = الموسوعة repo_desc = الوصف -template_select = اختر قالبا. +template_select = اختر قالبا repo_name_helper = الأسماء الحسنة للمستودعات تستخدم كلمات مفتاحية قصيرة وسهلة التذكر وفريدة. -default_branch = الفرع المبدئي +default_branch = الفرع الافتراضي all_branches = كل الفروع -migrate_items_issues = المسائل +migrate_items_issues = البلاغات projects.deletion_desc = حذف مشروع يحذف كل المسائل المرتبطة به. أتريد الاستمرار؟ -repo_desc_helper = أدخل وصفا قصيرا (اختياريا) -create_repo = أنشئ مستودعا +repo_desc_helper = أدخل وصفاً قصيراً (اختياري) +create_repo = إنشاء مستودع migrate_items_releases = الإصدارات already_forked = لقد اشتققت %s بالفعل license_helper = اختر ملف ترخيص. @@ -682,7 +794,7 @@ issues.remove_milestone_at = `أزال هذه المسألة من الهدف issues.filter_assginee_no_assignee = بلا مكلف issues.new.no_milestone = بلا هدف issues.new.projects = المشروعات -delete_preexisting_label = احذف +delete_preexisting_label = حذف issues.context.edit = عدّل branch.rename = غيّر اسم الفرع "%s" issue_labels = تصنيفات المسائل @@ -778,7 +890,7 @@ milestones.deletion_desc = حذف هدف يحذفه من كل المسائل ا issues.desc = نظّم إبلاغات العلل، والمهام، والأهداف. issues.choose.ignore_invalid_templates = أُهمِلت القوالب التالفة branch.renamed = غُيّر اسم الفرع %s إلى %s. -delete_preexisting = احذف الملفات الموجودة سابقا +delete_preexisting = حذف الملفات الموجودة مسبقاً branch.included_desc = هذا الفرع جزء من الفرع المبدئي trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع issues.reopened_at = `أعاد فتح هذه المسألة %s` @@ -852,7 +964,7 @@ issues.reopen_comment_issue = علّق وأعد فتحها issues.dependency.add = أضف اعتمادية… issues.label_deletion_desc = حذف تصنيف يحذفه من كل المسائل، أتريد الاستمرار؟ labels = التصنيفات -delete_preexisting_content = احذف الملفات في %s +delete_preexisting_content = حذف الملفات في %s milestones.deletion = احذف الهدف issues.comment_pull_merged_at = دمج الإيداع %[1]s إلى %[2]s %[3]s issues.new.closed_milestone = الأهداف التامة @@ -940,7 +1052,7 @@ issues.filter_assignee = مكلف issues.open_title = حالية download_file = نزّل الملف issues.attachment.download = `انقر لتنزيل "%s"` -download_archive = نزّل المستودع +download_archive = تنزيل المستودع download_tar = نزّل TAR.GZ download_zip = نزّل ZIP releases.desc = تتبع إصدارات المشروع وتنزيلاته. @@ -1349,6 +1461,172 @@ pulls.fast_forward_only_merge_pull_request = تسريع وحسب pulls.merge_conflict = تعذر الدمج: حدث نزاع خلال الدمج. مساعدة: جرب طريقة أخرى pulls.rebase_conflict = تعذر الدمج: حدث نزاع خلال إعادة تأسيس الإيداع: %[1]s. مساعدة: جرب طريقة أخرى pulls.has_merged = فشل: لقد تم دمج هذا الطلب، فلا يمكنك دمجه مجددا أو تغيير الفرع الهدف. +new_repo_helper = يحتوي المستودع على جميع ملفات المشروع، بما في ذلك سجل التعديلات. هل تستضيف واحدًا بالفعل على منصة أخرى؟ نقل المستودع. +new_from_template = استخدم قالباً +new_advanced = إعدادات مقتدمة +new_advanced_expand = انقر للتوسعة +new_from_template_description = يمكنك تحديد قالب مستودع موجود على هذا المثيل وتطبيق إعداداته. +owner_helper = قد لا تظهر بعض منتديات المجموعة في القائمة المنسدلة بسبب الحد الأقصى لعدد المستودعات. +fork_from = اشتق من +fork_visibility_helper = لا يمكن تغيير ظهور المستودع المشتّق. +generate_repo = توليد لمستودع +readme_helper_desc = هذا هو المكان الذي يمكنك فيه كتابة وصف كامل لمشروعك. +repo_gitignore_helper = حدد قوالب .gitignore +auto_init = تهيئة المستودع +default_branch_label = افتراضي +auto_init_description = ابدأ سجل Git بملف README، مع إمكانية إضافة ملفات الرخصة و.gitignore اختيارياً. +mirror_use_ssh.text = استخدم مصادقة SSH +mirror_address = استنساخ عبر URL +mirror_prune_desc = إزالة مراجع التتبع عن بُعد القديمة +mirror_interval_invalid = الفاصل الزمني للمرآة غير صالح. +readme_helper = حدد قالب ملف README +generate_from = التوليد من +mirror_use_ssh.not_available = المصادقة عبر SSH غير متاحة. +open_with_editor = افتح بـ %s +mirror_prune = تنقية +visibility_fork_helper = (سيؤثر تغيير ذلك على رؤية جميع المشتقات.) +clone_helper = تحتاج مساعدة في الاستنساخ؟ زُر المساعدة. +mirror_public_key = مفتاح SSH عام +size_format = %[1]s: %[2]s, %[3]s: %[4]s +visibility_description = فقط المالك أو أعضاء المؤسسة إذا كان لديهم حقوق، سيتمكنون من رؤيته. +license_helper_desc = تحدد الرخصة ما يمكن للآخرين فعله أو عدم فعله بشيفرة برمجيتك. لست متأكدًا من الرخصة المناسبة لمشروعك؟ طالع اختيار الرخصة. +mirror_denied_combination = لا يمكن استخدام المصادقة المستندة إلى المفتاح العام وكلمة المرور معاً. +repo_gitignore_helper_desc = اختر الملفات التي لا تريد تتبعها من قائمة القوالب الخاصة باللغات الشائعة. يتم تضمين القطع الأثرية النموذجية التي تم إنشاؤها بواسطة أدوات البناء الخاصة بكل لغة في .gitignore بشكل افتراضي. +stars = النجوم +default_branch_helper = الفرع الافتراضي هو الفرع الأساسي لطلبات السحب ،وعمليات إلإيداع. +mirror_use_ssh.helper = سيقوم Forgejo بعكس المستودع عبر Git عبر SSH وإنشاء زوج مفاتيح لك عند تحديد هذا الخيار. يجب عليك التأكد من أن المفتاح العام الذي تم إنشاؤه مخول للدفع إلى المستودع الوجهة. لا يمكنك استخدام التخويل المستند إلى كلمة المرور عند تحديد هذا الخيار. +desc.private = خاص +readme = README +mirror_interval = الفاصل الزمني للمرآة (وحدات الوقت الصحيحة هي 'h' ،'m' ،'s'). 0 لتعطيل المزامنة الدورية. (الحد الأدنى: %s) +desc.template = قالب +desc.public = عام +visibility = الرؤية +migrate_options_mirror_helper = سيكون هذا المستودع مرآة +adopt_preexisting_success = الملفات المعتمدة والمستودع الذي تم إنشاؤه من %s +archive.title_date = تمت أرشفة هذا المستودع على %s. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +migrate_options_lfs_endpoint.label = نقطة نهاية LFS +transfer.reject_desc = إلغاء النقل إلى ”%s“ +archive.title = تمت أرشفة هذا المستودع. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +stargazers = المميِّزون بنجمة +form.reach_limit_of_creation_n = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودعات %d. +mirror_sync_on_commit = المزامنة عند دفع الإيداعات +mirror_lfs_desc = تنشيط النسخ المتطابق لبيانات LFS. +author_search_tooltip = عرض كحد أقصى 30 مستخدمًا +template.git_content = محتوى Git (الفرع الافتراضي) +need_auth = المصادقة +migrate_options_lfs_endpoint.description.local = يتم دعم مسار الخادم المحلي أيضاً. +template.one_item = يجب على الأقل تحديد عنصر قالب واحد +archive.pull.noreview = هذا المستودع مؤرشف. لا يمكنك مراجعة طلبات السحب. +migrate.clone_address = ترحيل / استنساخ من عنوان URL +migrate.repo_desc_helper = اتركه فارغاً لاستيراد الوصف الموجود +archive.nocomment = التعليق غير ممكن لأن المستودع تمت أرشفته. +mirror_address_protocol_invalid = عنوان URL المقدم غير صالح. يمكن استخدام مواقع http(s):// أو git:// فقط للنسخ المتطابق. +mirror_lfs_endpoint = نقطة نهاية LFS +mirror_lfs_endpoint_desc = ستحاول المزامنة استخدام عنوان url المستنسخ إلى تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS المستودع مخزنة في مكان آخر. +mirror_password_blank_placeholder = (بلا تعيين) +delete_preexisting_success = الملفات المحذوفة غير المعتمدة في %s +blame_prior = عرض النّقد قبل هذا التغيير +blame.ignore_revs = جاري تجاهل المراجعات في .git-blame-ignore-revs. انقر هنا لتجاوز وعرض واجهة blame العادية. +desc.archived = مؤرشف +template.issue_labels = وسوم الإبلاغات +sync_fork.branch_behind_one = هذا الفرع هو %[1]d إيداع خلف %[2]s +sync_fork.button = مزامنة +transfer.no_permission_to_reject = لا تملك الصلاحية لرفض هذا النقل. +form.string_too_long = السلسلة المحددة أطول من d حرفاً. +migrate_options = خيارات الترحيل +migrate_options_lfs = ترحيل ملفات LFS +migrate.clone_local_path = أو مسار خادم محلي +mirror_password_help = تغيير اسم المستخدم لمسح كلمة المرور المخزنة. +watchers = المراقبون +template.items = عناصر القالب +template.topics = المواضيع +migrate_options_lfs_endpoint.placeholder = إذا تُركت فارغة، سيتم اشتقاق نقطة النهاية من عنوان URL المستنسخ +migrate.clone_address_desc = رابط HTTP(S) أو Git لاستنساخ مستودع موجود +migrate.github_token_desc = يمكنك وضع رمز فريد أو أكثر هنا مفصول بفواصل لجعل الترحيل أسرع من خلال التحايل على حد معدل GitHub API. تحذير: قد يؤدي إساءة استخدام هذه الميزة إلى انتهاك سياسة مزود الخدمة وقد يؤدي إلى حظر حسابك (حساباتك). +transfer.no_permission_to_accept = لا تملك الصلاحية لقبول هذا النقل. +transfer.reject = رفض النقل +transfer.accept_desc = النقل إلى ”%s“ +desc.internal = داخلي +summary_card_alt = بطاقة ملخص المستودع %s +transfer.accept = قبول النقل +blame.ignore_revs.failed = فشل تجاهل المراجعات في .git-blame-ignore-revs. +form.name_pattern_not_allowed = النمط ”%s“ غير مسموح به في اسم المستودع. +migrate_options_lfs_endpoint.description = سيحاول الترحيل استخدام مسار Git البعيد (remote) لـ تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS للمستودع مخزنة في مكان آخر. +migrate_items = عناصر الترحيل +adopt_preexisting_content = إنشاء مستودع من %s +migrate_repo = ترحيل المستودع +mirror_password_placeholder = (لم يتم تعديله) +sync_fork.branch_behind_few = هذا الفرع هو %[1]d إيداعات خلف %[2]s +mirror_address_desc = ضع أي بيانات اعتماد مطلوبة في قسم المصادقة. +mirror_last_synced = آخر مزامنة +mirror_lfs = تخزين الملفات الكبيرة (LFS) +stars_remove_warning = سيؤدي ذلك إلى إزالة جميع النجوم من هذا المستودع. +reactions_more = و %d أكثر +template.invalid = يجب تحديد مستودع القوالب +adopt_search = أدخل اسم المستخدم للبحث عن مستودعات غير معتمدة... (اتركه فارغاً للعثور على الكل) +adopt_preexisting = اعتماد الملفات الموجودة مسبقاً +language_other = أُخرى +adopt_preexisting_label = اعتماد الملفات +form.reach_limit_of_creation_1 = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودع %d. +form.name_reserved = تم حجز اسم المستودع ”%s“. +mirror_address_url_invalid = عنوان URL المقدم غير صالح. تأكد من تحرير مكونات عنوان URL بشكل صحيح. +migrate.migrate = الترحيل من %s +migrate.gogs.description = ترحيل البيانات من notabug.org أو مثيلات Gogs الأخرى. +migrate.onedev.description = ترحيل البيانات من code.onedev.io أو مثيلات OneDev الأخرى. +migrate.migrating_topics = ترحيل المواضيع +migrate.cancel_migrating_title = إلغاء الترحيل +generated_from = تم توليده من +star_guest_user = قم بتسجيل الدخول لإعطاء نجمة لهذا المستودع. +subscribe.pull.guest.tooltip = سجّل الدخول للاشتراك في طلب السحب هذا. +unwatch = إلغاء المشاهدة +quick_guide = دليل سريع +empty_message = لا يحتوي هذا المستودع على أي محتوى. +migrate.migrating_labels = ترحيل الوسوم +migrate.migrating_releases = ترحيل الإصدارات +watch_guest_user = سجّل الدخول لمشاهدة هذا المستودع. +star = نجمة +cite_this_repo = الاستشهاد بهذا المستودع +no_desc = لا يوجد وصف +migrate.github.description = ترحيل البيانات من github.com أو خادم GitHub Enterprise. +migrated_from_fake = +clone_this_repo = استنسخ هذا المستودع +fork_from_self = لا يمكنك اشتقاق مستودع تملكه. +fork_guest_user = سجّل الدخول لاشتقاق هذا المستودع. +subscribe.issue.guest.tooltip = سجّل الدخول للاشتراك في هذا البلاغ. +more_operations = المزيد من العمليات +push_exist_repo = دفع مستودع موجود من موجّه الأوامر +broken_message = لا يمكن قراءة بيانات Git التي يستند إليها هذا المستودع. اتصل بمسؤول هذا المثيل أو احذف هذا المستودع. +migrate.git.description = ترحيل مستودع فقط من أي خدمة Git. +watch = شاهد +migrate.migrating_git = ترحيل بيانات Git +migrate.gitea.description = ترحيل البيانات من gitea.com أو مثيلات Gitea الأخرى. +migrate.gitbucket.description = ترحيل البيانات من مثيلات GitBucket. +migrate.cancel_migrating_confirm = تريد إلغاء عملية الترحيل هذه؟ +migrate.permission_denied_blocked = لا يمكنك الاستيراد من مضيفين غير مسموح بهم، يُرجى الطلب من المسؤول التحقق من إعدادات ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. +migrate.gitlab.description = ترحيل البيانات من gitlab.com أو مثيلات GitLab الأخرى. +migrate.migrating = الترحيل من %s … +migrate.migrating_failed.error = أخفق الترحيل: %s +migrate.codebase.description = ترحيل البيانات من codebasehq.com. +migrate.invalid_local_path = المسار المحلي غير صالح. فهو غير موجود أو ليس مجلداً. +migrate.permission_denied = لا يُسمح لك باستيراد المستودعات المحلية. +migrate.failed = أخفق الترحيل: %v +migrate.migrate_items_options = رمز الوصول الفريد مطلوب لترحيل العناصر الإضافية +migrated_from = تم الترحيل من %[2]s +mirror_from = مرآة لـ +forked_from = مشتقّ من +create_new_repo_command = إنشاء مستودع جديد على موجّه الأوامر +fork = اشتقاق +migrate.migrating_failed_no_addr = أخفق الترحيل. +unstar = إزالة النجمة +migrate.migrating_issues = ترحيل البلاغات +migrate.migrating_pulls = ترحيل طلبات السحب +code = الكود +migrate.invalid_lfs_endpoint = نقطة نهاية LFS غير صالحة. +migrate.migrating_failed = فشل الترحيل من %s. +branch = فرع +template.git_hooks_tooltip = يتعذر عليك حاليًا تعديل أو إزالة خطافات Git بمجرد إضافتها. حدد هذا فقط إذا كنت تثق بمستودع القالب. +code.desc = الوصول إلى الشيفرة المصدرية والملفات والالتزامات والفروع. +tree = شجرة [mail] admin.new_user.text = من فضلك اضغط هنا لإدارة هذا المستخدم من لوحة الإدارة. diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index c7770c5ea1..e80f2bcc65 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -340,7 +340,7 @@ no_reply_address=Skrytá e-mailová doména no_reply_address_helper=Název domény pro uživatele se skrytou e-mailovou adresou. Příklad: pokud je název skryté e-mailové domény nastaven na „noreply.example.org“, uživatelské jméno „joe“ bude zaznamenáno v Gitu jako „joe@noreply.example.org“. password_algorithm=Hashovací algoritmus hesla invalid_password_algorithm=Neplatný algoritmus hashe hesla -password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. +password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a síly. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. enable_update_checker=Povolit kontrolu aktualizací env_config_keys=Konfigurace prostředí env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor: @@ -468,7 +468,7 @@ email_domain_blacklisted=Nemůžete se registrovat s vaší e-mailovou adresou. authorize_application=Autorizovat aplikaci authorize_redirect_notice=Budete přesměrováni na %s, pokud autorizujete tuto aplikaci. authorize_application_created_by=Tuto aplikaci vytvořil %s. -authorize_application_description=Pokud povolíte přístup, bude moci přistupovat a zapisovat do všech vašich informací o účtu včetně soukromých repozitářů a organizací. +authorize_application_description=Pokud udělíte přístup, bude moci přistupovat a zapisovat do všech vašich informací o účtu včetně soukromých repozitářů a organizací. authorize_title=Autorizovat „%s“ pro přístup k vašemu účtu? authorization_failed=Autorizace selhala authorization_failed_desc=Autorizace selhala, protože jsme detekovali neplatný požadavek. Kontaktujte prosím správce aplikace, kterou jste se pokoušeli autorizovat. @@ -724,7 +724,7 @@ following_one = %d sledovaný followers.title.one = Sledující followers.title.few = Sledující following.title.one = Sleduje -following.title.few = Sleudje +following.title.few = Sleduje public_activity.visibility_hint.self_private = Vaše aktivita je viditelná pouze vám a správcům instance. Nastavení. public_activity.visibility_hint.admin_private = Tato aktivita je pro vás viditelná, protože jste administrátor, ale uživatel chce, aby zůstala soukromá. public_activity.visibility_hint.self_public = Vaše aktivita je viditelná všem, mimo interakcí v soukromých prostorech. Nastavení. @@ -828,7 +828,7 @@ activations_pending=Čekající aktivace can_not_add_email_activations_pending=Existuje čekající aktivace, zkuste to znovu za pár minut, pokud chcete přidat nový e-mail. delete_email=Smazat email_deletion=Odstranit e-mailovou adresu -email_deletion_desc=E-mailová adresa a přidružené informace budou z vašeho účtu odstraněny. Revize Gitu s touto e-mailovou adresou zůstanou nezměněny. Pokračovat? +email_deletion_desc=Tato e-mailová adresa a přidružené informace budou z vašeho účtu odstraněny. Revize Gitu s touto e-mailovou adresou zůstanou nezměněny. Pokračovat? email_deletion_success=E-mailová adresa byla odstraněna. theme_update_success=Váš motiv vzhledu byl aktualizován. theme_update_error=Vybraný motiv vzhledu neexistuje. @@ -1155,7 +1155,7 @@ mirror_interval_invalid=Interval zrcadlení není platný. mirror_sync_on_commit=Synchronizovat při nahrávání revizí mirror_address=Klonovat z URL mirror_address_desc=Zadejte požadované přístupové údaje do sekce Ověření. -mirror_address_url_invalid=Poskytnutá URL je neplatná. Všechny části musíte správně nahradit escape sekvencí. +mirror_address_url_invalid=Zadaná adresa URL je neplatná. Ujistěte se, že jsou všechny části adresy správně escapované. mirror_address_protocol_invalid=Zadaná URL je neplatná. Mohou být zrcadleny pouze umístění http(s):// nebo git://. mirror_lfs=Úložiště velkých souborů (LFS) mirror_lfs_desc=Aktivovat zrcadlení dat LFS. @@ -1243,7 +1243,7 @@ migrate_items_releases=Vydání migrate_repo=Migrovat repozitář migrate.clone_address=Migrovat / klonovat z URL migrate.clone_address_desc=HTTP(S) nebo URL Git „clone“ existujícího repozitáře -migrate.github_token_desc=Můžete sem vložit jeden nebo více tokenů oddělených čárkou, abyste urychlili migraci kvůli omezení rychlosti rozhraní GitHub API. VAROVÁNÍ: Zneužití této funkce může vést k porušení zásad poskytovatele služeb a zablokování účtu. +migrate.github_token_desc=Sem můžete vložit jeden nebo více tokenů oddělených čárkami, abyste urychlili migraci obejitím omezení rychlosti rozhraní GitHub API. VAROVÁNÍ: Zneužití této funkce může vést k porušení zásad poskytovatele služeb a zablokování účtu. migrate.clone_local_path=nebo místní cesta serveru migrate.permission_denied=Není dovoleno importovat místní repozitáře. migrate.permission_denied_blocked=Nelze importovat z nepovolených hostitelů, prosím požádejte správce, aby zkontroloval nastavení ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -1875,7 +1875,7 @@ pulls.select_commit_hold_shift_for_range=Vyberte revizi. Podržte klávesu Shift pulls.review_only_possible_for_full_diff=Posouzení je možné pouze při zobrazení plného rozlišení pulls.filter_changes_by_commit=Filtrovat podle revize pulls.nothing_to_compare=Tyto větve jsou stejné. Není třeba vytvářet žádost o sloučení. -pulls.nothing_to_compare_have_tag = Vybraná větev a značka jsou shodné. +pulls.nothing_to_compare_have_tag = Vybrané větve a značky jsou shodné. pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tato žádost o sloučení bude prázdná. pulls.has_pull_request=`Žádost o sloučení mezi těmito větvemi již existuje: %[2]s#%[3]d` pulls.create=Vytvořit žádost o sloučení @@ -2023,7 +2023,7 @@ milestones.filter_sort.most_issues=Nejvíce problémů milestones.filter_sort.least_issues=Nejméně problémů signing.will_sign=Tato revize bude podepsána klíčem „%s“. -signing.wont_sign.error=Došlo k chybě při kontrole, zda může být revize podepsána. +signing.wont_sign.error=Při kontrole, zda může být revize podepsána, došlo k chybě. signing.wont_sign.nokey=Tato instance nemá žádný klíč k podepsání této revize. signing.wont_sign.never=Revize nebudou nikdy podepsány. signing.wont_sign.always=Revize budou vždy podepsány. @@ -3834,7 +3834,7 @@ owner.settings.cargo.initialize.error=Nepodařilo se inicializovat Cargo index: owner.settings.cargo.initialize.success=Index Cargo byl úspěšně vytvořen. owner.settings.cargo.rebuild=Znovu vytvořit index owner.settings.cargo.rebuild.error=Obnovení Cargo indexu se nezdařilo: %v -owner.settings.cargo.rebuild.success=Cargo Index byl úspěšně obnoven. +owner.settings.cargo.rebuild.success=Index Cargo byl úspěšně znovu sestaven. owner.settings.cleanuprules.title=Pravidla čištění owner.settings.cleanuprules.add=Přidat pravidlo pro čištění owner.settings.cleanuprules.edit=Upravit pravidlo pro čištění @@ -3991,7 +3991,7 @@ variables.update.success=Proměnná byla upravena. runners.none = Nejsou dostupné žádné runnery runs.workflow = Workflow runners = Runnery -runs.pushed_by = pushnuto uživatelem +runs.pushed_by = pushnuta uživatelem need_approval_desc = Potřebovat schválení pro spouštění workflowů pro žádosti o sloučení forků. runners.runner_manage_panel = Správa runnerů runs.no_job_without_needs = Workflow musí obsahovat alespoň jednu práci bez závislostí. diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 84bb4ccde1..8a87b4246a 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -830,7 +830,7 @@ 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_desc=Die 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_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. theme_update_error=Das ausgewählte Theme existiert nicht. @@ -1154,7 +1154,7 @@ mirror_interval_invalid=Das Spiegel-Intervall ist ungültig. mirror_sync_on_commit=Synchronisieren, wenn Commits gepusht wurden mirror_address=Klonen via URL mirror_address_desc=Gib alle erforderlichen Anmeldedaten im Abschnitt „Authentifizierung“ ein. -mirror_address_url_invalid=Die angegebene URL ist ungültig. Achte darauf, alle Komponenten der URL korrekt zu maskieren. +mirror_address_url_invalid=Die angegebene URL ist ungültig. Achte darauf, dass alle Komponenten der URL korrekt escaped wurden. mirror_address_protocol_invalid=Die angegebene URL ist ungültig. Nur Orte mit „http(s)://“ oder „git://“ können fürs Spiegeln benutzt werden. mirror_lfs=Großdatei-Speicher (LFS) mirror_lfs_desc=Spiegeln von LFS-Dateien aktivieren. @@ -1243,7 +1243,7 @@ migrate_items_releases=Releases migrate_repo=Repository migrieren migrate.clone_address=Migrations-/Klon-URL migrate.clone_address_desc=Die HTTP(S)- oder „git clone“-URL eines bereits existierenden Repositorys -migrate.github_token_desc=Du kannst hier ein oder mehrere Token durch Komma getrennt eintippen, um die Migration aufgrund der GitHub-API-Ratenlimitierung zu beschleunigen. WARNUNG: Der Missbrauch dieser Funktion kann gegen die Richtlinien des Diensteanbieters verstoßen und zur Kontosperrung führen. +migrate.github_token_desc=Du kannst hier ein oder mehrere Tokens durch Komma getrennt eingeben, um die Migration schneller zu machen, indem die GitHub-API-Ratenlimitierung umgangen wird. WARNUNG: Der Missbrauch dieser Funktion kann gegen die Richtlinien des Diensteanbieters verstoßen und zur Sperrung deines Kontos bzw. deiner Konten führen. migrate.clone_local_path=oder ein lokaler Serverpfad migrate.permission_denied=Du hast keine Berechtigung zum Importieren lokaler Repositorys. migrate.permission_denied_blocked=Du kannst von keinen nicht erlaubten Hosts importieren. Bitte fragen deinen Administrator, die Einstellungen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS zu überprüfen. @@ -2757,7 +2757,7 @@ generated = Erzeugt editor.invalid_commit_mail = Ungültige E-Mail für die Erstellung eines Commits. commits.renamed_from = Umbenannt von %s commits.browse_further = Weiter browsen -pulls.nothing_to_compare_have_tag = Der gewählte Branch/Tag ist gleich. +pulls.nothing_to_compare_have_tag = Die gewählten Branches/Tags sind gleich. pulls.status_checks_hide_all = Alle Prüfungen verbergen pulls.status_checks_show_all = Alle Prüfungen anzeigen pulls.cmd_instruction_hint = Anweisungen für die Kommandozeile betrachten @@ -3124,7 +3124,7 @@ dashboard.resync_all_hooks=Die „pre-receive“-, „update“- und „post-rec dashboard.reinit_missing_repos=Alle Git-Repositorys neu einlesen, für die Einträge existieren dashboard.sync_external_users=Externe Benutzerdaten synchronisieren dashboard.cleanup_hook_task_table=Hook-Task-Tabelle bereinigen -dashboard.cleanup_packages=Veraltete Pakete löschen +dashboard.cleanup_packages=Veraltete Pakete bereinigen dashboard.cleanup_actions=Abgelaufene Logs und Artefakte von Actions bereinigen dashboard.server_uptime=Server-Uptime dashboard.current_goroutine=Aktuelle Goroutinen diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 032b8b0435..85dfe8d4d9 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -313,7 +313,7 @@ no_reply_address = Domain ng nakatagong email no_reply_address_helper = Domain name para sa mga user na may nakatagong email address. Halimbawa, ang username na "kita" ay mala-log sa Git bilang "kita@noreply.example.org" kapag ang nakatagong email domain ay nakatakda sa "noreply.example.org". password_algorithm = Algorithm ng password hash invalid_password_algorithm = Hindi angkop na algorithm ng password hash -password_algorithm_helper = Itakda ang password hashing algorithm. Ang mga algorithm ay may magkakaibang mga kinakailangan at lakas. Ang algorithm ng Argon2 ay sa halip ay ligtas ngunit gumagamit ng maraming memory at maaaring hindi naaangkop para sa mga maliliit na sistema. +password_algorithm_helper = Itakda ang password hashing algorithm. Ang mga algorithm ay may magkakaibang mga kinakailangan at lakas. Ang algorithm na Argon2 ay sa halip ay ligtas ngunit gumagamit ng maraming memory at maaaring hindi naaangkop para sa mga maliliit na sistema. enable_update_checker = I-enable ang tagasuri ng update env_config_keys = Configuration ng Environment env_config_keys_prompt = Ang mga sumusunod na mga environment variable ay ilalapat rin sa iyong configuration file: @@ -760,7 +760,7 @@ comment_type_group_milestone = Milestone comment_type_group_issue_ref = Pagsangguni ng isyu keep_activity_private_popup = Makikita mo lang at mga tagapangasiwa ang iyong aktibidad can_not_add_email_activations_pending = Mayroong isang nakabinbing pag-activate, subukang muli sa loob ng ilang minuto kung nais mong magdagdag ng isang bagong email. -email_deletion_desc = Ang email address at mga kaugnay na impormasyon ay tatanggalin sa iyong account. Ang mga Git commit sa itong email address ay iiwanang hindi nabago. Magpatuloy? +email_deletion_desc = Ang email address na ito at mga kaugnay na impormasyon ay tatanggalin sa iyong account. Ang mga Git commit sa itong email address ay iiwanang hindi nabago. Magpatuloy? add_email = Idagdag ang email eddress gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig delete_token_success = Nabura na ang token. Ang mga application na gumagamit nito ay hindi na maa-access ang iyong account. @@ -1115,7 +1115,7 @@ default_branch = Default na branch default_branch_label = default mirror_prune = Pungusan mirror_prune_desc = Tanggalin ang mga antikuwado na sangguni ng remote-tracking -mirror_address_url_invalid = Ang ibinigay na url ay hindi wasto. Kailangan mong i-escape ang lahat ng mga components ng URL ng tama. +mirror_address_url_invalid = Hindi wasto ang ibinigay na URL. Siguraduhin na ang mga component ng URL ay na-escape nang tama. mirror_address_protocol_invalid = Ang ibinigay na URL ay hindi wasto. Ang http(s):// o git:// na lokasyon lamang ay magagamit para sa pag-mirror. mirror_lfs = Imbakan ng Malaking File (LFS) mirror_lfs_desc = I-activate ang pag-mirror ng LFS data. @@ -1203,7 +1203,7 @@ template.avatar = Avatar migrate_options = Mga opsyon sa paglipat migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na repositoryo need_auth = Awtorisasyon -migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account. +migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token dito na hinihiwalay ng mga kuwit upang gawing mas-mabilis ang pag-migrate sa pamamagitan ng pag-iwas sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng iyong (mga) account. template.invalid = Kailangang pumili ng kahit isang template na repositoryo migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maaari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa .git-blame-ignore-revs. @@ -2225,7 +2225,7 @@ milestones.modify = I-update ang milestone milestones.filter_sort.earliest_due_data = Pinakamalapit na takdang petsa milestones.filter_sort.least_complete = Hindi bababa sa kumpleto signing.will_sign = Isa-sign ang commit gamit ang key na "%s". -signing.wont_sign.error = May error na naganap habang sinusuri kung masa-sign ang commit. +signing.wont_sign.error = May error na naganap habang sinusuri kung mailalagda ang commit. signing.wont_sign.always = Palaging naka-sign ang mga commit. signing.wont_sign.twofa = Kailangang naka-enable ang authentikasyong two factor para naka-sign ang mga commit. wiki.delete_page_notice_1 = Ang pagtanggal sa pahina ng wiki na "%s" ay hindi na mababawi. Magpatuloy? @@ -2864,7 +2864,7 @@ dashboard.resync_all_sshprincipals = I-update ang ".ssh/authorized_principals" f dashboard.resync_all_hooks = I-resychronize ang mga pre-receive, update at post-receive hook para sa lahat ng mga repositoryo dashboard.cleanup_hook_task_table = Linisin ang hook_task table dashboard.cleanup_packages = Linisin ang mga na-expire na package -dashboard.cleanup_actions = Linisin ang mga na-expire na log at artifact mula sa mga aksyon +dashboard.cleanup_actions = Linisin ang mga nag-expire na log at artifact mula sa mga aksyon dashboard.server_uptime = Uptime ng server dashboard.current_goroutine = Mga kasalukuyang goroutine dashboard.total_memory_allocated = Kabuuan na na-allocate na memory diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 0c9df0afa0..0e70102012 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -340,7 +340,7 @@ no_reply_address=Domaine pour les courriels cachés no_reply_address_helper=Nom de domaine pour les utilisateurs ayant une adresse courriel cachée. Par exemple, l’utilisateur « fred » sera associé à « fred@noreply.example.org » par Git si le domaine est « noreply.example.org ». password_algorithm=Algorithme de hachage du mot de passe invalid_password_algorithm=Algorithme de hachage du mot de passe invalide -password_algorithm_helper=Définissez l’algorithme de hachage du mot de passe. Les algorithmes ont des exigences matérielles et une résistance différentes. L’algorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources. +password_algorithm_helper=Définissez l’algorithme de hachage du mot de passe. Les algorithmes ont des exigences et une résistance différentes. L’algorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources. enable_update_checker=Activer la vérification des mises-à-jour env_config_keys=Configuration de l'environnement env_config_keys_prompt=Les variables d'environnement suivantes seront également ajoutées à votre fichier de configuration : diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index e8ee85c61a..6a4d50c11c 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1152,7 +1152,7 @@ mirror_interval_invalid=Starplaiks starp spoguļošanu nav derīgs. mirror_sync_on_commit=Sinhronizēt, kad tiek aizgādāti iesūtījumi mirror_address=Klonēt no URL mirror_address_desc=Nepieciešamie pieslēgšanās dati jānorāda pilnvarošanas sadaļā. -mirror_address_url_invalid=Norādītais URL ir nederīgs. Visas URL daļas ir jānorāda pareizi. +mirror_address_url_invalid=Norādītais URL ir nederīgs. Jāpārliecinās, ka visas URL daļas ir pareizi norādītas. mirror_address_protocol_invalid=Norādītais URL ir nederīgs. Var spoguļot tikai no http(s):// vai git:// adresēm. mirror_lfs=Lielu datņu krātuve (LFS) mirror_lfs_desc=Aktivēt LFS datu spoguļošanu. @@ -1242,7 +1242,7 @@ migrate_items_releases=Laidienus migrate_repo=Pārcelt glabātavu migrate.clone_address=Pārcelt/klonēt no URL migrate.clone_address_desc=Esošas glabātavas HTTP(S) vai Git "clone" URL -migrate.github_token_desc=Šeit var pievienot vienu vai vairākas ar komatiem atdalītas pilnvaras, lai pārcelšana būtu ātrāka, ja tā tiek ierobežota no GitHub API puses. Uzmanību: šīs iespējas ļaunprātīga izmantošana var pārkāpt pakalpojumu sniedzēja noteikumus un novest pie piekļuves liegšanas kontam. +migrate.github_token_desc=Šeit var pievienot vienu vai vairākas ar komatiem atdalītas pilnvaras, lai padarītu pārcelšanu ātrāku ar GitHub API pieprasījumu biežuma ierobežojuma apiešanu. Uzmanību: šīs iespējas ļaunprātīga izmantošana var pārkāpt pakalpojumu sniedzēja noteikumus un var novest pie piekļuves liegšanas kontam/iem. migrate.clone_local_path=vai servera lokālais ceļš migrate.permission_denied=Nav ļauts ievietot vietējas glabātavas. migrate.permission_denied_blocked=Nav iespējams ievietot no neatļautiem saimniekdatoriem, lūgums vaicāt pārvaldītājam pārbaudīt ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS iestatījumus. diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index 791f1b84d3..849b3717a5 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -333,7 +333,7 @@ has_unconfirmed_mail = Moin %s, du hest eene nich utwiesen E-Mail-Adress (%s< non_local_account = Frömde Brukers könen hör Passwoord nich dör de Forgejo-Internett-Brukerschnittstee vernejen. openid_register_desc = De utköört OpenID-URI is unbekannt. Verbinn dat hier mit eenem nejen Konto. disable_forgot_password_mail = Konto-Torügghalen is utknipst, denn keene E-Mail is inricht. Bidde kuntakteer dienen Sied-Chef. -authorize_application_description = Wenn du de Togang verlöövst, kann dat all diene Konto-Informatioon lesen un schrieven, ok privaate Repos un Vereenigungen. +authorize_application_description = Wenn du Togang verlöövst, kann dat all diene Konto-Informatioon lesen un schrieven, ok privaate Repos un Vereenigungen. authorization_failed_desc = Dat Anmellen is fehlslagen, denn wi hebben eene ungültig Anfraag funnen. Bidde kuntakteer de Chef vun de Programm, wat du anmellen willst. twofa_scratch_used = Du hest dien Eenmaalpasswoord bruukt. Du büst to de Twee-Faktooren-Instellens-Sied umleit worden, waar du 2FA utknipsen of een nejes Eenmaalpasswoord maken kannst. oauth.signin.error.temporarily_unavailable = Anmellen fehlslagen, denn de Anmell-Server is jüüst nich verföögbaar. Bidde versöök dat naher noch eenmaal. @@ -843,7 +843,7 @@ profile_desc = Över di hidden_comment_types_description = Kommentaar-Arden, wat hier utköört sünd, worden in Gefall-Sieden nich wiest. Wenn du to’n Bispööl »Vermark« utköörst, worden all de »›Bruker‹ hett ›Vermark‹ hentoföögt/wegdaan«-Kommentaren wegdaan. email_desc = Diene Höövd-E-Mail-Adress word för Narichtens, Passwoord-Torügghalen un, wenn se nich verburgen is, Git-Aktioonen över ’t Internett bruukt. can_not_add_email_activations_pending = Een Aktiveren staht noch ut. Wenn du eene neje E-Mail-Adress hentofögen willst, versöök dat in een paar Menüten noch eenmaal. -email_deletion_desc = De E-Mail-Adress un daarmit verbunnen Informatioon word ut dienem Konto wegdaan. Git-Kommitterens vun deeser E-Mail-Adress worden nich ännert. Wiedermaken? +email_deletion_desc = Deese E-Mail-Adress un daarmit verbunnen Informatioon word ut dienem Konto wegdaan. Git-Kommitterens vun deeser E-Mail-Adress worden nich ännert. Wiedermaken? principal_desc = Deese SSH-Zertifikaat-Höövdmannen sünd mit dienem Konto verbunnen un geven kumpleten Togriep up diene Repositoriums. add_email_confirmation_sent = Eene Utwiesens-E-Mail is an »%s« schickt worden. Um diene E-Mail-Adress uttowiesen, kiek bidde in dienen E-Mail-Ingang un folg de Verwies daarin in de anner %s. ssh_desc = Deese publiken SSH-Slötels sünd mit dienem Konto verbunnen. De tohörig privaate Slötel gifft kumpleten Togriep up diene Repositoriums. SSH-Slötels, wat utwiest worden sünd, könen bruukt worden, um SSH-unnerschreven Git-Kommitterens uttowiesen. @@ -1638,7 +1638,7 @@ pulls.showing_specified_commit_range = Blots Ännerns vun Kommitterens %[1]s bit pulls.review_only_possible_for_full_diff = Nakieken gaht blots, wenn de hele Unnerscheed wiest word pulls.filter_changes_by_commit = Na Kommitteren filtern pulls.nothing_to_compare = Deese Twiegen sünd gliek. ’t is nich nödig, eenen Haalvörslag to maken. -pulls.nothing_to_compare_have_tag = De utköört Twieg/Mark sünd gliek. +pulls.nothing_to_compare_have_tag = De utköört Twiegen/Marken sünd gliek. pulls.create = Haalvörslag maken pulls.title_desc_one = will %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhren pulls.merged_title_desc_one = hett %[1]d Kommitteren vun %[2]s na %[3]s %[4]s tosamenföhrt @@ -2547,12 +2547,12 @@ issues.label_exclusive_desc = Benööm de Vermark Rebeet/Ding, daar issues.label_exclusive_warning = Elkeen anner Vermark in de sülve Rebeet word wegdaan, wenn de Vermarkens vun eenem Gefall of Haalvörslag bewarkt worden. blame.ignore_revs.failed = Kunn de Versioonen in de .git-blame-ignore-revs nich minnachten. invisible_runes_line = `Deese Rieg hett verburgen Unicode-Bookstavens` -mirror_address_url_invalid = De angeven URL is ungültig. Du muttst all Delen vun de URL recht utkielen. +mirror_address_url_invalid = De angeven URL is ungültig. Wees wiss, dat de Delen vun de URL recht utkielt sünd. mirror_address_protocol_invalid = De angeven URL is ungültig. Blots Steden vun de Aarden »http(s)://« of »git://« könen tum Spegeln bruukt worden. mirror_use_ssh.helper = Forgejo spegelt dat Repositorium mit Git över SSH un maakt för di een Slötelpaar, wenn du deese Instellen utköörst. Du muttst wiss maken, dat de maakt publike Slötel dat Recht kriggt, to de Enn-Repositorium to schuven. Wenn du dat utköörst, kannst du keen Anmellen mit Passwoord bruken. migrate.permission_denied_blocked = Du kannst nich vun verboden Servers importeren; bidde fraag de Chef, of he de Instellens ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS överprüfen maag. blame.ignore_revs = Minnacht Versioonen in .git-blame-ignore-revs. Klick hier, um daar överwegtogahn un de normaale Schüld-Ansicht to wiesen. -migrate.github_token_desc = Du kannst hier een of mehr Tekens angeven, wat mit Kommas trennt sünd, um dat Umtrecken fixer to maken, um de GitHub-API-Togrieps-Begrenz. WAHRSCHAU: Wenn du dat missbruukst, kannst du de Richtlienjen vun de Deenstbedriev verletzen un dien Konto kann sperrt worden. +migrate.github_token_desc = Du kannst hier een of mehr Tekens angeven, wat mit Kommas trennt sünd, um de GitHub-API-Togrieps-Begrenz to umgahn un so dat Umtrecken fixer to maken. WAHRSCHAU: Wenn du dat missbruukst, kannst du de Richtlienjen vun de Deenstbedriev verletzen un diene Konten könen villicht sperrt worden. issues.edit.already_changed = Kann Ännerns an de Gefall nich sekern. Dat schient, dat de Inholl al vun een anner Bruker ännert worden is. Bidde laad de Sied neei un versöök, dat dann noch eenmaal to bewarken, daarmit du hör Ännerns nich överschriffst broken_message = De Git-Daten unner deesem Repositorium könen nich lesen worden. Kuntakteer de Chef vun deeser Instanz of löske dat Repositorium. ambiguous_runes_header = `Deese Datei enthollt verwesselbaare Unicode-Bookstavens` diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 242b915137..78b7479625 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -468,7 +468,7 @@ email_domain_blacklisted=Você não pode se cadastrar com seu endereço de e-mai authorize_application=Autorizar aplicativo authorize_redirect_notice=Você será redirecionado para %s se você autorizar este aplicativo. authorize_application_created_by=Este aplicativo foi criado por %s. -authorize_application_description=Se você conceder o acesso, isso permitirá acessar e alterar todas as informações da sua conta, incluindo repositórios privados e organizações. +authorize_application_description=Se você conceder acesso, isso permitirá acessar e alterar todas as informações da sua conta, incluindo repositórios privados e organizações. authorize_title=Autorizar "%s" para acessar sua conta? authorization_failed=Autorização falhou authorization_failed_desc=A autorização falhou porque detectamos uma solicitação inválida. Entre em contato com o responsável do aplicativo que você tentou autorizar. @@ -829,7 +829,7 @@ activations_pending=Ativações pendentes can_not_add_email_activations_pending=Há uma ativação pendente, tente novamente em alguns minutos se quiser adicionar um novo e-mail. delete_email=Remover email_deletion=Remover endereço de e-mail -email_deletion_desc=O endereço de e-mail e informações relacionadas serão removidos de sua conta. Commits aplicados por este endereço de e-mail permanecerão inalterados. Continuar? +email_deletion_desc=Este endereço de e-mail e informações relacionadas serão removidos de sua conta. Commits aplicados por este endereço de e-mail permanecerão inalterados. Continuar? email_deletion_success=O endereço de e-mail foi removido. theme_update_success=Seu tema foi atualizado. theme_update_error=O tema selecionado não existe. @@ -1151,7 +1151,7 @@ mirror_interval_invalid=O intervalo do espelhamento não é válido. mirror_sync_on_commit=Sincronizar quando commits forem enviados mirror_address=Clonar a partir de URL mirror_address_desc=Coloque todas as credenciais necessárias na seção de autorização. -mirror_address_url_invalid=A URL fornecida é inválida. Você deve escapar todos os componentes da URL corretamente. +mirror_address_url_invalid=A URL fornecida é inválida. Certifique-se de escapar os componentes da URL corretamente. mirror_address_protocol_invalid=O URL fornecido é inválido. Somente locais http(s):// ou git:// podem ser usados para espelhamento. mirror_lfs=Armazenamento de Arquivo Grande (LFS) mirror_lfs_desc=Ativar espelhamento de dados LFS. @@ -1235,7 +1235,7 @@ migrate_items_releases=Versões migrate_repo=Migrar repositório migrate.clone_address=Migrar / Clonar de URL migrate.clone_address_desc=URL HTTP(S) ou comando git "clone" de um repositório existente -migrate.github_token_desc=Você pode colocar aqui um ou mais tokens separados por vírgulas para tornar a migração mais rápida para compensar o limite de taxa de API do GitHub. AVISO: abusar desse recurso pode violar a política do provedor de serviços e levar ao bloqueio da conta. +migrate.github_token_desc=Você pode colocar aqui um ou mais tokens separados por vírgulas para tornar a migração mais rápida ao evitar o limite de taxa de API do GitHub. AVISO: abusar desse recurso pode violar a política do provedor de serviços e pode levar ao bloqueio da(s) conta(s). migrate.clone_local_path=ou um caminho de servidor local migrate.permission_denied=Você não pode importar repositórios locais. migrate.permission_denied_blocked=Você não pode importar dos hosts não permitidos, por favor peça ao administrador para verificar as configurações ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -2773,7 +2773,7 @@ subscribe.issue.guest.tooltip = Faça login para receber notificações desta qu settings.federation_not_enabled = O recurso de federação não está habilitado em seu servidor. settings.trust_model.committer.desc = Uma assinatura de commit é considerada "confiável" caso corresponda ao autor do commit, caso contrário será definida como "discordante". Isso permite delegar a autoria de commits ao Forgejo, adicionando créditos ao autor original nos campos "Co-authored-by" e "Co-commited-by" no final do commit. A chave padrão do Forgejo deve corresponder à chave de um usuário no banco de dados. settings.wiki_branch_rename_success = O nome do ramo da wiki do repositório foi regularizado com sucesso. -pulls.nothing_to_compare_have_tag = O ramo/etiqueta escolhidos são iguais. +pulls.nothing_to_compare_have_tag = Os branches/etiquetas escolhidos são iguais. settings.sourcehut_builds.secrets = Segredos release.download_count_few = %s downloads release.hide_archive_links = Ocultar arquivos gerados automaticamente @@ -3603,7 +3603,7 @@ config.cache_test_slow = Teste de cache bem-sucedido, mas a resposta é lenta: % config.cache_test = Cache de Teste config.cache_test_failed = Falha ao sondar o cache: %v. self_check.database_collation_mismatch = Esperar que o banco de dados use o ordenamento: %s -dashboard.cleanup_actions = Limpar logs expirados e artefatos de ações +dashboard.cleanup_actions = Limpar logs expirados e artefatos de Actions emails.delete = Deletar email emails.delete_primary_email_error = Você não pode excluir o email principal. emails.deletion_success = O endereço de email foi excluído. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index c7cccc012c..1d96b2d4c8 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -471,7 +471,7 @@ email_domain_blacklisted=С данным адресом эл. почты рег authorize_application=Авторизация приложения authorize_redirect_notice=Вы будете перенаправлены на %s, если вы авторизуете это приложение. authorize_application_created_by=Это приложение было создано %s. -authorize_application_description=Если вы предоставите доступ, оно сможет получить доступ и редактировать любую информацию о вашей учётной записи, включая содержимое частных репозиториев и организаций. +authorize_application_description=Если вы предоставите доступ, он сможет получить доступ и редактировать любую информацию о вашей учётной записи, включая содержимое частных репозиториев и организаций. authorize_title=Разрешить «%s» доступ к вашей учётной записи? authorization_failed=Ошибка авторизации authorization_failed_desc=Ошибка авторизации, обнаружен неверный запрос. Пожалуйста, свяжитесь с автором приложения, которое вы пытались авторизовать. @@ -1068,7 +1068,7 @@ change_username_redirect_prompt.with_cooldown.few = Прежнее имя буд keep_pronouns_private = Показывать местоимения только зарегистрированным пользователям keep_pronouns_private.description = Местоимения будут скрыты от пользователей, не имеющих учётных записей на сервере. quota.applies_to_user = Эти ограничения хранилища применяются к вашей учётной записи -quota.applies_to_org = Эти ограничения хранилища применяются к этой организации +quota.applies_to_org = К этой организации применяются следующие ограничения quota.sizes.repos.public = Общедоступные репозитории storage_overview = Использование места quota = Ограничения хранилища @@ -1232,7 +1232,7 @@ migrate_items_releases=Выпуски migrate_repo=Перенос репозитория migrate.clone_address=Перенос / Клонирование по URL migrate.clone_address_desc=HTTP/HTTPS или Git адрес существующего репозитория -migrate.github_token_desc=Вы можете указать один или несколько разделенных запятыми токенов, чтобы ускорить перенос за счёт обхода ограничений частоты обращений к API GitHub. ПРЕДУПРЕЖДЕНИЕ: злоупотребление этой функцией может нарушить условия предоставления услуг и привести к блокировке учётной записи. +migrate.github_token_desc=Вы можете поместить здесь один или несколько токенов, разделенных запятыми, чтобы ускорить миграцию за счет обхода ограничения скорости API GitHub. ПРЕДУПРЕЖДЕНИЕ: Злоупотребление этой функцией может нарушить политику поставщика услуг и привести к блокировке вашей учетной записи (ов). migrate.clone_local_path=или локальный путь на сервере migrate.permission_denied=У вас нет прав на импорт локальных репозиториев. migrate.permission_denied_blocked=Вы не можете импортировать с запрещённых хостов, пожалуйста, попросите администратора проверить настройки ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -2698,7 +2698,7 @@ error.csv.invalid_field_count=Не удается отобразить этот mirror_address_protocol_invalid = Эта ссылка недействительна. Для зеркалирования можно использовать только расположения http(s):// и git:// . fork_no_valid_owners = Невозможно создать ответвление этого репозитория, т.к. здесь нет действующих владельцев. new_repo_helper = Репозиторий содержит все файлы проекта и историю изменений. Уже где-то есть репозиторий? Выполните перенос. -mirror_address_url_invalid = Эта ссылка недействительна. Необходимо правильно указать все части адреса. +mirror_address_url_invalid = Указанный URL-адрес недействителен. Убедитесь, что компоненты URL-адреса экранированы правильно. issues.comment.blocked_by_user = Вы не можете комментировать под этой задачей, т.к. вы заблокированы владельцем репозитория или автором задачи. pulls.blocked_by_user = Невозможно создать запрос на слияние в этом репозитории, т.к. вы заблокированы его владельцем. settings.add_collaborator_blocked_our = Невозможно добавить соучастника, т.к. он заблокирован в этом репозитории. @@ -2756,7 +2756,7 @@ ambiguous_runes_description = `Этот файл содержит символы editor.invalid_commit_mail = Неправильная почта для создания коммита. pulls.has_merged = Слияние не удалось: запрос уже был слит, изменение целевой ветви или повторное слияние невозможно. settings.enter_repo_name = Введите имя владельца и название репозитория как указано: -signing.wont_sign.error = Не удалось проверить возможность подписать коммит. +signing.wont_sign.error = Произошла ошибка при проверке возможности подписания коммита. signing.wont_sign.nokey = Сервер не предоставляет ключ для подписи коммита. settings.wiki_globally_editable = Разрешить редактирование вики всем пользователям settings.webhook.test_delivery_desc_disabled = Активируйте этот веб-хук для проверки тестовым событием. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index e071fa76f4..332a855e88 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -320,7 +320,7 @@ sqlite3_not_available=Ця версія Forgejo не підтримує SQLite3. invalid_db_setting=Налаштування бази даних є некоректними: %v invalid_repo_path=Помилковий шлях до кореня репозиторію: %v invalid_app_data_path=Некоректний шлях до даних програми: %v -run_user_not_match=Ім'я в «Користувач, від якого запустити» не є ім'ям поточного користувача: %s -> %s +run_user_not_match=Ім'я в «Користувач, від якого запустити» не є ім'ям поточного користувача: %s → %s internal_token_failed=Не вдалося згенерувати внутрішній токен: %v secret_key_failed=Не вдалося згенерувати секретний ключ: %v save_config_failed=Не в змозі зберегти конфігурацію: %v @@ -792,7 +792,7 @@ activate_email=Надіслати активацію activations_pending=Активації в очікуванні delete_email=Видалити email_deletion=Видалити адресу електронної пошти -email_deletion_desc=Електронна адреса та пов'язана з нею інформація буде видалена з вашого облікового запису. Git коміти, здійснені через цю електронну адресу, залишиться без змін. Продовжити? +email_deletion_desc=Цю електронну адресу та пов'язану з нею інформацію буде видалено з вашого облікового запису. Коміти Git, здійснені через цю електронну адресу, залишаться без змін. Продовжити? email_deletion_success=Адресу електронної пошти було видалено. theme_update_success=Тему оновлено. theme_update_error=Вибрана тема не існує. @@ -908,7 +908,7 @@ oauth2_application_create_description=Програми OAuth2 надають в authorized_oauth2_applications=Авторизовані програми OAuth2 revoke_key=Відкликати -revoke_oauth2_grant=Скасувати доступ +revoke_oauth2_grant=Відкликати доступ revoke_oauth2_grant_description=Скасування доступу для цієї програми третьої сторони не дозволить їй отримувати доступ до ваших даних. Ви впевнені? twofa_desc=Двофакторна автентифікація підвищує безпеку вашого облікового запису. @@ -934,7 +934,7 @@ manage_account_links_desc=Ці зовнішні акаунти прив'язан account_links_not_available=Наразі немає зовнішніх облікових записів, пов'язаних із вашим обліковим записом Forgejo. link_account=Прив'язати обліковий запис remove_account_link=Видалити пов'язаний обліковий запис -remove_account_link_desc=Видалення пов'язаного облікового запису відкликає його доступ до вашого облікового запису Forgejo. Продовжити? +remove_account_link_desc=Видалення пов'язаного облікового запису скасує його доступ до вашого облікового запису Forgejo. Продовжити? remove_account_link_success=Зв'язаний обліковий запис видалено. @@ -1092,7 +1092,7 @@ template_description=Шаблонні репозиторії дозволяют visibility=Видимість visibility_description=Тільки власник або члени організації які мають віповідні права, зможуть побачити. visibility_helper_forced=Адміністратор вашого сайту налаштував параметри: всі нові репозиторії будуть приватними. -visibility_fork_helper=(Ці зміни вплинуть на всі форки.) +visibility_fork_helper=(Буде змінено видимість усіх форків.) clone_helper=Потрібна допомога у клонуванні? Відвідайте сторінку Допомога. fork_repo=Створити форк репозиторію fork_from=Форк з @@ -1157,9 +1157,9 @@ delete_preexisting_success=Видалено неприйняті файли в % blame_prior=Переглянути анотацію, що передує цій зміні -transfer.accept=Дозволити трансфер +transfer.accept=Прийняти передачу transfer.accept_desc=`Перемістити до "%s"` -transfer.reject=Відхилити трансфер +transfer.reject=Відхилити передачу transfer.reject_desc=`Скасувати переміщення до "%s"` desc.private=Приватний @@ -1957,7 +1957,7 @@ settings.transfer_notices_1=- Ви втратите доступ до репоз settings.transfer_notices_2=- Ви збережете доступ, якщо новим власником стане організація, власником якої ви є. settings.transfer_notices_3=- Якщо репозиторій є приватним і передається окремому користувачеві, ця дія гарантує, що користувач має хоча б дозвіл на читаня репозитарію (і при необхідності змінює права дозволів). settings.transfer_owner=Новий власник -settings.transfer_perform=Здійснити перенесення +settings.transfer_perform=Здійснити передачу settings.transfer_started=`Цей репозиторій чекає підтвердження перенесення від "%s"` settings.transfer_succeed=Репозиторій був перенесений. settings.signing_settings=Параметри перевірки підпису @@ -2335,7 +2335,7 @@ issues.filter_poster_no_select = Усі автори pulls.merged_info_text = Гілку %s тепер можна видалити. find_file.go_to_file = Знайти файл visibility_helper = Зробити репозиторій приватним -projects.card_type.desc = Попередній вигляд карток +projects.card_type.desc = Попередній перегляд карток projects.card_type.text_only = Лише текст projects.card_type.images_and_text = Зображення і текст issues.filter_poster = Автор @@ -2572,7 +2572,7 @@ new_repo_helper = Репозиторій містить усі файли про issues.reference_link = Посилання: %s object_format_helper = Формат об'єктів репозиторія. Змінити потім неможливо. SHA1 — найсумісніший. mirror_interval = Інтервал синхронізації дзеркала (часові одиниці — «h», «m», «s»). 0 вимикає синхронізацію. (Мінімальний інтервал: %s) -mirror_address_url_invalid = Вказано хибну URL-адресу. Замініть escape-послідовностями всі неоднозначні складники адреси. +mirror_address_url_invalid = Вказано хибну URL-адресу. Впевніться, що всі неоднозначні складники адреси замінено escape-послідовностями. mirror_address_protocol_invalid = Хибна URL-адреса. Лише адреси http(s):// чи git:// можна використовувати для віддзеркалення. stars_remove_warning = Це вилучить усі зірки репозиторія. mirror_use_ssh.not_available = SSH-вхід недоступний. @@ -2825,6 +2825,21 @@ settings.mirror_settings.docs.disabled_pull_mirror.instructions = Налашту issues.label_templates.fail_to_load_file = Не вдалося завантажити файл шаблону міток «%s»: %v migrate.github.description = Перенесіть дані з github.com або сервера GitHub Enterprise. migrate.cancel_migrating_confirm = Бажаєте скасувати перенесення? +transfer.no_permission_to_accept = У вас немає дозволу прийняти цю передачу. +transfer.no_permission_to_reject = У вас немає дозволу відхилити цю передачу. +issues.choose.ignore_invalid_templates = Недійсні шаблони проігноровано +issues.choose.invalid_templates = Знайдено %v недійсних шаблонів +settings.transfer_abort_success = Передачу репозиторію %s успішно скасовано. +pulls.cmd_instruction_checkout_title = Переключення гілок +pulls.cmd_instruction_checkout_desc = З репозиторію вашого проєкту переключіться на нову гілку і протестуйте зміни. +issues.review.pending.tooltip = Цей коментар наразі невидимий для інших користувачів. Щоб надіслати коментарі у черзі, виберіть «%s» → «%s/%s/%s» у верхній частині сторінки. +issues.dependency.issue_batch_close_blocked = Неможливо виконати пакетне закриття вибраних задач, оскільки задача #%d має відкриті залежності +commit.revert = Вивернути +commit.revert-header = Вивернути: %s +editor.revert = Вивернути %s на: +commit.revert-content = Оберіть гілку, на яку вивернути: +pulls.cmd_instruction_merge_warning = Увага: в цьому репозиторії не ввімкнено «Автовизначення ручного об'єднання», тож позначити цей запит як об'єднаний вручну вам доведеться самостійно. +topic.format_prompt = Теми повинні починатися з літери або цифри, можуть містити дефіси («-») і крапки («.») і мати довжину до 35 символів. Дозволено використання лише малих літер. [graphs] contributors.what = внески @@ -2956,6 +2971,10 @@ teams.invite.by = Вас запрошує %s teams.invite_team_member.list = Запрошення в очікуванні form.name_pattern_not_allowed = Вираз «%s» не може бути частиною назви організації. teams.add_nonexistent_repo = Репозиторій, який ви намагаєтеся додати, не існує. Спочатку створіть його. +settings.change_orgname_prompt = Зауважте, зміна назви організації також змінить URL-адресу організації і звільнить стару назву. +teams.none_access_helper = Опція «Немає доступу» впливає лише на приватні репозиторії. +teams.general_access_helper = Дозволи учасників будуть визначатися відповідно до наведеної нижче таблиці дозволів. +teams.general_access = Індивідуальний доступ [admin] dashboard=Панель управління @@ -3008,7 +3027,7 @@ dashboard.resync_all_sshprincipals=Оновити файл «.ssh/authorized_pri dashboard.resync_all_hooks=Пересинхронізувати хуки pre-receive, update та post-receive в усіх репозиторіях dashboard.reinit_missing_repos=Переініціалізувати усі репозитрії git-файли яких втрачено dashboard.sync_external_users=Синхронізувати дані зовнішніх користувачів -dashboard.cleanup_hook_task_table=Очистити hook_task таблицю +dashboard.cleanup_hook_task_table=Очистити таблицю hook_task dashboard.server_uptime=Час роботи сервера dashboard.current_goroutine=Поточна кількість Goroutines dashboard.current_memory_usage=Поточне використання пам'яті @@ -3030,16 +3049,16 @@ dashboard.mspan_structures_obtained=Отримано структур MSpan dashboard.mcache_structures_usage=Використання структур MCache dashboard.mcache_structures_obtained=Отримано структур MCache dashboard.profiling_bucket_hash_table_obtained=Отримано хеш-таблиць профілювання -dashboard.gc_metadata_obtained=Отримано метаданих збирача сміття (GC) +dashboard.gc_metadata_obtained=Отримано метаданих збирача сміття dashboard.other_system_allocation_obtained=Отримання інших виділень пам'яті -dashboard.next_gc_recycle=Наступний цикл збирача сміття (GC) -dashboard.last_gc_time=З останнього запуску збирача сміття (GC) +dashboard.next_gc_recycle=Наступний цикл збирача сміття +dashboard.last_gc_time=З останнього запуску збирача сміття dashboard.total_gc_time=Загальна пауза збирача сміття (GC) -dashboard.total_gc_pause=Загальна пауза збирача сміття (GC) -dashboard.last_gc_pause=Остання пауза збирача сміття (GC) -dashboard.gc_times=Кількість запусків збирача сміття (GC) -dashboard.delete_old_actions=Видалити всі старі дії з бази даних -dashboard.delete_old_actions.started=Видалення всіх старі дії з бази даних розпочато. +dashboard.total_gc_pause=Загальна пауза збирача сміття +dashboard.last_gc_pause=Остання пауза збирача сміття +dashboard.gc_times=Кількість запусків збирача сміття +dashboard.delete_old_actions=Видалити всі старі активності з бази даних +dashboard.delete_old_actions.started=Розпочато видалення всіх старих активностей з бази даних. users.user_manage_panel=Керування обліковими записами users.new_account=Створити обліковий запис @@ -3247,7 +3266,7 @@ config.git_version=Версія Git config.repo_root_path=Шлях до кореня репозиторію config.lfs_root_path=Кореневий шлях LFS config.log_file_root_path=Шлях до лог файлу -config.script_type=Тип скрипта +config.script_type=Тип скрипту config.reverse_auth_user=Користувач для авторизації на зворотному проксі config.ssh_config=Конфігурація SSH @@ -3296,7 +3315,7 @@ config.default_enable_timetracking=Увімкнути відстеження ч config.default_allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу config.no_reply_address=Домен прихованих адрес електронної пошти config.default_visibility_organization=Видимість за замовчуванням для нових організацій -config.default_enable_dependencies=Увімкнути залежності задачі за замовчуванням +config.default_enable_dependencies=Увімкнути залежності задач за замовчуванням config.webhook_config=Конфігурація вебхуків config.queue_length=Довжина черги @@ -3327,7 +3346,7 @@ config.session_config=Конфігурація сесії config.session_provider=Провайдер сесії config.provider_config=Конфігурація постачальника config.cookie_name=Назва файлу cookie -config.gc_interval_time=Інтервал запуску збирача сміття (GC) +config.gc_interval_time=Інтервал запуску збирача сміття config.session_life_time=Час життя сесії config.https_only=Тільки HTTPS config.cookie_life_time=Час життя файлу cookie @@ -3342,12 +3361,12 @@ config.git_disable_diff_highlight=Вимкнути підсвітку синта config.git_max_diff_lines=Максимум рядків на diff (на один файл) config.git_max_diff_line_characters=Максимум символів на diff (на одну строку) config.git_max_diff_files=Максимум diff-файлів (для показу) -config.git_gc_args=Аргументи збирача сміття (GC) +config.git_gc_args=Аргументи збирача сміття config.git_migrate_timeout=Тайм-аут міграції config.git_mirror_timeout=Тайм-аут оновлення дзеркала config.git_clone_timeout=Тайм-аут операції клонування -config.git_pull_timeout=Тайм-аут операції Pull -config.git_gc_timeout=Тайм-аут операції збирача сміття (GC) +config.git_pull_timeout=Тайм-аут операції отримання змін +config.git_gc_timeout=Тайм-аут операції збирача сміття config.log_config=Конфігурація журналу config.disabled_logger=Вимкнено @@ -3386,9 +3405,9 @@ monitor.queue.settings.submit=Оновити налаштування monitor.queue.settings.changed=Налаштування оновлено notices.system_notice_list=Сповіщення системи -notices.view_detail_header=Переглянути деталі повідомлення +notices.view_detail_header=Подробиці сповіщення notices.select_all=Вибрати все -notices.deselect_all=Скасувати виділення +notices.deselect_all=Скасувати вибір notices.inverse_selection=Інвертувати виділене notices.delete_selected=Видалити вибране notices.delete_all=Видалити всі cповіщення @@ -3481,6 +3500,11 @@ config.cache_test_slow = Перевірку кешу завершено успі config.cache_test_succeeded = Перевірку кешу завершено успішно, відповідь отримано через %s. config.cache_test = Перевірити кеш config.cache_test_failed = Не вдалося перевірити кеш: %v. +dashboard.rebuild_issue_indexer = Перебудувати індексатор задач +users.details = Дані користувача +auths.login_source_exist = Джерело автентифікації «%s» вже існує. +dashboard.delete_old_system_notices = Видалити всі старі сповіщення системи з бази даних +users.purge = Повністю видалити користувач_ку [action] @@ -3709,6 +3733,27 @@ maven.install2 = Запустити з командного рядка: npm.dependencies.bundle = Пакетні залежності npm.dependencies.peer = Однорангові залежності arch.pacman.repo.multi = %s має одну й ту саму версію в різних дистрибутивах. +owner.settings.cargo.rebuild = Перебудувати індекс +owner.settings.cleanuprules.none = Правил очистки ще немає. +owner.settings.cleanuprules.preview.none = Правило очистки не відповідає жодному пакунку. +owner.settings.cargo.initialize = Ініціалізувати індекс +owner.settings.cargo.initialize.description = Для реєстру Cargo потрібен спеціальний Git-репозиторій з індексом. Використання цієї опції (пере)створить репозиторій і автоматично його налаштує. +owner.settings.cargo.initialize.error = Не вдалося ініціалізувати індекс Cargo: %v +owner.settings.cargo.initialize.success = Індекс Cargo успішно створено. +owner.settings.cargo.rebuild.error = Не вдалося перебудувати індекс Cargo: %v +owner.settings.cargo.title = Індекс реєстру Cargo +owner.settings.cleanuprules.title = Правила очистки +owner.settings.cleanuprules.add = Додати правило очистки +owner.settings.cargo.rebuild.success = Індекс Cargo успішно перебудувано. +owner.settings.cleanuprules.success.update = Правило очистки оновлено. +owner.settings.cleanuprules.success.delete = Правило очистки видалено. +owner.settings.cleanuprules.edit = Редагувати правило очистки +owner.settings.cleanuprules.preview = Попередній перегляд правила очистки +owner.settings.cargo.rebuild.description = Перебудування може бути корисним, якщо індекс не синхронізовано зі збереженими пакунками Cargo. +rpm.distros.redhat = у дистрибутивах на основі RedHat +rpm.distros.suse = у дистрибутивах на основі SUSE +owner.settings.cargo.rebuild.no_index = Неможливо перебудувати, індекс не ініціалізовано. +alpine.registry.key = Завантажте відкритий RSA-ключ реєстру в папку /etc/apk/keys/ для перевірки підпису індексу: [secrets] deletion = Видалити секрет diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json index eca76a5ba7..6cca2187a0 100644 --- a/options/locale_next/locale_ar.json +++ b/options/locale_next/locale_ar.json @@ -137,5 +137,9 @@ "moderation.report_abuse": "الإبلاغ عن إساءة الاستخدام", "feed.atom.link": "موجز Atom", "admin.config.moderation_config": "تهيئة الإشراف", - "mail.actions.successful_run_after_failure_subject": "تم استعادة سير العمل %[1]s في المستودع %[2]s" + "mail.actions.successful_run_after_failure_subject": "تم استعادة سير العمل %[1]s في المستودع %[2]s", + "discussion.sidebar.reference": "مرجع", + "admin.moderation.moderation_reports": "تقارير الإشراف", + "admin.moderation.reports": "التقارير", + "admin.moderation.no_open_reports": "لا يوجد حالياً أية تقارير مفتوحة." } diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index b47c594348..8de6c46ad8 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -34,9 +34,9 @@ "discussion.locked": "Tato diskuze byla uzamčena. Komentování je omezené na přispěvatele.", "relativetime.future": "v budoucnu", "relativetime.years": { - "one": "Před %d rokem", - "few": "Před %d lety", - "other": "Před %d lety" + "one": "před %d rokem", + "few": "před %d lety", + "other": "před %d lety" }, "relativetime.1day": "včera", "relativetime.2days": "před dvěma dny", @@ -47,30 +47,30 @@ "relativetime.1year": "minulý rok", "relativetime.2years": "před dvěma lety", "relativetime.weeks": { - "one": "Před %d týdnem", - "few": "Před %d týdny", - "other": "Před %d týdny" + "one": "před %d týdnem", + "few": "před %d týdny", + "other": "před %d týdny" }, "relativetime.days": { - "one": "Před %d dnem", - "few": "Před %d dny", - "other": "Před %d dny" + "one": "před %d dnem", + "few": "před %d dny", + "other": "před %d dny" }, "relativetime.mins": { - "one": "Před %d minutou", - "few": "Před %d minutami", - "other": "Před %d minutami" + "one": "před %d minutou", + "few": "před %d minutami", + "other": "před %d minutami" }, "relativetime.hours": { - "one": "Před %d hodinou", - "few": "Před %d hodinami", - "other": "Před %d hodinami" + "one": "před %d hodinou", + "few": "před %d hodinami", + "other": "před %d hodinami" }, "relativetime.now": "nyní", "relativetime.months": { - "one": "Před %d měsícem", - "few": "Před %d měsíci", - "other": "Před %d měsíci" + "one": "před %d měsícem", + "few": "před %d měsíci", + "other": "před %d měsíci" }, "moderation.report_content": "Nahlásit obsah", "moderation.report_abuse_form.details": "Tento formulář je určen k nahlašování uživatelů, kteří si vytvářejí spamové profily, repozitáře, problémy, komentáře nebo se chovají nevhodně.", @@ -101,7 +101,7 @@ "editor.textarea.tab_hint": "Řádek je již odsazen. Pro opuštění editoru stiskněte znovu Tab nebo Escape.", "editor.textarea.shift_tab_hint": "Na tomto řádku není žádné odsazení. Pro opuštění editoru stiskněte znovu Shift + Tab nebo Escape.", "admin.dashboard.cleanup_offline_runners": "Vymazat offline runnery", - "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více", + "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více.", "avatar.constraints_hint": "Velikost vlastního avataru nesmí překročit %[1]s nebo být větší než %[2]dx%[3]d pixelů", "repo.diff.commit.next-short": "Další", "repo.diff.commit.previous-short": "Předchozí", @@ -113,5 +113,10 @@ "og.repo.summary_card.alt_description": "Karta se souhrnem repozitáře %[1]s, popsaným jako: %[2]s", "mail.actions.run_info_sha": "Revize: %[1]s", "repo.settings.push_mirror.branch_filter.label": "Filtr větve (nepovinný)", - "repo.settings.push_mirror.branch_filter.description": "Větve, které mají být zrcadleny. Ponechte prázdné pro zrcadlení všech větví. Syntaxi naleznete v dokumentaci %[2]s. Příklady: main, release/*" + "repo.settings.push_mirror.branch_filter.description": "Větve, které mají být zrcadleny. Ponechte prázdné pro zrcadlení všech větví. Syntaxi naleznete v dokumentaci %[2]s. Příklady: main, release/*", + "discussion.sidebar.reference": "Reference", + "admin.moderation.moderation_reports": "Hlášení moderace", + "admin.moderation.reports": "Hlášení", + "admin.moderation.deleted_content_ref": "Nahlášený obsah s typem %[1]v a ID %[2]d již neexistuje", + "admin.moderation.no_open_reports": "Momentálně nejsou otevřena žádná hlášení." } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 5a3ceef7e5..e8b0976994 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -93,7 +93,7 @@ "editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals Tab oder Escape, um den Editor zu verlassen.", "editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals Shift + Tab oder Escape, um den Editor zu verlassen.", "admin.dashboard.cleanup_offline_runners": "Aufräumen der Offline-Runner", - "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr", + "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr.", "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]d×%[3]d Pixel sein", "repo.diff.commit.next-short": "Nächste", "repo.diff.commit.previous-short": "Vorherige", @@ -105,5 +105,10 @@ "og.repo.summary_card.alt_description": "Zusammenfassungskarte des Repositorys %[1]s, beschrieben als %[2]s", "mail.actions.run_info_sha": "Commit: %[1]s", "repo.settings.push_mirror.branch_filter.label": "Branch-Filter (optional)", - "repo.settings.push_mirror.branch_filter.description": "Zu spiegelnde Branches. Leer lassen, um alle Branches zu spiegeln. Siehe die „%[2]s“-Dokumentation für die Syntax. Beispiele: main, release/*" + "repo.settings.push_mirror.branch_filter.description": "Zu spiegelnde Branches. Leer lassen, um alle Branches zu spiegeln. Siehe die „%[2]s“-Dokumentation für die Syntax. Beispiele: main, release/*", + "discussion.sidebar.reference": "Referenz", + "admin.moderation.deleted_content_ref": "Gemeldeter Inhalt vom Typ %[1]v und ID %[2]d existiert nicht mehr", + "admin.moderation.moderation_reports": "Moderationsmeldungen", + "admin.moderation.reports": "Meldungen", + "admin.moderation.no_open_reports": "Es gibt momentan keine offenen Meldungen." } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 6ec601c538..6eb2039ab4 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -93,7 +93,7 @@ "editor.textarea.tab_hint": "Naka-indent na ang linya. Pindutin ulit ang Tab o Escape para umalis sa editor.", "editor.textarea.shift_tab_hint": "Walang indentation sa linyang ito. Pindutin ang Shift + Tab ulit o Escape para umalis sa editor.", "admin.dashboard.cleanup_offline_runners": "Linisin ang mga offline na runner", - "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa", + "settings.visibility.description": "Maaapektuhan ng visibility ng profile ang kakayahan ng iba na i-access ang iyong mga hindi pribadong repositoryo. Matuto pa.", "avatar.constraints_hint": "Hindi maaaring lumagpas sa laking %[1]s o mas malaki sa %[2]dx%[3]d pixel ang custom na avatar", "repo.diff.commit.next-short": "Susunod", "repo.diff.commit.previous-short": "Nakaraan", @@ -105,5 +105,10 @@ "og.repo.summary_card.alt_description": "Card ng pangkalahatang ideya ng repositoryong %[1]s, inilalarawan bilang: %[2]s", "mail.actions.run_info_sha": "Commit: %[1]s", "repo.settings.push_mirror.branch_filter.label": "Filter ng branch (opsyonal)", - "repo.settings.push_mirror.branch_filter.description": "Mga branch na imi-mirror. Iwanang walang laman para i-mirror ang lahat ng mga branch. Tignan ang dokumentasyon ng %[2]s para sa syntax. Halimbawa: main, your-reality, release/*" + "repo.settings.push_mirror.branch_filter.description": "Mga branch na imi-mirror. Iwanang walang laman para i-mirror ang lahat ng mga branch. Tignan ang dokumentasyon ng %[2]s para sa syntax. Halimbawa: main, your-reality, release/*", + "discussion.sidebar.reference": "Sangguni", + "admin.moderation.moderation_reports": "Mga ulat sa moderation", + "admin.moderation.no_open_reports": "Kasalukuyang walang mga nakabukas na ulat.", + "admin.moderation.reports": "Mga ulat", + "admin.moderation.deleted_content_ref": "Hindi na umiiral ang inulat na nilalaman na may uri na %[1]v at ID %[2]d" } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index 438e0433d0..4a1a27ac0d 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -102,5 +102,6 @@ "settings.visibility.description": "La visibilité du profil affecte la capacité des autres à accéder à vos dépôts non-privés. Voir plus", "editor.textarea.shift_tab_hint": "Pas d'indentation sur cette ligne. Appuyez sur Maj + Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", "avatar.constraints_hint": "L'avatar personnalisé ne doit pas dépasser une taille de %[1]s ou être plus grand que %[2]dx%[3]d pixels", - "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur." + "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", + "discussion.sidebar.reference": "Référence" } diff --git a/options/locale_next/locale_hi.json b/options/locale_next/locale_hi.json index 0967ef424b..5eec8ae1e2 100644 --- a/options/locale_next/locale_hi.json +++ b/options/locale_next/locale_hi.json @@ -1 +1,81 @@ -{} +{ + "relativetime.2days": "दो दिन पहले", + "relativetime.1day": "कल", + "repo.form.cannot_create": "वो जगह जहाँ पे रिपॉजिटरी रखीं जातीं हैं वहां जगह ख़त्म", + "moderation.abuse_category.other_violations": "प्लेटफार्म का कोई रूल तोडा हैं", + "moderation.report_remarks": "टिप्पणी", + "repo.issue_indexer.title": "इशू ठौर", + "themes.names.forgejo-auto": "फॉरगेजो - सिस्टम थीम फॉलो करें", + "themes.names.forgejo-light": "फॉरगेजो लाइट", + "error.not_found.title": "पृष्ठ नहीं मिला", + "incorrect_root_url": "इस फॉरगेजो इंस्टैंस को %d सर्वर पे कॉन्फ़िगर किया गया है। आप फॉरगेजो को किसी अन्य url से देख रहे हैं, जिससे एप्लीकेशन टूटती है। अच्छा यूआरएल फॉरगेजो के एडमिन कण्ट्रोल करते हैं और रुट_यूआरएल जो की app. ini में है", + "themes.names.forgejo-dark": "फॉरगेजो डार्क", + "stars.list.none": "किसी ने भी चिन्हित/स्टार नहीं किया", + "watch.list.none": "कोई भी ये रिपॉजिटरी नहीं देख रहा", + "followers.incoming.list.self.none": "कोई भी प्रोफाइल फॉलो नहीं कर रहा", + "followers.incoming.list.none": "कोई भी यूजर को फॉलो नहीं कर रहा", + "followers.outgoing.list.self.none": "आप किसी को फॉलो ही नहीं कर रहे", + "followers.outgoing.list.none": "%s किसी को फॉलो नहीं कर रहे", + "relativetime.1week": "पिछले हफ्ते", + "relativetime.2weeks": "दो हफ्ते पहले", + "relativetime.1month": "पिछले महीने", + "relativetime.2months": "दो महीने पहले", + "relativetime.1year": "पिछले साल", + "relativetime.2years": "दो साल पहले", + "alert.asset_load_failed": "एसेट फाइल लोड नहीं हो पायी (path) ये पक्का करें की एसेट फाइल एक्सेस हो सकती है", + "alert.range_error": " एक अंक %d और %d के बीच में", + "search.milestone_kind": "माइलस्टोन ढूंढें…", + "home.welcome.no_activity": "गतिविधि नहीं", + "home.explore_repos": "रिपॉजिटरी निहारें", + "home.explore_users": "उसेर्स देखें", + "home.explore_orgs": "संस्थाएं देखें", + "relativetime.days": { + "one": "%d दिन पहले", + "other": "%d दिनों पहले" + }, + "repo.settings.push_mirror.branch_filter.label": "शाखा फ़िल्टर", + "repo.settings.push_mirror.branch_filter.description": "शाखा मिरर होनीं हैं। खली छोड़ने पर सब मिरर होंगी।", + "install.invalid_lfs_path": "LFS रुट नहीं बना पाया इस path पर", + "relativetime.years": { + "one": "%d साल पहले", + "other": "%d सालों पहले" + }, + "relativetime.weeks": { + "one": "%d हफ्ते पहले", + "other": "%d हफ़्तों पहले" + }, + "relativetime.months": { + "one": "%d महीने पहले", + "other": "%d महीनों पहले" + }, + "meta.last_line": "तीतर के दो पीछे तीतर तीतर के दो आगे तीतर बोलो कितने तीतर ?", + "relativetime.now": "अभी", + "avatar.constraints_hint": "कस्टम अवतार का फ़ाइल आकार 200 किलोबाइट से अधिक नहीं होना चाहिए और 125x125 पिक्सेल से बड़ा नहीं होना चाहिए", + "home.welcome.activity_hint": "आपकी फीड में अभी कुछ भी नहीं है। आपके कार्य और रिपॉजिटरी यहाँ दिखेंगे।", + "relativetime.future": "भविष्य में", + "moderation.report_abuse_form.details": "इस फॉर्म का प्रयोग वो करें जो बताना चाहते हैं स्पैम प्रोफाइल, रिपॉजिटरी, इशू, कमैंट्स या गलत पेशी", + "moderation.report_abuse_form.invalid": "इनवैलिड आर्गुमेंट", + "moderation.report_abuse_form.already_reported": "आप पहले शिकायत कर चुके हैं", + "moderation.abuse_category": "वर्ग", + "moderation.abuse_category.placeholder": "वर्ग चुनें", + "moderation.abuse_category.spam": "स्पैम", + "moderation.abuse_category.malware": "मैलवेयर", + "moderation.abuse_category.illegal_content": "गैर कानूनी कंटेंट", + "relativetime.mins": { + "one": "%d मिनट पहले", + "other": "" + }, + "profile.actions.tooltip": "और एक्शन्स", + "profile.edit.link": "प्रोफाइल बनाएं", + "feed.atom.link": "एटम फीड", + "keys.ssh.link": "SSH कीस", + "keys.gpg.link": "GPG कीस", + "admin.config.moderation_config": "सत्यापन कॉन्फ़िगरेशन", + "moderation.report_abuse": "कंप्लेंट करें", + "moderation.report_content": "कंप्लेंट करें", + "moderation.report_abuse_form.header": "कंप्लेंट करें एडमिनिस्ट्रेटर से", + "moderation.report_remarks.placeholder": "कुछ डिटेल्स बताओ जिस बारे में रिपोर्ट कर रहे हो", + "moderation.submit_report": "रिपोर्ट सबमिट करो", + "moderation.reporting_failed": "नयी रिपोर्ट सबमिट नहीं हो सकती", + "moderation.reported_thank_you": "रिपोर्ट के लिए शुक्रिया। एडमिन को बताया गया है" +} diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 5503763dd3..0bf903f026 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -101,7 +101,7 @@ "editor.textarea.tab_hint": "Rinda jau ir ar atkāpi. Spied Tab vēlreiz vai Escape, lai izietu no redaktora!", "editor.textarea.shift_tab_hint": "Šajā rindā nav atkāpes. Spied Shift + Tab vēlreiz vai Escape, lai izietu no redaktora!", "admin.dashboard.cleanup_offline_runners": "Notīrīt bezsaistes izpildītājus", - "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk", + "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk.", "avatar.constraints_hint": "Pielāgots profila attēls nevar pārsniegt %[1]s vai būt lielāks par %[2]dx%[3]d pikseļiem", "repo.diff.commit.next-short": "Nāk.", "repo.diff.commit.previous-short": "Iepr.", @@ -113,5 +113,10 @@ "og.repo.summary_card.alt_description": "Glabātavas %[1]s kopsavilkuma kartīte, aprakstīta kā: %[2]s", "mail.actions.run_info_sha": "Iesūtījums: %[1]s", "repo.settings.push_mirror.branch_filter.description": "Zarus, kurus spoguļot. Atstāt tukšu, lai spoguļotu visus zarus. Pierakstu skatīt %[2]s dokumentācijā. Piemēri: main, release/*", - "repo.settings.push_mirror.branch_filter.label": "Zaru atlasītājs (izvēles)" + "repo.settings.push_mirror.branch_filter.label": "Zaru atlasītājs (izvēles)", + "discussion.sidebar.reference": "Atsauce", + "admin.moderation.no_open_reports": "Pašlaik nav atvērtu pārskatu.", + "admin.moderation.moderation_reports": "Satura pārraudzības pārskati", + "admin.moderation.reports": "Pārskati", + "admin.moderation.deleted_content_ref": "Saturs, par kuru ziņots, ar veidu %[1]v un Id %[2]d vairs nepastāv" } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 4a796aaee9..f51077c610 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -93,7 +93,7 @@ "editor.textarea.tab_hint": "Rieg al inschuven. Drück weer Tab of Esc, um de Bewarker to verlaten.", "editor.textarea.shift_tab_hint": "Keen Inschuuv in deeser Rieg. Drück weer Umschalt+Tab of Esc, um de Bewarker to verlaten.", "admin.dashboard.cleanup_offline_runners": "Nich verbunnen Lopers uprümen", - "settings.visibility.description": "De Profil-Sichtbaarkeid maakt daar wat an, of un wo anner Lüü diene nich-privaaten Repositoriums ankieken könen. Mehr unnerhören", + "settings.visibility.description": "De Profil-Sichtbaarkeid maakt daar wat an, of un wo anner Lüü diene nich-privaaten Repositoriums ankieken könen. Mehr unnerhören.", "avatar.constraints_hint": "Dat eegene Kontobill düür nich groter as %[1]s wesen of groter as %[2]d×%[3]d Billtüttels wesen", "repo.diff.commit.next-short": "Anner", "repo.diff.commit.previous-short": "Vörig", @@ -105,5 +105,10 @@ "og.repo.summary_card.alt_description": "Tosamenfatens-Kaart vun de Repositorium %[1]s, beschrieven as: %[2]s", "mail.actions.run_info_sha": "Kommitteren: %[1]s", "repo.settings.push_mirror.branch_filter.description": "Twiegen tum Spegeln. Laat dat leeg, um all Twiegen to spegeln. Lees de %[2]s-Dokumenteren för de Syntax. Bispölen: main, release/*", - "repo.settings.push_mirror.branch_filter.label": "Twieg-Filter (wenn du willst)" + "repo.settings.push_mirror.branch_filter.label": "Twieg-Filter (wenn du willst)", + "discussion.sidebar.reference": "Nömen", + "admin.moderation.moderation_reports": "Moderatioons-Berichten", + "admin.moderation.no_open_reports": "Dat gifft jüüst keene open Berichten.", + "admin.moderation.reports": "Berichten", + "admin.moderation.deleted_content_ref": "Mellt Inholl mit Aard %[1]v un Kennteken %[2]d gifft dat nich mehr" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 80395b020b..67a9f79917 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -102,7 +102,7 @@ "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Esc para sair do editor.", "admin.dashboard.cleanup_offline_runners": "Limpar runners desconectados", "avatar.constraints_hint": "Imagem de perfil personalizada não pode exceder %[1]s em tamanho ou ser maior que %[2]dx%[3]d pixels", - "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais", + "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais.", "repo.diff.commit.next-short": "Próximo", "repo.diff.commit.previous-short": "Anterior", "profile.edit.link": "Editar perfil", @@ -113,5 +113,10 @@ "keys.ssh.link": "Chaves SSH", "mail.actions.run_info_sha": "Commit: %[1]s", "repo.settings.push_mirror.branch_filter.label": "Filtro de branches (opcional)", - "repo.settings.push_mirror.branch_filter.description": "Branches para espelhar. Deixe em branco para espelhar todos os branches. Veja %[2]s documentação sobre a sintaxe. Exemplos: main, release/*" + "repo.settings.push_mirror.branch_filter.description": "Branches para espelhar. Deixe em branco para espelhar todos os branches. Veja %[2]s documentação sobre a sintaxe. Exemplos: main, release/*", + "discussion.sidebar.reference": "Referência", + "admin.moderation.deleted_content_ref": "Conteúdo denunciado do tipo %[1]v e ID %[2]d não existe mais", + "admin.moderation.moderation_reports": "Denúncias para moderação", + "admin.moderation.reports": "Denúncias", + "admin.moderation.no_open_reports": "Não há denúncias abertas atualmente." } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 4710d3d868..c3b908ed07 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -113,5 +113,6 @@ "og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s", "repo.settings.push_mirror.branch_filter.label": "Filtro de ramos (opcional)", "repo.settings.push_mirror.branch_filter.description": "Ramos a serem espelhados. Deixe em branco para espelhar todos os ramos. Veja a %[2]s documentação sobre a sintaxe. Exemplos: main, release/*", - "mail.actions.run_info_sha": "Cometimento: %[1]s" + "mail.actions.run_info_sha": "Cometimento: %[1]s", + "discussion.sidebar.reference": "Referência" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index ecd58b8b62..cc6d99ca80 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -113,5 +113,6 @@ "og.repo.summary_card.alt_description": "Карточка со сводкой о репозитории %s. Описание: %[2]s", "mail.actions.run_info_sha": "Коммит: %[1]s", "repo.settings.push_mirror.branch_filter.description": "Синхронизируемые ветви. Оставьте пустым, чтобы синхронизировать все. Ознакомьтесь с синтаксисом в документации %[2]s. Примеры: main, release/*", - "repo.settings.push_mirror.branch_filter.label": "Выбор ветвей (опционально)" + "repo.settings.push_mirror.branch_filter.label": "Выбор ветвей (опционально)", + "discussion.sidebar.reference": "Ссылка" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index f014a8dc66..046738e334 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -101,7 +101,7 @@ "editor.textarea.tab_hint": "У рядку вже є відступ. Натисніть Tab ще раз або Esc, щоб вийти з редактора.", "editor.textarea.shift_tab_hint": "У цьому рядку немає відступів. Натисніть Shift + Tab ще раз або Esc, щоб вийти з редактора.", "admin.dashboard.cleanup_offline_runners": "Очистити неактивні раннери", - "settings.visibility.description": "Видимість профілю впливає на можливість інших користувачів отримати доступ до ваших неприватних репозиторіїв. Дізнатися більше", + "settings.visibility.description": "Видимість профілю впливає на можливість інших користувачів отримати доступ до ваших неприватних репозиторіїв. Дізнатися більше.", "avatar.constraints_hint": "Розмір користувацького аватара не може перевищувати %[1]s або бути більшим за %[2]d×%[3]d пікселів", "repo.diff.commit.next-short": "Наступний", "repo.diff.commit.previous-short": "Попередній", @@ -113,5 +113,9 @@ "og.repo.summary_card.alt_description": "Підсумкова картка репозиторію %[1]s з описом: %[2]s", "mail.actions.run_info_sha": "Коміт: %[1]s", "repo.settings.push_mirror.branch_filter.description": "Гілки для дзеркалювання. Залиште порожнім, щоб віддзеркалити всі гілки. Дивіться синтаксис у документації %[2]s. Приклади: main, release/*", - "repo.settings.push_mirror.branch_filter.label": "Фільтр гілок (необов'язково)" + "repo.settings.push_mirror.branch_filter.label": "Фільтр гілок (необов'язково)", + "discussion.sidebar.reference": "Посилання", + "admin.moderation.no_open_reports": "Відкритих скарг наразі немає.", + "admin.moderation.reports": "Скарги", + "admin.moderation.deleted_content_ref": "Вміст типу %[1]v з ідентифікатором %[2]d, на який подано скаргу, більше не існує" } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 467c990d3a..a36a4c3ea4 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -81,5 +81,6 @@ "og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s", "repo.settings.push_mirror.branch_filter.label": "分支过滤器(可选)", "repo.settings.push_mirror.branch_filter.description": "欲镜像的分支。留空以镜像所有分支。关于语法的更多信息,请参见 %[2]s 文档。例如:main, release/*", - "mail.actions.run_info_sha": "提交:%[1]s" + "mail.actions.run_info_sha": "提交:%[1]s", + "discussion.sidebar.reference": "引用" } From e271c24100c8974e135ebc1d1b3eaa6cd2062b10 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 25 Jul 2025 18:01:43 +0200 Subject: [PATCH 184/297] fix: assorted ActivityPub code only refactors (#8274) Fix parts of issue https://codeberg.org/forgejo/forgejo/issues/8221 and PR https://codeberg.org/forgejo/forgejo/pulls/4767 - PostgreSQL - TestActivityPubPerson/SignedRequestValidation ``` --- FAIL: TestActivityPubPerson/SignedRequestValidation (5.01s) api_activitypub_person_test.go:51: Error Trace: /workspace/forgejo/forgejo/tests/integration/api_activitypub_person_test.go:51 Error: Received unexpected error: Get "http://127.0.0.1:3002/api/v1/activitypub/user-id/2": context deadline exceeded (Client.Timeout exceeded while awaiting headers) Test: TestActivityPubPerson/SignedRequestValidation testlogger.go:411: 2025/06/24 00:12:27 ...eb/routing/logger.go:102:func1() [I] router: completed GET /api/v1/activitypub/user-id/2 for 127.0.0.1:50456, 200 OK in 5032.2ms @ activitypub/person.go:21(activitypub.Person) ``` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8274 Reviewed-by: Earl Warren Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- .deadcode-out | 3 + models/repo/repo_repository.go | 6 +- models/user/follow.go | 16 +- models/user/user_repository.go | 16 +- modules/activitypub/client.go | 7 +- routers/api/v1/activitypub/repository.go | 9 +- routers/api/v1/activitypub/reqsignature.go | 26 +-- routers/api/v1/activitypub/response.go | 34 ++++ routers/api/v1/api.go | 3 +- services/federation/error.go | 44 +++++ services/federation/federation_service.go | 19 +- ...{repo_like.go => repository_inbox_like.go} | 33 ++-- services/federation/repository_service.go | 19 ++ services/federation/result.go | 35 ++++ services/federation/signature_service.go | 11 +- .../integration/api_activitypub_actor_test.go | 77 ++++++++ .../api_activitypub_person_test.go | 113 +++++++++++ .../api_activitypub_repository_test.go | 176 ++++++++++++++++++ .../api_federation_httpsig_test.go | 82 ++++++++ .../integration/repo_star_federation_test.go | 81 ++++++++ 20 files changed, 741 insertions(+), 69 deletions(-) create mode 100644 services/federation/error.go rename services/federation/{repo_like.go => repository_inbox_like.go} (73%) create mode 100644 services/federation/repository_service.go create mode 100644 services/federation/result.go create mode 100644 tests/integration/api_activitypub_actor_test.go create mode 100644 tests/integration/api_activitypub_person_test.go create mode 100644 tests/integration/api_activitypub_repository_test.go create mode 100644 tests/integration/api_federation_httpsig_test.go create mode 100644 tests/integration/repo_star_federation_test.go diff --git a/.deadcode-out b/.deadcode-out index f9bee43043..31b04687dc 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -246,7 +246,10 @@ forgejo.org/services/context GetPrivateContext forgejo.org/services/federation + NewErrInternalf + ErrInternal.Error Init + NewServiceResultWithBytes forgejo.org/services/repository IsErrForkAlreadyExist diff --git a/models/repo/repo_repository.go b/models/repo/repo_repository.go index 0ba50e6614..9d586b8345 100644 --- a/models/repo/repo_repository.go +++ b/models/repo/repo_repository.go @@ -38,18 +38,18 @@ func StoreFollowingRepos(ctx context.Context, localRepoID int64, followingRepoLi } // Begin transaction - ctx, committer, err := db.TxContext((ctx)) + dbCtx, committer, err := db.TxContext((ctx)) if err != nil { return err } defer committer.Close() - _, err = db.GetEngine(ctx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) + _, err = db.GetEngine(dbCtx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) if err != nil { return err } for _, followingRepo := range followingRepoList { - _, err = db.GetEngine(ctx).Insert(followingRepo) + _, err = db.GetEngine(dbCtx).Insert(followingRepo) if err != nil { return err } diff --git a/models/user/follow.go b/models/user/follow.go index e32c226385..8663b2a943 100644 --- a/models/user/follow.go +++ b/models/user/follow.go @@ -39,21 +39,21 @@ func FollowUser(ctx context.Context, userID, followID int64) (err error) { return ErrBlockedByUser } - ctx, committer, err := db.TxContext(ctx) + dbCtx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if err = db.Insert(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if err = db.Insert(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() @@ -65,21 +65,21 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) { return nil } - ctx, committer, err := db.TxContext(ctx) + dbCtx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if _, err = db.DeleteByBean(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() diff --git a/models/user/user_repository.go b/models/user/user_repository.go index 85f44f1598..df864746e8 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -28,7 +28,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } // Begin transaction - ctx, committer, err := db.TxContext((ctx)) + txCtx, committer, err := db.TxContext(ctx) if err != nil { return err } @@ -39,7 +39,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } }() - if err := CreateUser(ctx, user, &overwrite); err != nil { + if err := CreateUser(txCtx, user, &overwrite); err != nil { return err } @@ -48,7 +48,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return err } - _, err = db.GetEngine(ctx).Insert(federatedUser) + _, err = db.GetEngine(txCtx).Insert(federatedUser) if err != nil { return err } @@ -70,7 +70,7 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -87,7 +87,7 @@ func GetFederatedUser(ctx context.Context, externalID string, federationHostID i if err != nil { return nil, nil, err } else if federatedUser == nil { - return nil, nil, fmt.Errorf("FederatedUser for externalId = %v and federationHostId = %v does not exist", externalID, federationHostID) + return nil, nil, fmt.Errorf("FederatedUser not found (given externalId: %v, federationHostId: %v)", externalID, federationHostID) } return user, federatedUser, nil } @@ -99,13 +99,13 @@ func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("Federated user %v does not exist", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table does not contain entry for user ID: %v", federatedUser.UserID) } has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -130,7 +130,7 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index fb6fa8b543..11a2fd94c3 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -66,6 +66,11 @@ type ClientFactory struct { // NewClient function func NewClientFactory() (c *ClientFactory, err error) { + return NewClientFactoryWithTimeout(5 * time.Second) +} + +// NewClient function +func NewClientFactoryWithTimeout(timeout time.Duration) (c *ClientFactory, err error) { if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil { return nil, err } else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil { @@ -77,7 +82,7 @@ func NewClientFactory() (c *ClientFactory, err error) { Transport: &http.Transport{ Proxy: proxy.Proxy(), }, - Timeout: 5 * time.Second, + Timeout: timeout, }, algs: setting.HttpsigAlgs, digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm), diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go index c506840f1c..3eaa6b82c5 100644 --- a/routers/api/v1/activitypub/repository.go +++ b/routers/api/v1/activitypub/repository.go @@ -71,10 +71,11 @@ func RepositoryInbox(ctx *context.APIContext) { repository := ctx.Repo.Repository log.Info("RepositoryInbox: repo: %v", repository) form := web.GetForm(ctx) - // TODO: Decide between like/undo{like} activity - httpStatus, title, err := federation.ProcessLikeActivity(ctx, form, repository.ID) + activity := form.(*ap.Activity) + result, err := federation.ProcessRepositoryInbox(ctx, activity, repository.ID) if err != nil { - ctx.Error(httpStatus, title, err) + ctx.Error(federation.HTTPStatus(err), "Processing Repository Inbox failed", result) + return } - ctx.Status(http.StatusNoContent) + responseServiceResult(ctx, result) } diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 91274249ec..38cb067b89 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -8,13 +8,13 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" - gitea_context "forgejo.org/services/context" + services_context "forgejo.org/services/context" "forgejo.org/services/federation" "github.com/42wim/httpsig" ) -func verifyHTTPUserOrInstanceSignature(ctx *gitea_context.APIContext) (authenticated bool, err error) { +func verifyHTTPUserOrInstanceSignature(ctx services_context.APIContext) (authenticated bool, err error) { if !setting.Federation.SignatureEnforced { return true, nil } @@ -28,9 +28,9 @@ func verifyHTTPUserOrInstanceSignature(ctx *gitea_context.APIContext) (authentic } signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) - pubKey, err := federation.FindOrCreateFederatedUserKey(ctx.Base, v.KeyId()) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) if err != nil || pubKey == nil { - pubKey, err = federation.FindOrCreateFederationHostKey(ctx.Base, v.KeyId()) + pubKey, err = federation.FindOrCreateFederationHostKey(ctx, v.KeyId()) if err != nil { return false, err } @@ -43,7 +43,7 @@ func verifyHTTPUserOrInstanceSignature(ctx *gitea_context.APIContext) (authentic return true, nil } -func verifyHTTPUserSignature(ctx *gitea_context.APIContext) (authenticated bool, err error) { +func verifyHTTPUserSignature(ctx services_context.APIContext) (authenticated bool, err error) { if !setting.Federation.SignatureEnforced { return true, nil } @@ -57,7 +57,7 @@ func verifyHTTPUserSignature(ctx *gitea_context.APIContext) (authenticated bool, } signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) - pubKey, err := federation.FindOrCreateFederatedUserKey(ctx.Base, v.KeyId()) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) if err != nil { return false, err } @@ -70,9 +70,9 @@ func verifyHTTPUserSignature(ctx *gitea_context.APIContext) (authenticated bool, } // ReqHTTPSignature function -func ReqHTTPUserOrInstanceSignature() func(ctx *gitea_context.APIContext) { - return func(ctx *gitea_context.APIContext) { - if authenticated, err := verifyHTTPUserOrInstanceSignature(ctx); err != nil { +func ReqHTTPUserOrInstanceSignature() func(ctx *services_context.APIContext) { + return func(ctx *services_context.APIContext) { + if authenticated, err := verifyHTTPUserOrInstanceSignature(*ctx); err != nil { log.Warn("verifyHttpSignatures failed: %v", err) ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { @@ -81,10 +81,10 @@ func ReqHTTPUserOrInstanceSignature() func(ctx *gitea_context.APIContext) { } } -// ReqHTTPSignature function -func ReqHTTPUserSignature() func(ctx *gitea_context.APIContext) { - return func(ctx *gitea_context.APIContext) { - if authenticated, err := verifyHTTPUserSignature(ctx); err != nil { +// ReqHTTPUserSignature function +func ReqHTTPUserSignature() func(ctx *services_context.APIContext) { + return func(ctx *services_context.APIContext) { + if authenticated, err := verifyHTTPUserSignature(*ctx); err != nil { log.Warn("verifyHttpSignatures failed: %v", err) ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { diff --git a/routers/api/v1/activitypub/response.go b/routers/api/v1/activitypub/response.go index a97f363cc2..64413cebb1 100644 --- a/routers/api/v1/activitypub/response.go +++ b/routers/api/v1/activitypub/response.go @@ -10,12 +10,46 @@ import ( "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/services/context" + "forgejo.org/services/federation" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" ) // Respond with an ActivityStreams object +func responseServiceResult(ctx *context.APIContext, result federation.ServiceResult) { + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + + switch { + case result.StatusOnly(): + ctx.Resp.WriteHeader(result.HTTPStatus) + return + case result.WithBytes(): + ctx.Resp.WriteHeader(result.HTTPStatus) + if _, err := ctx.Resp.Write(result.Bytes); err != nil { + log.Error("Error writing a response: %v", err) + ctx.Error(http.StatusInternalServerError, "Error writing a response", err) + return + } + case result.WithActivity(): + binary, err := jsonld.WithContext( + jsonld.IRI(ap.ActivityBaseURI), + jsonld.IRI(ap.SecurityContextURI), + jsonld.IRI(forgefed.ForgeFedNamespaceURI), + ).Marshal(result.Activity) + if err != nil { + ctx.ServerError("Marshal", err) + return + } + ctx.Resp.WriteHeader(result.HTTPStatus) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } + } +} + +// Respond with an ActivityStreams object +// Deprecated func response(ctx *context.APIContext, v any) { binary, err := jsonld.WithContext( jsonld.IRI(ap.ActivityBaseURI), diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 6a51f33bd8..2f806ba35d 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -81,7 +81,6 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" user_model "forgejo.org/models/user" - "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" @@ -853,7 +852,7 @@ func Routes() *web.Route { m.Group("/repository-id/{repository-id}", func() { m.Get("", activitypub.ReqHTTPUserSignature(), activitypub.Repository) m.Post("/inbox", - bind(forgefed.ForgeLike{}), + bind(ap.Activity{}), activitypub.ReqHTTPUserSignature(), activitypub.RepositoryInbox) }, context.RepositoryIDAssignmentAPI()) diff --git a/services/federation/error.go b/services/federation/error.go new file mode 100644 index 0000000000..425035d0d5 --- /dev/null +++ b/services/federation/error.go @@ -0,0 +1,44 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "fmt" + "net/http" +) + +type ErrNotAcceptable struct { + Message string +} + +func NewErrNotAcceptablef(format string, a ...any) ErrNotAcceptable { + message := fmt.Sprintf(format, a...) + return ErrNotAcceptable{Message: message} +} + +func (err ErrNotAcceptable) Error() string { + return fmt.Sprintf("NotAcceptable: %v", err.Message) +} + +type ErrInternal struct { + Message string +} + +func NewErrInternalf(format string, a ...any) ErrInternal { + message := fmt.Sprintf(format, a...) + return ErrInternal{Message: message} +} + +func (err ErrInternal) Error() string { + return fmt.Sprintf("InternalServerError: %v", err.Message) +} + +func HTTPStatus(err error) int { + switch err.(type) { + case ErrNotAcceptable: + return http.StatusNotAcceptable + default: + return http.StatusInternalServerError + } +} diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index b71d8d2575..36788e725a 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -18,7 +18,6 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/modules/validation" - context_service "forgejo.org/services/context" "github.com/google/uuid" ) @@ -27,7 +26,7 @@ func Init() error { return nil } -func FindOrCreateFederationHost(ctx *context_service.Base, actorURI string) (*forgefed.FederationHost, error) { +func FindOrCreateFederationHost(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) { rawActorID, err := fm.NewActorID(actorURI) if err != nil { return nil, err @@ -46,7 +45,7 @@ func FindOrCreateFederationHost(ctx *context_service.Base, actorURI string) (*fo return federationHost, nil } -func FindOrCreateFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { +func FindOrCreateFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { user, federatedUser, federationHost, err := findFederatedUser(ctx, actorURI) if err != nil { return nil, nil, nil, err @@ -57,20 +56,21 @@ func FindOrCreateFederatedUser(ctx *context_service.Base, actorURI string) (*use } if user != nil { - log.Trace("Found local federatedUser: %#v", user) + log.Trace("Local ActivityPub user found (actorURI: %#v, user: %#v)", actorURI, user) } else { + log.Trace("Attempting to create new user and federatedUser for actorURI: %#v", actorURI) user, federatedUser, err = createUserFromAP(ctx, personID, federationHost.ID) if err != nil { return nil, nil, nil, err } - log.Trace("Created federatedUser from ap: %#v", user) + log.Trace("Created user %#v with federatedUser %#v from distant server", user, federatedUser) } log.Trace("Got user: %v", user.Name) return user, federatedUser, federationHost, nil } -func findFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { +func findFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { federationHost, err := FindOrCreateFederationHost(ctx, actorURI) if err != nil { return nil, nil, nil, err @@ -90,6 +90,7 @@ func findFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, func createFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) { actionsUser := user.NewAPServerActor() + clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, err @@ -161,7 +162,7 @@ func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID return nil, nil, err } - log.Info("Fetched valid person:%q", person) + log.Info("Fetched valid person from distant server: %q", person) localFqdn, err := url.ParseRequestURI(setting.AppURL) if err != nil { @@ -220,7 +221,7 @@ func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID }, } - log.Info("Fetch federatedUser:%q", federatedUser) + log.Info("Fetched person's %q federatedUser from distant server: %q", person, federatedUser) return &newUser, &federatedUser, nil } @@ -234,6 +235,6 @@ func createUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI return nil, nil, err } - log.Info("Created federatedUser:%q", federatedUser) + log.Info("Created federatedUser: %q", federatedUser) return newUser, federatedUser, nil } diff --git a/services/federation/repo_like.go b/services/federation/repository_inbox_like.go similarity index 73% rename from services/federation/repo_like.go rename to services/federation/repository_inbox_like.go index c1e6500c61..478a12d92c 100644 --- a/services/federation/repo_like.go +++ b/services/federation/repository_inbox_like.go @@ -5,7 +5,6 @@ package federation import ( "context" - "errors" "fmt" "net/http" "time" @@ -18,6 +17,8 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/validation" context_service "forgejo.org/services/context" + + ap "github.com/go-ap/activitypub" ) // ProcessLikeActivity receives a ForgeLike activity and does the following: @@ -27,32 +28,32 @@ import ( // Validation of incoming RepositoryID against Local RepositoryID // Star the repo if it wasn't already stared // Do some mitigation against out of order attacks -func ProcessLikeActivity(ctx *context_service.APIContext, form any, repositoryID int64) (int, string, error) { - activity := form.(*fm.ForgeLike) - if res, err := validation.IsValid(activity); !res { - return http.StatusNotAcceptable, "Invalid activity", err +func ProcessLikeActivity(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { + constructorLikeActivity, _ := fm.NewForgeLike(activity.Actor.GetLink().String(), activity.Object.GetLink().String(), activity.StartTime) + if res, err := validation.IsValid(constructorLikeActivity); !res { + return ServiceResult{}, NewErrNotAcceptablef("Invalid activity: %v", err) } log.Trace("Activity validated: %#v", activity) // parse actorID (person) actorURI := activity.Actor.GetID().String() - user, _, federationHost, err := FindOrCreateFederatedUser(ctx.Base, actorURI) + user, _, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI) if err != nil { - ctx.Error(http.StatusNotAcceptable, "Federated user not found", err) - return http.StatusInternalServerError, "FindOrCreateFederatedUser", err + log.Error("Federated user not found (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("FindOrCreateFederatedUser failed: %v", err) } - if !activity.IsNewer(federationHost.LatestActivity) { - return http.StatusNotAcceptable, "Activity out of order.", errors.New("Activity already processed") + if !constructorLikeActivity.IsNewer(federationHost.LatestActivity) { + return ServiceResult{}, NewErrNotAcceptablef("LatestActivity: activity already processed: %v", err) } // parse objectID (repository) - objectID, err := fm.NewRepositoryID(activity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) + objectID, err := fm.NewRepositoryID(constructorLikeActivity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) if err != nil { - return http.StatusNotAcceptable, "Invalid objectId", err + return ServiceResult{}, NewErrNotAcceptablef("Parsing repo objectID failed: %v", err) } if objectID.ID != fmt.Sprint(repositoryID) { - return http.StatusNotAcceptable, "Invalid objectId", err + return ServiceResult{}, NewErrNotAcceptablef("Invalid repoId: %v", err) } log.Trace("Object accepted: %#v", objectID) @@ -61,16 +62,16 @@ func ProcessLikeActivity(ctx *context_service.APIContext, form any, repositoryID if !alreadyStared { err = repo.StarRepo(ctx, user.ID, repositoryID, true) if err != nil { - return http.StatusNotAcceptable, "Error staring", err + return ServiceResult{}, NewErrNotAcceptablef("Staring failed: %v", err) } } federationHost.LatestActivity = activity.StartTime err = forgefed.UpdateFederationHost(ctx, federationHost) if err != nil { - return http.StatusNotAcceptable, "Error updating federatedHost", err + return ServiceResult{}, NewErrNotAcceptablef("Updating federatedHost failed: %v", err) } - return 0, "", nil + return NewServiceResultStatusOnly(http.StatusNoContent), nil } // Create or update a list of FollowingRepo structs diff --git a/services/federation/repository_service.go b/services/federation/repository_service.go new file mode 100644 index 0000000000..7891d786e2 --- /dev/null +++ b/services/federation/repository_service.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + + ap "github.com/go-ap/activitypub" +) + +func ProcessRepositoryInbox(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { + switch activity.Type { + case ap.LikeType: + return ProcessLikeActivity(ctx, activity, repositoryID) + default: + return ServiceResult{}, NewErrNotAcceptablef("Not a like activity: %v", activity.Type) + } +} diff --git a/services/federation/result.go b/services/federation/result.go new file mode 100644 index 0000000000..47afb2bdf6 --- /dev/null +++ b/services/federation/result.go @@ -0,0 +1,35 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import "github.com/go-ap/activitypub" + +type ServiceResult struct { + HTTPStatus int + Bytes []byte + Activity activitypub.Activity + withBytes bool + withActivity bool + statusOnly bool +} + +func NewServiceResultStatusOnly(status int) ServiceResult { + return ServiceResult{HTTPStatus: status, statusOnly: true} +} + +func NewServiceResultWithBytes(status int, bytes []byte) ServiceResult { + return ServiceResult{HTTPStatus: status, Bytes: bytes, withBytes: true} +} + +func (serviceResult ServiceResult) WithBytes() bool { + return serviceResult.withBytes +} + +func (serviceResult ServiceResult) WithActivity() bool { + return serviceResult.withActivity +} + +func (serviceResult ServiceResult) StatusOnly() bool { + return serviceResult.statusOnly +} diff --git a/services/federation/signature_service.go b/services/federation/signature_service.go index e5102b89d8..fd8cbb39cd 100644 --- a/services/federation/signature_service.go +++ b/services/federation/signature_service.go @@ -4,6 +4,7 @@ package federation import ( + "context" "crypto/x509" "database/sql" "encoding/pem" @@ -15,13 +16,12 @@ import ( "forgejo.org/models/user" "forgejo.org/modules/activitypub" fm "forgejo.org/modules/forgefed" - context_service "forgejo.org/services/context" ap "github.com/go-ap/activitypub" ) // Factory function for ActorID. Created struct is asserted to be valid -func NewActorIDFromKeyID(ctx *context_service.Base, uri string) (fm.ActorID, error) { +func NewActorIDFromKeyID(ctx context.Context, uri string) (fm.ActorID, error) { parsedURI, err := url.Parse(uri) parsedURI.Fragment = "" if err != nil { @@ -54,7 +54,7 @@ func NewActorIDFromKeyID(ctx *context_service.Base, uri string) (fm.ActorID, err return result, err } -func FindOrCreateFederatedUserKey(ctx *context_service.Base, keyID string) (pubKey any, err error) { +func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any, err error) { var federatedUser *user.FederatedUser var keyURL *url.URL @@ -122,7 +122,7 @@ func FindOrCreateFederatedUserKey(ctx *context_service.Base, keyID string) (pubK return nil, nil } -func FindOrCreateFederationHostKey(ctx *context_service.Base, keyID string) (pubKey any, err error) { +func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey any, err error) { keyURL, err := url.Parse(keyID) if err != nil { return nil, err @@ -183,8 +183,9 @@ func FindOrCreateFederationHostKey(ctx *context_service.Base, keyID string) (pub return nil, nil } -func fetchKeyFromAp(ctx *context_service.Base, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) { +func fetchKeyFromAp(ctx context.Context, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) { actionsUser := user.NewAPServerActor() + clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, nil, nil, err diff --git a/tests/integration/api_activitypub_actor_test.go b/tests/integration/api_activitypub_actor_test.go new file mode 100644 index 0000000000..42232bd640 --- /dev/null +++ b/tests/integration/api_activitypub_actor_test.go @@ -0,0 +1,77 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + "testing" + + "forgejo.org/modules/forgefed" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + "forgejo.org/services/federation" + "forgejo.org/tests" + + ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubActor(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + defer tests.PrepareTestEnv(t)() + + req := NewRequest(t, "GET", "/api/v1/activitypub/actor") + resp := MakeRequest(t, req, http.StatusOK) + assert.Contains(t, resp.Body.String(), "@context") + + var actor ap.Actor + err := actor.UnmarshalJSON(resp.Body.Bytes()) + require.NoError(t, err) + + assert.Equal(t, ap.ApplicationType, actor.Type) + assert.Equal(t, "ghost", actor.PreferredUsername.String()) + keyID := actor.GetID().String() + assert.Regexp(t, "activitypub/actor$", keyID) + assert.Regexp(t, "activitypub/actor/inbox$", actor.Inbox.GetID().String()) + + pubKey := actor.PublicKey + assert.NotNil(t, pubKey) + publicKeyID := keyID + "#main-key" + assert.Equal(t, pubKey.ID.String(), publicKeyID) + + pubKeyPem := pubKey.PublicKeyPem + assert.NotNil(t, pubKeyPem) + assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem) +} + +func TestActorNewFromKeyId(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + ctx, _ := contexttest.MockAPIContext(t, "/api/v1/activitypub/actor") + sut, err := federation.NewActorIDFromKeyID(ctx.Base, fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", u)) + require.NoError(t, err) + + port, err := strconv.ParseUint(u.Port(), 10, 16) + require.NoError(t, err) + + assert.Equal(t, forgefed.ActorID{ + ID: "actor", + HostSchema: "http", + Path: "api/v1/activitypub", + Host: setting.Domain, + HostPort: uint16(port), + UnvalidatedInput: fmt.Sprintf("http://%s:%d/api/v1/activitypub/actor", setting.Domain, port), + IsPortSupplemented: false, + }, sut) + }) +} diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go new file mode 100644 index 0000000000..277b150a1e --- /dev/null +++ b/tests/integration/api_activitypub_person_test.go @@ -0,0 +1,113 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + "forgejo.org/tests" + + ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubPerson(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, localUrl *url.URL) { + defer test.MockVariableValue(&setting.AppURL, localUrl.String())() + + localUserID := 2 + localUserName := "user2" + localUserURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", localUrl, localUserID) + + // distantURL := federatedSrv.URL + // distantUser15URL := fmt.Sprintf("%s/api/v1/activitypub/user-id/15", distantURL) + + // Unsigned request + t.Run("UnsignedRequest", func(t *testing.T) { + req := NewRequest(t, "GET", localUserURL) + MakeRequest(t, req, http.StatusBadRequest) + }) + + // Signed request + t.Run("SignedRequestValidation", func(t *testing.T) { + ctx, _ := contexttest.MockAPIContext(t, localUserURL) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, + mock.Persons[0].KeyID(federatedSrv.URL)) + require.NoError(t, err) + + resp, err := c.GetBody(localUserURL) + require.NoError(t, err) + + var person ap.Person + err = person.UnmarshalJSON(resp) + require.NoError(t, err) + + assert.Equal(t, ap.PersonType, person.Type) + assert.Equal(t, localUserName, person.PreferredUsername.String()) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d$", localUserID), person.GetID()) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/inbox$", localUserID), person.Inbox.GetID().String()) + + assert.NotNil(t, person.PublicKey) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d#main-key$", localUserID), person.PublicKey.ID) + + assert.NotNil(t, person.PublicKey.PublicKeyPem) + assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", person.PublicKey.PublicKeyPem) + }) + }) +} + +func TestActivityPubMissingPerson(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + req := NewRequest(t, "GET", "/api/v1/activitypub/user-id/999999999") + resp := MakeRequest(t, req, http.StatusNotFound) + assert.Contains(t, resp.Body.String(), "user does not exist") +} + +func TestActivityPubPersonInbox(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + defer test.MockVariableValue(&setting.AppURL, u.String())() + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + + user1url := u.JoinPath("/api/v1/activitypub/user-id/1").String() + "#main-key" + user2inboxurl := u.JoinPath("/api/v1/activitypub/user-id/2/inbox").String() + ctx, _ := contexttest.MockAPIContext(t, user2inboxurl) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + c, err := cf.WithKeys(ctx, user1, user1url) + require.NoError(t, err) + + // Signed request "succeeds" + resp, err := c.Post([]byte{}, user2inboxurl) + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + }) +} diff --git a/tests/integration/api_activitypub_repository_test.go b/tests/integration/api_activitypub_repository_test.go new file mode 100644 index 0000000000..b4be0407b9 --- /dev/null +++ b/tests/integration/api_activitypub_repository_test.go @@ -0,0 +1,176 @@ +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "forgejo.org/models/forgefed" + "forgejo.org/models/unittest" + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + forgefed_modules "forgejo.org/modules/forgefed" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubRepository(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + repositoryID := 2 + + localRepository := fmt.Sprintf("%sapi/v1/activitypub/repository-id/%d", u, repositoryID) + + ctx, _ := contexttest.MockAPIContext(t, localRepository) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, + mock.Persons[0].KeyID(federatedSrv.URL)) + require.NoError(t, err) + + resp, err := c.GetBody(localRepository) + require.NoError(t, err) + assert.Contains(t, string(resp), "@context") + + var repository forgefed_modules.Repository + err = repository.UnmarshalJSON(resp) + require.NoError(t, err) + + assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%d$", repositoryID), repository.GetID().String()) + }) +} + +func TestActivityPubMissingRepository(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + repositoryID := 9999999 + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)) + resp := MakeRequest(t, req, http.StatusNotFound) + assert.Contains(t, resp.Body.String(), "repository does not exist") +} + +func TestActivityPubRepositoryInboxValid(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + repositoryID := 2 + timeNow := time.Now().UTC() + localRepoInbox := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() + + ctx, _ := contexttest.MockAPIContext(t, localRepoInbox) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, + mock.Persons[0].KeyID(federatedSrv.URL)) + require.NoError(t, err) + + activity1 := []byte(fmt.Sprintf( + `{"type":"Like",`+ + `"startTime":"%s",`+ + `"actor":"%s/api/v1/activitypub/user-id/15",`+ + `"object":"%s"}`, + timeNow.Format(time.RFC3339), + federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) + t.Logf("activity: %s", activity1) + resp, err := c.Post(activity1, localRepoInbox) + + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) + federatedUser := unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "15", FederationHostID: federationHost.ID}) + unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) + + // A like activity by a different user of the same federated host. + activity2 := []byte(fmt.Sprintf( + `{"type":"Like",`+ + `"startTime":"%s",`+ + `"actor":"%s/api/v1/activitypub/user-id/30",`+ + `"object":"%s"}`, + // Make sure this activity happens later then the one before + timeNow.Add(time.Second).Format(time.RFC3339), + federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) + t.Logf("activity: %s", activity2) + resp, err = c.Post(activity2, localRepoInbox) + + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) + unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) + + // The same user sends another like activity + otherRepositoryID := 3 + otherRepoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", otherRepositoryID)).String() + activity3 := []byte(fmt.Sprintf( + `{"type":"Like",`+ + `"startTime":"%s",`+ + `"actor":"%s/api/v1/activitypub/user-id/30",`+ + `"object":"%s"}`, + // Make sure this activity happens later then the ones before + timeNow.Add(time.Second*2).Format(time.RFC3339), + federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", otherRepositoryID)).String())) + t.Logf("activity: %s", activity3) + resp, err = c.Post(activity3, otherRepoInboxURL) + + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) + unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) + + // Replay activity2. + resp, err = c.Post(activity2, localRepoInbox) + require.NoError(t, err) + assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) + }) +} + +func TestActivityPubRepositoryInboxInvalid(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + apServerActor := user.NewAPServerActor() + repositoryID := 2 + localRepo2Inbox := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() + + ctx, _ := contexttest.MockAPIContext(t, localRepo2Inbox) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeys(ctx, apServerActor, apServerActor.KeyID()) + require.NoError(t, err) + + activity := []byte(`{"type":"Wrong"}`) + resp, err := c.Post(activity, localRepo2Inbox) + require.NoError(t, err) + assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) + }) +} diff --git a/tests/integration/api_federation_httpsig_test.go b/tests/integration/api_federation_httpsig_test.go new file mode 100644 index 0000000000..a8deaa315f --- /dev/null +++ b/tests/integration/api_federation_httpsig_test.go @@ -0,0 +1,82 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + "forgejo.org/models/db" + "forgejo.org/models/forgefed" + "forgejo.org/models/unittest" + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFederationHttpSigValidation(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + userID := 2 + userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID) + + user1 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 1}) + + clientFactory, err := activitypub.GetClientFactory(db.DefaultContext) + require.NoError(t, err) + + apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.KeyID()) + require.NoError(t, err) + + // Unsigned request + t.Run("UnsignedRequest", func(t *testing.T) { + req := NewRequest(t, "GET", userURL) + MakeRequest(t, req, http.StatusBadRequest) + }) + + // Signed request + t.Run("SignedRequest", func(t *testing.T) { + resp, err := apClient.Get(userURL) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + + // HACK HACK HACK: the host part of the URL gets set to which IP forgejo is + // listening on, NOT localhost, which is the Domain given to forgejo which + // is then used for eg. the keyID all requests + applicationKeyID := fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", setting.AppURL) + actorKeyID := fmt.Sprintf("%sapi/v1/activitypub/user-id/1#main-key", setting.AppURL) + + // Check for cached public keys + t.Run("ValidateCaches", func(t *testing.T) { + host, err := forgefed.FindFederationHostByKeyID(db.DefaultContext, applicationKeyID) + require.NoError(t, err) + assert.NotNil(t, host) + assert.True(t, host.PublicKey.Valid) + + _, user, err := user.FindFederatedUserByKeyID(db.DefaultContext, actorKeyID) + require.NoError(t, err) + assert.NotNil(t, user) + assert.True(t, user.PublicKey.Valid) + }) + + // Disable signature validation + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + + // Unsigned request + t.Run("SignatureValidationDisabled", func(t *testing.T) { + req := NewRequest(t, "GET", userURL) + MakeRequest(t, req, http.StatusOK) + }) + }) +} diff --git a/tests/integration/repo_star_federation_test.go b/tests/integration/repo_star_federation_test.go new file mode 100644 index 0000000000..ae9a4e9f83 --- /dev/null +++ b/tests/integration/repo_star_federation_test.go @@ -0,0 +1,81 @@ +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "forgejo.org/models/forgefed" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + fm "forgejo.org/modules/forgefed" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/modules/validation" + "forgejo.org/tests" +) + +func TestActivityPubRepoFollowing(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) + session := loginUser(t, user.Name) + + t.Run("Add a following repo", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + link := fmt.Sprintf("/%s/settings", repo.FullName()) + + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, link), + "action": "federation", + "following_repos": fmt.Sprintf("%s/api/v1/activitypub/repository-id/1", federatedSrv.URL), + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + // Verify it was added. + federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) + unittest.AssertExistsAndLoadBean(t, &repo_model.FollowingRepo{ + ExternalID: "1", + FederationHostID: federationHost.ID, + }) + }) + + t.Run("Star a repo having a following repo", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + repoLink := fmt.Sprintf("/%s", repo.FullName()) + link := fmt.Sprintf("%s/action/star", repoLink) + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, repoLink), + }) + + session.MakeRequest(t, req, http.StatusOK) + + // Verify distant server received a like activity + like := fm.ForgeLike{} + err := like.UnmarshalJSON([]byte(mock.LastPost)) + if err != nil { + t.Errorf("Error unmarshalling ForgeLike: %q", err) + } + if isValid, err := validation.IsValid(like); !isValid { + t.Errorf("ForgeLike is not valid: %q", err) + } + activityType := like.Type + object := like.Object.GetLink().String() + isLikeType := activityType == "Like" + isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1") + if !isLikeType || !isCorrectObject { + t.Error("Activity is not a like for this repo") + } + }) +} From b1b418a939f5830f4938d2d02d3eae437c810045 Mon Sep 17 00:00:00 2001 From: Lynoure Braakman Date: Fri, 25 Jul 2025 22:21:37 +0200 Subject: [PATCH 185/297] RELEASE-NOTES.md to refer to the release notes directory on master [skip ci] (#8675) For any given version, the release notes in that version are lagging behind, so to reduce confusion of someone just wanting to see the release notes of this version they are looking at, changed the RELEASE-NOTES.md to refer to the release notes directory on the main branch. I'm not 100% happy with now having that point to the main branch, and the more specific links pointing relatively, but maybe it's good enough. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8675 Reviewed-by: Robert Wolff Co-authored-by: Lynoure Braakman Co-committed-by: Lynoure Braakman --- RELEASE-NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 32f7b8c264..c688045d1c 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,7 +4,7 @@ A minor or major Forgejo release is published every [three months](https://forge A [patch or minor release](https://semver.org/spec/v2.0.0.html) (e.g. upgrading from v7.0.0 to v7.0.1 or v7.1.0) does not require manual intervention. But [major releases](https://semver.org/spec/v2.0.0.html#spec-item-8) where the first version number changes (e.g. upgrading from v1.21 to v7.0) contain breaking changes and the release notes explain how to deal with them. -The release notes of each release [are available in the release-notes-published directory of this repository](release-notes-published), starting with [Forgejo 7.0.7](release-notes-published/7.0.7.md) and [Forgejo 8.0.1](release-notes-published/8.0.1.md). +The release notes of each release [are available in the release-notes-published directory of this repository](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/release-notes-published), starting with [Forgejo 7.0.7](release-notes-published/7.0.7.md) and [Forgejo 8.0.1](release-notes-published/8.0.1.md). ## 9.0.2 From 24014c349ee4267424ebae5b779e5a40ac65153e Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 26 Jul 2025 05:16:55 +0200 Subject: [PATCH 186/297] fix: store code challenge correctly in session (#8678) - Even though the test file contains some good extensive testing, it didn't bother to actually call `/login/oauth/access_token` to see if the received code actually resulted into a access token. - The fix itself is... well yeah self-explanatory. - Resolves forgejo/forgejo#8669 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8678 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- routers/web/auth/oauth.go | 2 +- tests/integration/oauth_test.go | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index f287e0e900..1119e1947b 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -489,7 +489,7 @@ func AuthorizeOAuth(ctx *context.Context) { }, form.RedirectURI) return } - if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallenge); err != nil { + if err := ctx.Session.Set("CodeChallenge", form.CodeChallenge); err != nil { handleAuthorizeError(ctx, AuthorizeError{ ErrorCode: ErrorCodeServerError, ErrorDescription: "cannot set code challenge", diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index 188b0426da..ebfd386ee0 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -1547,3 +1547,64 @@ func TestSignUpViaOAuth2FA(t *testing.T) { // Make sure user has to go through 2FA. assert.Equal(t, "/user/webauthn", test.RedirectURL(resp)) } + +func TestAccessTokenWithPKCE(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + var u *url.URL + t.Run("Grant", func(t *testing.T) { + session := loginUser(t, "user4") + req := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{ + "_csrf": GetCSRF(t, session, "/login/oauth/authorize?client_id=ce5a1322-42a7-11ed-b878-0242ac120002&redirect_uri=b&response_type=code&code_challenge_method=plain&code_challenge=CODE&state=thestate"), + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "redirect_uri": "b", + "state": "thestate", + "granted": "true", + }) + resp := session.MakeRequest(t, req, http.StatusSeeOther) + + var err error + u, err = url.Parse(test.RedirectURL(resp)) + require.NoError(t, err) + }) + + t.Run("Incorrect code verfifier", func(t *testing.T) { + req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{ + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "code": u.Query().Get("code"), + "code_verifier": "just a guess", + "grant_type": "authorization_code", + "redirect_uri": "b", + }) + resp := MakeRequest(t, req, http.StatusBadRequest) + + var respBody map[string]any + DecodeJSON(t, resp, &respBody) + + if assert.Len(t, respBody, 2) { + assert.Equal(t, "unauthorized_client", respBody["error"]) + assert.Equal(t, "failed PKCE code challenge", respBody["error_description"]) + } + }) + + t.Run("Get access token", func(t *testing.T) { + req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{ + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "code": u.Query().Get("code"), + "code_verifier": "CODE", + "grant_type": "authorization_code", + "redirect_uri": "b", + }) + resp := MakeRequest(t, req, http.StatusOK) + + var respBody map[string]any + DecodeJSON(t, resp, &respBody) + + if assert.Len(t, respBody, 4) { + assert.NotEmpty(t, respBody["access_token"]) + assert.NotEmpty(t, respBody["token_type"]) + assert.NotEmpty(t, respBody["expires_in"]) + assert.NotEmpty(t, respBody["refresh_token"]) + } + }) +} From 02de040a5e995dc45ed66470a45f31703f706991 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 26 Jul 2025 05:44:58 +0200 Subject: [PATCH 187/297] feat: add tracing logs after process is complete (#8680) - Add the written HTTP status after completing the HTTP response. This makes it easier to find that one request that returns a different status code (ref. https://codeberg.org/Codeberg/Community/issues/2049#issue-1972600) - Add the affected amount of rows and last insert ID after the SQL query is done, I have not yet a concrete use-case but this might help with debugging which ID corresponds to some SQL query that someone might want to take a closer look at and if some SQL query affects more than necessary amount of rows. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8680 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- models/db/engine.go | 9 +++++++++ routers/common/middleware.go | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/models/db/engine.go b/models/db/engine.go index 76025f7d67..42b9150696 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -386,6 +386,15 @@ func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, erro } func (TracingHook) AfterProcess(c *contexts.ContextHook) error { + if c.Result != nil { + if rowsAffected, err := c.Result.RowsAffected(); err == nil { + trace.Logf(c.Ctx, "rows affected", "%d", rowsAffected) + } + if lastID, err := c.Result.LastInsertId(); err == nil { + trace.Logf(c.Ctx, "last insert id", "%d", lastID) + } + } + c.Ctx.Value(sqlTask{}).(*trace.Task).End() return nil } diff --git a/routers/common/middleware.go b/routers/common/middleware.go index d44f046a1e..7bc4890a43 100644 --- a/routers/common/middleware.go +++ b/routers/common/middleware.go @@ -46,7 +46,11 @@ func ProtocolMiddlewares() (handlers []any) { defer finished() trace.Log(ctx, "method", req.Method) trace.Log(ctx, "url", req.RequestURI) - next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx))) + + respWriter := context.WrapResponseWriter(resp) + next.ServeHTTP(respWriter, req.WithContext(cache.WithCacheContext(ctx))) + + trace.Logf(ctx, "status", "%d", respWriter.WrittenStatus()) }) }) From d22f9d1f3808da4a77827c0362a85fdaa08cea91 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 26 Jul 2025 05:47:06 +0200 Subject: [PATCH 188/297] fix: show mergebox when only manual merge is allowed (#8681) - If a repository only has the 'manual merge' strategy allowed, the mergebox should still be shown. - The condition that checks if all merge strategies are disabled didn't check for the manual merge strategy. - Add a integration test that demonstrates this fix is effective. Reported-by: apteryx Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8681 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- templates/repo/issue/view_content/pull.tmpl | 2 +- tests/integration/pull_test.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 58d85dbf1b..bc578a6f4c 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -199,7 +199,7 @@ {{if .AllowMerge}} {{/* user is allowed to merge */}} {{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}} - {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}} + {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly $prUnit.PullRequestsConfig.AllowManualMerge}} {{$hasPendingPullRequestMergeTip := ""}} {{if .HasPendingPullRequestMerge}} {{$createdPRMergeStr := DateUtils.TimeSince .PendingPullRequestMerge.CreatedUnix}} diff --git a/tests/integration/pull_test.go b/tests/integration/pull_test.go index fd9dbf8888..36daf35f7b 100644 --- a/tests/integration/pull_test.go +++ b/tests/integration/pull_test.go @@ -136,3 +136,24 @@ func TestPullCombinedReviewRequest(t *testing.T) { helper(t, "detach", "9", "removed review request for user11") helper(t, "detach", "2", "removed review requests for user11, user2") } + +func TestShowMergeForManualMerge(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Only allow manual merge strategy for this repository. + pullRepoUnit := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoUnit{ID: 5, RepoID: 1, Type: unit.TypePullRequests}) + pullRepoUnit.Config = &repo_model.PullRequestsConfig{ + AllowManualMerge: true, + DefaultMergeStyle: repo_model.MergeStyleManuallyMerged, + } + repo_model.UpdateRepoUnit(t.Context(), pullRepoUnit) + + session := loginUser(t, "user2") + + req := NewRequest(t, "GET", "/user2/repo1/pulls/5") + resp := session.MakeRequest(t, req, http.StatusOK) + + // Assert that the mergebox is shown. + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, "#pull-request-merge-form", true) +} From ee35b617b799daa4c9efc43f7016c589be16a518 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 26 Jul 2025 16:14:42 +0200 Subject: [PATCH 189/297] chore: correctly prepare test env (#8686) - Ref forgejo/forgejo#8681 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8686 Co-authored-by: Gusted Co-committed-by: Gusted --- tests/integration/pull_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/pull_test.go b/tests/integration/pull_test.go index 36daf35f7b..8ff715a1b5 100644 --- a/tests/integration/pull_test.go +++ b/tests/integration/pull_test.go @@ -138,7 +138,7 @@ func TestPullCombinedReviewRequest(t *testing.T) { } func TestShowMergeForManualMerge(t *testing.T) { - defer tests.PrintCurrentTest(t)() + defer tests.PrepareTestEnv(t)() // Only allow manual merge strategy for this repository. pullRepoUnit := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoUnit{ID: 5, RepoID: 1, Type: unit.TypePullRequests}) From 4694cbf95be9082196497006913117c6d7668c15 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Sun, 27 Jul 2025 10:05:55 +0200 Subject: [PATCH 190/297] fix: don't mark files in AGit PRs as editable (#8642) Fixes #8639. For your convenience this PR is made via AGit for you to see the issue directly in Codeberg ^^ ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. I am not sure how to best write a unit test for this. It looks like there isn't any AGit PR in the default test database. The integration test in `test/integration/pull_review_test.go` creates one by manually running Git commands. I could just copy all that code to do the same in another test, but I feel bad about duplicating this. Can they be sensibly migrated to some helpers module that can be reused across tests? Or can we add an AGit PR to the test database? Or can we modify the `Flow` attribute of a PR in a test just for the sake of simulating that it's an AGit one? ### 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8642 Reviewed-by: Gusted Co-authored-by: Antonin Delpeuch Co-committed-by: Antonin Delpeuch --- routers/web/repo/pull.go | 2 +- tests/integration/pull_diff_test.go | 91 +++++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index c9a2bb4e08..48e132df5c 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1152,7 +1152,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi ctx.ServerError("GetUserRepoPermission", err) return } - ctx.Data["HeadBranchIsEditable"] = pull.HeadRepo.CanEnableEditor() && issues_model.CanMaintainerWriteToBranch(ctx, headRepoPerm, pull.HeadBranch, ctx.Doer) + ctx.Data["HeadBranchIsEditable"] = pull.HeadRepo.CanEnableEditor() && issues_model.CanMaintainerWriteToBranch(ctx, headRepoPerm, pull.HeadBranch, ctx.Doer) && pull.Flow != issues_model.PullRequestFlowAGit ctx.Data["SourceRepoLink"] = pull.HeadRepo.Link() ctx.Data["HeadBranch"] = pull.HeadBranch } diff --git a/tests/integration/pull_diff_test.go b/tests/integration/pull_diff_test.go index 6db9fc25d8..7442909aec 100644 --- a/tests/integration/pull_diff_test.go +++ b/tests/integration/pull_diff_test.go @@ -4,32 +4,45 @@ package integration import ( + "fmt" "net/http" + "net/url" + "os" + "path" + "strings" "testing" + "time" + issues_model "forgejo.org/models/issues" + unit_model "forgejo.org/models/unit" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/git" + files_service "forgejo.org/services/repository/files" "forgejo.org/tests" "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestPullDiff_CompletePRDiff(t *testing.T) { - doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files", []string{"test1.txt", "test10.txt", "test2.txt", "test3.txt", "test4.txt", "test5.txt", "test6.txt", "test7.txt", "test8.txt", "test9.txt"}) + doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files", []string{"test1.txt", "test10.txt", "test2.txt", "test3.txt", "test4.txt", "test5.txt", "test6.txt", "test7.txt", "test8.txt", "test9.txt"}, true) } func TestPullDiff_SingleCommitPRDiff(t *testing.T) { - doTestPRDiff(t, "/user2/commitsonpr/pulls/1/commits/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", []string{"test3.txt"}) + doTestPRDiff(t, "/user2/commitsonpr/pulls/1/commits/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", []string{"test3.txt"}, true) } func TestPullDiff_CommitRangePRDiff(t *testing.T) { - doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", []string{"test2.txt", "test3.txt", "test4.txt"}) + doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", []string{"test2.txt", "test3.txt", "test4.txt"}, true) } func TestPullDiff_StartingFromBaseToCommitPRDiff(t *testing.T) { - doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", []string{"test1.txt", "test2.txt", "test3.txt"}) + doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", []string{"test1.txt", "test2.txt", "test3.txt"}, true) } -func doTestPRDiff(t *testing.T, prDiffURL string, expectedFilenames []string) { +func doTestPRDiff(t *testing.T, prDiffURL string, expectedFilenames []string, editable bool) { defer tests.PrepareTestEnv(t)() session := loginUser(t, "user2") @@ -51,5 +64,73 @@ func doTestPRDiff(t *testing.T, prDiffURL string, expectedFilenames []string) { fileContents.Each(func(i int, s *goquery.Selection) { filename, _ := s.Attr("data-old-filename") assert.Equal(t, expectedFilenames[i], filename) + doc.AssertElement(t, "h4.diff-file-header a.button[href=\"/user2/commitsonpr/_edit/branch1/"+filename+"\"]", editable) + }) +} + +func TestPullDiff_AGitNotEditable(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user2.Name) + + // Create temporary repository. + repo, _, f := tests.CreateDeclarativeRepo(t, user2, "myrepo", + []unit_model.Type{unit_model.TypePullRequests}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "FUNFACT", + ContentReader: strings.NewReader("Smithy was the runner up to be Forgejo's name"), + }, + }, + ) + defer f() + + clone := func(t *testing.T, clone string) string { + t.Helper() + + dstPath := t.TempDir() + cloneURL, _ := url.Parse(clone) + cloneURL.User = url.UserPassword("user2", userPassword) + require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + + return dstPath + } + + firstCommit := func(t *testing.T, dstPath string) { + t.Helper() + + require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o600)) + require.NoError(t, git.AddChanges(dstPath, true)) + require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add README.", + })) + } + dstPath := clone(t, fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name)) + + // Create first commit. + firstCommit(t, dstPath) + + // Create agit PR. + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath})) + + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Index: 1, BaseRepoID: repo.ID}) + assert.Equal(t, issues_model.PullRequestFlowAGit, pr.Flow) + + resp := session.MakeRequest(t, NewRequest(t, "GET", fmt.Sprintf("/%s/pulls/%d/files", repo.FullName(), pr.Index)), http.StatusOK) + + doc := NewHTMLParser(t, resp.Body) + // There is no edit button on any changed file + doc.AssertElement(t, "h4.diff-file-header a.button[href^=\"/"+repo.FullName()+"/_edit\"]", false) }) } From 5624b9f094f57a74614d7dbefb5ede767db9e022 Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Sun, 27 Jul 2025 10:12:28 +0200 Subject: [PATCH 191/297] fix(ui): issue comment review request targets when aggregated (#8689) regression of !8239 ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8689 Reviewed-by: Earl Warren Co-authored-by: Robert Wolff Co-committed-by: Robert Wolff --- .../repo/issue/view_content/comments.tmpl | 6 +- tests/integration/issue_comment_test.go | 105 ++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 454467a4d0..a212bb871a 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -759,11 +759,11 @@ {{if and (eq (len .RemovedRequestReview) 1) (eq (len .AddedRequestReview) 0) (eq ((index .RemovedRequestReview 0).ID) .PosterID) (eq ((index .RemovedRequestReview 0).Type) "user")}} {{ctx.Locale.Tr "repo.issues.review.remove_review_request_self" ""}} {{else if and .AddedRequestReview (not .RemovedRequestReview)}} - {{ctx.Locale.TrN (len .AddedRequestReview) "repo.issues.review.add_review_request" "repo.issues.review.add_review_requests" (RenderReviewRequest .AddedRequestReview) ""}} + {{ctx.Locale.TrN (len .AddedRequestReview) "repo.issues.review.add_review_request" "repo.issues.review.add_review_requests" (RenderReviewRequest $.Context .AddedRequestReview) ""}} {{else if and (not .AddedRequestReview) .RemovedRequestReview}} - {{ctx.Locale.TrN (len .RemovedRequestReview) "repo.issues.review.remove_review_request" "repo.issues.review.remove_review_requests" (RenderReviewRequest .RemovedRequestReview) ""}} + {{ctx.Locale.TrN (len .RemovedRequestReview) "repo.issues.review.remove_review_request" "repo.issues.review.remove_review_requests" (RenderReviewRequest $.Context .RemovedRequestReview) ""}} {{else}} - {{ctx.Locale.Tr "repo.issues.review.add_remove_review_requests" (RenderReviewRequest .AddedRequestReview) (RenderReviewRequest .RemovedRequestReview) ""}} + {{ctx.Locale.Tr "repo.issues.review.add_remove_review_requests" (RenderReviewRequest $.Context .AddedRequestReview) (RenderReviewRequest $.Context .RemovedRequestReview) ""}} {{end}} diff --git a/tests/integration/issue_comment_test.go b/tests/integration/issue_comment_test.go index eda643fa79..a604378479 100644 --- a/tests/integration/issue_comment_test.go +++ b/tests/integration/issue_comment_test.go @@ -315,6 +315,111 @@ func TestIssueCommentChangeReviewRequest(t *testing.T) { assert.Empty(t, htmlDoc.Find("#issuecomment-"+strconv.FormatInt(comment6.ID, 10)+" .text").Text()) } +func TestIssueCommentChangeReviewRequestAggregated(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 6}) + require.NoError(t, pull.LoadIssue(db.DefaultContext)) + issue := pull.Issue + require.NoError(t, issue.LoadRepo(db.DefaultContext)) + + user1, err := user_model.GetUserByID(db.DefaultContext, 1) + require.NoError(t, err) + user2, err := user_model.GetUserByID(db.DefaultContext, 2) + require.NoError(t, err) + team1, err := org_model.GetTeamByID(db.DefaultContext, 2) + require.NoError(t, err) + assert.NotNil(t, team1) + label, err := issues_model.GetLabelByID(db.DefaultContext, 1) + require.NoError(t, err) + assert.NotNil(t, label) + + // Request from other + comment1, err := issues_model.AddReviewRequest(db.DefaultContext, issue, user2, user1) + require.NoError(t, err) + // Add label + issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypeLabel, + Doer: user1, + Issue: issue, + Label: label, + Repo: issue.Repo, + }) + + // Refuse review + comment2, err := issues_model.RemoveReviewRequest(db.DefaultContext, issue, user2, user2) + require.NoError(t, err) + // Remove label + issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypeLabel, + Doer: user2, + Issue: issue, + Label: label, + Repo: issue.Repo, + }) + + // Request from other + comment3, err := issues_model.AddReviewRequest(db.DefaultContext, issue, user2, user1) + require.NoError(t, err) + // Request from team + comment4, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team1, user1) + require.NoError(t, err) + // Add label + issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypeLabel, + Doer: user1, + Issue: issue, + Label: label, + Repo: issue.Repo, + }) + + // Remove request from team + comment5, err := issues_model.RemoveTeamReviewRequest(db.DefaultContext, issue, team1, user2) + require.NoError(t, err) + // Request from other + comment6, err := issues_model.AddReviewRequest(db.DefaultContext, issue, user1, user2) + require.NoError(t, err) + // Remove label + issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypeLabel, + Doer: user2, + Issue: issue, + Label: label, + Repo: issue.Repo, + }) + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/org3/repo3/pulls/2") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + // Request from other + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment1.ID, 10), + "octicon-eye", "User One", "/user1", + []string{"user1 requested review from user2"}, + []string{"/user1", "#issuecomment-" + strconv.FormatInt(comment1.ID, 10), "/org3/repo3/pulls?labels=1", "/user2"}) + + // Refuse review + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment2.ID, 10), + "octicon-eye", "< Ur Tw ><", "/user2", + []string{"user2 refused to review"}, + []string{"/user2", "#issuecomment-" + strconv.FormatInt(comment2.ID, 10), "/org3/repo3/pulls?labels=1"}) + + // Request review from other and from team + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment3.ID, 10), + "octicon-eye", "User One", "/user1", + []string{"user1 requested reviews from user2, team1"}, + []string{"/user1", "#issuecomment-" + strconv.FormatInt(comment3.ID, 10), "/org3/repo3/pulls?labels=1", "/user2", "/org/org3/teams/team1"}) + assert.Empty(t, htmlDoc.Find("#issuecomment-"+strconv.FormatInt(comment4.ID, 10)+" .text").Text()) + + // Remove and add request + testIssueCommentChangeEvent(t, htmlDoc, strconv.FormatInt(comment5.ID, 10), + "octicon-eye", "< Ur Tw ><", "/user2", + []string{"user2 requested reviews from user1 and removed review requests for team1"}, + []string{"/user2", "#issuecomment-" + strconv.FormatInt(comment5.ID, 10), "/org3/repo3/pulls?labels=1", "/user1", "/org/org3/teams/team1"}) + assert.Empty(t, htmlDoc.Find("#issuecomment-"+strconv.FormatInt(comment6.ID, 10)+" .text").Text()) +} + func TestIssueCommentChangeLock(t *testing.T) { defer tests.PrepareTestEnv(t)() From 421cfba3cfb46081c9b8c19d6049f1ad9d947b9d Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 27 Jul 2025 10:17:33 +0200 Subject: [PATCH 192/297] fix: return error when user is not repo writer (#8690) - If the doer isn't a issue/pull writer, return a error. - Fixes a panic (NPE), because the callers of `prepareForReplaceOrAdd` simply checked if there was a error returned to see if the user was allowed. It didn't check if a statuscode was written. This is specifically a issue when the automatic token by Forgejo actions is used. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8690 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- routers/api/v1/repo/issue_label.go | 2 +- tests/integration/api_issue_label_test.go | 25 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 3b2935305c..f2e79ea417 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -384,7 +384,7 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { ctx.Status(http.StatusForbidden) - return nil, nil, nil + return nil, nil, errors.New("not issue/pull writer") } err = issue_service.SetIssueUpdateDate(ctx, issue, form.Updated, ctx.Doer) diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go index 160774a7e5..665e4b2f2a 100644 --- a/tests/integration/api_issue_label_test.go +++ b/tests/integration/api_issue_label_test.go @@ -336,3 +336,28 @@ func TestAPIModifyOrgLabels(t *testing.T) { AddTokenAuth(token) MakeRequest(t, req, http.StatusNoContent) } + +func TestAPIReplaceIssueLabelsActionsToken(t *testing.T) { + require.NoError(t, unittest.LoadFixtures()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}) + label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47}) + task.RepoID = repo.ID + task.OwnerID = owner.ID + task.IsForkPullRequest = true // Read permission. + require.NoError(t, task.GenerateToken()) + + // Explicitly need "is_fork_pull_request". + require.NoError(t, actions_model.UpdateTask(t.Context(), task, "repo_id", "owner_id", "is_fork_pull_request", "token", "token_salt", "token_hash", "token_last_eight")) + + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels", + owner.Name, repo.Name, issue.Index) + req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{ + Labels: []any{label.Name}, + }).AddTokenAuth(task.Token) + MakeRequest(t, req, http.StatusForbidden) +} From 370b498e6dc1ad6e468d1fcad1b4ff9111b523e2 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 28 Jul 2025 08:14:35 +0200 Subject: [PATCH 193/297] Lock file maintenance (forgejo) (#8703) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on Monday ( * 0-3 * * 1 ) (UTC), 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. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8703 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 564 ++++++++++++++--------------- web_src/fomantic/package-lock.json | 20 +- 2 files changed, 278 insertions(+), 306 deletions(-) diff --git a/package-lock.json b/package-lock.json index d69bddd887..12f7c404fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -231,18 +231,18 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -525,21 +525,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", - "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.0.3", + "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", - "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "license": "MIT", "optional": true, @@ -548,9 +548,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", - "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", "dev": true, "license": "MIT", "optional": true, @@ -559,9 +559,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -591,9 +591,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -607,9 +607,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -623,9 +623,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -639,9 +639,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -655,9 +655,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -671,9 +671,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -687,9 +687,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -703,9 +703,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -719,9 +719,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -735,9 +735,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -751,9 +751,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -767,9 +767,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -783,9 +783,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -799,9 +799,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -815,9 +815,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -831,9 +831,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -847,9 +847,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -863,9 +863,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -879,9 +879,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -895,9 +895,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", "cpu": [ "arm64" ], @@ -911,9 +911,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -927,9 +927,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -943,9 +943,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -959,9 +959,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -1185,9 +1185,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1994,39 +1994,11 @@ } }, "node_modules/@keyv/serialize": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz", - "integrity": "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.0.tgz", + "integrity": "sha512-RlDgexML7Z63Q8BSaqhXdCYNBy/JQnqYIwxofUrNLGCblOMHp+xux2Q8nLMLlPpgHQPoU0Do8Z6btCpRBEqZ8g==", "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^6.0.3" - } - }, - "node_modules/@keyv/serialize/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } + "license": "MIT" }, "node_modules/@kurkle/color": { "version": "0.3.4", @@ -2101,9 +2073,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.1.tgz", - "integrity": "sha512-lCQNpV8R4lgsGcjX5667UiuDLk2micCtjtxR1YKbBXvN5w2v+FeLYoHrTSSrjwXdMcDYvE4ZBPvKT31dfeSmmA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz", + "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -2289,9 +2261,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.0.tgz", - "integrity": "sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.0.tgz", + "integrity": "sha512-9f3nSTFI2ivfxc7/tHBHcJ8pRnp8ROrELvsVprlQPVvcZ+j5zztYd+PTJGpyIOAdTvNwNrpCXswKSeoQcyGjMQ==", "cpu": [ "arm" ], @@ -2303,9 +2275,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.0.tgz", - "integrity": "sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.0.tgz", + "integrity": "sha512-tFZSEhqJ8Yrpe50TzOdeoYi72gi/jsnT7y8Qrozf3cNu28WX+s6I3XzEPUAqoaT9SAS8Xz9AzGTFlxxCH/w20w==", "cpu": [ "arm64" ], @@ -2317,9 +2289,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.0.tgz", - "integrity": "sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.0.tgz", + "integrity": "sha512-+DikIIs+p6yU2hF51UaWG8BnHbq90X0QIOt5zqSKSZxY+G3qqdLih214e9InJal21af2PuuxkDectetGfbVPJw==", "cpu": [ "arm64" ], @@ -2331,9 +2303,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.0.tgz", - "integrity": "sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.0.tgz", + "integrity": "sha512-5a+NofhdEB/WimSlFMskbFQn1vqz1FWryYpA99trmZGO6qEmiS0IsX6w4B3d91U878Q2ZQdiaFF1gxX4P147og==", "cpu": [ "x64" ], @@ -2345,9 +2317,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.0.tgz", - "integrity": "sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.0.tgz", + "integrity": "sha512-igr/RlKPS3OCy4jD3XBmAmo3UAcNZkJSubRsw1JeM8bAbwf15k/3eMZXD91bnjheijJiOJcga3kfCLKjV8IXNg==", "cpu": [ "arm64" ], @@ -2359,9 +2331,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.0.tgz", - "integrity": "sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.0.tgz", + "integrity": "sha512-MdigWzPSHlQzB1xZ+MdFDWTAH+kcn7UxjEBoOKuaso7z1DRlnAnrknB1mTtNOQ+GdPI8xgExAGwHeqQjntR0Cg==", "cpu": [ "x64" ], @@ -2373,9 +2345,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.0.tgz", - "integrity": "sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.0.tgz", + "integrity": "sha512-dmZseE0ZwA/4yy1+BwFrDqFTjjNg24GO9xSrb1weVbt6AFkhp5pz1gVS7IMtfIvoWy8yp6q/zN0bKnefRUImvQ==", "cpu": [ "arm" ], @@ -2387,9 +2359,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.0.tgz", - "integrity": "sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.0.tgz", + "integrity": "sha512-fzhfn6p9Cfm3W8UrWKIa4l7Wfjs/KGdgaswMBBE3KY3Ta43jg2XsPrAtfezHpsRk0Nx+TFuS3hZk/To2N5kFPQ==", "cpu": [ "arm" ], @@ -2401,9 +2373,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.0.tgz", - "integrity": "sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.0.tgz", + "integrity": "sha512-vVDD+iPDPmJQ5nAQ5Tifq3ywdv60FartglFI8VOCK+hcU9aoG0qlQTsDJP97O5yiTaTqlneZWoARMcVC5nyUoQ==", "cpu": [ "arm64" ], @@ -2415,9 +2387,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.0.tgz", - "integrity": "sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.0.tgz", + "integrity": "sha512-0d0jx08fzDHCzXqrtCMEEyxKU0SvJrWmUjUDE2/KDQ2UDJql0tfiwYvEx1oHELClKO8CNdE+AGJj+RqXscZpdQ==", "cpu": [ "arm64" ], @@ -2429,9 +2401,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.0.tgz", - "integrity": "sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.0.tgz", + "integrity": "sha512-XBYu9oW9eKJadWn8M7hkTZsD4yG+RrsTrVEgyKwb4L72cpJjRbRboTG9Lg9fec8MxJp/cfTHAocg4mnismQR8A==", "cpu": [ "loong64" ], @@ -2442,10 +2414,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.0.tgz", - "integrity": "sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.0.tgz", + "integrity": "sha512-wJaRvcT17PoOK6Ggcfo3nouFlybHvARBS4jzT0PC/lg17fIJHcDS2fZz3sD+iA4nRlho2zE6OGbU0HvwATdokQ==", "cpu": [ "ppc64" ], @@ -2457,9 +2429,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.0.tgz", - "integrity": "sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.0.tgz", + "integrity": "sha512-GZ5bkMFteAGkcmh8x0Ok4LSa+L62Ez0tMsHPX6JtR0wl4Xc3bQcrFHDiR5DGLEDFtGrXih4Nd/UDaFqs968/wA==", "cpu": [ "riscv64" ], @@ -2471,9 +2443,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.0.tgz", - "integrity": "sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.0.tgz", + "integrity": "sha512-7CjPw6FflFsVOUfWOrVrREiV3IYXG4RzZ1ZQUaT3BtSK8YXN6x286o+sruPZJESIaPebYuFowmg54ZdrkVBYog==", "cpu": [ "riscv64" ], @@ -2485,9 +2457,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.0.tgz", - "integrity": "sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.0.tgz", + "integrity": "sha512-nmvnl0ZiuysltcB/cKjUh40Rx4FbSyueERDsl2FLvLYr6pCgSsvGr3SocUT84svSpmloS7f1DRWqtRha74Gi1w==", "cpu": [ "s390x" ], @@ -2499,9 +2471,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.0.tgz", - "integrity": "sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.0.tgz", + "integrity": "sha512-Cv+moII5C8RM6gZbR3cb21o6rquVDZrN2o81maROg1LFzBz2dZUwIQSxFA8GtGZ/F2KtsqQ2z3eFPBb6akvQNg==", "cpu": [ "x64" ], @@ -2513,9 +2485,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.0.tgz", - "integrity": "sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.0.tgz", + "integrity": "sha512-PHcMG8DZTM9RCIjp8QIfN0VYtX0TtBPnWOTRurFhoCDoi9zptUZL2k7pCs+5rgut7JAiUsYy+huyhVKPcmxoog==", "cpu": [ "x64" ], @@ -2527,9 +2499,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.0.tgz", - "integrity": "sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.0.tgz", + "integrity": "sha512-1SI/Rd47e8aQJeFWMDg16ET+fjvCcD/CzeaRmIEPmb05hx+3cCcwIF4ebUag4yTt/D1peE+Mgp0+Po3M358cAA==", "cpu": [ "arm64" ], @@ -2541,9 +2513,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.0.tgz", - "integrity": "sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.0.tgz", + "integrity": "sha512-JwOCYxmumFDfDhx4kNyz6kTVK3gWzBIvVdMNzQMRDubcoGRDniOOmo6DDNP42qwZx3Bp9/6vWJ+kNzNqXoHmeA==", "cpu": [ "ia32" ], @@ -2555,9 +2527,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.0.tgz", - "integrity": "sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.0.tgz", + "integrity": "sha512-IPMIfrfkG1GaEXi+JSsQEx8x9b4b+hRZXO7KYc2pKio3zO2/VDXDs6B9Ts/nnO+25Fk1tdAVtUn60HKKPPzDig==", "cpu": [ "x64" ], @@ -3520,9 +3492,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz", - "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==", + "version": "20.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", + "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -4727,9 +4699,9 @@ } }, "node_modules/acorn-import-phases": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.3.tgz", - "integrity": "sha512-jtKLnfoOzm28PazuQ4dVBcE9Jeo6ha1GAJvq3N0LlNOszmTfx+wSycBehn+FN0RnyeR77IBxN/qVYMw0Rlj0Xw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "license": "MIT", "engines": { "node": ">=10.13.0" @@ -5312,24 +5284,24 @@ } }, "node_modules/cacheable": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.1.tgz", - "integrity": "sha512-Fa2BZY0CS9F0PFc/6aVA6tgpOdw+hmv9dkZOlHXII5v5Hw+meJBIWDcPrG9q/dXxGcNbym5t77fzmawrBQfTmQ==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.10.3.tgz", + "integrity": "sha512-M6p10iJ/VT0wT7TLIGUnm958oVrU2cUK8pQAVU21Zu7h8rbk/PeRtRWrvHJBql97Bhzk3g1N6+2VKC+Rjxna9Q==", "dev": true, "license": "MIT", "dependencies": { "hookified": "^1.10.0", - "keyv": "^5.3.4" + "keyv": "^5.4.0" } }, "node_modules/cacheable/node_modules/keyv": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.4.tgz", - "integrity": "sha512-ypEvQvInNpUe+u+w8BIcPkQvEqXquyyibWE/1NB5T2BTzIpS5cGEV1LZskDzPSTvNAaT4+5FutvzlvnkxOSKlw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.4.0.tgz", + "integrity": "sha512-TMckyVjEoacG5IteUpUrOBsFORtheqziVyyY2dLUwg1jwTb8u48LX4TgmtogkNl9Y9unaEJ1luj10fGyjMGFOQ==", "dev": true, "license": "MIT", "dependencies": { - "@keyv/serialize": "^1.0.3" + "@keyv/serialize": "^1.1.0" } }, "node_modules/call-bind": { @@ -6971,9 +6943,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.182", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz", - "integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==", + "version": "1.5.191", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz", + "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7225,9 +7197,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -7237,32 +7209,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/esbuild-loader": { @@ -8740,9 +8712,9 @@ } }, "node_modules/hookified": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.10.0.tgz", - "integrity": "sha512-dJw0492Iddsj56U1JsSTm9E/0B/29a1AuoSLRAte8vQg/kaTGF3IgjEWT8c8yG4cC10+HisE1x5QAwR0Xwc+DA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.11.0.tgz", + "integrity": "sha512-aDdIN3GyU5I6wextPplYdfmWCo+aLmjjVbntmX6HLD5RCi/xKsivYEBhnRD+d9224zFf008ZpLMPlWF0ZodYZw==", "dev": true, "license": "MIT" }, @@ -10315,9 +10287,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "dev": true, "license": "MIT" }, @@ -11408,9 +11380,9 @@ } }, "node_modules/napi-postinstall": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", - "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz", + "integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==", "dev": true, "license": "MIT", "bin": { @@ -14019,23 +13991,23 @@ } }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.1.tgz", - "integrity": "sha512-zcmsHjg2B2zjuBgjdnB+9q0+cWcgWfykIcsDkWDB4GTPtl1eXUA+gTI6sO0u01AqK3cliHryTU55/b2Ow1hfZg==", + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.3.tgz", + "integrity": "sha512-D+w75Ub8T55yor7fPgN06rkCAUbAYw2vpxJmmjv/GDAcvCnv9g7IvHhIZoxzRZThrXPFI2maeY24pPbtyYU7Lg==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^6.1.10" + "flat-cache": "^6.1.12" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.11.tgz", - "integrity": "sha512-zfOAns94mp7bHG/vCn9Ru2eDCmIxVQ5dELUHKjHfDEOJmHNzE+uGa6208kfkgmtym4a0FFjEuFksCXFacbVhSg==", + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.12.tgz", + "integrity": "sha512-U+HqqpZPPXP5d24bWuRzjGqVqUcw64k4nZAbruniDwdRg0H10tvN7H6ku1tjhA4rg5B9GS3siEvwO2qjJJ6f8Q==", "dev": true, "license": "MIT", "dependencies": { - "cacheable": "^1.10.1", + "cacheable": "^1.10.3", "flatted": "^3.3.3", "hookified": "^1.10.0" } @@ -14158,13 +14130,13 @@ } }, "node_modules/stylus/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">= 12" } }, "node_modules/sucrase": { @@ -14678,9 +14650,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -15174,15 +15146,15 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" @@ -15316,9 +15288,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -15358,9 +15330,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.0.tgz", - "integrity": "sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==", + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.0.tgz", + "integrity": "sha512-ONmkT3Ud3IfW15nl7l4qAZko5/2iZ5ALVBDh02ZSZ5IGVLJSYkRcRa3iB58VyEIyoofs9m2xdVrm+lTi97+3pw==", "dev": true, "license": "MIT", "dependencies": { @@ -15374,26 +15346,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.0", - "@rollup/rollup-android-arm64": "4.45.0", - "@rollup/rollup-darwin-arm64": "4.45.0", - "@rollup/rollup-darwin-x64": "4.45.0", - "@rollup/rollup-freebsd-arm64": "4.45.0", - "@rollup/rollup-freebsd-x64": "4.45.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.0", - "@rollup/rollup-linux-arm-musleabihf": "4.45.0", - "@rollup/rollup-linux-arm64-gnu": "4.45.0", - "@rollup/rollup-linux-arm64-musl": "4.45.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.0", - "@rollup/rollup-linux-riscv64-gnu": "4.45.0", - "@rollup/rollup-linux-riscv64-musl": "4.45.0", - "@rollup/rollup-linux-s390x-gnu": "4.45.0", - "@rollup/rollup-linux-x64-gnu": "4.45.0", - "@rollup/rollup-linux-x64-musl": "4.45.0", - "@rollup/rollup-win32-arm64-msvc": "4.45.0", - "@rollup/rollup-win32-ia32-msvc": "4.45.0", - "@rollup/rollup-win32-x64-msvc": "4.45.0", + "@rollup/rollup-android-arm-eabi": "4.46.0", + "@rollup/rollup-android-arm64": "4.46.0", + "@rollup/rollup-darwin-arm64": "4.46.0", + "@rollup/rollup-darwin-x64": "4.46.0", + "@rollup/rollup-freebsd-arm64": "4.46.0", + "@rollup/rollup-freebsd-x64": "4.46.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.0", + "@rollup/rollup-linux-arm-musleabihf": "4.46.0", + "@rollup/rollup-linux-arm64-gnu": "4.46.0", + "@rollup/rollup-linux-arm64-musl": "4.46.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.0", + "@rollup/rollup-linux-ppc64-gnu": "4.46.0", + "@rollup/rollup-linux-riscv64-gnu": "4.46.0", + "@rollup/rollup-linux-riscv64-musl": "4.46.0", + "@rollup/rollup-linux-s390x-gnu": "4.46.0", + "@rollup/rollup-linux-x64-gnu": "4.46.0", + "@rollup/rollup-linux-x64-musl": "4.46.0", + "@rollup/rollup-win32-arm64-msvc": "4.46.0", + "@rollup/rollup-win32-ia32-msvc": "4.46.0", + "@rollup/rollup-win32-x64-msvc": "4.46.0", "fsevents": "~2.3.2" } }, @@ -15481,9 +15453,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index 669363b7fa..073339afc6 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -494,9 +494,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.0.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz", - "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==", + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", "license": "MIT", "dependencies": { "undici-types": "~7.8.0" @@ -2005,9 +2005,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.182", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz", - "integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==", + "version": "1.5.191", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz", + "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -4638,12 +4638,12 @@ } }, "node_modules/is": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.2.tgz", + "integrity": "sha512-a2xr4E3s1PjDS8ORcGgXpWx6V+liNs+O3JRD2mb9aeugD7rtkkZ0zgLdYgw0tWsKhsdiezGYptSiMlVazCBTuQ==", "license": "MIT", "engines": { - "node": "*" + "node": ">= 0.4" } }, "node_modules/is-absolute": { From 6f1e4be0c1c796322a5c445f7b95b4c4268883e5 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 28 Jul 2025 09:27:32 +0200 Subject: [PATCH 194/297] Update dependency forgejo/release-notes-assistant to v1.3.5 (forgejo) (#8701) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [forgejo/release-notes-assistant](https://code.forgejo.org/forgejo/release-notes-assistant) | patch | `v1.3.3` -> `v1.3.5` | --- ### Release Notes
    forgejo/release-notes-assistant (forgejo/release-notes-assistant) ### [`v1.3.5`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.5) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.4...v1.3.5) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/98): fix: there can be more than one tag decorating a commit ### [`v1.3.4`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.4) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.3...v1.3.4) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/97): fix: %(decorate) is not supported by git 2.39
    --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8701 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/release-notes-assistant-milestones.yml | 2 +- .forgejo/workflows/release-notes-assistant.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index 6c61774325..bf666dee6a 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -6,7 +6,7 @@ on: env: RNA_WORKDIR: /srv/rna - RNA_VERSION: v1.3.3 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index b1acde6030..d813e5fc0f 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,7 +8,7 @@ on: - labeled env: - RNA_VERSION: v1.3.3 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: From 4f0c2ec25880bbafff4e7c99ba75045d4f0718e2 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 28 Jul 2025 10:33:09 +0200 Subject: [PATCH 195/297] Update renovate to v41.43.5 (forgejo) (#8700) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index ecc39e7c62..c2923f7d0e 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:41.42.5 + image: data.forgejo.org/renovate/renovate:41.43.5 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 06d71a51c1..b46b23a44a 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.35.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.42.5 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@41.43.5 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From b2c8a1cfd357b1f1c5bbc4a2c9abf3b15bb54f67 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 28 Jul 2025 10:55:04 +0200 Subject: [PATCH 196/297] Revert "fix: assorted ActivityPub code only refactors (#8274)" (#8705) This reverts commit e271c24100c8974e135ebc1d1b3eaa6cd2062b10. It was an experiment to verify that adding a delay to the test make a difference. But it does not so... reverting. @jerger before engaging in a refactor, it is necessary to get to the bottom of this: - Find the root cause of those failures - Fix it in a minimal way Refs https://codeberg.org/forgejo/forgejo/pulls/8274#issuecomment-5987215 --- - https://codeberg.org/forgejo/forgejo/actions/runs/92182/jobs/9 - https://codeberg.org/forgejo/forgejo/actions/runs/92182/jobs/10 ``` --- FAIL: TestFederationHttpSigValidation (11.34s) testlogger.go:411: 2025/07/28 00:23:46 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspace/forgejo/forgejo/tests/gitea-lfs-meta testlogger.go:411: 2025/07/28 00:23:52 ...ypub/reqsignature.go:76:func1() [W] verifyHttpSignatures failed: neither "Signature" nor "Authorization" have signature parameters testlogger.go:411: 2025/07/28 00:23:52 ...eb/routing/logger.go:102:func1() [I] router: completed GET http://127.0.0.1:3002/api/v1/activitypub/user-id/2 for test-mock:12345, 400 Bad Request in 5.3ms @ activitypub/reqsignature.go:74(activitypub.ReqHTTPUserOrInstanceSignature) testlogger.go:411: 2025/07/28 00:23:52 ...ces/auth/httpsign.go:70:Verify() [W] Failed authentication attempt from 127.0.0.1:43244 testlogger.go:411: 2025/07/28 00:23:55 ...eb/routing/logger.go:68:func1() [W] router: slow GET /api/v1/activitypub/user-id/2 for 127.0.0.1:43244, elapsed 3684.7ms @ activitypub/reqsignature.go:74(activitypub.ReqHTTPUserOrInstanceSignature) --- FAIL: TestFederationHttpSigValidation/SignedRequest (5.01s) api_federation_httpsig_test.go:50: Error Trace: /workspace/forgejo/forgejo/tests/integration/api_federation_httpsig_test.go:50 Error: Received unexpected error: Get "http://127.0.0.1:3002/api/v1/activitypub/user-id/2": context deadline exceeded (Client.Timeout exceeded while awaiting headers) Test: TestFederationHttpSigValidation/SignedRequest --- FAIL: TestFederationHttpSigValidation/ValidateCaches (0.00s) api_federation_httpsig_test.go:64: Error Trace: /workspace/forgejo/forgejo/tests/integration/api_federation_httpsig_test.go:64 Error: Expected value not to be nil. Test: TestFederationHttpSigValidation/ValidateCaches test_utils.go:247: PrepareTestEnv:Process "GET: /api/v1/activitypub/user-id/2" cancelled panic: runtime error: invalid memory address or nil pointer dereference [recovered] panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4cc464a] ``` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8705 Reviewed-by: jerger Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- .deadcode-out | 3 - models/repo/repo_repository.go | 6 +- models/user/follow.go | 16 +- models/user/user_repository.go | 16 +- modules/activitypub/client.go | 7 +- routers/api/v1/activitypub/repository.go | 9 +- routers/api/v1/activitypub/reqsignature.go | 26 +-- routers/api/v1/activitypub/response.go | 34 ---- routers/api/v1/api.go | 3 +- services/federation/error.go | 44 ----- services/federation/federation_service.go | 19 +- ...{repository_inbox_like.go => repo_like.go} | 33 ++-- services/federation/repository_service.go | 19 -- services/federation/result.go | 35 ---- services/federation/signature_service.go | 11 +- .../integration/api_activitypub_actor_test.go | 77 -------- .../api_activitypub_person_test.go | 113 ----------- .../api_activitypub_repository_test.go | 176 ------------------ .../api_federation_httpsig_test.go | 82 -------- .../integration/repo_star_federation_test.go | 81 -------- 20 files changed, 69 insertions(+), 741 deletions(-) delete mode 100644 services/federation/error.go rename services/federation/{repository_inbox_like.go => repo_like.go} (73%) delete mode 100644 services/federation/repository_service.go delete mode 100644 services/federation/result.go delete mode 100644 tests/integration/api_activitypub_actor_test.go delete mode 100644 tests/integration/api_activitypub_person_test.go delete mode 100644 tests/integration/api_activitypub_repository_test.go delete mode 100644 tests/integration/api_federation_httpsig_test.go delete mode 100644 tests/integration/repo_star_federation_test.go diff --git a/.deadcode-out b/.deadcode-out index 31b04687dc..f9bee43043 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -246,10 +246,7 @@ forgejo.org/services/context GetPrivateContext forgejo.org/services/federation - NewErrInternalf - ErrInternal.Error Init - NewServiceResultWithBytes forgejo.org/services/repository IsErrForkAlreadyExist diff --git a/models/repo/repo_repository.go b/models/repo/repo_repository.go index 9d586b8345..0ba50e6614 100644 --- a/models/repo/repo_repository.go +++ b/models/repo/repo_repository.go @@ -38,18 +38,18 @@ func StoreFollowingRepos(ctx context.Context, localRepoID int64, followingRepoLi } // Begin transaction - dbCtx, committer, err := db.TxContext((ctx)) + ctx, committer, err := db.TxContext((ctx)) if err != nil { return err } defer committer.Close() - _, err = db.GetEngine(dbCtx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) + _, err = db.GetEngine(ctx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) if err != nil { return err } for _, followingRepo := range followingRepoList { - _, err = db.GetEngine(dbCtx).Insert(followingRepo) + _, err = db.GetEngine(ctx).Insert(followingRepo) if err != nil { return err } diff --git a/models/user/follow.go b/models/user/follow.go index 8663b2a943..e32c226385 100644 --- a/models/user/follow.go +++ b/models/user/follow.go @@ -39,21 +39,21 @@ func FollowUser(ctx context.Context, userID, followID int64) (err error) { return ErrBlockedByUser } - dbCtx, committer, err := db.TxContext(ctx) + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if err = db.Insert(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if err = db.Insert(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() @@ -65,21 +65,21 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) { return nil } - dbCtx, committer, err := db.TxContext(ctx) + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if _, err = db.DeleteByBean(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() diff --git a/models/user/user_repository.go b/models/user/user_repository.go index df864746e8..85f44f1598 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -28,7 +28,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } // Begin transaction - txCtx, committer, err := db.TxContext(ctx) + ctx, committer, err := db.TxContext((ctx)) if err != nil { return err } @@ -39,7 +39,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } }() - if err := CreateUser(txCtx, user, &overwrite); err != nil { + if err := CreateUser(ctx, user, &overwrite); err != nil { return err } @@ -48,7 +48,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return err } - _, err = db.GetEngine(txCtx).Insert(federatedUser) + _, err = db.GetEngine(ctx).Insert(federatedUser) if err != nil { return err } @@ -70,7 +70,7 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -87,7 +87,7 @@ func GetFederatedUser(ctx context.Context, externalID string, federationHostID i if err != nil { return nil, nil, err } else if federatedUser == nil { - return nil, nil, fmt.Errorf("FederatedUser not found (given externalId: %v, federationHostId: %v)", externalID, federationHostID) + return nil, nil, fmt.Errorf("FederatedUser for externalId = %v and federationHostId = %v does not exist", externalID, federationHostID) } return user, federatedUser, nil } @@ -99,13 +99,13 @@ func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("FederatedUser table does not contain entry for user ID: %v", federatedUser.UserID) + return nil, nil, fmt.Errorf("Federated user %v does not exist", federatedUser.UserID) } has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -130,7 +130,7 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) + return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index 11a2fd94c3..fb6fa8b543 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -66,11 +66,6 @@ type ClientFactory struct { // NewClient function func NewClientFactory() (c *ClientFactory, err error) { - return NewClientFactoryWithTimeout(5 * time.Second) -} - -// NewClient function -func NewClientFactoryWithTimeout(timeout time.Duration) (c *ClientFactory, err error) { if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil { return nil, err } else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil { @@ -82,7 +77,7 @@ func NewClientFactoryWithTimeout(timeout time.Duration) (c *ClientFactory, err e Transport: &http.Transport{ Proxy: proxy.Proxy(), }, - Timeout: timeout, + Timeout: 5 * time.Second, }, algs: setting.HttpsigAlgs, digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm), diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go index 3eaa6b82c5..c506840f1c 100644 --- a/routers/api/v1/activitypub/repository.go +++ b/routers/api/v1/activitypub/repository.go @@ -71,11 +71,10 @@ func RepositoryInbox(ctx *context.APIContext) { repository := ctx.Repo.Repository log.Info("RepositoryInbox: repo: %v", repository) form := web.GetForm(ctx) - activity := form.(*ap.Activity) - result, err := federation.ProcessRepositoryInbox(ctx, activity, repository.ID) + // TODO: Decide between like/undo{like} activity + httpStatus, title, err := federation.ProcessLikeActivity(ctx, form, repository.ID) if err != nil { - ctx.Error(federation.HTTPStatus(err), "Processing Repository Inbox failed", result) - return + ctx.Error(httpStatus, title, err) } - responseServiceResult(ctx, result) + ctx.Status(http.StatusNoContent) } diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 38cb067b89..91274249ec 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -8,13 +8,13 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" - services_context "forgejo.org/services/context" + gitea_context "forgejo.org/services/context" "forgejo.org/services/federation" "github.com/42wim/httpsig" ) -func verifyHTTPUserOrInstanceSignature(ctx services_context.APIContext) (authenticated bool, err error) { +func verifyHTTPUserOrInstanceSignature(ctx *gitea_context.APIContext) (authenticated bool, err error) { if !setting.Federation.SignatureEnforced { return true, nil } @@ -28,9 +28,9 @@ func verifyHTTPUserOrInstanceSignature(ctx services_context.APIContext) (authent } signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) - pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx.Base, v.KeyId()) if err != nil || pubKey == nil { - pubKey, err = federation.FindOrCreateFederationHostKey(ctx, v.KeyId()) + pubKey, err = federation.FindOrCreateFederationHostKey(ctx.Base, v.KeyId()) if err != nil { return false, err } @@ -43,7 +43,7 @@ func verifyHTTPUserOrInstanceSignature(ctx services_context.APIContext) (authent return true, nil } -func verifyHTTPUserSignature(ctx services_context.APIContext) (authenticated bool, err error) { +func verifyHTTPUserSignature(ctx *gitea_context.APIContext) (authenticated bool, err error) { if !setting.Federation.SignatureEnforced { return true, nil } @@ -57,7 +57,7 @@ func verifyHTTPUserSignature(ctx services_context.APIContext) (authenticated boo } signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) - pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx.Base, v.KeyId()) if err != nil { return false, err } @@ -70,9 +70,9 @@ func verifyHTTPUserSignature(ctx services_context.APIContext) (authenticated boo } // ReqHTTPSignature function -func ReqHTTPUserOrInstanceSignature() func(ctx *services_context.APIContext) { - return func(ctx *services_context.APIContext) { - if authenticated, err := verifyHTTPUserOrInstanceSignature(*ctx); err != nil { +func ReqHTTPUserOrInstanceSignature() func(ctx *gitea_context.APIContext) { + return func(ctx *gitea_context.APIContext) { + if authenticated, err := verifyHTTPUserOrInstanceSignature(ctx); err != nil { log.Warn("verifyHttpSignatures failed: %v", err) ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { @@ -81,10 +81,10 @@ func ReqHTTPUserOrInstanceSignature() func(ctx *services_context.APIContext) { } } -// ReqHTTPUserSignature function -func ReqHTTPUserSignature() func(ctx *services_context.APIContext) { - return func(ctx *services_context.APIContext) { - if authenticated, err := verifyHTTPUserSignature(*ctx); err != nil { +// ReqHTTPSignature function +func ReqHTTPUserSignature() func(ctx *gitea_context.APIContext) { + return func(ctx *gitea_context.APIContext) { + if authenticated, err := verifyHTTPUserSignature(ctx); err != nil { log.Warn("verifyHttpSignatures failed: %v", err) ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { diff --git a/routers/api/v1/activitypub/response.go b/routers/api/v1/activitypub/response.go index 64413cebb1..a97f363cc2 100644 --- a/routers/api/v1/activitypub/response.go +++ b/routers/api/v1/activitypub/response.go @@ -10,46 +10,12 @@ import ( "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/services/context" - "forgejo.org/services/federation" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" ) // Respond with an ActivityStreams object -func responseServiceResult(ctx *context.APIContext, result federation.ServiceResult) { - ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) - - switch { - case result.StatusOnly(): - ctx.Resp.WriteHeader(result.HTTPStatus) - return - case result.WithBytes(): - ctx.Resp.WriteHeader(result.HTTPStatus) - if _, err := ctx.Resp.Write(result.Bytes); err != nil { - log.Error("Error writing a response: %v", err) - ctx.Error(http.StatusInternalServerError, "Error writing a response", err) - return - } - case result.WithActivity(): - binary, err := jsonld.WithContext( - jsonld.IRI(ap.ActivityBaseURI), - jsonld.IRI(ap.SecurityContextURI), - jsonld.IRI(forgefed.ForgeFedNamespaceURI), - ).Marshal(result.Activity) - if err != nil { - ctx.ServerError("Marshal", err) - return - } - ctx.Resp.WriteHeader(result.HTTPStatus) - if _, err = ctx.Resp.Write(binary); err != nil { - log.Error("write to resp err: %v", err) - } - } -} - -// Respond with an ActivityStreams object -// Deprecated func response(ctx *context.APIContext, v any) { binary, err := jsonld.WithContext( jsonld.IRI(ap.ActivityBaseURI), diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 2f806ba35d..6a51f33bd8 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -81,6 +81,7 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" user_model "forgejo.org/models/user" + "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" @@ -852,7 +853,7 @@ func Routes() *web.Route { m.Group("/repository-id/{repository-id}", func() { m.Get("", activitypub.ReqHTTPUserSignature(), activitypub.Repository) m.Post("/inbox", - bind(ap.Activity{}), + bind(forgefed.ForgeLike{}), activitypub.ReqHTTPUserSignature(), activitypub.RepositoryInbox) }, context.RepositoryIDAssignmentAPI()) diff --git a/services/federation/error.go b/services/federation/error.go deleted file mode 100644 index 425035d0d5..0000000000 --- a/services/federation/error.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package federation - -import ( - "fmt" - "net/http" -) - -type ErrNotAcceptable struct { - Message string -} - -func NewErrNotAcceptablef(format string, a ...any) ErrNotAcceptable { - message := fmt.Sprintf(format, a...) - return ErrNotAcceptable{Message: message} -} - -func (err ErrNotAcceptable) Error() string { - return fmt.Sprintf("NotAcceptable: %v", err.Message) -} - -type ErrInternal struct { - Message string -} - -func NewErrInternalf(format string, a ...any) ErrInternal { - message := fmt.Sprintf(format, a...) - return ErrInternal{Message: message} -} - -func (err ErrInternal) Error() string { - return fmt.Sprintf("InternalServerError: %v", err.Message) -} - -func HTTPStatus(err error) int { - switch err.(type) { - case ErrNotAcceptable: - return http.StatusNotAcceptable - default: - return http.StatusInternalServerError - } -} diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index 36788e725a..b71d8d2575 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -18,6 +18,7 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/modules/validation" + context_service "forgejo.org/services/context" "github.com/google/uuid" ) @@ -26,7 +27,7 @@ func Init() error { return nil } -func FindOrCreateFederationHost(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) { +func FindOrCreateFederationHost(ctx *context_service.Base, actorURI string) (*forgefed.FederationHost, error) { rawActorID, err := fm.NewActorID(actorURI) if err != nil { return nil, err @@ -45,7 +46,7 @@ func FindOrCreateFederationHost(ctx context.Context, actorURI string) (*forgefed return federationHost, nil } -func FindOrCreateFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { +func FindOrCreateFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { user, federatedUser, federationHost, err := findFederatedUser(ctx, actorURI) if err != nil { return nil, nil, nil, err @@ -56,21 +57,20 @@ func FindOrCreateFederatedUser(ctx context.Context, actorURI string) (*user.User } if user != nil { - log.Trace("Local ActivityPub user found (actorURI: %#v, user: %#v)", actorURI, user) + log.Trace("Found local federatedUser: %#v", user) } else { - log.Trace("Attempting to create new user and federatedUser for actorURI: %#v", actorURI) user, federatedUser, err = createUserFromAP(ctx, personID, federationHost.ID) if err != nil { return nil, nil, nil, err } - log.Trace("Created user %#v with federatedUser %#v from distant server", user, federatedUser) + log.Trace("Created federatedUser from ap: %#v", user) } log.Trace("Got user: %v", user.Name) return user, federatedUser, federationHost, nil } -func findFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { +func findFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { federationHost, err := FindOrCreateFederationHost(ctx, actorURI) if err != nil { return nil, nil, nil, err @@ -90,7 +90,6 @@ func findFederatedUser(ctx context.Context, actorURI string) (*user.User, *user. func createFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) { actionsUser := user.NewAPServerActor() - clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, err @@ -162,7 +161,7 @@ func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID return nil, nil, err } - log.Info("Fetched valid person from distant server: %q", person) + log.Info("Fetched valid person:%q", person) localFqdn, err := url.ParseRequestURI(setting.AppURL) if err != nil { @@ -221,7 +220,7 @@ func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID }, } - log.Info("Fetched person's %q federatedUser from distant server: %q", person, federatedUser) + log.Info("Fetch federatedUser:%q", federatedUser) return &newUser, &federatedUser, nil } @@ -235,6 +234,6 @@ func createUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI return nil, nil, err } - log.Info("Created federatedUser: %q", federatedUser) + log.Info("Created federatedUser:%q", federatedUser) return newUser, federatedUser, nil } diff --git a/services/federation/repository_inbox_like.go b/services/federation/repo_like.go similarity index 73% rename from services/federation/repository_inbox_like.go rename to services/federation/repo_like.go index 478a12d92c..c1e6500c61 100644 --- a/services/federation/repository_inbox_like.go +++ b/services/federation/repo_like.go @@ -5,6 +5,7 @@ package federation import ( "context" + "errors" "fmt" "net/http" "time" @@ -17,8 +18,6 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/validation" context_service "forgejo.org/services/context" - - ap "github.com/go-ap/activitypub" ) // ProcessLikeActivity receives a ForgeLike activity and does the following: @@ -28,32 +27,32 @@ import ( // Validation of incoming RepositoryID against Local RepositoryID // Star the repo if it wasn't already stared // Do some mitigation against out of order attacks -func ProcessLikeActivity(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { - constructorLikeActivity, _ := fm.NewForgeLike(activity.Actor.GetLink().String(), activity.Object.GetLink().String(), activity.StartTime) - if res, err := validation.IsValid(constructorLikeActivity); !res { - return ServiceResult{}, NewErrNotAcceptablef("Invalid activity: %v", err) +func ProcessLikeActivity(ctx *context_service.APIContext, form any, repositoryID int64) (int, string, error) { + activity := form.(*fm.ForgeLike) + if res, err := validation.IsValid(activity); !res { + return http.StatusNotAcceptable, "Invalid activity", err } log.Trace("Activity validated: %#v", activity) // parse actorID (person) actorURI := activity.Actor.GetID().String() - user, _, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI) + user, _, federationHost, err := FindOrCreateFederatedUser(ctx.Base, actorURI) if err != nil { - log.Error("Federated user not found (%s): %v", actorURI, err) - return ServiceResult{}, NewErrNotAcceptablef("FindOrCreateFederatedUser failed: %v", err) + ctx.Error(http.StatusNotAcceptable, "Federated user not found", err) + return http.StatusInternalServerError, "FindOrCreateFederatedUser", err } - if !constructorLikeActivity.IsNewer(federationHost.LatestActivity) { - return ServiceResult{}, NewErrNotAcceptablef("LatestActivity: activity already processed: %v", err) + if !activity.IsNewer(federationHost.LatestActivity) { + return http.StatusNotAcceptable, "Activity out of order.", errors.New("Activity already processed") } // parse objectID (repository) - objectID, err := fm.NewRepositoryID(constructorLikeActivity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) + objectID, err := fm.NewRepositoryID(activity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) if err != nil { - return ServiceResult{}, NewErrNotAcceptablef("Parsing repo objectID failed: %v", err) + return http.StatusNotAcceptable, "Invalid objectId", err } if objectID.ID != fmt.Sprint(repositoryID) { - return ServiceResult{}, NewErrNotAcceptablef("Invalid repoId: %v", err) + return http.StatusNotAcceptable, "Invalid objectId", err } log.Trace("Object accepted: %#v", objectID) @@ -62,16 +61,16 @@ func ProcessLikeActivity(ctx context.Context, activity *ap.Activity, repositoryI if !alreadyStared { err = repo.StarRepo(ctx, user.ID, repositoryID, true) if err != nil { - return ServiceResult{}, NewErrNotAcceptablef("Staring failed: %v", err) + return http.StatusNotAcceptable, "Error staring", err } } federationHost.LatestActivity = activity.StartTime err = forgefed.UpdateFederationHost(ctx, federationHost) if err != nil { - return ServiceResult{}, NewErrNotAcceptablef("Updating federatedHost failed: %v", err) + return http.StatusNotAcceptable, "Error updating federatedHost", err } - return NewServiceResultStatusOnly(http.StatusNoContent), nil + return 0, "", nil } // Create or update a list of FollowingRepo structs diff --git a/services/federation/repository_service.go b/services/federation/repository_service.go deleted file mode 100644 index 7891d786e2..0000000000 --- a/services/federation/repository_service.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package federation - -import ( - "context" - - ap "github.com/go-ap/activitypub" -) - -func ProcessRepositoryInbox(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { - switch activity.Type { - case ap.LikeType: - return ProcessLikeActivity(ctx, activity, repositoryID) - default: - return ServiceResult{}, NewErrNotAcceptablef("Not a like activity: %v", activity.Type) - } -} diff --git a/services/federation/result.go b/services/federation/result.go deleted file mode 100644 index 47afb2bdf6..0000000000 --- a/services/federation/result.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package federation - -import "github.com/go-ap/activitypub" - -type ServiceResult struct { - HTTPStatus int - Bytes []byte - Activity activitypub.Activity - withBytes bool - withActivity bool - statusOnly bool -} - -func NewServiceResultStatusOnly(status int) ServiceResult { - return ServiceResult{HTTPStatus: status, statusOnly: true} -} - -func NewServiceResultWithBytes(status int, bytes []byte) ServiceResult { - return ServiceResult{HTTPStatus: status, Bytes: bytes, withBytes: true} -} - -func (serviceResult ServiceResult) WithBytes() bool { - return serviceResult.withBytes -} - -func (serviceResult ServiceResult) WithActivity() bool { - return serviceResult.withActivity -} - -func (serviceResult ServiceResult) StatusOnly() bool { - return serviceResult.statusOnly -} diff --git a/services/federation/signature_service.go b/services/federation/signature_service.go index fd8cbb39cd..e5102b89d8 100644 --- a/services/federation/signature_service.go +++ b/services/federation/signature_service.go @@ -4,7 +4,6 @@ package federation import ( - "context" "crypto/x509" "database/sql" "encoding/pem" @@ -16,12 +15,13 @@ import ( "forgejo.org/models/user" "forgejo.org/modules/activitypub" fm "forgejo.org/modules/forgefed" + context_service "forgejo.org/services/context" ap "github.com/go-ap/activitypub" ) // Factory function for ActorID. Created struct is asserted to be valid -func NewActorIDFromKeyID(ctx context.Context, uri string) (fm.ActorID, error) { +func NewActorIDFromKeyID(ctx *context_service.Base, uri string) (fm.ActorID, error) { parsedURI, err := url.Parse(uri) parsedURI.Fragment = "" if err != nil { @@ -54,7 +54,7 @@ func NewActorIDFromKeyID(ctx context.Context, uri string) (fm.ActorID, error) { return result, err } -func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any, err error) { +func FindOrCreateFederatedUserKey(ctx *context_service.Base, keyID string) (pubKey any, err error) { var federatedUser *user.FederatedUser var keyURL *url.URL @@ -122,7 +122,7 @@ func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any return nil, nil } -func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey any, err error) { +func FindOrCreateFederationHostKey(ctx *context_service.Base, keyID string) (pubKey any, err error) { keyURL, err := url.Parse(keyID) if err != nil { return nil, err @@ -183,9 +183,8 @@ func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey an return nil, nil } -func fetchKeyFromAp(ctx context.Context, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) { +func fetchKeyFromAp(ctx *context_service.Base, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) { actionsUser := user.NewAPServerActor() - clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, nil, nil, err diff --git a/tests/integration/api_activitypub_actor_test.go b/tests/integration/api_activitypub_actor_test.go deleted file mode 100644 index 42232bd640..0000000000 --- a/tests/integration/api_activitypub_actor_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "strconv" - "testing" - - "forgejo.org/modules/forgefed" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - "forgejo.org/services/contexttest" - "forgejo.org/services/federation" - "forgejo.org/tests" - - ap "github.com/go-ap/activitypub" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActivityPubActor(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - defer tests.PrepareTestEnv(t)() - - req := NewRequest(t, "GET", "/api/v1/activitypub/actor") - resp := MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), "@context") - - var actor ap.Actor - err := actor.UnmarshalJSON(resp.Body.Bytes()) - require.NoError(t, err) - - assert.Equal(t, ap.ApplicationType, actor.Type) - assert.Equal(t, "ghost", actor.PreferredUsername.String()) - keyID := actor.GetID().String() - assert.Regexp(t, "activitypub/actor$", keyID) - assert.Regexp(t, "activitypub/actor/inbox$", actor.Inbox.GetID().String()) - - pubKey := actor.PublicKey - assert.NotNil(t, pubKey) - publicKeyID := keyID + "#main-key" - assert.Equal(t, pubKey.ID.String(), publicKeyID) - - pubKeyPem := pubKey.PublicKeyPem - assert.NotNil(t, pubKeyPem) - assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem) -} - -func TestActorNewFromKeyId(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - ctx, _ := contexttest.MockAPIContext(t, "/api/v1/activitypub/actor") - sut, err := federation.NewActorIDFromKeyID(ctx.Base, fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", u)) - require.NoError(t, err) - - port, err := strconv.ParseUint(u.Port(), 10, 16) - require.NoError(t, err) - - assert.Equal(t, forgefed.ActorID{ - ID: "actor", - HostSchema: "http", - Path: "api/v1/activitypub", - Host: setting.Domain, - HostPort: uint16(port), - UnvalidatedInput: fmt.Sprintf("http://%s:%d/api/v1/activitypub/actor", setting.Domain, port), - IsPortSupplemented: false, - }, sut) - }) -} diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go deleted file mode 100644 index 277b150a1e..0000000000 --- a/tests/integration/api_activitypub_person_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "testing" - "time" - - "forgejo.org/models/unittest" - user_model "forgejo.org/models/user" - "forgejo.org/modules/activitypub" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - "forgejo.org/services/contexttest" - "forgejo.org/tests" - - ap "github.com/go-ap/activitypub" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActivityPubPerson(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - onGiteaRun(t, func(t *testing.T, localUrl *url.URL) { - defer test.MockVariableValue(&setting.AppURL, localUrl.String())() - - localUserID := 2 - localUserName := "user2" - localUserURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", localUrl, localUserID) - - // distantURL := federatedSrv.URL - // distantUser15URL := fmt.Sprintf("%s/api/v1/activitypub/user-id/15", distantURL) - - // Unsigned request - t.Run("UnsignedRequest", func(t *testing.T) { - req := NewRequest(t, "GET", localUserURL) - MakeRequest(t, req, http.StatusBadRequest) - }) - - // Signed request - t.Run("SignedRequestValidation", func(t *testing.T) { - ctx, _ := contexttest.MockAPIContext(t, localUserURL) - cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) - require.NoError(t, err) - - c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, - mock.Persons[0].KeyID(federatedSrv.URL)) - require.NoError(t, err) - - resp, err := c.GetBody(localUserURL) - require.NoError(t, err) - - var person ap.Person - err = person.UnmarshalJSON(resp) - require.NoError(t, err) - - assert.Equal(t, ap.PersonType, person.Type) - assert.Equal(t, localUserName, person.PreferredUsername.String()) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d$", localUserID), person.GetID()) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/inbox$", localUserID), person.Inbox.GetID().String()) - - assert.NotNil(t, person.PublicKey) - assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d#main-key$", localUserID), person.PublicKey.ID) - - assert.NotNil(t, person.PublicKey.PublicKeyPem) - assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", person.PublicKey.PublicKeyPem) - }) - }) -} - -func TestActivityPubMissingPerson(t *testing.T) { - defer tests.PrepareTestEnv(t)() - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - req := NewRequest(t, "GET", "/api/v1/activitypub/user-id/999999999") - resp := MakeRequest(t, req, http.StatusNotFound) - assert.Contains(t, resp.Body.String(), "user does not exist") -} - -func TestActivityPubPersonInbox(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - defer test.MockVariableValue(&setting.AppURL, u.String())() - user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - - user1url := u.JoinPath("/api/v1/activitypub/user-id/1").String() + "#main-key" - user2inboxurl := u.JoinPath("/api/v1/activitypub/user-id/2/inbox").String() - ctx, _ := contexttest.MockAPIContext(t, user2inboxurl) - cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) - require.NoError(t, err) - c, err := cf.WithKeys(ctx, user1, user1url) - require.NoError(t, err) - - // Signed request "succeeds" - resp, err := c.Post([]byte{}, user2inboxurl) - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - }) -} diff --git a/tests/integration/api_activitypub_repository_test.go b/tests/integration/api_activitypub_repository_test.go deleted file mode 100644 index b4be0407b9..0000000000 --- a/tests/integration/api_activitypub_repository_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "testing" - "time" - - "forgejo.org/models/forgefed" - "forgejo.org/models/unittest" - "forgejo.org/models/user" - "forgejo.org/modules/activitypub" - forgefed_modules "forgejo.org/modules/forgefed" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - "forgejo.org/services/contexttest" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestActivityPubRepository(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - repositoryID := 2 - - localRepository := fmt.Sprintf("%sapi/v1/activitypub/repository-id/%d", u, repositoryID) - - ctx, _ := contexttest.MockAPIContext(t, localRepository) - cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) - require.NoError(t, err) - - c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, - mock.Persons[0].KeyID(federatedSrv.URL)) - require.NoError(t, err) - - resp, err := c.GetBody(localRepository) - require.NoError(t, err) - assert.Contains(t, string(resp), "@context") - - var repository forgefed_modules.Repository - err = repository.UnmarshalJSON(resp) - require.NoError(t, err) - - assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%d$", repositoryID), repository.GetID().String()) - }) -} - -func TestActivityPubMissingRepository(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - repositoryID := 9999999 - req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)) - resp := MakeRequest(t, req, http.StatusNotFound) - assert.Contains(t, resp.Body.String(), "repository does not exist") -} - -func TestActivityPubRepositoryInboxValid(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - repositoryID := 2 - timeNow := time.Now().UTC() - localRepoInbox := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() - - ctx, _ := contexttest.MockAPIContext(t, localRepoInbox) - cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) - require.NoError(t, err) - - c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, - mock.Persons[0].KeyID(federatedSrv.URL)) - require.NoError(t, err) - - activity1 := []byte(fmt.Sprintf( - `{"type":"Like",`+ - `"startTime":"%s",`+ - `"actor":"%s/api/v1/activitypub/user-id/15",`+ - `"object":"%s"}`, - timeNow.Format(time.RFC3339), - federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) - t.Logf("activity: %s", activity1) - resp, err := c.Post(activity1, localRepoInbox) - - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - - federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) - federatedUser := unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "15", FederationHostID: federationHost.ID}) - unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) - - // A like activity by a different user of the same federated host. - activity2 := []byte(fmt.Sprintf( - `{"type":"Like",`+ - `"startTime":"%s",`+ - `"actor":"%s/api/v1/activitypub/user-id/30",`+ - `"object":"%s"}`, - // Make sure this activity happens later then the one before - timeNow.Add(time.Second).Format(time.RFC3339), - federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) - t.Logf("activity: %s", activity2) - resp, err = c.Post(activity2, localRepoInbox) - - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - - federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) - unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) - - // The same user sends another like activity - otherRepositoryID := 3 - otherRepoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", otherRepositoryID)).String() - activity3 := []byte(fmt.Sprintf( - `{"type":"Like",`+ - `"startTime":"%s",`+ - `"actor":"%s/api/v1/activitypub/user-id/30",`+ - `"object":"%s"}`, - // Make sure this activity happens later then the ones before - timeNow.Add(time.Second*2).Format(time.RFC3339), - federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", otherRepositoryID)).String())) - t.Logf("activity: %s", activity3) - resp, err = c.Post(activity3, otherRepoInboxURL) - - require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) - - federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) - unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) - - // Replay activity2. - resp, err = c.Post(activity2, localRepoInbox) - require.NoError(t, err) - assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) - }) -} - -func TestActivityPubRepositoryInboxInvalid(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - apServerActor := user.NewAPServerActor() - repositoryID := 2 - localRepo2Inbox := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() - - ctx, _ := contexttest.MockAPIContext(t, localRepo2Inbox) - cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) - require.NoError(t, err) - - c, err := cf.WithKeys(ctx, apServerActor, apServerActor.KeyID()) - require.NoError(t, err) - - activity := []byte(`{"type":"Wrong"}`) - resp, err := c.Post(activity, localRepo2Inbox) - require.NoError(t, err) - assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) - }) -} diff --git a/tests/integration/api_federation_httpsig_test.go b/tests/integration/api_federation_httpsig_test.go deleted file mode 100644 index a8deaa315f..0000000000 --- a/tests/integration/api_federation_httpsig_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2025 The Forgejo Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "net/url" - "testing" - - "forgejo.org/models/db" - "forgejo.org/models/forgefed" - "forgejo.org/models/unittest" - "forgejo.org/models/user" - "forgejo.org/modules/activitypub" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/routers" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestFederationHttpSigValidation(t *testing.T) { - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - - onGiteaRun(t, func(t *testing.T, u *url.URL) { - userID := 2 - userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID) - - user1 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 1}) - - clientFactory, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - - apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.KeyID()) - require.NoError(t, err) - - // Unsigned request - t.Run("UnsignedRequest", func(t *testing.T) { - req := NewRequest(t, "GET", userURL) - MakeRequest(t, req, http.StatusBadRequest) - }) - - // Signed request - t.Run("SignedRequest", func(t *testing.T) { - resp, err := apClient.Get(userURL) - require.NoError(t, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) - }) - - // HACK HACK HACK: the host part of the URL gets set to which IP forgejo is - // listening on, NOT localhost, which is the Domain given to forgejo which - // is then used for eg. the keyID all requests - applicationKeyID := fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", setting.AppURL) - actorKeyID := fmt.Sprintf("%sapi/v1/activitypub/user-id/1#main-key", setting.AppURL) - - // Check for cached public keys - t.Run("ValidateCaches", func(t *testing.T) { - host, err := forgefed.FindFederationHostByKeyID(db.DefaultContext, applicationKeyID) - require.NoError(t, err) - assert.NotNil(t, host) - assert.True(t, host.PublicKey.Valid) - - _, user, err := user.FindFederatedUserByKeyID(db.DefaultContext, actorKeyID) - require.NoError(t, err) - assert.NotNil(t, user) - assert.True(t, user.PublicKey.Valid) - }) - - // Disable signature validation - defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() - - // Unsigned request - t.Run("SignatureValidationDisabled", func(t *testing.T) { - req := NewRequest(t, "GET", userURL) - MakeRequest(t, req, http.StatusOK) - }) - }) -} diff --git a/tests/integration/repo_star_federation_test.go b/tests/integration/repo_star_federation_test.go deleted file mode 100644 index ae9a4e9f83..0000000000 --- a/tests/integration/repo_star_federation_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. -// SPDX-License-Identifier: MIT - -package integration - -import ( - "fmt" - "net/http" - "strings" - "testing" - - "forgejo.org/models/forgefed" - repo_model "forgejo.org/models/repo" - "forgejo.org/models/unittest" - user_model "forgejo.org/models/user" - fm "forgejo.org/modules/forgefed" - "forgejo.org/modules/setting" - "forgejo.org/modules/test" - "forgejo.org/modules/validation" - "forgejo.org/tests" -) - -func TestActivityPubRepoFollowing(t *testing.T) { - defer tests.PrepareTestEnv(t)() - defer test.MockVariableValue(&setting.Federation.Enabled, true)() - - mock := test.NewFederationServerMock() - federatedSrv := mock.DistantServer(t) - defer federatedSrv.Close() - - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) - session := loginUser(t, user.Name) - - t.Run("Add a following repo", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - link := fmt.Sprintf("/%s/settings", repo.FullName()) - - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": GetCSRF(t, session, link), - "action": "federation", - "following_repos": fmt.Sprintf("%s/api/v1/activitypub/repository-id/1", federatedSrv.URL), - }) - session.MakeRequest(t, req, http.StatusSeeOther) - - // Verify it was added. - federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) - unittest.AssertExistsAndLoadBean(t, &repo_model.FollowingRepo{ - ExternalID: "1", - FederationHostID: federationHost.ID, - }) - }) - - t.Run("Star a repo having a following repo", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - repoLink := fmt.Sprintf("/%s", repo.FullName()) - link := fmt.Sprintf("%s/action/star", repoLink) - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": GetCSRF(t, session, repoLink), - }) - - session.MakeRequest(t, req, http.StatusOK) - - // Verify distant server received a like activity - like := fm.ForgeLike{} - err := like.UnmarshalJSON([]byte(mock.LastPost)) - if err != nil { - t.Errorf("Error unmarshalling ForgeLike: %q", err) - } - if isValid, err := validation.IsValid(like); !isValid { - t.Errorf("ForgeLike is not valid: %q", err) - } - activityType := like.Type - object := like.Object.GetLink().String() - isLikeType := activityType == "Like" - isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1") - if !isLikeType || !isCorrectObject { - t.Error("Activity is not a like for this repo") - } - }) -} From 29eaab5ff420c7ee97441689bba0a3d9f659ddc1 Mon Sep 17 00:00:00 2001 From: Leni Kadali Date: Mon, 28 Jul 2025 14:52:13 +0200 Subject: [PATCH 197/297] feat: add configurable timeout for automatically removing resolved reports (#7940) Supersedes [this PR](https://codeberg.org/lenikadali/forgejo/pulls/1) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7940 Reviewed-by: Otto Reviewed-by: Gusted Co-authored-by: Leni Kadali Co-committed-by: Leni Kadali --- custom/conf/app.example.ini | 5 ++ models/forgejo_migrations/migrate.go | 3 + models/forgejo_migrations/v38.go | 19 ++++++ models/moderation/abuse_report.go | 21 ++++++ modules/setting/moderation.go | 21 +++++- modules/setting/setting.go | 5 +- services/cron/tasks_extended.go | 22 ++++++ services/moderation/main_test.go | 17 +++++ services/moderation/reporting.go | 41 +++++++++++ services/moderation/reporting_test.go | 97 +++++++++++++++++++++++++++ 10 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 models/forgejo_migrations/v38.go create mode 100644 services/moderation/main_test.go create mode 100644 services/moderation/reporting_test.go diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 71598663b2..8f7c4658ce 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1586,6 +1586,11 @@ LEVEL = Info ;; If enabled it will be possible for users to report abusive content (new actions are added in the UI and /report_abuse route will be enabled) and a new Moderation section will be added to Admin settings where the reports can be reviewed. ;ENABLED = false +;; How long to keep resolved abuse reports for. +;; Applies to reports that have been marked as ignored or handled +;; Can be 1 hour, 7 days etc +;KEEP_RESOLVED_REPORTS_FOR = 0 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[openid] diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 384f382c82..a22d35dd21 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -111,7 +111,10 @@ var migrations = []*Migration{ NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), // v35 -> v36 NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), + // v36 -> v37 NewMigration("Add `branch_filter` to `push_mirror` table", AddPushMirrorBranchFilter), + // v37 -> v38 + NewMigration("Add `resolved_unix` column to `abuse_report` table", AddResolvedUnixToAbuseReport), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v38.go b/models/forgejo_migrations/v38.go new file mode 100644 index 0000000000..24240f15a0 --- /dev/null +++ b/models/forgejo_migrations/v38.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "forgejo.org/modules/timeutil" + + "xorm.io/xorm" +) + +func AddResolvedUnixToAbuseReport(x *xorm.Engine) error { + type AbuseReport struct { + ID int64 `xorm:"pk autoincr"` + ResolvedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"` + } + + return x.Sync(&AbuseReport{}) +} diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go index 9852268910..0bf8aab174 100644 --- a/models/moderation/abuse_report.go +++ b/models/moderation/abuse_report.go @@ -8,6 +8,7 @@ import ( "database/sql" "errors" "slices" + "time" "forgejo.org/models/db" "forgejo.org/modules/log" @@ -111,6 +112,7 @@ type AbuseReport struct { // The ID of the corresponding shadow-copied content when exists; otherwise null. ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` + ResolvedUnix timeutil.TimeStamp `xorm:"DEFAULT NULL"` } var ErrSelfReporting = errors.New("reporting yourself is not allowed") @@ -161,6 +163,25 @@ func ReportAbuse(ctx context.Context, report *AbuseReport) error { return err } +// GetResolvedReports gets all resolved reports +func GetResolvedReports(ctx context.Context, keepReportsFor time.Duration) ([]*AbuseReport, error) { + cond := builder.And( + builder.Or( + builder.Eq{"`status`": ReportStatusTypeHandled}, + builder.Eq{"`status`": ReportStatusTypeIgnored}, + ), + ) + + if keepReportsFor > 0 { + cond = cond.And(builder.Lt{"resolved_unix": time.Now().Add(-keepReportsFor).Unix()}) + } + + abuseReports := make([]*AbuseReport, 0, 30) + return abuseReports, db.GetEngine(ctx). + Where(cond). + Find(&abuseReports) +} + /* // MarkAsHandled will change the status to 'Handled' for all reports linked to the same item (user, repository, issue or comment). func MarkAsHandled(ctx context.Context, contentType ReportedContentType, contentID int64) error { diff --git a/modules/setting/moderation.go b/modules/setting/moderation.go index 5f35a284d6..799efed761 100644 --- a/modules/setting/moderation.go +++ b/modules/setting/moderation.go @@ -3,13 +3,28 @@ package setting +import ( + "fmt" + "time" +) + // Moderation settings var Moderation = struct { - Enabled bool `ini:"ENABLED"` + Enabled bool `ini:"ENABLED"` + KeepResolvedReportsFor time.Duration `ini:"KEEP_RESOLVED_REPORTS_FOR"` }{ Enabled: false, } -func loadModerationFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "moderation", &Moderation) +func loadModerationFrom(rootCfg ConfigProvider) error { + sec := rootCfg.Section("moderation") + err := sec.MapTo(&Moderation) + if err != nil { + return fmt.Errorf("failed to map Moderation settings: %v", err) + } + + // keep reports for one week by default. Since time.Duration stops at the unit of an hour + // we are using the value of 24 (hours) * 7 (days) which gives us the value of 168 + Moderation.KeepResolvedReportsFor = sec.Key("KEEP_RESOLVED_REPORTS_FOR").MustDuration(168 * time.Hour) + return nil } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 75c24580b2..9644d9b83b 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -140,6 +140,10 @@ func loadCommonSettingsFrom(cfg ConfigProvider) error { if err := loadActionsFrom(cfg); err != nil { return err } + if err := loadModerationFrom(cfg); err != nil { + return err + } + loadUIFrom(cfg) loadAdminFrom(cfg) loadAPIFrom(cfg) @@ -221,7 +225,6 @@ func LoadSettings() { loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadF3From(CfgProvider) - loadModerationFrom(CfgProvider) } // LoadSettingsForInstall initializes the settings for install diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 322fe27ca0..3006601366 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -15,6 +15,7 @@ import ( issue_indexer "forgejo.org/modules/indexer/issues" "forgejo.org/modules/setting" "forgejo.org/modules/updatechecker" + moderation_service "forgejo.org/services/moderation" repo_service "forgejo.org/services/repository" archiver_service "forgejo.org/services/repository/archiver" user_service "forgejo.org/services/user" @@ -225,6 +226,24 @@ func registerRebuildIssueIndexer() { }) } +func registerRemoveResolvedReports() { + type ReportConfig struct { + BaseConfig + ConfigKeepResolvedReportsFor time.Duration + } + RegisterTaskFatal("remove_resolved_reports", &ReportConfig{ + BaseConfig: BaseConfig{ + Enabled: false, + RunAtStart: false, + Schedule: "@every 24h", + }, + ConfigKeepResolvedReportsFor: setting.Moderation.KeepResolvedReportsFor, + }, func(ctx context.Context, _ *user_model.User, config Config) error { + reportConfig := config.(*ReportConfig) + return moderation_service.RemoveResolvedReports(ctx, reportConfig.ConfigKeepResolvedReportsFor) + }) +} + func initExtendedTasks() { registerDeleteInactiveUsers() registerDeleteRepositoryArchives() @@ -240,4 +259,7 @@ func initExtendedTasks() { registerDeleteOldSystemNotices() registerGCLFS() registerRebuildIssueIndexer() + if setting.Moderation.Enabled { + registerRemoveResolvedReports() + } } diff --git a/services/moderation/main_test.go b/services/moderation/main_test.go new file mode 100644 index 0000000000..3a268260d2 --- /dev/null +++ b/services/moderation/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "testing" + + "forgejo.org/models/unittest" + + _ "forgejo.org/models/forgefed" + _ "forgejo.org/models/moderation" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/services/moderation/reporting.go b/services/moderation/reporting.go index e01156dc11..3d1bb5b32c 100644 --- a/services/moderation/reporting.go +++ b/services/moderation/reporting.go @@ -4,8 +4,11 @@ package moderation import ( + stdCtx "context" "errors" + "time" + "forgejo.org/models/db" "forgejo.org/models/issues" "forgejo.org/models/moderation" "forgejo.org/models/perm" @@ -127,3 +130,41 @@ func CanReport(ctx context.Context, doer *user.User, contentType moderation.Repo return hasAccess, nil } + +// RemoveResolvedReports removes resolved reports +func RemoveResolvedReports(ctx stdCtx.Context, keepReportsFor time.Duration) error { + log.Trace("Doing: RemoveResolvedReports") + + if keepReportsFor <= 0 { + return nil + } + + err := db.WithTx(ctx, func(ctx stdCtx.Context) error { + resolvedReports, err := moderation.GetResolvedReports(ctx, keepReportsFor) + if err != nil { + return err + } + + for _, report := range resolvedReports { + _, err := db.GetEngine(ctx).ID(report.ID).Delete(&moderation.AbuseReport{}) + if err != nil { + return err + } + + if report.ShadowCopyID.Valid { + _, err := db.GetEngine(ctx).ID(report.ShadowCopyID).Delete(&moderation.AbuseReportShadowCopy{}) + if err != nil { + return err + } + } + } + + return nil + }) + if err != nil { + return err + } + + log.Trace("Finished: RemoveResolvedReports") + return nil +} diff --git a/services/moderation/reporting_test.go b/services/moderation/reporting_test.go new file mode 100644 index 0000000000..70925bf184 --- /dev/null +++ b/services/moderation/reporting_test.go @@ -0,0 +1,97 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "testing" + "time" + + "forgejo.org/models/db" + report_model "forgejo.org/models/moderation" + "forgejo.org/models/unittest" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/require" +) + +func TestRemoveResolvedReportsWhenNoTimeSet(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + // reportAge needs to be an int64 to match what timeutil.Day expects so we cast the value + reportAge := int64(20) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeHandled, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*reportAge), + } + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // No reports should be deleted when the default time to keep is 0 + err = RemoveResolvedReports(db.DefaultContext, time.Second*0) + require.NoError(t, err) + unittest.AssertExistsIf(t, true, resolvedReport) +} + +func TestRemoveResolvedReportsWhenMatchTimeSet(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + // keepReportsFor needs to an int64 to match what timeutil.Day expects so we cast the value + keepReportsFor := int64(4) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeHandled, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*keepReportsFor), + } + + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // Report should be deleted when older than the default time to keep + err = RemoveResolvedReports(db.DefaultContext, time.Second*4) + require.NoError(t, err) + unittest.AssertExistsIf(t, false, resolvedReport) +} + +func TestRemoveResolvedReportsWhenTimeSetButReportNew(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeHandled, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStampNow(), + } + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // Report should not be deleted when newer than the default time to keep + err = RemoveResolvedReports(db.DefaultContext, time.Second*4) + require.NoError(t, err) + unittest.AssertExistsIf(t, true, resolvedReport) +} + +func TestDoesNotRemoveOpenReports(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + // keepReportsFor needs to an int64 to match what timeutil.Day expects so we cast the value + keepReportsFor := int64(4) + resolvedReport := &report_model.AbuseReport{ + Status: report_model.ReportStatusTypeOpen, + ReporterID: 1, ContentType: report_model.ReportedContentTypeRepository, + ContentID: 2, Category: report_model.AbuseCategoryTypeOther, + CreatedUnix: timeutil.TimeStampNow(), + ResolvedUnix: timeutil.TimeStamp(time.Now().Unix() - timeutil.Day*keepReportsFor), + } + + _, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(resolvedReport) + require.NoError(t, err) + + // Report should not be deleted when open + // and older than the default time to keep + err = RemoveResolvedReports(db.DefaultContext, time.Second*4) + require.NoError(t, err) + unittest.AssertExistsIf(t, true, resolvedReport) +} From 106707b40fbcb168f3bad05155227fec24b59c48 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Mon, 28 Jul 2025 15:16:27 +0200 Subject: [PATCH 198/297] feat(ui): pretty-print commit counts and other numbers (#8704) Currently on https://codeberg.org/forgejo/forgejo for instance, the commit count is shown as "23378 commits", instead of "23,378 commits". This makes sure such numbers are printed with thousands separators if they are big enough. Preview: - https://codeberg.org/attachments/681add48-337b-42c0-a0ae-ac5ad4671b39 - https://codeberg.org/attachments/8b5d4e40-bdd6-4942-905f-6ddeac8509a9 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8704 Reviewed-by: Gusted Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Antonin Delpeuch Co-committed-by: Antonin Delpeuch --- templates/repo/commits_table.tmpl | 2 +- templates/repo/sub_menu.tmpl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index 621fc44bf5..c0e4f5ca46 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -1,7 +1,7 @@

    {{if or .PageIsCommits (gt .CommitCount 0)}} - {{ctx.Locale.TrN .CommitCount "repo.n_commit_one" "repo.n_commit_few" (StringUtils.ToString .CommitCount)}} + {{ctx.Locale.TrN .CommitCount "repo.n_commit_one" "repo.n_commit_few" (ctx.Locale.PrettyNumber .CommitCount)}} {{else if .IsNothingToCompare}} {{ctx.Locale.Tr "repo.commits.nothing_to_compare"}} {{else}} diff --git a/templates/repo/sub_menu.tmpl b/templates/repo/sub_menu.tmpl index ee1b959a7c..6216ad6191 100644 --- a/templates/repo/sub_menu.tmpl +++ b/templates/repo/sub_menu.tmpl @@ -3,14 +3,14 @@
    {{if and (.Permission.CanRead $.UnitTypeCode) (not .IsEmptyRepo)}} - {{svg "octicon-history"}} {{ctx.Locale.TrN .CommitsCount "repo.n_commit_one" "repo.n_commit_few" (printf "%d" .CommitsCount | SafeHTML)}} + {{svg "octicon-history"}} {{ctx.Locale.TrN .CommitsCount "repo.n_commit_one" "repo.n_commit_few" (printf "%s" (ctx.Locale.PrettyNumber .CommitsCount) | SafeHTML)}} - {{svg "octicon-git-branch"}} {{ctx.Locale.TrN .BranchesCount "repo.n_branch_one" "repo.n_branch_few" (printf "%d" .BranchesCount | SafeHTML)}} + {{svg "octicon-git-branch"}} {{ctx.Locale.TrN .BranchesCount "repo.n_branch_one" "repo.n_branch_few" (printf "%s" (ctx.Locale.PrettyNumber .BranchesCount) | SafeHTML)}} {{if $.Permission.CanRead $.UnitTypeCode}} - {{svg "octicon-tag"}} {{ctx.Locale.TrN .NumTags "repo.n_tag_one" "repo.n_tag_few" (printf "%d" .NumTags | SafeHTML)}} + {{svg "octicon-tag"}} {{ctx.Locale.TrN .NumTags "repo.n_tag_one" "repo.n_tag_few" (printf "%s" (ctx.Locale.PrettyNumber .NumTags) | SafeHTML)}} {{end}} From 388e4eb44b8154cd75c99d7c58611f0d43e382b5 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Mon, 28 Jul 2025 15:17:29 +0200 Subject: [PATCH 199/297] fix: assorted ActivityPub code only refactors (#8708) Fix parts of issue #8221 and part of PR #4767 Is linked to https://codeberg.org/forgejo/forgejo/pulls/8274 The commit 555f6e57ad fixes timeout forgejo/forgejo#8274 (Kommentar) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8708 Reviewed-by: Earl Warren Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- .deadcode-out | 3 + models/repo/repo_repository.go | 6 +- models/user/follow.go | 16 +- models/user/user_repository.go | 16 +- modules/activitypub/client.go | 7 +- routers/api/v1/activitypub/repository.go | 9 +- routers/api/v1/activitypub/reqsignature.go | 26 +-- routers/api/v1/activitypub/response.go | 34 ++++ routers/api/v1/api.go | 3 +- services/federation/error.go | 44 +++++ services/federation/federation_service.go | 19 +- ...{repo_like.go => repository_inbox_like.go} | 33 ++-- services/federation/repository_service.go | 19 ++ services/federation/result.go | 35 ++++ services/federation/signature_service.go | 11 +- .../integration/api_activitypub_actor_test.go | 77 ++++++++ .../api_activitypub_person_test.go | 113 +++++++++++ .../api_activitypub_repository_test.go | 176 ++++++++++++++++++ .../api_federation_httpsig_test.go | 85 +++++++++ .../integration/repo_star_federation_test.go | 81 ++++++++ 20 files changed, 744 insertions(+), 69 deletions(-) create mode 100644 services/federation/error.go rename services/federation/{repo_like.go => repository_inbox_like.go} (73%) create mode 100644 services/federation/repository_service.go create mode 100644 services/federation/result.go create mode 100644 tests/integration/api_activitypub_actor_test.go create mode 100644 tests/integration/api_activitypub_person_test.go create mode 100644 tests/integration/api_activitypub_repository_test.go create mode 100644 tests/integration/api_federation_httpsig_test.go create mode 100644 tests/integration/repo_star_federation_test.go diff --git a/.deadcode-out b/.deadcode-out index f9bee43043..31b04687dc 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -246,7 +246,10 @@ forgejo.org/services/context GetPrivateContext forgejo.org/services/federation + NewErrInternalf + ErrInternal.Error Init + NewServiceResultWithBytes forgejo.org/services/repository IsErrForkAlreadyExist diff --git a/models/repo/repo_repository.go b/models/repo/repo_repository.go index 0ba50e6614..9d586b8345 100644 --- a/models/repo/repo_repository.go +++ b/models/repo/repo_repository.go @@ -38,18 +38,18 @@ func StoreFollowingRepos(ctx context.Context, localRepoID int64, followingRepoLi } // Begin transaction - ctx, committer, err := db.TxContext((ctx)) + dbCtx, committer, err := db.TxContext((ctx)) if err != nil { return err } defer committer.Close() - _, err = db.GetEngine(ctx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) + _, err = db.GetEngine(dbCtx).Where("repo_id=?", localRepoID).Delete(FollowingRepo{}) if err != nil { return err } for _, followingRepo := range followingRepoList { - _, err = db.GetEngine(ctx).Insert(followingRepo) + _, err = db.GetEngine(dbCtx).Insert(followingRepo) if err != nil { return err } diff --git a/models/user/follow.go b/models/user/follow.go index e32c226385..8663b2a943 100644 --- a/models/user/follow.go +++ b/models/user/follow.go @@ -39,21 +39,21 @@ func FollowUser(ctx context.Context, userID, followID int64) (err error) { return ErrBlockedByUser } - ctx, committer, err := db.TxContext(ctx) + dbCtx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if err = db.Insert(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if err = db.Insert(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() @@ -65,21 +65,21 @@ func UnfollowUser(ctx context.Context, userID, followID int64) (err error) { return nil } - ctx, committer, err := db.TxContext(ctx) + dbCtx, committer, err := db.TxContext(ctx) if err != nil { return err } defer committer.Close() - if _, err = db.DeleteByBean(ctx, &Follow{UserID: userID, FollowID: followID}); err != nil { + if _, err = db.DeleteByBean(dbCtx, &Follow{UserID: userID, FollowID: followID}); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?", followID); err != nil { return err } - if _, err = db.Exec(ctx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { + if _, err = db.Exec(dbCtx, "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?", userID); err != nil { return err } return committer.Commit() diff --git a/models/user/user_repository.go b/models/user/user_repository.go index 85f44f1598..df864746e8 100644 --- a/models/user/user_repository.go +++ b/models/user/user_repository.go @@ -28,7 +28,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } // Begin transaction - ctx, committer, err := db.TxContext((ctx)) + txCtx, committer, err := db.TxContext(ctx) if err != nil { return err } @@ -39,7 +39,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat } }() - if err := CreateUser(ctx, user, &overwrite); err != nil { + if err := CreateUser(txCtx, user, &overwrite); err != nil { return err } @@ -48,7 +48,7 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat return err } - _, err = db.GetEngine(ctx).Insert(federatedUser) + _, err = db.GetEngine(txCtx).Insert(federatedUser) if err != nil { return err } @@ -70,7 +70,7 @@ func FindFederatedUser(ctx context.Context, externalID string, federationHostID if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -87,7 +87,7 @@ func GetFederatedUser(ctx context.Context, externalID string, federationHostID i if err != nil { return nil, nil, err } else if federatedUser == nil { - return nil, nil, fmt.Errorf("FederatedUser for externalId = %v and federationHostId = %v does not exist", externalID, federationHostID) + return nil, nil, fmt.Errorf("FederatedUser not found (given externalId: %v, federationHostId: %v)", externalID, federationHostID) } return user, federatedUser, nil } @@ -99,13 +99,13 @@ func GetFederatedUserByUserID(ctx context.Context, userID int64) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("Federated user %v does not exist", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table does not contain entry for user ID: %v", federatedUser.UserID) } has, err = db.GetEngine(ctx).ID(federatedUser.UserID).Get(user) if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { @@ -130,7 +130,7 @@ func FindFederatedUserByKeyID(ctx context.Context, keyID string) (*User, *Federa if err != nil { return nil, nil, err } else if !has { - return nil, nil, fmt.Errorf("User %v for federated user is missing", federatedUser.UserID) + return nil, nil, fmt.Errorf("FederatedUser table contains entry for user ID %v, but no user with this ID exists", federatedUser.UserID) } if res, err := validation.IsValid(*user); !res { diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index fb6fa8b543..11a2fd94c3 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -66,6 +66,11 @@ type ClientFactory struct { // NewClient function func NewClientFactory() (c *ClientFactory, err error) { + return NewClientFactoryWithTimeout(5 * time.Second) +} + +// NewClient function +func NewClientFactoryWithTimeout(timeout time.Duration) (c *ClientFactory, err error) { if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil { return nil, err } else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil { @@ -77,7 +82,7 @@ func NewClientFactory() (c *ClientFactory, err error) { Transport: &http.Transport{ Proxy: proxy.Proxy(), }, - Timeout: 5 * time.Second, + Timeout: timeout, }, algs: setting.HttpsigAlgs, digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm), diff --git a/routers/api/v1/activitypub/repository.go b/routers/api/v1/activitypub/repository.go index c506840f1c..3eaa6b82c5 100644 --- a/routers/api/v1/activitypub/repository.go +++ b/routers/api/v1/activitypub/repository.go @@ -71,10 +71,11 @@ func RepositoryInbox(ctx *context.APIContext) { repository := ctx.Repo.Repository log.Info("RepositoryInbox: repo: %v", repository) form := web.GetForm(ctx) - // TODO: Decide between like/undo{like} activity - httpStatus, title, err := federation.ProcessLikeActivity(ctx, form, repository.ID) + activity := form.(*ap.Activity) + result, err := federation.ProcessRepositoryInbox(ctx, activity, repository.ID) if err != nil { - ctx.Error(httpStatus, title, err) + ctx.Error(federation.HTTPStatus(err), "Processing Repository Inbox failed", result) + return } - ctx.Status(http.StatusNoContent) + responseServiceResult(ctx, result) } diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 91274249ec..38cb067b89 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -8,13 +8,13 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" - gitea_context "forgejo.org/services/context" + services_context "forgejo.org/services/context" "forgejo.org/services/federation" "github.com/42wim/httpsig" ) -func verifyHTTPUserOrInstanceSignature(ctx *gitea_context.APIContext) (authenticated bool, err error) { +func verifyHTTPUserOrInstanceSignature(ctx services_context.APIContext) (authenticated bool, err error) { if !setting.Federation.SignatureEnforced { return true, nil } @@ -28,9 +28,9 @@ func verifyHTTPUserOrInstanceSignature(ctx *gitea_context.APIContext) (authentic } signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) - pubKey, err := federation.FindOrCreateFederatedUserKey(ctx.Base, v.KeyId()) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) if err != nil || pubKey == nil { - pubKey, err = federation.FindOrCreateFederationHostKey(ctx.Base, v.KeyId()) + pubKey, err = federation.FindOrCreateFederationHostKey(ctx, v.KeyId()) if err != nil { return false, err } @@ -43,7 +43,7 @@ func verifyHTTPUserOrInstanceSignature(ctx *gitea_context.APIContext) (authentic return true, nil } -func verifyHTTPUserSignature(ctx *gitea_context.APIContext) (authenticated bool, err error) { +func verifyHTTPUserSignature(ctx services_context.APIContext) (authenticated bool, err error) { if !setting.Federation.SignatureEnforced { return true, nil } @@ -57,7 +57,7 @@ func verifyHTTPUserSignature(ctx *gitea_context.APIContext) (authenticated bool, } signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0]) - pubKey, err := federation.FindOrCreateFederatedUserKey(ctx.Base, v.KeyId()) + pubKey, err := federation.FindOrCreateFederatedUserKey(ctx, v.KeyId()) if err != nil { return false, err } @@ -70,9 +70,9 @@ func verifyHTTPUserSignature(ctx *gitea_context.APIContext) (authenticated bool, } // ReqHTTPSignature function -func ReqHTTPUserOrInstanceSignature() func(ctx *gitea_context.APIContext) { - return func(ctx *gitea_context.APIContext) { - if authenticated, err := verifyHTTPUserOrInstanceSignature(ctx); err != nil { +func ReqHTTPUserOrInstanceSignature() func(ctx *services_context.APIContext) { + return func(ctx *services_context.APIContext) { + if authenticated, err := verifyHTTPUserOrInstanceSignature(*ctx); err != nil { log.Warn("verifyHttpSignatures failed: %v", err) ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { @@ -81,10 +81,10 @@ func ReqHTTPUserOrInstanceSignature() func(ctx *gitea_context.APIContext) { } } -// ReqHTTPSignature function -func ReqHTTPUserSignature() func(ctx *gitea_context.APIContext) { - return func(ctx *gitea_context.APIContext) { - if authenticated, err := verifyHTTPUserSignature(ctx); err != nil { +// ReqHTTPUserSignature function +func ReqHTTPUserSignature() func(ctx *services_context.APIContext) { + return func(ctx *services_context.APIContext) { + if authenticated, err := verifyHTTPUserSignature(*ctx); err != nil { log.Warn("verifyHttpSignatures failed: %v", err) ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { diff --git a/routers/api/v1/activitypub/response.go b/routers/api/v1/activitypub/response.go index a97f363cc2..64413cebb1 100644 --- a/routers/api/v1/activitypub/response.go +++ b/routers/api/v1/activitypub/response.go @@ -10,12 +10,46 @@ import ( "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/services/context" + "forgejo.org/services/federation" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" ) // Respond with an ActivityStreams object +func responseServiceResult(ctx *context.APIContext, result federation.ServiceResult) { + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + + switch { + case result.StatusOnly(): + ctx.Resp.WriteHeader(result.HTTPStatus) + return + case result.WithBytes(): + ctx.Resp.WriteHeader(result.HTTPStatus) + if _, err := ctx.Resp.Write(result.Bytes); err != nil { + log.Error("Error writing a response: %v", err) + ctx.Error(http.StatusInternalServerError, "Error writing a response", err) + return + } + case result.WithActivity(): + binary, err := jsonld.WithContext( + jsonld.IRI(ap.ActivityBaseURI), + jsonld.IRI(ap.SecurityContextURI), + jsonld.IRI(forgefed.ForgeFedNamespaceURI), + ).Marshal(result.Activity) + if err != nil { + ctx.ServerError("Marshal", err) + return + } + ctx.Resp.WriteHeader(result.HTTPStatus) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } + } +} + +// Respond with an ActivityStreams object +// Deprecated func response(ctx *context.APIContext, v any) { binary, err := jsonld.WithContext( jsonld.IRI(ap.ActivityBaseURI), diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 6a51f33bd8..2f806ba35d 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -81,7 +81,6 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" user_model "forgejo.org/models/user" - "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" @@ -853,7 +852,7 @@ func Routes() *web.Route { m.Group("/repository-id/{repository-id}", func() { m.Get("", activitypub.ReqHTTPUserSignature(), activitypub.Repository) m.Post("/inbox", - bind(forgefed.ForgeLike{}), + bind(ap.Activity{}), activitypub.ReqHTTPUserSignature(), activitypub.RepositoryInbox) }, context.RepositoryIDAssignmentAPI()) diff --git a/services/federation/error.go b/services/federation/error.go new file mode 100644 index 0000000000..425035d0d5 --- /dev/null +++ b/services/federation/error.go @@ -0,0 +1,44 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "fmt" + "net/http" +) + +type ErrNotAcceptable struct { + Message string +} + +func NewErrNotAcceptablef(format string, a ...any) ErrNotAcceptable { + message := fmt.Sprintf(format, a...) + return ErrNotAcceptable{Message: message} +} + +func (err ErrNotAcceptable) Error() string { + return fmt.Sprintf("NotAcceptable: %v", err.Message) +} + +type ErrInternal struct { + Message string +} + +func NewErrInternalf(format string, a ...any) ErrInternal { + message := fmt.Sprintf(format, a...) + return ErrInternal{Message: message} +} + +func (err ErrInternal) Error() string { + return fmt.Sprintf("InternalServerError: %v", err.Message) +} + +func HTTPStatus(err error) int { + switch err.(type) { + case ErrNotAcceptable: + return http.StatusNotAcceptable + default: + return http.StatusInternalServerError + } +} diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index b71d8d2575..36788e725a 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -18,7 +18,6 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/modules/validation" - context_service "forgejo.org/services/context" "github.com/google/uuid" ) @@ -27,7 +26,7 @@ func Init() error { return nil } -func FindOrCreateFederationHost(ctx *context_service.Base, actorURI string) (*forgefed.FederationHost, error) { +func FindOrCreateFederationHost(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) { rawActorID, err := fm.NewActorID(actorURI) if err != nil { return nil, err @@ -46,7 +45,7 @@ func FindOrCreateFederationHost(ctx *context_service.Base, actorURI string) (*fo return federationHost, nil } -func FindOrCreateFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { +func FindOrCreateFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { user, federatedUser, federationHost, err := findFederatedUser(ctx, actorURI) if err != nil { return nil, nil, nil, err @@ -57,20 +56,21 @@ func FindOrCreateFederatedUser(ctx *context_service.Base, actorURI string) (*use } if user != nil { - log.Trace("Found local federatedUser: %#v", user) + log.Trace("Local ActivityPub user found (actorURI: %#v, user: %#v)", actorURI, user) } else { + log.Trace("Attempting to create new user and federatedUser for actorURI: %#v", actorURI) user, federatedUser, err = createUserFromAP(ctx, personID, federationHost.ID) if err != nil { return nil, nil, nil, err } - log.Trace("Created federatedUser from ap: %#v", user) + log.Trace("Created user %#v with federatedUser %#v from distant server", user, federatedUser) } log.Trace("Got user: %v", user.Name) return user, federatedUser, federationHost, nil } -func findFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { +func findFederatedUser(ctx context.Context, actorURI string) (*user.User, *user.FederatedUser, *forgefed.FederationHost, error) { federationHost, err := FindOrCreateFederationHost(ctx, actorURI) if err != nil { return nil, nil, nil, err @@ -90,6 +90,7 @@ func findFederatedUser(ctx *context_service.Base, actorURI string) (*user.User, func createFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) { actionsUser := user.NewAPServerActor() + clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, err @@ -161,7 +162,7 @@ func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID return nil, nil, err } - log.Info("Fetched valid person:%q", person) + log.Info("Fetched valid person from distant server: %q", person) localFqdn, err := url.ParseRequestURI(setting.AppURL) if err != nil { @@ -220,7 +221,7 @@ func fetchUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID }, } - log.Info("Fetch federatedUser:%q", federatedUser) + log.Info("Fetched person's %q federatedUser from distant server: %q", person, federatedUser) return &newUser, &federatedUser, nil } @@ -234,6 +235,6 @@ func createUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI return nil, nil, err } - log.Info("Created federatedUser:%q", federatedUser) + log.Info("Created federatedUser: %q", federatedUser) return newUser, federatedUser, nil } diff --git a/services/federation/repo_like.go b/services/federation/repository_inbox_like.go similarity index 73% rename from services/federation/repo_like.go rename to services/federation/repository_inbox_like.go index c1e6500c61..478a12d92c 100644 --- a/services/federation/repo_like.go +++ b/services/federation/repository_inbox_like.go @@ -5,7 +5,6 @@ package federation import ( "context" - "errors" "fmt" "net/http" "time" @@ -18,6 +17,8 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/validation" context_service "forgejo.org/services/context" + + ap "github.com/go-ap/activitypub" ) // ProcessLikeActivity receives a ForgeLike activity and does the following: @@ -27,32 +28,32 @@ import ( // Validation of incoming RepositoryID against Local RepositoryID // Star the repo if it wasn't already stared // Do some mitigation against out of order attacks -func ProcessLikeActivity(ctx *context_service.APIContext, form any, repositoryID int64) (int, string, error) { - activity := form.(*fm.ForgeLike) - if res, err := validation.IsValid(activity); !res { - return http.StatusNotAcceptable, "Invalid activity", err +func ProcessLikeActivity(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { + constructorLikeActivity, _ := fm.NewForgeLike(activity.Actor.GetLink().String(), activity.Object.GetLink().String(), activity.StartTime) + if res, err := validation.IsValid(constructorLikeActivity); !res { + return ServiceResult{}, NewErrNotAcceptablef("Invalid activity: %v", err) } log.Trace("Activity validated: %#v", activity) // parse actorID (person) actorURI := activity.Actor.GetID().String() - user, _, federationHost, err := FindOrCreateFederatedUser(ctx.Base, actorURI) + user, _, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI) if err != nil { - ctx.Error(http.StatusNotAcceptable, "Federated user not found", err) - return http.StatusInternalServerError, "FindOrCreateFederatedUser", err + log.Error("Federated user not found (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("FindOrCreateFederatedUser failed: %v", err) } - if !activity.IsNewer(federationHost.LatestActivity) { - return http.StatusNotAcceptable, "Activity out of order.", errors.New("Activity already processed") + if !constructorLikeActivity.IsNewer(federationHost.LatestActivity) { + return ServiceResult{}, NewErrNotAcceptablef("LatestActivity: activity already processed: %v", err) } // parse objectID (repository) - objectID, err := fm.NewRepositoryID(activity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) + objectID, err := fm.NewRepositoryID(constructorLikeActivity.Object.GetID().String(), string(forgefed.ForgejoSourceType)) if err != nil { - return http.StatusNotAcceptable, "Invalid objectId", err + return ServiceResult{}, NewErrNotAcceptablef("Parsing repo objectID failed: %v", err) } if objectID.ID != fmt.Sprint(repositoryID) { - return http.StatusNotAcceptable, "Invalid objectId", err + return ServiceResult{}, NewErrNotAcceptablef("Invalid repoId: %v", err) } log.Trace("Object accepted: %#v", objectID) @@ -61,16 +62,16 @@ func ProcessLikeActivity(ctx *context_service.APIContext, form any, repositoryID if !alreadyStared { err = repo.StarRepo(ctx, user.ID, repositoryID, true) if err != nil { - return http.StatusNotAcceptable, "Error staring", err + return ServiceResult{}, NewErrNotAcceptablef("Staring failed: %v", err) } } federationHost.LatestActivity = activity.StartTime err = forgefed.UpdateFederationHost(ctx, federationHost) if err != nil { - return http.StatusNotAcceptable, "Error updating federatedHost", err + return ServiceResult{}, NewErrNotAcceptablef("Updating federatedHost failed: %v", err) } - return 0, "", nil + return NewServiceResultStatusOnly(http.StatusNoContent), nil } // Create or update a list of FollowingRepo structs diff --git a/services/federation/repository_service.go b/services/federation/repository_service.go new file mode 100644 index 0000000000..7891d786e2 --- /dev/null +++ b/services/federation/repository_service.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + + ap "github.com/go-ap/activitypub" +) + +func ProcessRepositoryInbox(ctx context.Context, activity *ap.Activity, repositoryID int64) (ServiceResult, error) { + switch activity.Type { + case ap.LikeType: + return ProcessLikeActivity(ctx, activity, repositoryID) + default: + return ServiceResult{}, NewErrNotAcceptablef("Not a like activity: %v", activity.Type) + } +} diff --git a/services/federation/result.go b/services/federation/result.go new file mode 100644 index 0000000000..47afb2bdf6 --- /dev/null +++ b/services/federation/result.go @@ -0,0 +1,35 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import "github.com/go-ap/activitypub" + +type ServiceResult struct { + HTTPStatus int + Bytes []byte + Activity activitypub.Activity + withBytes bool + withActivity bool + statusOnly bool +} + +func NewServiceResultStatusOnly(status int) ServiceResult { + return ServiceResult{HTTPStatus: status, statusOnly: true} +} + +func NewServiceResultWithBytes(status int, bytes []byte) ServiceResult { + return ServiceResult{HTTPStatus: status, Bytes: bytes, withBytes: true} +} + +func (serviceResult ServiceResult) WithBytes() bool { + return serviceResult.withBytes +} + +func (serviceResult ServiceResult) WithActivity() bool { + return serviceResult.withActivity +} + +func (serviceResult ServiceResult) StatusOnly() bool { + return serviceResult.statusOnly +} diff --git a/services/federation/signature_service.go b/services/federation/signature_service.go index e5102b89d8..fd8cbb39cd 100644 --- a/services/federation/signature_service.go +++ b/services/federation/signature_service.go @@ -4,6 +4,7 @@ package federation import ( + "context" "crypto/x509" "database/sql" "encoding/pem" @@ -15,13 +16,12 @@ import ( "forgejo.org/models/user" "forgejo.org/modules/activitypub" fm "forgejo.org/modules/forgefed" - context_service "forgejo.org/services/context" ap "github.com/go-ap/activitypub" ) // Factory function for ActorID. Created struct is asserted to be valid -func NewActorIDFromKeyID(ctx *context_service.Base, uri string) (fm.ActorID, error) { +func NewActorIDFromKeyID(ctx context.Context, uri string) (fm.ActorID, error) { parsedURI, err := url.Parse(uri) parsedURI.Fragment = "" if err != nil { @@ -54,7 +54,7 @@ func NewActorIDFromKeyID(ctx *context_service.Base, uri string) (fm.ActorID, err return result, err } -func FindOrCreateFederatedUserKey(ctx *context_service.Base, keyID string) (pubKey any, err error) { +func FindOrCreateFederatedUserKey(ctx context.Context, keyID string) (pubKey any, err error) { var federatedUser *user.FederatedUser var keyURL *url.URL @@ -122,7 +122,7 @@ func FindOrCreateFederatedUserKey(ctx *context_service.Base, keyID string) (pubK return nil, nil } -func FindOrCreateFederationHostKey(ctx *context_service.Base, keyID string) (pubKey any, err error) { +func FindOrCreateFederationHostKey(ctx context.Context, keyID string) (pubKey any, err error) { keyURL, err := url.Parse(keyID) if err != nil { return nil, err @@ -183,8 +183,9 @@ func FindOrCreateFederationHostKey(ctx *context_service.Base, keyID string) (pub return nil, nil } -func fetchKeyFromAp(ctx *context_service.Base, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) { +func fetchKeyFromAp(ctx context.Context, keyURL url.URL) (pubKey any, pubKeyBytes []byte, apPerson *ap.Person, err error) { actionsUser := user.NewAPServerActor() + clientFactory, err := activitypub.GetClientFactory(ctx) if err != nil { return nil, nil, nil, err diff --git a/tests/integration/api_activitypub_actor_test.go b/tests/integration/api_activitypub_actor_test.go new file mode 100644 index 0000000000..42232bd640 --- /dev/null +++ b/tests/integration/api_activitypub_actor_test.go @@ -0,0 +1,77 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + "testing" + + "forgejo.org/modules/forgefed" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + "forgejo.org/services/federation" + "forgejo.org/tests" + + ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubActor(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + defer tests.PrepareTestEnv(t)() + + req := NewRequest(t, "GET", "/api/v1/activitypub/actor") + resp := MakeRequest(t, req, http.StatusOK) + assert.Contains(t, resp.Body.String(), "@context") + + var actor ap.Actor + err := actor.UnmarshalJSON(resp.Body.Bytes()) + require.NoError(t, err) + + assert.Equal(t, ap.ApplicationType, actor.Type) + assert.Equal(t, "ghost", actor.PreferredUsername.String()) + keyID := actor.GetID().String() + assert.Regexp(t, "activitypub/actor$", keyID) + assert.Regexp(t, "activitypub/actor/inbox$", actor.Inbox.GetID().String()) + + pubKey := actor.PublicKey + assert.NotNil(t, pubKey) + publicKeyID := keyID + "#main-key" + assert.Equal(t, pubKey.ID.String(), publicKeyID) + + pubKeyPem := pubKey.PublicKeyPem + assert.NotNil(t, pubKeyPem) + assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem) +} + +func TestActorNewFromKeyId(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + ctx, _ := contexttest.MockAPIContext(t, "/api/v1/activitypub/actor") + sut, err := federation.NewActorIDFromKeyID(ctx.Base, fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", u)) + require.NoError(t, err) + + port, err := strconv.ParseUint(u.Port(), 10, 16) + require.NoError(t, err) + + assert.Equal(t, forgefed.ActorID{ + ID: "actor", + HostSchema: "http", + Path: "api/v1/activitypub", + Host: setting.Domain, + HostPort: uint16(port), + UnvalidatedInput: fmt.Sprintf("http://%s:%d/api/v1/activitypub/actor", setting.Domain, port), + IsPortSupplemented: false, + }, sut) + }) +} diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go new file mode 100644 index 0000000000..277b150a1e --- /dev/null +++ b/tests/integration/api_activitypub_person_test.go @@ -0,0 +1,113 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + "forgejo.org/tests" + + ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubPerson(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, localUrl *url.URL) { + defer test.MockVariableValue(&setting.AppURL, localUrl.String())() + + localUserID := 2 + localUserName := "user2" + localUserURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", localUrl, localUserID) + + // distantURL := federatedSrv.URL + // distantUser15URL := fmt.Sprintf("%s/api/v1/activitypub/user-id/15", distantURL) + + // Unsigned request + t.Run("UnsignedRequest", func(t *testing.T) { + req := NewRequest(t, "GET", localUserURL) + MakeRequest(t, req, http.StatusBadRequest) + }) + + // Signed request + t.Run("SignedRequestValidation", func(t *testing.T) { + ctx, _ := contexttest.MockAPIContext(t, localUserURL) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, + mock.Persons[0].KeyID(federatedSrv.URL)) + require.NoError(t, err) + + resp, err := c.GetBody(localUserURL) + require.NoError(t, err) + + var person ap.Person + err = person.UnmarshalJSON(resp) + require.NoError(t, err) + + assert.Equal(t, ap.PersonType, person.Type) + assert.Equal(t, localUserName, person.PreferredUsername.String()) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d$", localUserID), person.GetID()) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/inbox$", localUserID), person.Inbox.GetID().String()) + + assert.NotNil(t, person.PublicKey) + assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d#main-key$", localUserID), person.PublicKey.ID) + + assert.NotNil(t, person.PublicKey.PublicKeyPem) + assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", person.PublicKey.PublicKeyPem) + }) + }) +} + +func TestActivityPubMissingPerson(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + req := NewRequest(t, "GET", "/api/v1/activitypub/user-id/999999999") + resp := MakeRequest(t, req, http.StatusNotFound) + assert.Contains(t, resp.Body.String(), "user does not exist") +} + +func TestActivityPubPersonInbox(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + defer test.MockVariableValue(&setting.AppURL, u.String())() + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + + user1url := u.JoinPath("/api/v1/activitypub/user-id/1").String() + "#main-key" + user2inboxurl := u.JoinPath("/api/v1/activitypub/user-id/2/inbox").String() + ctx, _ := contexttest.MockAPIContext(t, user2inboxurl) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + c, err := cf.WithKeys(ctx, user1, user1url) + require.NoError(t, err) + + // Signed request "succeeds" + resp, err := c.Post([]byte{}, user2inboxurl) + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + }) +} diff --git a/tests/integration/api_activitypub_repository_test.go b/tests/integration/api_activitypub_repository_test.go new file mode 100644 index 0000000000..b4be0407b9 --- /dev/null +++ b/tests/integration/api_activitypub_repository_test.go @@ -0,0 +1,176 @@ +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "forgejo.org/models/forgefed" + "forgejo.org/models/unittest" + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + forgefed_modules "forgejo.org/modules/forgefed" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubRepository(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + repositoryID := 2 + + localRepository := fmt.Sprintf("%sapi/v1/activitypub/repository-id/%d", u, repositoryID) + + ctx, _ := contexttest.MockAPIContext(t, localRepository) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, + mock.Persons[0].KeyID(federatedSrv.URL)) + require.NoError(t, err) + + resp, err := c.GetBody(localRepository) + require.NoError(t, err) + assert.Contains(t, string(resp), "@context") + + var repository forgefed_modules.Repository + err = repository.UnmarshalJSON(resp) + require.NoError(t, err) + + assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%d$", repositoryID), repository.GetID().String()) + }) +} + +func TestActivityPubMissingRepository(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + repositoryID := 9999999 + req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)) + resp := MakeRequest(t, req, http.StatusNotFound) + assert.Contains(t, resp.Body.String(), "repository does not exist") +} + +func TestActivityPubRepositoryInboxValid(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + repositoryID := 2 + timeNow := time.Now().UTC() + localRepoInbox := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() + + ctx, _ := contexttest.MockAPIContext(t, localRepoInbox) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, + mock.Persons[0].KeyID(federatedSrv.URL)) + require.NoError(t, err) + + activity1 := []byte(fmt.Sprintf( + `{"type":"Like",`+ + `"startTime":"%s",`+ + `"actor":"%s/api/v1/activitypub/user-id/15",`+ + `"object":"%s"}`, + timeNow.Format(time.RFC3339), + federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) + t.Logf("activity: %s", activity1) + resp, err := c.Post(activity1, localRepoInbox) + + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) + federatedUser := unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "15", FederationHostID: federationHost.ID}) + unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) + + // A like activity by a different user of the same federated host. + activity2 := []byte(fmt.Sprintf( + `{"type":"Like",`+ + `"startTime":"%s",`+ + `"actor":"%s/api/v1/activitypub/user-id/30",`+ + `"object":"%s"}`, + // Make sure this activity happens later then the one before + timeNow.Add(time.Second).Format(time.RFC3339), + federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID)).String())) + t.Logf("activity: %s", activity2) + resp, err = c.Post(activity2, localRepoInbox) + + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) + unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) + + // The same user sends another like activity + otherRepositoryID := 3 + otherRepoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", otherRepositoryID)).String() + activity3 := []byte(fmt.Sprintf( + `{"type":"Like",`+ + `"startTime":"%s",`+ + `"actor":"%s/api/v1/activitypub/user-id/30",`+ + `"object":"%s"}`, + // Make sure this activity happens later then the ones before + timeNow.Add(time.Second*2).Format(time.RFC3339), + federatedSrv.URL, u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d", otherRepositoryID)).String())) + t.Logf("activity: %s", activity3) + resp, err = c.Post(activity3, otherRepoInboxURL) + + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + federatedUser = unittest.AssertExistsAndLoadBean(t, &user.FederatedUser{ExternalID: "30", FederationHostID: federationHost.ID}) + unittest.AssertExistsAndLoadBean(t, &user.User{ID: federatedUser.UserID}) + + // Replay activity2. + resp, err = c.Post(activity2, localRepoInbox) + require.NoError(t, err) + assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) + }) +} + +func TestActivityPubRepositoryInboxInvalid(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + apServerActor := user.NewAPServerActor() + repositoryID := 2 + localRepo2Inbox := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String() + + ctx, _ := contexttest.MockAPIContext(t, localRepo2Inbox) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeys(ctx, apServerActor, apServerActor.KeyID()) + require.NoError(t, err) + + activity := []byte(`{"type":"Wrong"}`) + resp, err := c.Post(activity, localRepo2Inbox) + require.NoError(t, err) + assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) + }) +} diff --git a/tests/integration/api_federation_httpsig_test.go b/tests/integration/api_federation_httpsig_test.go new file mode 100644 index 0000000000..f5c4c78648 --- /dev/null +++ b/tests/integration/api_federation_httpsig_test.go @@ -0,0 +1,85 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "forgejo.org/models/db" + "forgejo.org/models/forgefed" + "forgejo.org/models/unittest" + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFederationHttpSigValidation(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + userID := 2 + userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID) + + user1 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 1}) + + ctx, _ := contexttest.MockAPIContext(t, userURL) + clientFactory, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + apClient, err := clientFactory.WithKeys(ctx, user1, user1.KeyID()) + require.NoError(t, err) + + // Unsigned request + t.Run("UnsignedRequest", func(t *testing.T) { + req := NewRequest(t, "GET", userURL) + MakeRequest(t, req, http.StatusBadRequest) + }) + + // Signed request + t.Run("SignedRequest", func(t *testing.T) { + resp, err := apClient.Get(userURL) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + + // HACK HACK HACK: the host part of the URL gets set to which IP forgejo is + // listening on, NOT localhost, which is the Domain given to forgejo which + // is then used for eg. the keyID all requests + applicationKeyID := fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", setting.AppURL) + actorKeyID := fmt.Sprintf("%sapi/v1/activitypub/user-id/1#main-key", setting.AppURL) + + // Check for cached public keys + t.Run("ValidateCaches", func(t *testing.T) { + host, err := forgefed.FindFederationHostByKeyID(db.DefaultContext, applicationKeyID) + require.NoError(t, err) + assert.NotNil(t, host) + assert.True(t, host.PublicKey.Valid) + + _, user, err := user.FindFederatedUserByKeyID(db.DefaultContext, actorKeyID) + require.NoError(t, err) + assert.NotNil(t, user) + assert.True(t, user.PublicKey.Valid) + }) + + // Disable signature validation + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + + // Unsigned request + t.Run("SignatureValidationDisabled", func(t *testing.T) { + req := NewRequest(t, "GET", userURL) + MakeRequest(t, req, http.StatusOK) + }) + }) +} diff --git a/tests/integration/repo_star_federation_test.go b/tests/integration/repo_star_federation_test.go new file mode 100644 index 0000000000..ae9a4e9f83 --- /dev/null +++ b/tests/integration/repo_star_federation_test.go @@ -0,0 +1,81 @@ +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "forgejo.org/models/forgefed" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + fm "forgejo.org/modules/forgefed" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/modules/validation" + "forgejo.org/tests" +) + +func TestActivityPubRepoFollowing(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) + session := loginUser(t, user.Name) + + t.Run("Add a following repo", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + link := fmt.Sprintf("/%s/settings", repo.FullName()) + + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, link), + "action": "federation", + "following_repos": fmt.Sprintf("%s/api/v1/activitypub/repository-id/1", federatedSrv.URL), + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + // Verify it was added. + federationHost := unittest.AssertExistsAndLoadBean(t, &forgefed.FederationHost{HostFqdn: "127.0.0.1"}) + unittest.AssertExistsAndLoadBean(t, &repo_model.FollowingRepo{ + ExternalID: "1", + FederationHostID: federationHost.ID, + }) + }) + + t.Run("Star a repo having a following repo", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + repoLink := fmt.Sprintf("/%s", repo.FullName()) + link := fmt.Sprintf("%s/action/star", repoLink) + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, repoLink), + }) + + session.MakeRequest(t, req, http.StatusOK) + + // Verify distant server received a like activity + like := fm.ForgeLike{} + err := like.UnmarshalJSON([]byte(mock.LastPost)) + if err != nil { + t.Errorf("Error unmarshalling ForgeLike: %q", err) + } + if isValid, err := validation.IsValid(like); !isValid { + t.Errorf("ForgeLike is not valid: %q", err) + } + activityType := like.Type + object := like.Object.GetLink().String() + isLikeType := activityType == "Like" + isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1") + if !isLikeType || !isCorrectObject { + t.Error("Activity is not a like for this repo") + } + }) +} From bc0d14119c9ee12f8c17fd97d4b9614dafe0d638 Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 28 Jul 2025 17:27:12 +0200 Subject: [PATCH 200/297] chore: add missing translation (#8709) Ref: forgejo/forgejo#7940 > Unable to register cron task remove_resolved_reports Error: translation is missing for task "remove_resolved_reports", please add translation for "admin.dashboard.remove_resolved_reports" Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8709 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- options/locale_next/locale_en-US.json | 1 + 1 file changed, 1 insertion(+) diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index c2c682a4db..d861c42d66 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -107,6 +107,7 @@ "editor.textarea.tab_hint": "Line already indented. Press Tab again or Escape to leave the editor.", "editor.textarea.shift_tab_hint": "No indentation on this line. Press Shift + Tab again or Escape to leave the editor.", "admin.dashboard.cleanup_offline_runners": "Cleanup offline runners", + "admin.dashboard.remove_resolved_reports": "Remove resolved reports", "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more.", "avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels", "og.repo.summary_card.alt_description": "Summary card of repository %[1]s, described as: %[2]s", From 13e48ead92516d1122b3a30a5a8fc73ee45dec10 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 29 Jul 2025 01:03:36 +0200 Subject: [PATCH 201/297] feat: migrate action secrets to `keying` to store them more securely (#8692) - Use the keying module, that was introduced in forgejo/forgejo#5041, to store action secrets safely and securely in the database. - Introduce a central function that sets the secret, `SetSecret` and let the caller do the update call. This is similar to how the twofactor (TOTP) models does it. Ref. https://codeberg.org/forgejo/forgejo/pulls/6074 - Add a relaxed migration, that is run inside a transaction. If it cannot decrypt a action secret, then it's deleted. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8692 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v39.go | 78 +++++++++++++ models/forgejo_migrations/v39_test.go | 52 +++++++++ .../secret.yml | 14 +++ models/secret/main_test.go | 17 +++ models/secret/secret.go | 46 ++++---- models/secret/secret_test.go | 103 ++++++++++++++++++ modules/keying/keying.go | 2 + services/secrets/secrets.go | 12 +- 9 files changed, 293 insertions(+), 33 deletions(-) create mode 100644 models/forgejo_migrations/v39.go create mode 100644 models/forgejo_migrations/v39_test.go create mode 100644 models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml create mode 100644 models/secret/main_test.go create mode 100644 models/secret/secret_test.go diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index a22d35dd21..c5553388ea 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -115,6 +115,8 @@ var migrations = []*Migration{ NewMigration("Add `branch_filter` to `push_mirror` table", AddPushMirrorBranchFilter), // v37 -> v38 NewMigration("Add `resolved_unix` column to `abuse_report` table", AddResolvedUnixToAbuseReport), + // v38 -> v39 + NewMigration("Migrate `data` column of `secret` table to store keying material", MigrateActionSecretsToKeying), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v39.go b/models/forgejo_migrations/v39.go new file mode 100644 index 0000000000..9af1c250b3 --- /dev/null +++ b/models/forgejo_migrations/v39.go @@ -0,0 +1,78 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "context" + "fmt" + + "forgejo.org/models/db" + secret_model "forgejo.org/models/secret" + "forgejo.org/modules/log" + "forgejo.org/modules/secret" + "forgejo.org/modules/setting" + + "xorm.io/xorm" + "xorm.io/xorm/schemas" +) + +func MigrateActionSecretsToKeying(x *xorm.Engine) error { + return db.WithTx(db.DefaultContext, func(ctx context.Context) error { + sess := db.GetEngine(ctx) + + switch x.Dialect().URI().DBType { + case schemas.MYSQL: + if _, err := sess.Exec("ALTER TABLE `secret` MODIFY `data` BLOB"); err != nil { + return err + } + case schemas.SQLITE: + if _, err := sess.Exec("ALTER TABLE `secret` RENAME COLUMN `data` TO `data_backup`"); err != nil { + return err + } + if _, err := sess.Exec("ALTER TABLE `secret` ADD COLUMN `data` BLOB"); err != nil { + return err + } + if _, err := sess.Exec("UPDATE `secret` SET `data` = `data_backup`"); err != nil { + return err + } + if _, err := sess.Exec("ALTER TABLE `secret` DROP COLUMN `data_backup`"); err != nil { + return err + } + case schemas.POSTGRES: + if _, err := sess.Exec("ALTER TABLE `secret` ALTER COLUMN `data` SET DATA TYPE bytea USING data::text::bytea"); err != nil { + return err + } + } + + oldEncryptionKey := setting.SecretKey + + messages := make([]string, 0, 100) + ids := make([]int64, 0, 100) + + err := db.Iterate(ctx, nil, func(ctx context.Context, bean *secret_model.Secret) error { + secretBytes, err := secret.DecryptSecret(oldEncryptionKey, string(bean.Data)) + if err != nil { + messages = append(messages, fmt.Sprintf("secret.id=%d, secret.name=%q, secret.repo_id=%d, secret.owner_id=%d: secret.DecryptSecret(): %v", bean.ID, bean.Name, bean.RepoID, bean.OwnerID, err)) + ids = append(ids, bean.ID) + return nil + } + + bean.SetSecret(secretBytes) + _, err = sess.Cols("data").ID(bean.ID).Update(bean) + return err + }) + + if err == nil { + if len(ids) > 0 { + log.Error("Forgejo migration[37]: The following action secrets were found to be corrupted and removed from the database.") + for _, message := range messages { + log.Error("Forgejo migration[37]: %s", message) + } + + _, err = sess.In("id", ids).NoAutoCondition().NoAutoTime().Delete(&secret_model.Secret{}) + } + } + return err + }) +} diff --git a/models/forgejo_migrations/v39_test.go b/models/forgejo_migrations/v39_test.go new file mode 100644 index 0000000000..42934d912f --- /dev/null +++ b/models/forgejo_migrations/v39_test.go @@ -0,0 +1,52 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations + +import ( + "testing" + + migration_tests "forgejo.org/models/migrations/test" + "forgejo.org/models/secret" + "forgejo.org/modules/keying" + "forgejo.org/modules/timeutil" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_MigrateActionSecretToKeying(t *testing.T) { + type Secret struct { + ID int64 + OwnerID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL"` + RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0"` + Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"` + Data string `xorm:"LONGTEXT"` // encrypted data + CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` + } + + // Prepare and load the testing database + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Secret)) + defer deferable() + if x == nil || t.Failed() { + return + } + + cnt, err := x.Table("secret").Count() + require.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + require.NoError(t, MigrateActionSecretsToKeying(x)) + + cnt, err = x.Table("secret").Count() + require.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var secret secret.Secret + _, err = x.Table("secret").ID(1).Get(&secret) + require.NoError(t, err) + + secretBytes, err := keying.DeriveKey(keying.ContextActionSecret).Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) + require.NoError(t, err) + assert.Equal(t, []byte("A deep dark secret"), secretBytes) +} diff --git a/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml b/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml new file mode 100644 index 0000000000..908b428321 --- /dev/null +++ b/models/migrations/fixtures/Test_MigrateActionSecretToKeying/secret.yml @@ -0,0 +1,14 @@ +- + id: 1 + owner_id: 2 + repo_id: 1 + name: SECRET_1 + data: 02458e5f341b2d5081a31283559843b6b7543ab98ed213d2b15b5cef94385fa348afa7e0875122e9 + created_unix: 1753556968 +- + id: 2 + owner_id: 2 + repo_id: 1 + name: BADBAD + data: badbad + created_unix: 1753556968 diff --git a/models/secret/main_test.go b/models/secret/main_test.go new file mode 100644 index 0000000000..85bfec0c4f --- /dev/null +++ b/models/secret/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package secret_test + +import ( + "testing" + + "forgejo.org/models/unittest" + + _ "forgejo.org/models" + _ "forgejo.org/models/activities" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/models/secret/secret.go b/models/secret/secret.go index 7be7f454a1..6f6867db52 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -11,9 +11,8 @@ import ( actions_model "forgejo.org/models/actions" "forgejo.org/models/db" actions_module "forgejo.org/modules/actions" + "forgejo.org/modules/keying" "forgejo.org/modules/log" - secret_module "forgejo.org/modules/secret" - "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" "forgejo.org/modules/util" @@ -39,7 +38,7 @@ type Secret struct { OwnerID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL"` RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0"` Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"` - Data string `xorm:"LONGTEXT"` // encrypted data + Data []byte `xorm:"BLOB"` // encrypted data CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } @@ -67,17 +66,21 @@ func InsertEncryptedSecret(ctx context.Context, ownerID, repoID int64, name, dat return nil, fmt.Errorf("%w: ownerID and repoID cannot be both zero, global secrets are not supported", util.ErrInvalidArgument) } - encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) - if err != nil { - return nil, err - } secret := &Secret{ OwnerID: ownerID, RepoID: repoID, Name: strings.ToUpper(name), - Data: encrypted, } - return secret, db.Insert(ctx, secret) + + return secret, db.WithTx(ctx, func(ctx context.Context) error { + if err := db.Insert(ctx, secret); err != nil { + return err + } + + secret.SetSecret(data) + _, err := db.GetEngine(ctx).ID(secret.ID).Cols("data").Update(secret) + return err + }) } func init() { @@ -113,21 +116,9 @@ func (opts FindSecretsOptions) ToConds() builder.Cond { return cond } -// UpdateSecret changes org or user reop secret. -func UpdateSecret(ctx context.Context, secretID int64, data string) error { - encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) - if err != nil { - return err - } - - s := &Secret{ - Data: encrypted, - } - affected, err := db.GetEngine(ctx).ID(secretID).Cols("data").Update(s) - if affected != 1 { - return ErrSecretNotFound{} - } - return err +func (s *Secret) SetSecret(data string) { + key := keying.DeriveKey(keying.ContextActionSecret) + s.Data = key.Encrypt([]byte(data), keying.ColumnAndID("data", s.ID)) } func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) { @@ -155,13 +146,14 @@ func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[ return nil, err } + key := keying.DeriveKey(keying.ContextActionSecret) for _, secret := range append(ownerSecrets, repoSecrets...) { - v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data) + v, err := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) if err != nil { - log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) + log.Error("unable to decrypt secret[id=%d,name=%q]: %v", secret.ID, secret.Name, err) return nil, err } - secrets[secret.Name] = v + secrets[secret.Name] = string(v) } return secrets, nil diff --git a/models/secret/secret_test.go b/models/secret/secret_test.go new file mode 100644 index 0000000000..15142d207b --- /dev/null +++ b/models/secret/secret_test.go @@ -0,0 +1,103 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package secret + +import ( + "testing" + + "forgejo.org/models/actions" + "forgejo.org/models/repo" + "forgejo.org/models/unittest" + "forgejo.org/modules/keying" + "forgejo.org/modules/util" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInsertEncryptedSecret(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Global secret", func(t *testing.T) { + secret, err := InsertEncryptedSecret(t.Context(), 0, 0, "GLOBAL_SECRET", "some common secret") + require.ErrorIs(t, err, util.ErrInvalidArgument) + assert.Nil(t, secret) + }) + + key := keying.DeriveKey(keying.ContextActionSecret) + + t.Run("Insert repository secret", func(t *testing.T) { + secret, err := InsertEncryptedSecret(t.Context(), 0, 1, "REPO_SECRET", "some repository secret") + require.NoError(t, err) + assert.NotNil(t, secret) + assert.Equal(t, "REPO_SECRET", secret.Name) + assert.EqualValues(t, 1, secret.RepoID) + assert.NotEmpty(t, secret.Data) + + // Assert the secret is stored in the database. + unittest.AssertExistsAndLoadBean(t, &Secret{RepoID: 1, Name: "REPO_SECRET", Data: secret.Data}) + + t.Run("Keying", func(t *testing.T) { + // Cannot decrypt with different ID. + plainText, err := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID+1)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Cannot decrypt with different column. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("metadata", secret.ID)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Can decrypt with correct column and ID. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) + require.NoError(t, err) + assert.EqualValues(t, "some repository secret", plainText) + }) + }) + + t.Run("Insert owner secret", func(t *testing.T) { + secret, err := InsertEncryptedSecret(t.Context(), 2, 0, "OWNER_SECRET", "some owner secret") + require.NoError(t, err) + assert.NotNil(t, secret) + assert.Equal(t, "OWNER_SECRET", secret.Name) + assert.EqualValues(t, 2, secret.OwnerID) + assert.NotEmpty(t, secret.Data) + + // Assert the secret is stored in the database. + unittest.AssertExistsAndLoadBean(t, &Secret{OwnerID: 2, Name: "OWNER_SECRET", Data: secret.Data}) + + t.Run("Keying", func(t *testing.T) { + // Cannot decrypt with different ID. + plainText, err := key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID+1)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Cannot decrypt with different column. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("metadata", secret.ID)) + require.Error(t, err) + assert.Nil(t, plainText) + + // Can decrypt with correct column and ID. + plainText, err = key.Decrypt(secret.Data, keying.ColumnAndID("data", secret.ID)) + require.NoError(t, err) + assert.EqualValues(t, "some owner secret", plainText) + }) + }) + + t.Run("Get secrets", func(t *testing.T) { + secrets, err := GetSecretsOfTask(t.Context(), &actions.ActionTask{ + Job: &actions.ActionRunJob{ + Run: &actions.ActionRun{ + RepoID: 1, + Repo: &repo.Repository{ + OwnerID: 2, + }, + }, + }, + }) + require.NoError(t, err) + assert.Equal(t, "some owner secret", secrets["OWNER_SECRET"]) + assert.Equal(t, "some repository secret", secrets["REPO_SECRET"]) + }) +} diff --git a/modules/keying/keying.go b/modules/keying/keying.go index c859e30e9f..f39e16aeed 100644 --- a/modules/keying/keying.go +++ b/modules/keying/keying.go @@ -58,6 +58,8 @@ var ( ContextPushMirror Context = "pushmirror" // Used for the `two_factor` table. ContextTOTP Context = "totp" + // Used for the `secret` table. + ContextActionSecret Context = "action_secret" ) // Derive *the* key for a given context, this is a deterministic function. diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 2d5aebdbc1..2de83ef5a2 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -15,16 +15,16 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data return nil, false, err } - s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ + s, exists, err := db.Get[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, Name: name, - }) + }.ToConds()) if err != nil { return nil, false, err } - if len(s) == 0 { + if !exists { s, err := secret_model.InsertEncryptedSecret(ctx, ownerID, repoID, name, data) if err != nil { return nil, false, err @@ -32,11 +32,11 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data return s, true, nil } - if err := secret_model.UpdateSecret(ctx, s[0].ID, data); err != nil { + s.SetSecret(data) + if _, err := db.GetEngine(ctx).Cols("data").ID(s.ID).Update(s); err != nil { return nil, false, err } - - return s[0], false, nil + return s, false, nil } func DeleteSecretByID(ctx context.Context, ownerID, repoID, secretID int64) error { From e2dbbbfbc608347a5c58daa222e6071aae3155b8 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 29 Jul 2025 03:54:22 +0200 Subject: [PATCH 202/297] Update dependency webpack to v5.101.0 (forgejo) (#8717) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8717 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 12f7c404fb..eface926ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.100.2", + "webpack": "5.101.0", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.0" }, @@ -15639,9 +15639,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.100.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", - "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", + "version": "5.101.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.0.tgz", + "integrity": "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", diff --git a/package.json b/package.json index 466f8fbb01..b0dc05c6d1 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.100.2", + "webpack": "5.101.0", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.0" }, From b2469c2a9ce4f682c1e63beb8e185fb1e60d0fcc Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 29 Jul 2025 08:17:17 +0200 Subject: [PATCH 203/297] fix: allow admins to always rename users (#8715) Do not apply the rename restriction of non-local users if the doer is an admin (changes via the admin interface). This is a conscious choice and the admin knows better if they make such changes. Regression of c59a057297 Resolves forgejo/forgejo#3657 ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8715): allow admins to always rename users Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8715 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- services/user/user.go | 3 ++- services/user/user_test.go | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/services/user/user.go b/services/user/user.go index d90fbac978..d682d5a434 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -47,7 +47,8 @@ func renameUser(ctx context.Context, u *user_model.User, newUserName string, doe } // Non-local users are not allowed to change their username. - if !u.IsOrganization() && !u.IsLocal() { + // If the doer is an admin, then allow the rename - they know better. + if !doerIsAdmin && !u.IsOrganization() && !u.IsLocal() { return user_model.ErrUserIsNotLocal{ UID: u.ID, Name: u.Name, diff --git a/services/user/user_test.go b/services/user/user_test.go index 36f2776ad8..f1cab60a6d 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -145,10 +145,16 @@ func TestRenameUser(t *testing.T) { t.Run("Non-Local", func(t *testing.T) { u := &user_model.User{ + ID: 2, + Name: "old-name", Type: user_model.UserTypeIndividual, LoginType: auth.OAuth2, } - require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename"), user_model.ErrUserIsNotLocal{}) + require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename2"), user_model.ErrUserIsNotLocal{UID: 2, Name: "old-name"}) + + t.Run("Admin", func(t *testing.T) { + require.NoError(t, AdminRenameUser(t.Context(), u, "user_rename2")) + }) }) t.Run("Same username", func(t *testing.T) { From 01f1f4ac75df65d22e55c01b7c19c888b6b4c582 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Tue, 29 Jul 2025 10:18:58 +0200 Subject: [PATCH 204/297] chore: revert release-notes-assistant to v1.3.3 (#8722) to reduce the number of requests to the API to obtain each pull request to the minimum. tests are not needed because this version has been working fine over the past year Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8722 Reviewed-by: Michael Kriese Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- .forgejo/workflows/release-notes-assistant.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index d813e5fc0f..b1acde6030 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,7 +8,7 @@ on: - labeled env: - RNA_VERSION: v1.3.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.3 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: From d3ca92df79555007f102f930e721a68d96eb29cf Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 29 Jul 2025 23:43:21 +0200 Subject: [PATCH 205/297] Update dependency forgejo/release-notes-assistant to v1.3.6 (forgejo) (#8726) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [forgejo/release-notes-assistant](https://code.forgejo.org/forgejo/release-notes-assistant) | patch | `v1.3.3` -> `v1.3.6` | | [forgejo/release-notes-assistant](https://code.forgejo.org/forgejo/release-notes-assistant) | patch | `v1.3.5` -> `v1.3.6` | --- ### Release Notes
    forgejo/release-notes-assistant (forgejo/release-notes-assistant) ### [`v1.3.6`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.6) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.5...v1.3.6) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/100): fix: convert EOL \r\n to \n when getting a single pull request - other - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/96): Update https://data.forgejo.org/actions/forgejo-release action to v2.7.0 ### [`v1.3.5`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.5) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.4...v1.3.5) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/98): fix: there can be more than one tag decorating a commit ### [`v1.3.4`](https://code.forgejo.org/forgejo/release-notes-assistant/releases/tag/v1.3.4) [Compare Source](https://code.forgejo.org/forgejo/release-notes-assistant/compare/v1.3.3...v1.3.4) - bug fixes - [PR](https://placeholder:ca61bc9776c376e293039231cd01158c2c2f0a4f@code.forgejo.org/forgejo/release-notes-assistant/pulls/97): fix: %(decorate) is not supported by git 2.39
    --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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 these updates again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8726 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/release-notes-assistant-milestones.yml | 2 +- .forgejo/workflows/release-notes-assistant.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/release-notes-assistant-milestones.yml b/.forgejo/workflows/release-notes-assistant-milestones.yml index bf666dee6a..ff2afe7303 100644 --- a/.forgejo/workflows/release-notes-assistant-milestones.yml +++ b/.forgejo/workflows/release-notes-assistant-milestones.yml @@ -6,7 +6,7 @@ on: env: RNA_WORKDIR: /srv/rna - RNA_VERSION: v1.3.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.6 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: diff --git a/.forgejo/workflows/release-notes-assistant.yml b/.forgejo/workflows/release-notes-assistant.yml index b1acde6030..f47fc59422 100644 --- a/.forgejo/workflows/release-notes-assistant.yml +++ b/.forgejo/workflows/release-notes-assistant.yml @@ -8,7 +8,7 @@ on: - labeled env: - RNA_VERSION: v1.3.3 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org + RNA_VERSION: v1.3.6 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org jobs: release-notes: From 93b13e4d5243d7b76e49ef64aab7f8d5a59b5b4d Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 30 Jul 2025 13:12:17 +0200 Subject: [PATCH 206/297] chore(ci): send mail when daily integration tests fail (#8727) ## Testing - 24h after this is merged - push a commit to https://codeberg.org/forgejo-integration/forgejo/src/branch/forgejo with an error - cancel all workflows except for https://codeberg.org/forgejo-integration/forgejo/actions?workflow=testing-integration.yml&actor=0&status=0 - verify a notification was sent to forgejo-integration-actions@forgejo.org about the error - update the user research discussion at https://codeberg.org/forgejo/user-research/issues/64 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8727 Reviewed-by: Michael Kriese Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- .forgejo/workflows/testing-integration.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 102a2d9774..518b56c463 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -1,7 +1,8 @@ # # Additional integration tests designed to run once a day when # `mirror.yml` pushes to https://codeberg.org/forgejo-integration/forgejo -# and send a notification via email should they fail. +# and send a notification via email to the contact email of the +# organization should they fail. # # For debug purposes: # @@ -22,6 +23,8 @@ on: - 'forgejo' - 'v*/forgejo' +enable-email-notifications: true + jobs: test-unit: # if: vars.ROLE == 'forgejo-coding' From 1761dae2db70288878e31f95ee433de629347723 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Wed, 30 Jul 2025 15:40:12 +0200 Subject: [PATCH 207/297] test: add logger settings tests (#8729) fully cover existing behavior - ref #8667 I used Github Copilot for some code autocompletions. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8729 Reviewed-by: Earl Warren Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- modules/setting/log_test.go | 182 +++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 5 deletions(-) diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go index 223bd68285..528921c669 100644 --- a/modules/setting/log_test.go +++ b/modules/setting/log_test.go @@ -10,16 +10,13 @@ import ( "forgejo.org/modules/json" "forgejo.org/modules/log" + "forgejo.org/modules/test" "github.com/stretchr/testify/require" ) func initLoggersByConfig(t *testing.T, config string) (*log.LoggerManager, func()) { - oldLogConfig := Log - Log = LogGlobalConfig{} - defer func() { - Log = oldLogConfig - }() + defer test.MockVariableValue(&Log, LogGlobalConfig{})() cfg, err := NewConfigProviderFromData(config) require.NoError(t, err) @@ -29,6 +26,17 @@ func initLoggersByConfig(t *testing.T, config string) (*log.LoggerManager, func( return manager, manager.Close } +func initLoggerConfig(t *testing.T, config string) ConfigProvider { + defer test.MockVariableValue(&Log, LogGlobalConfig{})() + + cfg, err := NewConfigProviderFromData(config) + require.NoError(t, err) + + prepareLoggerConfig(cfg) + + return cfg +} + func toJSON(v any) string { b, _ := json.MarshalIndent(v, "", "\t") return string(b) @@ -395,3 +403,167 @@ COMPRESSION_LEVEL = 4 expected = strings.ReplaceAll(expected, "$FILENAME-1", tempPath("file-xxx.log")) require.JSONEq(t, expected, toJSON(dump)) } + +func TestLegacyLoggerMigrations(t *testing.T) { + type Cases = []struct { + name string + cfg string + exp string + } + + runCases := func(t *testing.T, key string, cases Cases) { + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + cfg := initLoggerConfig(t, c.cfg) + require.Equal(t, c.exp, cfg.Section("log").Key(key).String()) + }) + } + } + + t.Run("default", func(t *testing.T) { + runCases(t, "logger.default.MODE", Cases{ + { + "uses default value for default logger", + "", + ",", + }, + { + "uses logger.default.MODE for default logger", + `[log] +logger.default.MODE = file +`, + "file", + }, + }) + }) + + t.Run("access", func(t *testing.T) { + runCases(t, "logger.access.MODE", Cases{ + { + "uses default value for access logger", + "", + "", + }, + { + "uses ACCESS for access logger", + `[log] +ACCESS = file +`, + "file", + }, + { + "ENABLE_ACCESS_LOG=true doesn't change access logger", + `[log] +ENABLE_ACCESS_LOG = true +logger.access.MODE = console +`, + "console", + }, + { + "ENABLE_ACCESS_LOG=false disables access logger", + `[log] +ENABLE_ACCESS_LOG = false +logger.access.MODE = console +`, + "", + }, + { + "logger.access.MODE has precedence over ACCESS for access logger", + `[log] +ACCESS = file +logger.access.MODE = console +`, + "console", + }, + { + "ENABLE_ACCESS_LOG doesn't enable access logger", + `[log] +ENABLE_ACCESS_LOG = true +`, + "", // should be `,` + }, + }) + }) + + t.Run("router", func(t *testing.T) { + runCases(t, "logger.router.MODE", Cases{ + { + "uses default value for router logger", + "", + ",", + }, + { + "uses ROUTER for router logger", + `[log] +ROUTER = file +`, + "file", + }, + { + "DISABLE_ROUTER_LOG=false doesn't change router logger", + `[log] +ROUTER = file +DISABLE_ROUTER_LOG = false +`, + "file", + }, + { + "DISABLE_ROUTER_LOG=true disables router logger", + `[log] +DISABLE_ROUTER_LOG = true +logger.router.MODE = console +`, + "", + }, + { + "logger.router.MODE as precedence over ROUTER for router logger", + `[log] +ROUTER = file +logger.router.MODE = console +`, + "console", + }, + }) + }) + + t.Run("xorm", func(t *testing.T) { + runCases(t, "logger.xorm.MODE", Cases{ + { + "uses default value for xorm logger", + "", + ",", + }, + { + "uses XORM for xorm logger", + `[log] +XORM = file +`, + "file", + }, + { + "ENABLE_XORM_LOG=true doesn't change xorm logger", + `[log] +ENABLE_XORM_LOG = true +logger.xorm.MODE = console +`, + "console", + }, + { + "ENABLE_XORM_LOG=false disables xorm logger", + `[log] +ENABLE_XORM_LOG = false +logger.xorm.MODE = console +`, + "", + }, + { + "logger.xorm.MODE has precedence over XORM for xorm logger", + `[log] +XORM = file +logger.xorm.MODE = console +`, + "console", + }, + }) + }) +} From 98e2a64e2d6a7d045d986280225fa5fc9bfdb4f6 Mon Sep 17 00:00:00 2001 From: pkpkpkpk Date: Wed, 30 Jul 2025 18:02:46 +0200 Subject: [PATCH 208/297] fix: allow double digit epoch for Debian packages (#8671) Debian packages were capped for a single digit epoch, relax that requirement to a double digit epoch. This is allowed by Debian. Resolves forgejo/forgejo#8649 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8671 Reviewed-by: Gusted Co-authored-by: pkpkpkpk Co-committed-by: pkpkpkpk --- modules/packages/debian/metadata.go | 2 +- modules/packages/debian/metadata_test.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/packages/debian/metadata.go b/modules/packages/debian/metadata.go index e44801654b..1729a2206e 100644 --- a/modules/packages/debian/metadata.go +++ b/modules/packages/debian/metadata.go @@ -46,7 +46,7 @@ var ( // https://www.debian.org/doc/debian-policy/ch-controlfields.html#source namePattern = regexp.MustCompile(`\A[a-z0-9][a-z0-9+-.]+\z`) // https://www.debian.org/doc/debian-policy/ch-controlfields.html#version - versionPattern = regexp.MustCompile(`\A(?:[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`) + versionPattern = regexp.MustCompile(`\A(?:[1-9]?[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`) ) type Package struct { diff --git a/modules/packages/debian/metadata_test.go b/modules/packages/debian/metadata_test.go index cfcbc57ee0..079b9c19c8 100644 --- a/modules/packages/debian/metadata_test.go +++ b/modules/packages/debian/metadata_test.go @@ -167,6 +167,14 @@ func TestParseControlFile(t *testing.T) { require.ErrorIs(t, err, ErrInvalidArchitecture) }) + t.Run("ValidVersionEpoch", func(t *testing.T) { + for _, version := range []string{"0:1.2.3-test", "1:1.2.3-test", "9:1.2.3-test", "10:1.2.3-test", "37:1.2.3-test", "99:1.2.3-test"} { + p, err := ParseControlFile(buildContent(packageName, version, packageArchitecture)) + require.NoError(t, err) + assert.NotNil(t, p) + } + }) + t.Run("Valid", func(t *testing.T) { content := buildContent(packageName, packageVersion, packageArchitecture) full := content.String() From 022ab86988202ffcaed19000ccb9f751403b245c Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Wed, 30 Jul 2025 18:08:28 +0200 Subject: [PATCH 209/297] chore(api): update swagger method descripitons (#8728) Speaking from personal experience, when exploring the API I find myself trying to parse the exact meaning of many descriptions for a while, and I also have to get used to many different kinds of inconsistencies and grammar issues. This PR improves a few of these. Some I tried to reword to make them easier to understand, for others I just improved consistency a little, like capitalization. This area needs more work, this PR just makes some progress. Anything that is improved in this one can be improved further in later PRs, so in review please focus on regressions if you find any. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8728 Reviewed-by: Earl Warren Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- models/repo/topic.go | 2 +- routers/api/v1/admin/email.go | 4 +- routers/api/v1/admin/hooks.go | 4 +- routers/api/v1/admin/user.go | 14 ++--- routers/api/v1/org/action.go | 22 ++++---- routers/api/v1/org/avatar.go | 8 +-- routers/api/v1/org/org.go | 2 +- routers/api/v1/repo/avatar.go | 8 +-- routers/api/v1/repo/mirror.go | 8 +-- routers/api/v1/repo/pull_review.go | 4 +- routers/api/v1/repo/topic.go | 6 +-- routers/api/v1/user/app.go | 22 ++++---- routers/api/v1/user/avatar.go | 8 +-- routers/api/v1/user/email.go | 12 ++--- routers/api/v1/user/gpg_key.go | 8 +-- routers/api/v1/user/settings.go | 8 +-- routers/api/v1/user/user.go | 4 +- templates/swagger/v1_json.tmpl | 82 +++++++++++++++--------------- 18 files changed, 113 insertions(+), 113 deletions(-) diff --git a/models/repo/topic.go b/models/repo/topic.go index 4a3bdc7d8c..9086f17627 100644 --- a/models/repo/topic.go +++ b/models/repo/topic.go @@ -164,7 +164,7 @@ func FindTopics(ctx context.Context, opts *FindTopicOptions) ([]*Topic, int64, e orderBy := "topic.repo_count DESC" if opts.RepoID > 0 { sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") - orderBy = "topic.name" // when render topics for a repo, it's better to sort them by name, to get consistent result + orderBy = "topic.name" // When rendering topics for a repo, it's better to sort them by name to get consistent results } if opts.PageSize > 0 { sess = db.SetSessionPagination(sess, opts) diff --git a/routers/api/v1/admin/email.go b/routers/api/v1/admin/email.go index 906780a44b..9f6ef0fedf 100644 --- a/routers/api/v1/admin/email.go +++ b/routers/api/v1/admin/email.go @@ -17,7 +17,7 @@ import ( func GetAllEmails(ctx *context.APIContext) { // swagger:operation GET /admin/emails admin adminGetAllEmails // --- - // summary: List all emails + // summary: List all users' email addresses // produces: // - application/json // parameters: @@ -60,7 +60,7 @@ func GetAllEmails(ctx *context.APIContext) { func SearchEmail(ctx *context.APIContext) { // swagger:operation GET /admin/emails/search admin adminSearchEmails // --- - // summary: Search all emails + // summary: Search users' email addresses // produces: // - application/json // parameters: diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go index b3db2eb5e3..36ca6831e6 100644 --- a/routers/api/v1/admin/hooks.go +++ b/routers/api/v1/admin/hooks.go @@ -17,11 +17,11 @@ import ( webhook_service "forgejo.org/services/webhook" ) -// ListHooks list system's webhooks +// ListHooks list system webhooks func ListHooks(ctx *context.APIContext) { // swagger:operation GET /admin/hooks admin adminListHooks // --- - // summary: List system's webhooks + // summary: List global (system) webhooks // produces: // - application/json // parameters: diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index de54f21d94..f3e321a047 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -51,11 +51,11 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64 u.LoginSource = source.ID } -// CreateUser create a user +// CreateUser create a user account func CreateUser(ctx *context.APIContext) { // swagger:operation POST /admin/users admin adminCreateUser // --- - // summary: Create a user + // summary: Create a user account // consumes: // - application/json // produces: @@ -274,7 +274,7 @@ func EditUser(ctx *context.APIContext) { func DeleteUser(ctx *context.APIContext) { // swagger:operation DELETE /admin/users/{username} admin adminDeleteUser // --- - // summary: Delete a user + // summary: Delete user account // produces: // - application/json // parameters: @@ -324,11 +324,11 @@ func DeleteUser(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// CreatePublicKey api for creating a public key to a user +// CreatePublicKey adds an SSH public key to user's account func CreatePublicKey(ctx *context.APIContext) { // swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey // --- - // summary: Add a public key on behalf of a user + // summary: Add an SSH public key to user's account // consumes: // - application/json // produces: @@ -356,11 +356,11 @@ func CreatePublicKey(ctx *context.APIContext) { user.CreateUserPublicKey(ctx, *form, ctx.ContextUser.ID) } -// DeleteUserPublicKey api for deleting a user's public key +// DeleteUserPublicKey removes an SSH public key from user's account func DeleteUserPublicKey(ctx *context.APIContext) { // swagger:operation DELETE /admin/users/{username}/keys/{id} admin adminDeleteUserPublicKey // --- - // summary: Delete a user's public key + // summary: Remove a public key from user's account // produces: // - application/json // parameters: diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 0d8550a019..8b330aa752 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -20,11 +20,11 @@ import ( secret_service "forgejo.org/services/secrets" ) -// ListActionsSecrets list an organization's actions secrets +// ListActionsSecrets lists actions secrets of an organization func (Action) ListActionsSecrets(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets // --- - // summary: List an organization's actions secrets + // summary: List actions secrets of an organization // produces: // - application/json // parameters: @@ -218,7 +218,7 @@ func (Action) SearchActionRunJobs(ctx *context.APIContext) { func (Action) ListVariables(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList // --- - // summary: Get an org-level variables list + // summary: List variables of an organization // produces: // - application/json // parameters: @@ -266,11 +266,11 @@ func (Action) ListVariables(ctx *context.APIContext) { ctx.JSON(http.StatusOK, variables) } -// GetVariable get an org-level variable +// GetVariable gives organization's variable func (Action) GetVariable(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable // --- - // summary: Get an org-level variable + // summary: Get organization's variable by name // produces: // - application/json // parameters: @@ -315,11 +315,11 @@ func (Action) GetVariable(ctx *context.APIContext) { ctx.JSON(http.StatusOK, variable) } -// DeleteVariable delete an org-level variable +// DeleteVariable deletes an organization's variable func (Action) DeleteVariable(ctx *context.APIContext) { // swagger:operation DELETE /orgs/{org}/actions/variables/{variablename} organization deleteOrgVariable // --- - // summary: Delete an org-level variable + // summary: Delete organization's variable by name // produces: // - application/json // parameters: @@ -359,11 +359,11 @@ func (Action) DeleteVariable(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// CreateVariable create an org-level variable +// CreateVariable creates a new variable in organization func (Action) CreateVariable(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable // --- - // summary: Create an org-level variable + // summary: Create a new variable in organization // consumes: // - application/json // produces: @@ -423,11 +423,11 @@ func (Action) CreateVariable(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// UpdateVariable update an org-level variable +// UpdateVariable updates variable in organization func (Action) UpdateVariable(ctx *context.APIContext) { // swagger:operation PUT /orgs/{org}/actions/variables/{variablename} organization updateOrgVariable // --- - // summary: Update an org-level variable + // summary: Update variable in organization // consumes: // - application/json // produces: diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go index 824a9f3495..043da3186f 100644 --- a/routers/api/v1/org/avatar.go +++ b/routers/api/v1/org/avatar.go @@ -13,11 +13,11 @@ import ( user_service "forgejo.org/services/user" ) -// UpdateAvatarupdates the Avatar of an Organisation +// UpdateAvatar updates an organization's avatar func UpdateAvatar(ctx *context.APIContext) { // swagger:operation POST /orgs/{org}/avatar organization orgUpdateAvatar // --- - // summary: Update Avatar + // summary: Update an organization's avatar // produces: // - application/json // parameters: @@ -52,11 +52,11 @@ func UpdateAvatar(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// DeleteAvatar deletes the Avatar of an Organisation +// DeleteAvatar deletes an organization's avatar func DeleteAvatar(ctx *context.APIContext) { // swagger:operation DELETE /orgs/{org}/avatar organization orgDeleteAvatar // --- - // summary: Delete Avatar + // summary: Delete an organization's avatar. It will be replaced by a default one // produces: // - application/json // parameters: diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 87bc27be63..a4ea924979 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -180,7 +180,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { func GetAll(ctx *context.APIContext) { // swagger:operation Get /orgs organization orgGetAll // --- - // summary: Get list of organizations + // summary: List all organizations // produces: // - application/json // parameters: diff --git a/routers/api/v1/repo/avatar.go b/routers/api/v1/repo/avatar.go index 84aafe764d..12308ce2df 100644 --- a/routers/api/v1/repo/avatar.go +++ b/routers/api/v1/repo/avatar.go @@ -13,11 +13,11 @@ import ( repo_service "forgejo.org/services/repository" ) -// UpdateVatar updates the Avatar of an Repo +// UpdateVatar updates repo avatar func UpdateAvatar(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/avatar repository repoUpdateAvatar // --- - // summary: Update avatar + // summary: Update a repository's avatar // produces: // - application/json // parameters: @@ -56,11 +56,11 @@ func UpdateAvatar(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// UpdateAvatar deletes the Avatar of an Repo +// DeleteAvatar deletes repo avatar func DeleteAvatar(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/avatar repository repoDeleteAvatar // --- - // summary: Delete avatar + // summary: Delete a repository's avatar // produces: // - application/json // parameters: diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index f08867dee4..08ef68cbfc 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -251,11 +251,11 @@ func GetPushMirrorByName(ctx *context.APIContext) { ctx.JSON(http.StatusOK, m) } -// AddPushMirror adds a push mirror to a repository +// AddPushMirror sets up a new push mirror in a repository func AddPushMirror(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/push_mirrors repository repoAddPushMirror // --- - // summary: add a push mirror to the repository + // summary: Set up a new push mirror in a repository // consumes: // - application/json // produces: @@ -296,11 +296,11 @@ func AddPushMirror(ctx *context.APIContext) { CreatePushMirror(ctx, pushMirror) } -// DeletePushMirrorByRemoteName deletes a push mirror from a repository by remoteName +// DeletePushMirrorByRemoteName removes a push mirror from a repository by remoteName func DeletePushMirrorByRemoteName(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/push_mirrors/{name} repository repoDeletePushMirror // --- - // summary: deletes a push mirror from a repository by remoteName + // summary: Remove a push mirror from a repository by remoteName // produces: // - application/json // parameters: diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 830a62bf54..06b47fef70 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -716,7 +716,7 @@ func prepareSingleReview(ctx *context.APIContext) (*issues_model.Review, *issues func CreateReviewRequests(ctx *context.APIContext) { // swagger:operation POST /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoCreatePullReviewRequests // --- - // summary: create review requests for a pull request + // summary: Create review requests for a pull request // produces: // - application/json // parameters: @@ -757,7 +757,7 @@ func CreateReviewRequests(ctx *context.APIContext) { func DeleteReviewRequests(ctx *context.APIContext) { // swagger:operation DELETE /repos/{owner}/{repo}/pulls/{index}/requested_reviewers repository repoDeletePullReviewRequests // --- - // summary: cancel review requests for a pull request + // summary: Cancel review requests for a pull request // produces: // - application/json // parameters: diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index daa637936e..8829e37bc3 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -253,17 +253,17 @@ func DeleteTopic(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// TopicSearch search for creating topic +// TopicSearch searches known topics, i.e. when adding a topic to a repository func TopicSearch(ctx *context.APIContext) { // swagger:operation GET /topics/search repository topicSearch // --- - // summary: search topics via keyword + // summary: Search for topics by keyword // produces: // - application/json // parameters: // - name: q // in: query - // description: keywords to search + // description: keyword to search for // required: true // type: string // - name: page diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index 138ad5a1d2..65955913fd 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -71,11 +71,11 @@ func ListAccessTokens(ctx *context.APIContext) { ctx.JSON(http.StatusOK, &apiTokens) } -// CreateAccessToken create access tokens +// CreateAccessToken creates an access token for doer func CreateAccessToken(ctx *context.APIContext) { // swagger:operation POST /users/{username}/tokens user userCreateToken // --- - // summary: Create an access token + // summary: Generate an access token for the current user // consumes: // - application/json // produces: @@ -141,11 +141,11 @@ func CreateAccessToken(ctx *context.APIContext) { }) } -// DeleteAccessToken delete access tokens +// DeleteAccessToken deletes an access token from doer's account func DeleteAccessToken(ctx *context.APIContext) { // swagger:operation DELETE /users/{username}/tokens/{token} user userDeleteAccessToken // --- - // summary: delete an access token + // summary: Delete an access token from current user's account // produces: // - application/json // parameters: @@ -214,7 +214,7 @@ func DeleteAccessToken(ctx *context.APIContext) { func CreateOauth2Application(ctx *context.APIContext) { // swagger:operation POST /user/applications/oauth2 user userCreateOAuth2Application // --- - // summary: creates a new OAuth2 application + // summary: Creates a new OAuth2 application // produces: // - application/json // parameters: @@ -298,11 +298,11 @@ func ListOauth2Applications(ctx *context.APIContext) { ctx.JSON(http.StatusOK, &apiApps) } -// DeleteOauth2Application delete OAuth2 Application +// DeleteOauth2Application delete OAuth2 application func DeleteOauth2Application(ctx *context.APIContext) { // swagger:operation DELETE /user/applications/oauth2/{id} user userDeleteOAuth2Application // --- - // summary: delete an OAuth2 Application + // summary: Delete an OAuth2 application // produces: // - application/json // parameters: @@ -334,11 +334,11 @@ func DeleteOauth2Application(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// GetOauth2Application get OAuth2 Application +// GetOauth2Application returns an OAuth2 application func GetOauth2Application(ctx *context.APIContext) { // swagger:operation GET /user/applications/oauth2/{id} user userGetOAuth2Application // --- - // summary: get an OAuth2 Application + // summary: Get an OAuth2 application // produces: // - application/json // parameters: @@ -377,11 +377,11 @@ func GetOauth2Application(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.ToOAuth2Application(app)) } -// UpdateOauth2Application update OAuth2 Application +// UpdateOauth2Application updates an OAuth2 application func UpdateOauth2Application(ctx *context.APIContext) { // swagger:operation PATCH /user/applications/oauth2/{id} user userUpdateOAuth2Application // --- - // summary: update an OAuth2 Application, this includes regenerating the client secret + // summary: Update an OAuth2 application, this includes regenerating the client secret // produces: // - application/json // parameters: diff --git a/routers/api/v1/user/avatar.go b/routers/api/v1/user/avatar.go index 453682a37b..2b0659c251 100644 --- a/routers/api/v1/user/avatar.go +++ b/routers/api/v1/user/avatar.go @@ -13,11 +13,11 @@ import ( user_service "forgejo.org/services/user" ) -// UpdateAvatar updates the Avatar of an User +// UpdateAvatar updates doer's avatar func UpdateAvatar(ctx *context.APIContext) { // swagger:operation POST /user/avatar user userUpdateAvatar // --- - // summary: Update Avatar + // summary: Update avatar of the current user // produces: // - application/json // parameters: @@ -49,11 +49,11 @@ func UpdateAvatar(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -// DeleteAvatar deletes the Avatar of an User +// DeleteAvatar deletes doer's avatar func DeleteAvatar(ctx *context.APIContext) { // swagger:operation DELETE /user/avatar user userDeleteAvatar // --- - // summary: Delete Avatar + // summary: Delete avatar of the current user. It will be replaced by a default one // produces: // - application/json // responses: diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index 03d8d14b90..38da23442d 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -16,12 +16,12 @@ import ( user_service "forgejo.org/services/user" ) -// ListEmails list all of the authenticated user's email addresses +// ListEmails lists doer's all email addresses // see https://github.com/gogits/go-gogs-client/wiki/Users-Emails#list-email-addresses-for-a-user func ListEmails(ctx *context.APIContext) { // swagger:operation GET /user/emails user userListEmails // --- - // summary: List the authenticated user's email addresses + // summary: List all email addresses of the current user // produces: // - application/json // responses: @@ -44,11 +44,11 @@ func ListEmails(ctx *context.APIContext) { ctx.JSON(http.StatusOK, &apiEmails) } -// AddEmail add an email address +// AddEmail adds an email address to doer's account func AddEmail(ctx *context.APIContext) { // swagger:operation POST /user/emails user userAddEmail // --- - // summary: Add email addresses + // summary: Add an email addresses to the current user's account // produces: // - application/json // parameters: @@ -102,11 +102,11 @@ func AddEmail(ctx *context.APIContext) { ctx.JSON(http.StatusCreated, apiEmails) } -// DeleteEmail delete email +// DeleteEmail deletes an email address from doer's account func DeleteEmail(ctx *context.APIContext) { // swagger:operation DELETE /user/emails user userDeleteEmail // --- - // summary: Delete email addresses + // summary: Delete email addresses from the current user's account // produces: // - application/json // parameters: diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 886e33b205..bb7f3d3522 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -249,11 +249,11 @@ type swaggerUserCurrentPostGPGKey struct { Form api.CreateGPGKeyOption } -// CreateGPGKey create a GPG key belonging to the authenticated user +// CreateGPGKey adds a GPG public key doer's account func CreateGPGKey(ctx *context.APIContext) { // swagger:operation POST /user/gpg_keys user userCurrentPostGPGKey // --- - // summary: Create a GPG key + // summary: Add a GPG public key to current user's account // consumes: // - application/json // produces: @@ -274,11 +274,11 @@ func CreateGPGKey(ctx *context.APIContext) { CreateUserGPGKey(ctx, *form, ctx.Doer.ID) } -// DeleteGPGKey remove a GPG key belonging to the authenticated user +// DeleteGPGKey removes a GPG public key from doer's account func DeleteGPGKey(ctx *context.APIContext) { // swagger:operation DELETE /user/gpg_keys/{id} user userCurrentDeleteGPGKey // --- - // summary: Remove a GPG key + // summary: Remove a GPG public key from current user's account // produces: // - application/json // parameters: diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go index 134b448718..53455bcd75 100644 --- a/routers/api/v1/user/settings.go +++ b/routers/api/v1/user/settings.go @@ -14,11 +14,11 @@ import ( user_service "forgejo.org/services/user" ) -// GetUserSettings returns user settings +// GetUserSettings returns doer's account settings func GetUserSettings(ctx *context.APIContext) { // swagger:operation GET /user/settings user getUserSettings // --- - // summary: Get user settings + // summary: Get current user's account settings // produces: // - application/json // responses: @@ -31,11 +31,11 @@ func GetUserSettings(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.User2UserSettings(ctx.Doer)) } -// UpdateUserSettings returns user settings +// UpdateUserSettings updates settings in doer's account func UpdateUserSettings(ctx *context.APIContext) { // swagger:operation PATCH /user/settings user updateUserSettings // --- - // summary: Update user settings + // summary: Update settings in current user's account // parameters: // - name: body // in: body diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 19f7440047..2bd2829d09 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -266,7 +266,7 @@ func ListBlockedUsers(ctx *context.APIContext) { func BlockUser(ctx *context.APIContext) { // swagger:operation PUT /user/block/{username} user userBlockUser // --- - // summary: Blocks a user from the doer. + // summary: Blocks a user from the doer // produces: // - application/json // parameters: @@ -299,7 +299,7 @@ func BlockUser(ctx *context.APIContext) { func UnblockUser(ctx *context.APIContext) { // swagger:operation PUT /user/unblock/{username} user userUnblockUser // --- - // summary: Unblocks a user from the doer. + // summary: Unblocks a user from the doer // produces: // - application/json // parameters: diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e03f5d57c1..a610620eab 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -239,7 +239,7 @@ "tags": [ "admin" ], - "summary": "List all emails", + "summary": "List all users' email addresses", "operationId": "adminGetAllEmails", "parameters": [ { @@ -273,7 +273,7 @@ "tags": [ "admin" ], - "summary": "Search all emails", + "summary": "Search users' email addresses", "operationId": "adminSearchEmails", "parameters": [ { @@ -313,7 +313,7 @@ "tags": [ "admin" ], - "summary": "List system's webhooks", + "summary": "List global (system) webhooks", "operationId": "adminListHooks", "parameters": [ { @@ -1220,7 +1220,7 @@ "tags": [ "admin" ], - "summary": "Create a user", + "summary": "Create a user account", "operationId": "adminCreateUser", "parameters": [ { @@ -1255,7 +1255,7 @@ "tags": [ "admin" ], - "summary": "Delete a user", + "summary": "Delete user account", "operationId": "adminDeleteUser", "parameters": [ { @@ -1342,7 +1342,7 @@ "tags": [ "admin" ], - "summary": "Add a public key on behalf of a user", + "summary": "Add an SSH public key to user's account", "operationId": "adminCreatePublicKey", "parameters": [ { @@ -1381,7 +1381,7 @@ "tags": [ "admin" ], - "summary": "Delete a user's public key", + "summary": "Remove a public key from user's account", "operationId": "adminDeleteUserPublicKey", "parameters": [ { @@ -2158,7 +2158,7 @@ "tags": [ "organization" ], - "summary": "Get list of organizations", + "summary": "List all organizations", "operationId": "orgGetAll", "parameters": [ { @@ -2381,7 +2381,7 @@ "tags": [ "organization" ], - "summary": "List an organization's actions secrets", + "summary": "List actions secrets of an organization", "operationId": "orgListActionsSecrets", "parameters": [ { @@ -2514,7 +2514,7 @@ "tags": [ "organization" ], - "summary": "Get an org-level variables list", + "summary": "List variables of an organization", "operationId": "getOrgVariablesList", "parameters": [ { @@ -2558,7 +2558,7 @@ "tags": [ "organization" ], - "summary": "Get an org-level variable", + "summary": "Get organization's variable by name", "operationId": "getOrgVariable", "parameters": [ { @@ -2598,7 +2598,7 @@ "tags": [ "organization" ], - "summary": "Update an org-level variable", + "summary": "Update variable in organization", "operationId": "updateOrgVariable", "parameters": [ { @@ -2648,7 +2648,7 @@ "tags": [ "organization" ], - "summary": "Create an org-level variable", + "summary": "Create a new variable in organization", "operationId": "createOrgVariable", "parameters": [ { @@ -2695,7 +2695,7 @@ "tags": [ "organization" ], - "summary": "Delete an org-level variable", + "summary": "Delete organization's variable by name", "operationId": "deleteOrgVariable", "parameters": [ { @@ -2788,7 +2788,7 @@ "tags": [ "organization" ], - "summary": "Update Avatar", + "summary": "Update an organization's avatar", "operationId": "orgUpdateAvatar", "parameters": [ { @@ -2822,7 +2822,7 @@ "tags": [ "organization" ], - "summary": "Delete Avatar", + "summary": "Delete an organization's avatar. It will be replaced by a default one", "operationId": "orgDeleteAvatar", "parameters": [ { @@ -5730,7 +5730,7 @@ "tags": [ "repository" ], - "summary": "Update avatar", + "summary": "Update a repository's avatar", "operationId": "repoUpdateAvatar", "parameters": [ { @@ -5771,7 +5771,7 @@ "tags": [ "repository" ], - "summary": "Delete avatar", + "summary": "Delete a repository's avatar", "operationId": "repoDeleteAvatar", "parameters": [ { @@ -13690,7 +13690,7 @@ "tags": [ "repository" ], - "summary": "create review requests for a pull request", + "summary": "Create review requests for a pull request", "operationId": "repoCreatePullReviewRequests", "parameters": [ { @@ -13743,7 +13743,7 @@ "tags": [ "repository" ], - "summary": "cancel review requests for a pull request", + "summary": "Cancel review requests for a pull request", "operationId": "repoDeletePullReviewRequests", "parameters": [ { @@ -14558,7 +14558,7 @@ "tags": [ "repository" ], - "summary": "add a push mirror to the repository", + "summary": "Set up a new push mirror in a repository", "operationId": "repoAddPushMirror", "parameters": [ { @@ -14702,7 +14702,7 @@ "tags": [ "repository" ], - "summary": "deletes a push mirror from a repository by remoteName", + "summary": "Remove a push mirror from a repository by remoteName", "operationId": "repoDeletePushMirror", "parameters": [ { @@ -18026,12 +18026,12 @@ "tags": [ "repository" ], - "summary": "search topics via keyword", + "summary": "Search for topics by keyword", "operationId": "topicSearch", "parameters": [ { "type": "string", - "description": "keywords to search", + "description": "keyword to search for", "name": "q", "in": "query", "required": true @@ -18490,7 +18490,7 @@ "tags": [ "user" ], - "summary": "creates a new OAuth2 application", + "summary": "Creates a new OAuth2 application", "operationId": "userCreateOAuth2Application", "parameters": [ { @@ -18526,7 +18526,7 @@ "tags": [ "user" ], - "summary": "get an OAuth2 Application", + "summary": "Get an OAuth2 application", "operationId": "userGetOAuth2Application", "parameters": [ { @@ -18560,7 +18560,7 @@ "tags": [ "user" ], - "summary": "delete an OAuth2 Application", + "summary": "Delete an OAuth2 application", "operationId": "userDeleteOAuth2Application", "parameters": [ { @@ -18594,7 +18594,7 @@ "tags": [ "user" ], - "summary": "update an OAuth2 Application, this includes regenerating the client secret", + "summary": "Update an OAuth2 application, this includes regenerating the client secret", "operationId": "userUpdateOAuth2Application", "parameters": [ { @@ -18638,7 +18638,7 @@ "tags": [ "user" ], - "summary": "Update Avatar", + "summary": "Update avatar of the current user", "operationId": "userUpdateAvatar", "parameters": [ { @@ -18668,7 +18668,7 @@ "tags": [ "user" ], - "summary": "Delete Avatar", + "summary": "Delete avatar of the current user. It will be replaced by a default one", "operationId": "userDeleteAvatar", "responses": { "204": { @@ -18691,7 +18691,7 @@ "tags": [ "user" ], - "summary": "Blocks a user from the doer.", + "summary": "Blocks a user from the doer", "operationId": "userBlockUser", "parameters": [ { @@ -18729,7 +18729,7 @@ "tags": [ "user" ], - "summary": "List the authenticated user's email addresses", + "summary": "List all email addresses of the current user", "operationId": "userListEmails", "responses": { "200": { @@ -18750,7 +18750,7 @@ "tags": [ "user" ], - "summary": "Add email addresses", + "summary": "Add an email addresses to the current user's account", "operationId": "userAddEmail", "parameters": [ { @@ -18783,7 +18783,7 @@ "tags": [ "user" ], - "summary": "Delete email addresses", + "summary": "Delete email addresses from the current user's account", "operationId": "userDeleteEmail", "parameters": [ { @@ -19080,7 +19080,7 @@ "tags": [ "user" ], - "summary": "Create a GPG key", + "summary": "Add a GPG public key to current user's account", "operationId": "userCurrentPostGPGKey", "parameters": [ { @@ -19152,7 +19152,7 @@ "tags": [ "user" ], - "summary": "Remove a GPG key", + "summary": "Remove a GPG public key from current user's account", "operationId": "userCurrentDeleteGPGKey", "parameters": [ { @@ -19862,7 +19862,7 @@ "tags": [ "user" ], - "summary": "Get user settings", + "summary": "Get current user's account settings", "operationId": "getUserSettings", "responses": { "200": { @@ -19883,7 +19883,7 @@ "tags": [ "user" ], - "summary": "Update user settings", + "summary": "Update settings in current user's account", "operationId": "updateUserSettings", "parameters": [ { @@ -20230,7 +20230,7 @@ "tags": [ "user" ], - "summary": "Unblocks a user from the doer.", + "summary": "Unblocks a user from the doer", "operationId": "userUnblockUser", "parameters": [ { @@ -20903,7 +20903,7 @@ "tags": [ "user" ], - "summary": "Create an access token", + "summary": "Generate an access token for the current user", "operationId": "userCreateToken", "parameters": [ { @@ -20945,7 +20945,7 @@ "tags": [ "user" ], - "summary": "delete an access token", + "summary": "Delete an access token from current user's account", "operationId": "userDeleteAccessToken", "parameters": [ { From 79af994eae32214f0137ce33aa1439108692de78 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 31 Jul 2025 13:41:59 +0200 Subject: [PATCH 210/297] chore: github.com/nektos/act is now at code.forgejo.org/forgejo/runner/v9 (#8742) Refs: https://code.forgejo.org/forgejo/runner/releases/tag/v9.0.1 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8742 Reviewed-by: Michael Kriese Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- assets/go-licenses.json | 10 +++++----- go.mod | 8 +++----- go.sum | 12 ++++++------ models/actions/run.go | 2 +- models/actions/task.go | 2 +- modules/actions/workflows.go | 6 +++--- renovate.json | 2 -- routers/web/repo/actions/actions.go | 2 +- routers/web/repo/view.go | 2 +- services/actions/commit_status.go | 2 +- services/actions/job_emitter.go | 2 +- services/actions/notifier_helper.go | 4 ++-- services/actions/schedule_tasks.go | 4 ++-- services/actions/workflows.go | 4 ++-- 14 files changed, 29 insertions(+), 33 deletions(-) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index c3b261320c..59d933a6d6 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -34,6 +34,11 @@ "path": "code.forgejo.org/forgejo/reply/LICENSE", "licenseText": "MIT License\n\nCopyright (c) The Forgejo Authors\nCopyright (c) Discourse\nCopyright (c) Claudemiro\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, + { + "name": "code.forgejo.org/forgejo/runner/v9/act", + "path": "code.forgejo.org/forgejo/runner/v9/act/LICENSE", + "licenseText": "Copyright (c) 2023-2025 The Forgejo Authors\nCopyright (c) 2022 The Gitea Authors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" + }, { "name": "code.forgejo.org/go-chi/binding", "path": "code.forgejo.org/go-chi/binding/LICENSE", @@ -764,11 +769,6 @@ "path": "github.com/munnerz/goautoneg/LICENSE", "licenseText": "Copyright (c) 2011, Open Knowledge Foundation Ltd.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in\n the documentation and/or other materials provided with the\n distribution.\n\n Neither the name of the Open Knowledge Foundation Ltd. nor the\n names of its contributors may be used to endorse or promote\n products derived from this software without specific prior written\n permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, - { - "name": "github.com/nektos/act/pkg", - "path": "github.com/nektos/act/pkg/LICENSE", - "licenseText": "MIT License\n\nCopyright (c) 2022 The Gitea Authors\nCopyright (c) 2019\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" - }, { "name": "github.com/niklasfasching/go-org/org", "path": "github.com/niklasfasching/go-org/org/LICENSE", diff --git a/go.mod b/go.mod index 9cf9183b8e..167fe117c1 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( code.forgejo.org/forgejo/go-rpmutils v1.0.0 code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/reply v1.0.2 + code.forgejo.org/forgejo/runner/v9 v9.0.1 code.forgejo.org/go-chi/binding v1.0.1 code.forgejo.org/go-chi/cache v1.0.1 code.forgejo.org/go-chi/captcha v1.0.2 @@ -77,7 +78,6 @@ require ( github.com/microcosm-cc/bluemonday v1.0.27 github.com/minio/minio-go/v7 v7.0.94 github.com/msteinert/pam/v2 v2.1.0 - github.com/nektos/act v0.2.52 github.com/niklasfasching/go-org v1.9.1 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 @@ -105,7 +105,7 @@ require ( golang.org/x/sync v0.16.0 golang.org/x/sys v0.34.0 golang.org/x/text v0.27.0 - google.golang.org/protobuf v1.36.4 + google.golang.org/protobuf v1.36.6 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 @@ -236,7 +236,7 @@ require ( go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect golang.org/x/mod v0.26.0 // indirect - golang.org/x/time v0.11.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.35.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/warnings.v0 v0.1.2 // indirect @@ -244,8 +244,6 @@ require ( replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 -replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.33.0 - replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 replace github.com/gliderlabs/ssh => code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 diff --git a/go.sum b/go.sum index 1367fc8d10..a8af308a15 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ code.forgejo.org/f3/gof3/v3 v3.11.0 h1:f/xToKwqTgxG6PYxvewywjDQyCcyHEEJ6sZqUitFs code.forgejo.org/f3/gof3/v3 v3.11.0/go.mod h1:4FaRUNSQGBiD1M0DuB0yNv+Z2wMtlOeckgygHSSq4KQ= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= -code.forgejo.org/forgejo/act v1.33.0 h1:ayQTXkpk+Vj5/yQMNZagA0xpQgGVeSbcrPXcIS3K1kY= -code.forgejo.org/forgejo/act v1.33.0/go.mod h1:WkmxVBteC4zoyQGYp8ZFZY7Xb+jat+b7ChvqW6TxqF8= code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEjb3jaYYtmSE= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/go-rpmutils v1.0.0 h1:RZGGeKt70p/WaIEL97pyT6uiiEIoN8/aLmS5Z6WmX0M= @@ -16,6 +14,8 @@ code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/Uf code.forgejo.org/forgejo/levelqueue v1.0.0/go.mod h1:fmG6zhVuqim2rxSFOoasgXO8V2W/k9U31VVYqLIRLhQ= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= +code.forgejo.org/forgejo/runner/v9 v9.0.1 h1:Qdg7c0yvqgjXj9NMQ+Vsq+GuUoGshHFHGYF79Bz+/2g= +code.forgejo.org/forgejo/runner/v9 v9.0.1/go.mod h1:b6JWcpwHnBwLuWeQERM4pCRzFbnsEvC9gagQfxbZeJw= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY= @@ -679,8 +679,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -700,8 +700,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/models/actions/run.go b/models/actions/run.go index 69592120e9..6e0c70f8cf 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -21,7 +21,7 @@ import ( "forgejo.org/modules/util" webhook_module "forgejo.org/modules/webhook" - "github.com/nektos/act/pkg/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" "xorm.io/builder" ) diff --git a/models/actions/task.go b/models/actions/task.go index 7c85ffc232..88b30196e3 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -17,8 +17,8 @@ import ( "forgejo.org/modules/timeutil" "forgejo.org/modules/util" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" lru "github.com/hashicorp/golang-lru/v2" - "github.com/nektos/act/pkg/jobparser" "xorm.io/builder" ) diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index b04f97df0f..26d862ac4a 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -13,10 +13,10 @@ import ( api "forgejo.org/modules/structs" webhook_module "forgejo.org/modules/webhook" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/model" + "code.forgejo.org/forgejo/runner/v9/act/workflowpattern" "github.com/gobwas/glob" - "github.com/nektos/act/pkg/jobparser" - "github.com/nektos/act/pkg/model" - "github.com/nektos/act/pkg/workflowpattern" "gopkg.in/yaml.v3" ) diff --git a/renovate.json b/renovate.json index e0d7bd5456..d76b6c7d23 100644 --- a/renovate.json +++ b/renovate.json @@ -28,8 +28,6 @@ "matchPackageNames": [ "docker.io/bitnami/minio", "github.com/go-ap/activitypub", - "github.com/nektos/act", - "gitea.com/gitea/act" ], "dependencyDashboardApproval": true }, diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 6f1f19b107..0c07e1968e 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -27,7 +27,7 @@ import ( "forgejo.org/services/context" "forgejo.org/services/convert" - "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/model" ) const ( diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 8e1028968e..36791fc770 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -56,7 +56,7 @@ import ( repo_service "forgejo.org/services/repository" files_service "forgejo.org/services/repository/files" - "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/model" _ "golang.org/x/image/bmp" // for processing bmp images _ "golang.org/x/image/webp" // for processing webp images diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index b054f2036c..d588c0f178 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -19,7 +19,7 @@ import ( webhook_module "forgejo.org/modules/webhook" commitstatus_service "forgejo.org/services/repository/commitstatus" - "github.com/nektos/act/pkg/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" ) // CreateCommitStatus creates a commit status for the given job. diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go index 8178d210ce..03428736b8 100644 --- a/services/actions/job_emitter.go +++ b/services/actions/job_emitter.go @@ -13,7 +13,7 @@ import ( "forgejo.org/modules/graceful" "forgejo.org/modules/queue" - "github.com/nektos/act/pkg/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" "xorm.io/builder" ) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index e31817a5e2..2b4273df31 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -30,8 +30,8 @@ import ( webhook_module "forgejo.org/modules/webhook" "forgejo.org/services/convert" - "github.com/nektos/act/pkg/jobparser" - "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + "code.forgejo.org/forgejo/runner/v9/act/model" ) type methodCtx struct{} diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index d275837cbb..fb9c5094c5 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -18,8 +18,8 @@ import ( "forgejo.org/modules/timeutil" webhook_module "forgejo.org/modules/webhook" - "github.com/nektos/act/pkg/jobparser" - act_model "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + act_model "code.forgejo.org/forgejo/runner/v9/act/model" "xorm.io/builder" ) diff --git a/services/actions/workflows.go b/services/actions/workflows.go index 22417d4e32..b5875a7a50 100644 --- a/services/actions/workflows.go +++ b/services/actions/workflows.go @@ -24,8 +24,8 @@ import ( "forgejo.org/modules/webhook" "forgejo.org/services/convert" - "github.com/nektos/act/pkg/jobparser" - act_model "github.com/nektos/act/pkg/model" + "code.forgejo.org/forgejo/runner/v9/act/jobparser" + act_model "code.forgejo.org/forgejo/runner/v9/act/model" ) type InputRequiredErr struct { From be7b87c1c26540873ec18539e93091bdc368b9d7 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Thu, 31 Jul 2025 13:00:55 +0000 Subject: [PATCH 211/297] i18n: update of translations from Codeberg Translate Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: BlackSpirits Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Hiraku Co-authored-by: Juno Takano Co-authored-by: Lzebulon Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: adf19 Co-authored-by: amv-bamboo Co-authored-by: dobrovolskyi Co-authored-by: earl-warren Co-authored-by: justbispo Co-authored-by: retarded-beast Co-authored-by: xtex Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_PT/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ar/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/be/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/pt_PT/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/sv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_ar.ini | 62 +++++++++++--- options/locale/locale_be.ini | 14 +++- options/locale/locale_de-DE.ini | 26 +++--- options/locale/locale_fr-FR.ini | 4 +- options/locale/locale_pt-PT.ini | 12 +-- options/locale/locale_ru-RU.ini | 22 ++--- options/locale/locale_sv-SE.ini | 6 ++ options/locale/locale_uk-UA.ini | 112 ++++++++++++++++---------- options/locale/locale_zh-CN.ini | 6 +- options/locale_next/locale_ar.json | 3 +- options/locale_next/locale_de-DE.json | 3 +- options/locale_next/locale_fil.json | 3 +- options/locale_next/locale_fr-FR.json | 11 ++- options/locale_next/locale_lv-LV.json | 3 +- options/locale_next/locale_nds.json | 3 +- options/locale_next/locale_pt-BR.json | 3 +- options/locale_next/locale_pt-PT.json | 9 ++- options/locale_next/locale_ru-RU.json | 9 ++- options/locale_next/locale_uk-UA.json | 4 +- 19 files changed, 211 insertions(+), 104 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 0b81b9cc03..d41677d540 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -1006,7 +1006,7 @@ issues.dependency.issue_no_dependencies = بلا اعتمادية. issues.filter_sort.mostcomment = الأكثر تعليقات issues.label.filter_sort.reverse_by_size = الأكبر حجما release.message = صِف هذا الإصدار -editor.cancel_lower = ألغِ +editor.cancel_lower = إلغاء issues.label.filter_sort.reverse_alphabetically = أبجديا معكوسا trust_model_helper = اختر نموذج الثقة للتحقق من التوقيعات. الخيارات المتاحة هي: issues.unlock_error = لا يمكن فك قفل مسألة غير مقفلة. @@ -1040,7 +1040,7 @@ issues.label_archive = أرشف التصنيف issues.choose.blank_about = أنشئ مسألة من القالب المبدئي. issues.filter_poster = منشئ release.delete_release = احذف الإصدار -editor.cancel = ألغِ +editor.cancel = إلغاء issues.change_title_at = `غيّر العنوان من %s إلى %s %s` branch.new_branch_from = أنشئ فرعًا جديدًا من "%s" issues.due_date_form_edit = "عدّل" @@ -1065,15 +1065,15 @@ projects.desc = أدر المسائل وطلبات الدمج في لوحات م ambiguous_runes_header = `يحتوي هذا الملف على محارف يونيكود غامضة` executable_file = ملف تنفيذي settings.webhook.response = الاستجابة -editor.delete = احذف %s -file_view_raw = أظهر المجرد +editor.delete = حذف %s +file_view_raw = أظهر الخام settings.add_webhook = أضف خطاف ويب settings.slack_channel = قناة commits = إيداعات commit = إيداع -editor.upload_file = ارفع ملفا +editor.upload_file = رفع ملف settings.webhook.headers = الترويسات -org_labels_desc_manage = أدرها +org_labels_desc_manage = إدارة editor.patch = طبّق الرقعة editor.must_have_write_access = يجب أن يكون لديك إمكانية الكتابة حتى تعدّل هذا الملف أو تقترح تعديلات. template.webhooks = خطاطيف الويب @@ -1097,12 +1097,12 @@ settings.webhook.payload = المحتوى invisible_runes_header = `يحتوي هذا الملف على محارف يونيكود غير مرئية` editor.filename_help = أضف مجلدا بكتابة اسمه ثم شرطة مائلة ('/'). احذف مجلدا بضغط زر Backspace أول شيء في خانة الاسم. editor.commit_changes = أودع التعديلات -editor.add_file = أضف ملفا +editor.add_file = إضافة ملف settings.githook_name = اسم الخطاف editor.fork_before_edit = يجب أن تشتق هذا المستودع حتى تعدّل هذا الملف أو تقترح تعديلات. projects.description_placeholder = الوصف tag = وسم -file_raw = مجرد +file_raw = خام projects.create = أنشئ مشروعا settings.update_webhook = حدّث خطاف الويب editor.push_rejected_no_message = لقد رفض الخادوم التعديل بغير رسالة. نرجو مراجعة خطاطيف جت. @@ -1121,7 +1121,7 @@ editor.no_changes_to_show = لا توجد تعديلات لعرضها. settings.webhook.test_delivery = اختبار التوصيل commit_graph.hide_pr_refs = أخفِ طلبات الدمج editor.new_file = ملف جديد -file_view_source = أظهر المصدر +file_view_source = عرض المصدر settings.webhook_deletion_success = أزيل خطاف الويب. projects.title = العنوان settings.slack_domain = النطاق @@ -1150,11 +1150,11 @@ settings.add_hook_success = أضيف خطاف الويب. commit.revert-header = إرجاع: %s editor.file_already_exists = يوجد فعلا في هذا المستودع ملف باسم "%s". settings.web_hook_name_matrix = متركس -editor.filename_cannot_be_empty = لا يمكن ترك اسم الملف فارغا. +editor.filename_cannot_be_empty = لا يمكن ترك اسم الملف فارغاً. editor.add_tmpl = أضف '<%s>' editor.new_branch_name_desc = اسم الفرع الجديد… release = إصدار -editor.delete_this_file = احذف الملف +editor.delete_this_file = حذف الملف editor.or = أو editor.push_rejected_summary = رسالة الرفض الكاملة: settings.webhook_deletion = أزل خطاف الويب @@ -1588,7 +1588,7 @@ star = نجمة cite_this_repo = الاستشهاد بهذا المستودع no_desc = لا يوجد وصف migrate.github.description = ترحيل البيانات من github.com أو خادم GitHub Enterprise. -migrated_from_fake = +migrated_from_fake =تم الترحيل من %[1]s clone_this_repo = استنسخ هذا المستودع fork_from_self = لا يمكنك اشتقاق مستودع تملكه. fork_guest_user = سجّل الدخول لاشتقاق هذا المستودع. @@ -1627,6 +1627,44 @@ branch = فرع template.git_hooks_tooltip = يتعذر عليك حاليًا تعديل أو إزالة خطافات Git بمجرد إضافتها. حدد هذا فقط إذا كنت تثق بمستودع القالب. code.desc = الوصول إلى الشيفرة المصدرية والملفات والالتزامات والفروع. tree = شجرة +clear_ref = 'مسح المرجع الحالي' +project = المشارييع +filter_branch_and_tag = تصفية الفرع أو العلامة +find_tag = البحث عن علامة +commit_graph = الرسم البياني للإيداع +editor.edit_this_file = عدل الملف +editor.signoff_desc = أضف مقطورة موقّعة من قِبل المُجرّد في نهاية رسالة سجل الدخول. +n_release_one = %s إصدار +symbolic_link = رابط رمزي +editor.cannot_edit_lfs_files = لا يمكن تحرير ملفات LFS في واجهة الويب. +editor.this_file_locked = الملف مقفل +n_commit_few = %s إيداعات +editor.file_delete_success = تم حذف الملف "%s". +editor.edit_file = عدّل الملف +commit.contained_in_default_branch = هذا الإيداع جزء من الفرع الافتراضي +line = سطر +lines = أسطر +normal_view = عرض عادي +n_branch_one = %s فرع +n_commit_one = %s إيداع +view_git_blame = عرض مسؤول تعديل git +editor.add_tmpl.filename = اسم الملف +editor.name_your_file = اسم ملفك… +editor.propose_file_change = اقتراح تغيير الملف +editor.new_branch_name = تسمية الفرع الجديد لهذا الإيداع +from_comment = (تعليق) +no_eol.text = لا EOL +no_eol.tooltip = هذا الملف لا يحتوي على نهاية لخط الشخصية. +stored_lfs = مخزن مع Git LFS +commit.contained_in = هذا الإيداع موجود في: +file_view_rendered = عرض المُخرج النهائي +n_branch_few = %s فروع +n_tag_one = %s علامة +n_tag_few = ‪%s علامات +n_release_few = %s إصدارات +file.title = %s عند %s +vendored = مضمن +file_follow = متابعة الروابط الرمزية [mail] admin.new_user.text = من فضلك اضغط هنا لإدارة هذا المستخدم من لوحة الإدارة. diff --git a/options/locale/locale_be.ini b/options/locale/locale_be.ini index fe04dadc3e..25aff3019c 100644 --- a/options/locale/locale_be.ini +++ b/options/locale/locale_be.ini @@ -7,10 +7,20 @@ sign_in = Увайсці sign_in_or = або sign_out = Выйсці sign_up = Зарэгістравацца -link_account = Звязаць Уліковы запіс +link_account = Звязаць уліковы запіс register = Рэгістрацыя version = Версія powered_by = Працуе на ℅s page = Старонка home = Галоўная Старонка -sign_in_with_provider = Увайсці з %s \ No newline at end of file +sign_in_with_provider = Увайсці з %s +template = Шаблон +language = Мова +notifications = Апавяшчэнні +create_new = Стварыць… +user_profile_and_more = Профіль і налады… +signed_in_as = Увайшоў як +enable_javascript = Гэты вэб-сайт патрабуе JavaScript. +toc = Змест +licenses = Ліцэнзіі +return_to_forgejo = Вярнуцца да Forgejo \ No newline at end of file diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 8a87b4246a..790cacf3e7 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -633,7 +633,7 @@ repository_files_already_exist=Dateien für dieses Repository sind bereits vorha repository_files_already_exist.adopt=Dateien für dieses Repository existieren bereits und können nur übernommen werden. repository_files_already_exist.delete=Dateien für dieses Repository sind bereits vorhanden. Du must sie löschen. repository_files_already_exist.adopt_or_delete=Dateien für dieses Repository existieren bereits. Du musst sie entweder übernehmen oder löschen. -visit_rate_limit=Das Rate-Limit bei der Gegenseite wurde erreicht. +visit_rate_limit=Die Ratenbegrenzung bei der Gegenseite wurde erreicht. 2fa_auth_required=Die Gegenseite benötigt Zweifaktorauthentifikation. org_name_been_taken=Der Organisationsname ist bereits vergeben. team_name_been_taken=Der Teamname ist bereits vergeben. @@ -1243,7 +1243,7 @@ migrate_items_releases=Releases migrate_repo=Repository migrieren migrate.clone_address=Migrations-/Klon-URL migrate.clone_address_desc=Die HTTP(S)- oder „git clone“-URL eines bereits existierenden Repositorys -migrate.github_token_desc=Du kannst hier ein oder mehrere Tokens durch Komma getrennt eingeben, um die Migration schneller zu machen, indem die GitHub-API-Ratenlimitierung umgangen wird. WARNUNG: Der Missbrauch dieser Funktion kann gegen die Richtlinien des Diensteanbieters verstoßen und zur Sperrung deines Kontos bzw. deiner Konten führen. +migrate.github_token_desc=Du kannst hier ein oder mehrere Tokens durch Komma getrennt eingeben, um die Migration schneller zu machen, indem die GitHub-API-Ratenbegrenzung umgangen wird. WARNUNG: Der Missbrauch dieser Funktion kann gegen die Richtlinien des Diensteanbieters verstoßen und zur Sperrung deines Kontos bzw. deiner Konten führen. migrate.clone_local_path=oder ein lokaler Serverpfad migrate.permission_denied=Du hast keine Berechtigung zum Importieren lokaler Repositorys. migrate.permission_denied_blocked=Du kannst von keinen nicht erlaubten Hosts importieren. Bitte fragen deinen Administrator, die Einstellungen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS zu überprüfen. @@ -1515,7 +1515,7 @@ issues.filter_assignees=Verantwortliche filtern issues.filter_milestones=Meilenstein filtern issues.filter_projects=Projekt filtern issues.filter_labels=Label filtern -issues.filter_reviewers=Sichter filtern +issues.filter_reviewers=Prüfer filtern issues.new=Neues Issue issues.new.title_empty=Der Titel kann nicht leer sein issues.new.labels=Labels @@ -1535,7 +1535,7 @@ issues.new.closed_milestone=Geschlossene Meilensteine issues.new.assignees=Zuständige issues.new.clear_assignees=Zuständige entfernen issues.new.no_assignees=Niemand zuständig -issues.new.no_reviewers=Keine Sichter +issues.new.no_reviewers=Keine Prüfer issues.choose.get_started=Los geht's issues.choose.open_external_link=Öffnen issues.choose.blank=Standard @@ -1599,7 +1599,7 @@ issues.filter_type.assigned_to_you=Dir zugewiesen issues.filter_type.created_by_you=Von dir erstellt issues.filter_type.mentioning_you=Hat dich erwähnt issues.filter_type.review_requested=Sichtung angefordert -issues.filter_type.reviewed_by_you=Von dir gesichtet +issues.filter_type.reviewed_by_you=Von dir überprüft issues.filter_sort=Sortieren issues.filter_sort.latest=Neueste issues.filter_sort.oldest=Älteste @@ -1809,7 +1809,7 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.approve=hat die Änderungen %s genehmigt -issues.review.comment=hat %s gesichtet +issues.review.comment=hat %s überprüft issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed_label=Verworfen issues.review.left_comment=hat einen Kommentar hinterlassen @@ -1822,7 +1822,7 @@ issues.review.remove_review_request_self=hat die Sichtung %s verweigert issues.review.pending=Ausstehend issues.review.pending.tooltip=Dieser Kommentar ist derzeit nicht für andere Benutzer sichtbar. Um deine ausstehenden Kommentare einzureichen, wähle „%s“ -> „%s/%s/%s“ oben auf der Seite. issues.review.review=Review -issues.review.reviewers=Sichter +issues.review.reviewers=Prüfer issues.review.outdated=Veraltet issues.review.outdated_description=Der Inhalt hat sich geändert, seit dieser Kommentar abgegeben wurde issues.review.option.show_outdated_comments=Veraltete Kommentare anzeigen @@ -1905,8 +1905,8 @@ pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht e pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin zusammenführen. pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt. -pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Sichter angefragt wurden. -pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Sichtern fehlt. +pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Prüfer angefragt wurden. +pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Prüfern fehlt. pulls.blocked_by_outdated_branch=Dieser Pull-Request ist blockiert, da er veraltet ist. pulls.blocked_by_changed_protected_files_1=Dieser Pull-Request ist blockiert, weil er eine geschützte Datei ändert: pulls.blocked_by_changed_protected_files_n=Dieser Pull-Request ist blockiert, weil er geschützte Dateien ändert: @@ -2470,8 +2470,8 @@ settings.protect_required_approvals=Erforderliche Genehmigungen settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Sichtungen. settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Positivliste beschränken settings.protect_approvals_whitelist_enabled_desc=Nur Sichtungen von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Sichtungen von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. -settings.protect_approvals_whitelist_users=Nutzer, die sichten dürfen -settings.protect_approvals_whitelist_teams=Teams, die sichten dürfen +settings.protect_approvals_whitelist_users=Autorisierte Prüfer +settings.protect_approvals_whitelist_teams=Teams, autorisiert zum Prüfen settings.dismiss_stale_approvals=Entferne alte Genehmigungen settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt. settings.require_signed_commits=Signierte Commits erforderlich @@ -2490,7 +2490,7 @@ settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ settings.protected_branch_deletion=Branch-Schutz löschen settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? settings.block_rejected_reviews=Zusammenführung bei abgelehnten Sichtungen blockieren -settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Sichter angefragt werden, auch wenn genügend Genehmigungen existieren. +settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Prüfer angefragt werden, auch wenn genügend Genehmigungen existieren. settings.block_on_official_review_requests=Merge bei offiziellen Sichtungsanfragen blockieren settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Sichtungsanfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist @@ -2902,7 +2902,7 @@ issues.reaction.alt_add = Füge %[1]s Reaktion zum Kommentar hinzu. issues.reaction.alt_remove = Entferne %[1]s Reaktion von diesem Kommentar. summary_card_alt = Zusammenfassungskarte des Repositorys %s release.summary_card_alt = Übersichtskarte eines Releases mit dem Titel „%s“ im Repository %s -archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gesichtet werden. +archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht überprüft werden. editor.commit_email = Commit-E-Mail commits.view_single_diff = Änderungen an dieser Datei, die in diesem Commit eingeführt wurden, betrachten pulls.editable = Bearbeitbar diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 0e70102012..90b7048df7 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1154,7 +1154,7 @@ mirror_sync = synchronisé mirror_sync_on_commit=Synchroniser quand les révisions sont soumis mirror_address=Cloner depuis une URL mirror_address_desc=Insérez tous les identifiants requis dans la section Autorisation. -mirror_address_url_invalid=L’URL fournie est invalide. Vous devez échapper tous les composants de l'URL correctement. +mirror_address_url_invalid=L’URL fournie est invalide. Assurez vous que tous les composants de l'URL sont correctement échappés. mirror_address_protocol_invalid=L'URL fournie est invalide. Seuls les protocoles http(s):// ou git:// peuvent référencer un miroir. mirror_lfs=Stockage de fichiers volumineux (LFS) mirror_lfs_desc=Activer la mise en miroir des données LFS. @@ -2757,7 +2757,7 @@ issues.comment.blocked_by_user = Vous ne pouvez pas créer un commentaire sur ce editor.invalid_commit_mail = Courriel invalide pour la création d'un commit. commits.browse_further = Continuer la navigation commits.renamed_from = Renommé depuis %s -pulls.nothing_to_compare_have_tag = La branche ou le tag sélectionné sont identiques. +pulls.nothing_to_compare_have_tag = La branche/tag sélectionné sont identiques. issues.blocked_by_user = Vous ne pouvez pas créer de tickets sur ce dépôt car vous avez été bloqué par son propriétaire. pulls.blocked_by_user = Vous ne pouvez pas créer une pull request sur ce dépôt car vous êtes bloqué par son propriétaire. wiki.cancel = Annuler diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 6dbc92e1f5..98a85e8e79 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -341,7 +341,7 @@ no_reply_address=Domínio dos emails ocultos no_reply_address_helper=Nome de domínio para utilizadores com um endereço de email oculto. Por exemplo, o nome de utilizador "silva" será registado no Git como "silva@semresposta.exemplo.org" se o domínio de email oculto estiver definido como "semresposta.exemplo.org". password_algorithm=Algoritmo de Hash da palavra-passe invalid_password_algorithm=Algoritmo de hash da senha inválido -password_algorithm_helper=Definir o algoritmo de hash da senha. Os algoritmos têm requisitos e resistência distintos. `argon2` é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas pequenos. +password_algorithm_helper=Defina o algoritmo de hash da palavra-passe. Os algoritmos têm requisitos e níveis de segurança diferentes. O algoritmo argon2 é bastante seguro, mas consome muita memória e pode não ser apropriado para sistemas com poucos recursos. enable_update_checker=Habilitar verificador de novidades env_config_keys=Configuração do ambiente env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu ficheiro de configuração: @@ -829,7 +829,7 @@ activations_pending=Habilitações pendentes can_not_add_email_activations_pending=Existe uma validação pendente. Tente de novo dentro de alguns minutos, se quiser adicionar um novo email. delete_email=Remover email_deletion=Remover endereço de email -email_deletion_desc=O endereço de email e informações relacionadas serão removidos da sua conta. Os cometimentos feitos no Git com este endereço de email permanecerão inalterados. Quer continuar? +email_deletion_desc=Este endereço de email e informações relacionadas serão removidos da sua conta. Os cometimentos feitos no Git com este endereço de email permanecerão inalterados. Quer continuar? email_deletion_success=O endereço de email foi removido. theme_update_success=O seu tema foi substituído. theme_update_error=O tema escolhido não existe. @@ -1060,7 +1060,7 @@ user_unblock_success = O utilizador foi desbloqueado com sucesso. language.title = Idioma predefinido keep_activity_private.description = O seu trabalho público apenas estará visível para si e para os administradores da instância. language.description = Este idioma vai ser guardado na sua conta e ser usado como o predefinido depois de iniciar sessão. -language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Ler mais. +language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Saiba mais. pronouns_custom_label = Pronomes personalizados user_block_yourself = Não se pode bloquear a si próprio. change_username_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia. Pode ainda reivindicar o nome de utilizador antigo durante o período de espera. @@ -1157,7 +1157,7 @@ mirror_sync=sincronizado mirror_sync_on_commit=Sincronizar quando forem enviados cometimentos mirror_address=Clonar a partir do URL mirror_address_desc=Coloque, na secção de autorização, as credenciais que, eventualmente, sejam necessárias. -mirror_address_url_invalid=O URL fornecido é inválido. Tem que codificar adequadamente todos os componentes do URL. +mirror_address_url_invalid=O URL fornecido é inválido. Certifique-se de que os componentes da URL estão codificados corretamente. mirror_address_protocol_invalid=O URL fornecido é inválido. Só se pode replicar a partir de endereços http(s):// ou git://. mirror_lfs=Armazenamento de Ficheiros Grandes (LFS) mirror_lfs_desc=Habilitar a réplica de dados LFS. @@ -1247,7 +1247,7 @@ migrate_items_releases=Lançamentos migrate_repo=Migrar o repositório migrate.clone_address=Migrar / clonar a partir do URL migrate.clone_address_desc=O URL de clonagem HTTP(S) ou Git de um repositório existente -migrate.github_token_desc=Pode colocar aqui um ou mais códigos separados por vírgulas para tornar mais rápida a migração, para compensar a limitação de velocidade da API do GitHub. AVISO: O abuso desta funcionalidade poderá violar a política do seu fornecedor de serviço e levar ao bloqueio da conta. +migrate.github_token_desc=Pode colocar aqui um ou mais códigos aqui, separados por vírgulas, para tornar mais rápida a migração, contornando a limitação de taxa da API do GitHub. AVISO: O uso abusivo desta funcionalidade poderá violar a política do seu fornecedor de serviço e levar ao bloqueio da(s) sua(s) conta(s). migrate.clone_local_path=ou uma localização no servidor local migrate.permission_denied=Não está autorizado a importar repositórios locais. migrate.permission_denied_blocked=Não pode importar de servidores não permitidos, por favor peça ao administrador para verificar as configurações ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -1880,7 +1880,7 @@ pulls.select_commit_hold_shift_for_range=Escolha o comentimento. Mantenha premid pulls.review_only_possible_for_full_diff=A revisão só é possível ao visualizar o diff completo pulls.filter_changes_by_commit=Filtrar por cometimento pulls.nothing_to_compare=Estes ramos são iguais. Não há necessidade de criar um pedido de integração. -pulls.nothing_to_compare_have_tag=O ramo/etiqueta escolhidos são iguais. +pulls.nothing_to_compare_have_tag=Os ramos/etiquetas escolhidos são iguais. pulls.nothing_to_compare_and_allow_empty_pr=Estes ramos são iguais. Este pedido de integração ficará vazio. pulls.has_pull_request=`Já existe um pedido de integração entre estes ramos: %[2]s#%[3]d` pulls.create=Criar um pedido de integração diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 1d96b2d4c8..f5848d632d 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -471,7 +471,7 @@ email_domain_blacklisted=С данным адресом эл. почты рег authorize_application=Авторизация приложения authorize_redirect_notice=Вы будете перенаправлены на %s, если вы авторизуете это приложение. authorize_application_created_by=Это приложение было создано %s. -authorize_application_description=Если вы предоставите доступ, он сможет получить доступ и редактировать любую информацию о вашей учётной записи, включая содержимое частных репозиториев и организаций. +authorize_application_description=Если вы предоставите доступ, оно сможет получить доступ и редактировать любую информацию о вашей учётной записи, включая содержимое частных репозиториев и организаций. authorize_title=Разрешить «%s» доступ к вашей учётной записи? authorization_failed=Ошибка авторизации authorization_failed_desc=Ошибка авторизации, обнаружен неверный запрос. Пожалуйста, свяжитесь с автором приложения, которое вы пытались авторизовать. @@ -755,7 +755,7 @@ public_profile=Публичный профиль biography_placeholder=Кратко расскажите о себе другим! (Можно использовать Markdown) location_placeholder=Пусть все знают, откуда вы profile_desc=Ваш профиль -password_username_disabled=Нелокальным пользователям запрещено изменение их имени пользователя. Для получения более подробной информации обратитесь к администратору сайта. +password_username_disabled=Нелокальные пользователи не могут изменить имя. Для подробностей обратитесь к администрации сайта. full_name=Полное имя website=Веб-сайт location=Местоположение @@ -766,7 +766,7 @@ update_language_not_found=Язык «%s» недоступен. update_language_success=Язык обновлён. update_profile_success=Ваш профиль успешно обновлён. change_username=Ваше имя пользователя было изменено. -change_username_prompt=Обратите внимание: изменение имени пользователя также меняет URL вашей учётной записи. +change_username_prompt=Учтите, что при изменении имени пользователя ссылка на ваш профиль тоже будет изменена. change_username_redirect_prompt=Старое имя будет перенаправлять на новое до тех пор, пока оно не будет занято. continue=Далее cancel=Отмена @@ -1053,7 +1053,7 @@ hints = Подсказки additional_repo_units_hint = Предлагать включить больше разделов в репозиториях update_hints = Обновить подсказки update_hints_success = Подсказки обновлены. -additional_repo_units_hint_description = Показывать подсказку "Включить больше разделов" в репозиториях, в которых включены не все разделы. +additional_repo_units_hint_description = Показывать подсказку «Включить больше разделов» в репозиториях, в которых включены не все разделы. pronouns_custom = Другие pronouns = Местоимения pronouns_unspecified = Не указаны @@ -1068,7 +1068,7 @@ change_username_redirect_prompt.with_cooldown.few = Прежнее имя буд keep_pronouns_private = Показывать местоимения только зарегистрированным пользователям keep_pronouns_private.description = Местоимения будут скрыты от пользователей, не имеющих учётных записей на сервере. quota.applies_to_user = Эти ограничения хранилища применяются к вашей учётной записи -quota.applies_to_org = К этой организации применяются следующие ограничения +quota.applies_to_org = Эти ограничения хранилища применяются к этой организации quota.sizes.repos.public = Общедоступные репозитории storage_overview = Использование места quota = Ограничения хранилища @@ -1232,7 +1232,7 @@ migrate_items_releases=Выпуски migrate_repo=Перенос репозитория migrate.clone_address=Перенос / Клонирование по URL migrate.clone_address_desc=HTTP/HTTPS или Git адрес существующего репозитория -migrate.github_token_desc=Вы можете поместить здесь один или несколько токенов, разделенных запятыми, чтобы ускорить миграцию за счет обхода ограничения скорости API GitHub. ПРЕДУПРЕЖДЕНИЕ: Злоупотребление этой функцией может нарушить политику поставщика услуг и привести к блокировке вашей учетной записи (ов). +migrate.github_token_desc=Вы можете указать один или несколько токенов, разделенных запятыми, чтобы ускорить перенос за счёт обхода ограничения частоты обращений к API GitHub. ПРЕДУПРЕЖДЕНИЕ: злоупотребление этой функцией может нарушить политику поставщика услуг и привести к блокировке вашей учётной записи. migrate.clone_local_path=или локальный путь на сервере migrate.permission_denied=У вас нет прав на импорт локальных репозиториев. migrate.permission_denied_blocked=Вы не можете импортировать с запрещённых хостов, пожалуйста, попросите администратора проверить настройки ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. @@ -2698,7 +2698,7 @@ error.csv.invalid_field_count=Не удается отобразить этот mirror_address_protocol_invalid = Эта ссылка недействительна. Для зеркалирования можно использовать только расположения http(s):// и git:// . fork_no_valid_owners = Невозможно создать ответвление этого репозитория, т.к. здесь нет действующих владельцев. new_repo_helper = Репозиторий содержит все файлы проекта и историю изменений. Уже где-то есть репозиторий? Выполните перенос. -mirror_address_url_invalid = Указанный URL-адрес недействителен. Убедитесь, что компоненты URL-адреса экранированы правильно. +mirror_address_url_invalid = Указанная ссылка недействительна. Убедитесь, что все части экранированы правильно. issues.comment.blocked_by_user = Вы не можете комментировать под этой задачей, т.к. вы заблокированы владельцем репозитория или автором задачи. pulls.blocked_by_user = Невозможно создать запрос на слияние в этом репозитории, т.к. вы заблокированы его владельцем. settings.add_collaborator_blocked_our = Невозможно добавить соучастника, т.к. он заблокирован в этом репозитории. @@ -2756,7 +2756,7 @@ ambiguous_runes_description = `Этот файл содержит символы editor.invalid_commit_mail = Неправильная почта для создания коммита. pulls.has_merged = Слияние не удалось: запрос уже был слит, изменение целевой ветви или повторное слияние невозможно. settings.enter_repo_name = Введите имя владельца и название репозитория как указано: -signing.wont_sign.error = Произошла ошибка при проверке возможности подписания коммита. +signing.wont_sign.error = Не удалось проверить возможность подписания коммита. signing.wont_sign.nokey = Сервер не предоставляет ключ для подписи коммита. settings.wiki_globally_editable = Разрешить редактирование вики всем пользователям settings.webhook.test_delivery_desc_disabled = Активируйте этот веб-хук для проверки тестовым событием. @@ -3120,7 +3120,7 @@ dashboard.resync_all_hooks=Повторно синхронизировать х dashboard.reinit_missing_repos=Переинициализировать все отсутствующие Git репозитории, для которых существуют записи dashboard.sync_external_users=Синхронизировать данные сторонних пользователей dashboard.cleanup_hook_task_table=Очистить таблицу hook_task -dashboard.cleanup_packages=Очистка устаревших пакетов +dashboard.cleanup_packages=Удалить устаревшие пакеты dashboard.server_uptime=Время работы dashboard.current_goroutine=Выполняемые goroutines dashboard.current_memory_usage=Текущее использование памяти @@ -3582,7 +3582,7 @@ self_check.no_problem_found = Пока проблем не обнаружено. auths.tip.gitea = Зарегистрируйте новое приложение OAuth2. Доступна инструкция: %s auths.tips.oauth2.general.tip = При регистрации нового приложения OAuth2 ссылка обратного перенаправления должна быть: self_check.database_fix_mysql = Пользователи MySQL и MariaDB могут исправить проблемы с сопоставлением командой "forgejo doctor convert". Также можно вручную вписать "ALTER ... COLLATE ..." в SQL. -dashboard.cleanup_actions = Очистить устаревшие журналы и артефакты Действий +dashboard.cleanup_actions = Удалить устаревшие журналы и артефакты Действий dashboard.sync_repo_branches = Синхронизировать ветви из Git в базу данных assets = Кодовые объекты dashboard.sync_tag.started = Начата синхронизация тегов @@ -3845,7 +3845,7 @@ owner.settings.cargo.initialize.error=Не удалось инициализир owner.settings.cargo.initialize.success=Индекс Cargo успешно создан. owner.settings.cargo.rebuild=Перестроить индекс owner.settings.cargo.rebuild.error=Не удалось перестроить индекс Cargo: %v -owner.settings.cargo.rebuild.success=Индекс Cargo успешно перестроен. +owner.settings.cargo.rebuild.success=Индекс Cargo был перестроен успешно. owner.settings.cleanuprules.title=Правила очистки owner.settings.cleanuprules.add=Добавить правило очистки owner.settings.cleanuprules.edit=Изменить правило очистки diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 5b06c5e3b5..553db1b19a 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -204,15 +204,20 @@ buttons.switch_to_legacy.tooltip = Använd legacy-redigeraren istället link_modal.url = Url link_modal.description = Beskrivning link_modal.header = Lägg till en länk +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 [filter] string.asc = A - Ö +string.desc = Ö - A [error] occurred = Ett fel har inträffat server_internal = Internt serverfel network_error = Nätverksfel report_message = Om du tror att detta är en Forgejo-bugg, sök efter ärenden på Codeberg eller öppna ett nytt ärende om det behövs. +not_found = Målet kunde inte hittas. [startpage] app_desc=En smidig, självhostad Git-tjänst @@ -333,6 +338,7 @@ internal_token_failed = Misslyckades att generera intern token: %v password_algorithm = Hashalgoritm för lösenord 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 [home] uname_holder=Användarnamn eller e-postadress diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 332a855e88..8531446658 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -15,7 +15,7 @@ page=Сторінка template=Шаблон language=Мова notifications=Сповіщення -active_stopwatch=Трекер робочого часу +active_stopwatch=Облік робочого часу create_new=Створити… user_profile_and_more=Профіль і налаштування… signed_in_as=Увійшов як @@ -660,13 +660,13 @@ Location = Розташування AccessToken = Токен доступу unable_verify_ssh_key = Не вдалося перевірити ключ SSH, перевірте його на наявність помилок. repository_force_private = Увімкнено примусову приватність: приватні репозиторії не можна зробити публічними. -must_use_public_key = Ключ, який ви надали, є приватним. Будь ласка, нікуди не завантажуйте свій приватний ключ. Використовуйте замість нього публічний ключ. +must_use_public_key = Ключ, який ви надали, є приватним. Будь ласка, нікуди не завантажуйте свій приватний ключ. Використовуйте замість нього відкритий ключ. openid_been_used = Адреса OpenID «%s» вже використовується. still_has_org = Ваш обліковий запис є учасником однієї або декількох організацій, спочатку покиньте їх. duplicate_invite_to_team = Цього користувача вже запрошено як учасника команди. organization_leave_success = Ви успішно покинули організацію %s. include_error = ` має містити підрядок «%s».` -invalid_group_team_map_error = ` призначення недійсне: %s` +invalid_group_team_map_error = ` зіставлення недійсне: %s` unsupported_login_type = Цей тип входу не підтримує видалення облікового запису. admin_cannot_delete_self = Ви не можете видалити себе, якщо ви є адміністратором. Спочатку зніміть із себе права адміністратора. unset_password = Для користувача не встановлено пароль. @@ -815,7 +815,7 @@ manage_gpg_keys=Керування ключами GPG add_key=Додати ключ ssh_desc=Ці відкриті ключі SSH повʼязані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. Підтверджені ключі можна використати для підтвердження комітів Git, підписаних із SSH. principal_desc=Ці настройки SSH сертифікатів вказані у вашому обліковому записі та надають повний доступ до ваших репозиторіїв. -gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. +gpg_desc=Ці відкриті ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. ssh_helper=Потрібна допомога? Дивіться гід на GitHub з генерації ключів SSH або виправлення типових неполадок SSH. gpg_helper= Потрібна допомога? Перегляньте посібник GitHub про GPG . add_new_key=Додати SSH ключ @@ -895,7 +895,7 @@ oauth2_applications_desc=Програми OAuth2 дають можливість remove_oauth2_application=Видалити програму OAuth2 remove_oauth2_application_desc=Видалення програми OAuth2 скасує доступ до всіх підписаних токенів доступу. Продовжити? remove_oauth2_application_success=Програму видалено. -create_oauth2_application=Створити новий додаток OAuth2 +create_oauth2_application=Створити нову програму OAuth2 create_oauth2_application_button=Створити програму oauth2_application_name=Назва програми save_application=Зберегти @@ -999,7 +999,7 @@ comment_type_group_pull_request_push = Додані коміти permissions_public_only = Тільки публічні select_permissions = Виберіть дозволи permissions_access_all = Усі (публічні, приватні й обмежені) -create_oauth2_application_success = Ви успішно створили новий додаток OAuth2. +create_oauth2_application_success = Ви успішно створили нову програму OAuth2. keep_email_private_popup = Ваша адреса електронної пошти не буде відображатися у вашому профілі і не буде використовуватися за замовчуванням для комітів, зроблених через веб-інтерфейс, таких як завантаження файлів, редагування і об'єднання комітів. Натомість ви можете використовувати спеціальну адресу %s для прив'язки комітів до свого облікового запису. Ця опція не вплине на існуючі коміти. blocked_since = Заблокований з %s can_not_add_email_activations_pending = Очікується активація, спробуйте ще раз за кілька хвилин, якщо хочете додати нову адресу електронної пошти. @@ -1023,7 +1023,7 @@ pronouns_custom_label = Інші займенники repo_and_org_access = Доступ до репозиторію та організації change_username_redirect_prompt.with_cooldown.few = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d днів. Протягом періоду захисту ви ще можете повернути собі старе ім'я. change_username_redirect_prompt.with_cooldown.one = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d день. Протягом періоду захисту ви ще можете повернути собі старе ім'я. -change_username_redirect_prompt = Старе ім'я користувача буде перенаправленням, поки хтось не присвоїть ім'я собі. +change_username_redirect_prompt = Старе ім'я користувача буде переспрямуванням, поки хтось не присвоїть ім'я собі. comment_type_group_lock = Стан блокування webauthn_alternative_tip = Можливо, ви бажаєте налаштувати додатковий спосіб входу. user_unblock_success = Користувач_ку успішно розблоковано. @@ -1200,7 +1200,7 @@ migrate_items_pullrequests=Запити на злиття migrate_items_merge_requests=Запити на об'єднання migrate_items_releases=Релізи migrate_repo=Перенести репозиторій -migrate.clone_address=Міграція / клонувати з URL-адреси +migrate.clone_address=Міграція / клонування з URL-адреси migrate.clone_address_desc=URL-адреса HTTP(S) або Git «clone» існуючого репозиторію migrate.clone_local_path=або шлях до локального серверу migrate.permission_denied=Вам не дозволено імпортувати локальні репозиторії. @@ -1549,9 +1549,9 @@ issues.unlock=Розблокування обговорення issues.lock.unknown_reason=Неможливо заблокувати задачу з невідомою причиною. issues.lock_duplicate=Задача не може бути заблокованим двічі. issues.unlock_error=Не можливо розблокувати задачу, яка не заблокована. -issues.lock_with_reason=заблоковано як %s та обмежене обговорення для співавторів %s -issues.lock_no_reason=заблоковано та обмежене обговорення для співавторів %s -issues.unlock_comment=розблоковане обговорення %s +issues.lock_with_reason=блокує як %s та обмежує обговорення до співавторів %s +issues.lock_no_reason=блокує та обмежує обговорення до співавторів %s +issues.unlock_comment=розблоковує обговорення %s issues.lock_confirm=Заблокувати issues.unlock_confirm=Розблокувати issues.lock.notice_1=- Інші користувачі не можуть додавати нові коментарі до цієї задачі. @@ -1701,8 +1701,8 @@ pulls.required_status_check_administrator=Як адміністратор ви pulls.can_auto_merge_desc=Цей запит можна об'єднати автоматично. pulls.cannot_auto_merge_desc=Цей запит на злиття не може бути злитий автоматично через конфлікти. pulls.cannot_auto_merge_helper=Злийте вручну для вирішення конфліктів. -pulls.num_conflicting_files_1=%d конфліктуючий файл -pulls.num_conflicting_files_n=%d конфліктуючі файли +pulls.num_conflicting_files_1=%d конфліктний файл +pulls.num_conflicting_files_n=%d конфліктних файлів pulls.approve_count_1=%d схвалення pulls.approve_count_n=%d схвалень pulls.reject_count_1=%d запит на зміну @@ -1817,7 +1817,7 @@ activity.active_prs_count_1=%d активний запит на activity.active_prs_count_n=%d активних запитів на злиття activity.merged_prs_count_1=Об'єднаний запит на злиття activity.merged_prs_count_n=Об'єднані запити на злиття -activity.opened_prs_count_1=Запропонований запит на злиття +activity.opened_prs_count_1=Запропоновані запити на злиття activity.opened_prs_count_n=Запропонованих запитів на злиття activity.title.user_1=%d користувачем activity.title.user_n=%d користувачами @@ -1839,8 +1839,8 @@ activity.closed_issue_label=Закрито activity.new_issues_count_1=Нова задача activity.new_issues_count_n=Нові задачі activity.new_issue_label=Відкриті -activity.title.unresolved_conv_1=%d Незавершене обговорення -activity.title.unresolved_conv_n=%d Незавершених обговорень +activity.title.unresolved_conv_1=%d незавершене обговорення +activity.title.unresolved_conv_n=%d незавершених обговорень activity.unresolved_conv_desc=Список всіх старих задач і Pull Request'ів з недавньої активністю, але ще не закритих або прийнятих. activity.unresolved_conv_label=Відкрити activity.title.releases_1=%d випуск @@ -1911,20 +1911,20 @@ settings.use_external_wiki=Використовувати зовнішню ві settings.external_wiki_url=URL зовнішньої вікі settings.external_wiki_url_error=Зовнішня URL-адреса wiki не є допустимою URL-адресою. settings.external_wiki_url_desc=Відвідувачі будуть перенаправлені на URL-адресу, коли вони клацають по вкладці. -settings.issues_desc=Увімкнути відстеження задач в репозиторію +settings.issues_desc=Увімкнути відстеження задач settings.use_internal_issue_tracker=Використовувати вбудовану систему відстеження задач -settings.use_external_issue_tracker=Використовувати зовнішню систему обліку задач +settings.use_external_issue_tracker=Використовувати зовнішню систему відстеження задач settings.external_tracker_url=URL зовнішньої системи відстеження задач settings.external_tracker_url_error=URL зовнішнього баг-трекера не є допустимою URL-адресою. settings.external_tracker_url_desc=Відвідувачі перенаправляються на зовнішню URL-адресу, коли натискають вкладку 'Задачі'. -settings.tracker_url_format=Формат URL зовнішнього трекера задач +settings.tracker_url_format=Формат URL зовнішньої системи відстеження задач settings.tracker_url_format_error=Неправильний формат URL-адреси зовнішнього баг-трекера. -settings.tracker_issue_style=Формат номера для зовнішньої системи обліку задач +settings.tracker_issue_style=Формат номера для зовнішньої системи відстеження задач settings.tracker_issue_style.numeric=Цифровий settings.tracker_issue_style.alphanumeric=Буквено-цифровий settings.tracker_url_format_desc=Використовуйте шаблони {user}, {repo} та {index} для імені користувача, репозиторію та номеру задічі. settings.enable_timetracker=Увімкнути відстеження часу -settings.allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу +settings.allow_only_contributors_to_track_time=Дозволити відстеження часу тільки учасникам розробки settings.pulls_desc=Увімкнути запити на злиття в репозиторій settings.pulls.ignore_whitespace=Ігнорувати пробіл у конфліктах settings.pulls.enable_autodetect_manual_merge=Увімкнути автовизначення ручного злиття (Примітка: у деяких особливий випадках можуть виникнуть помилки) @@ -2032,7 +2032,7 @@ settings.slack_icon_url=URL іконки settings.slack_color=Колір settings.discord_username=Ім'я кристувача settings.discord_icon_url=URL іконки -settings.event_desc=Тригер: +settings.event_desc=Спрацьовує на: settings.event_push_only=Push події settings.event_send_everything=Усі події settings.event_choose=Власні події… @@ -2530,7 +2530,7 @@ issues.new.assign_to_me = Призначити собі contributors.contribution_type.additions = Додавання settings.add_web_hook_desc = Інтегрувати %s у цей репозиторій. settings.event_wiki_desc = Вікі-сторінку створено, перейменовано, відредаговано або видалено. -settings.mirror_settings.push_mirror.copy_public_key = Копіювати публічний ключ +settings.mirror_settings.push_mirror.copy_public_key = Копіювати відкритий ключ editor.add_tmpl.filename = назва файлу settings.unarchive.button = Розархівувати object_format = Формат об'єкта @@ -2787,7 +2787,7 @@ pulls.auto_merge_newly_scheduled = Заплановано об'єднати за pulls.auto_merge_newly_scheduled_comment = `планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[1]s` comments.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст коментаря. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін pulls.auto_merge_canceled_schedule_comment = `скасовує автоматичне об'єднання цього запиту на злиття після успішного завершення всіх перевірок %[1]s` -signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає публічного ключа, пов'язаного з вашим обліковим записом. +signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає відкритого ключа, пов'язаного з вашим обліковим записом. signing.wont_sign.basesigned = Злиття не буде підписано, оскільки не підписано базовий коміт. signing.wont_sign.headsigned = Злиття не буде підписано, оскільки не підписано головний коміт. projects.column.new_submit = Створити стовпчик @@ -2840,6 +2840,17 @@ editor.revert = Вивернути %s на: commit.revert-content = Оберіть гілку, на яку вивернути: pulls.cmd_instruction_merge_warning = Увага: в цьому репозиторії не ввімкнено «Автовизначення ручного об'єднання», тож позначити цей запит як об'єднаний вручну вам доведеться самостійно. topic.format_prompt = Теми повинні починатися з літери або цифри, можуть містити дефіси («-») і крапки («.») і мати довжину до 35 символів. Дозволено використання лише малих літер. +issues.label_archive = Архівна мітка +editor.invalid_commit_mail = Недійсна пошта для створення коміту. +issues.label_exclusive = Ексклюзивний +issues.filter_type.reviewed_by_you = Перевірено вами +editor.push_out_of_date = Схоже, дані для відправки застаріли. +editor.commit_email = Пошта автора +issues.cancel_tracking_history = `скасовує відстеження часу %s` +issues.label_archive_tooltip = Архівовані мітки за замовчуванням виключаються з пропозицій під час пошуку за міткою. +settings.protect_status_check_matched = Збіг +settings.webhook.delivery.success = Подію додано до черги доставки. Може знадобитися кілька секунд, перш ніж вона з'явиться в історії доставки. +settings.protected_branch_required_rule_name = Необхідна назва правила [graphs] contributors.what = внески @@ -3082,10 +3093,10 @@ users.update_profile_success=Обліковий запис користувач users.edit_account=Редагувати обліковий запис users.max_repo_creation=Максимальна кількість репозиторіїв users.max_repo_creation_desc=(Введіть -1, щоб використовувати глобальний ліміт за замовчуванням.) -users.is_activated=Обліковий запис користувача увімкнено +users.is_activated=Активований обліковий запис users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора -users.is_restricted=Обмежений +users.is_restricted=Обмежений обліковий запис users.allow_git_hook=Може створювати Git-хуки users.allow_git_hook_tooltip=Git-хуки виконуються від імені користувача ОС, від якого запущено Forgejo, і мають той самий рівень доступу до хоста. Таким чином, користувачі зі спеціальними правами Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних Forgejo. Вони також здатні отримати права адміністратора Forgejo. users.allow_import_local=Може імпортувати локальні репозиторії @@ -3167,16 +3178,16 @@ auths.domain=Домен auths.host=Хост auths.port=Порт auths.bind_dn=Прив'язати DN -auths.bind_password=Прив'язати пароль +auths.bind_password=Пароль bind auths.user_base=База пошуку користувачів auths.user_dn=DN користувача auths.attribute_username=Атрибут імені користувача auths.attribute_username_placeholder=Залиште порожнім, щоб використовувати ім'я користувача для реєстрації. auths.attribute_name=Атрибут імені -auths.attribute_surname=Атрибут Surname -auths.attribute_mail=Атрибут Email -auths.attribute_ssh_public_key=Атрибут Відкритий SSH ключ -auths.attributes_in_bind=Витягувати атрибути в контексті Bind DN +auths.attribute_surname=Атрибут прізвища +auths.attribute_mail=Атрибут адреси email +auths.attribute_ssh_public_key=Атрибут відкритого ключа SSH +auths.attributes_in_bind=Отримувати атрибути в контексті bind DN auths.allow_deactivate_all=Дозволити порожньому результату пошуку відключити всіх користувачів auths.use_paged_search=Використовувати посторінковий пошук auths.search_page_size=Розмір сторінки @@ -3230,15 +3241,15 @@ auths.tips.oauth2.general=Автентифікація OAuth2 auths.tip.oauth2_provider=Постачальник OAuth2 auths.tip.bitbucket=Зареєструйте нового споживача OAuth на %s і додайте дозвіл «Обліковий запис» — «Читання» auths.tip.nextcloud=`Зареєструйте нового споживача OAuth у вашому екземплярі за допомогою наступного меню "Налаштування -> Безпека -> клієнт OAuth 2.0"` -auths.tip.dropbox=Створіть новий додаток на %s -auths.tip.facebook=Зареєструйте новий додаток на %s і додайте модуль «Facebook Login» -auths.tip.github=Зареєструйте новий додаток OAuth на %s +auths.tip.dropbox=Створіть нову програму на %s +auths.tip.facebook=Зареєструйте нову програму на %s і додайте модуль «Facebook Login» +auths.tip.github=Зареєструйте нову програму OAuth на %s auths.tip.gitlab=Додайте новий додаток на https://gitlab.com/profile/applications auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 в консолі Google API на сторінці %s auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (/.well-known/openid-configuration) для автоматичної настройки входу OAuth auths.tip.twitter=Перейдіть на %s, створіть програму і переконайтеся, що ввімкнено опцію «Дозволити використання цієї програми для входу через Twitter» -auths.tip.discord=Зареєструйте новий додаток на %s -auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» +auths.tip.discord=Зареєструйте нову програму на %s +auths.tip.yandex=Створіть нову програму на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) auths.edit=Редагувати джерело автентифікації auths.activated=Це джерело автентифікація активоване @@ -3312,7 +3323,7 @@ config.default_keep_email_private=Приховувати адреси елект config.default_allow_create_organization=Дозволити створення організацій за замовчуванням config.enable_timetracking=Увімкнути відстеження часу config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням -config.default_allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу +config.default_allow_only_contributors_to_track_time=Дозволити відстеження часу тільки учасникам розробки config.no_reply_address=Домен прихованих адрес електронної пошти config.default_visibility_organization=Видимість за замовчуванням для нових організацій config.default_enable_dependencies=Увімкнути залежності задач за замовчуванням @@ -3340,7 +3351,7 @@ config.cache_config=Конфігурація кешу config.cache_adapter=Адаптер кешу config.cache_interval=Інтервал кешування config.cache_conn=Підключення до кешу -config.cache_item_ttl=Час зберігання даних кешу +config.cache_item_ttl=Час зберігання даних у кеші config.session_config=Конфігурація сесії config.session_provider=Провайдер сесії @@ -3408,7 +3419,7 @@ notices.system_notice_list=Сповіщення системи notices.view_detail_header=Подробиці сповіщення notices.select_all=Вибрати все notices.deselect_all=Скасувати вибір -notices.inverse_selection=Інвертувати виділене +notices.inverse_selection=Інвертувати вибір notices.delete_selected=Видалити вибране notices.delete_all=Видалити всі cповіщення notices.type=Тип @@ -3453,8 +3464,8 @@ monitor.queue.settings.desc = Пули динамічно зростають у monitor.queue.settings.remove_all_items_done = Усі елементи в черзі видалено. monitor.queue.settings.remove_all_items = Видалити всі config.app_slogan = Гасло екземпляра -auths.tip.gitea = Зареєструйте новий додаток OAuth. Інструкцію можна знайти на %s -auths.tip.gitlab_new = Зареєструйте новий додаток на %s +auths.tip.gitea = Зареєструйте нову програму OAuth. Інструкцію можна знайти на %s +auths.tip.gitlab_new = Зареєструйте нову програму на %s monitor.duration = Тривалість (с) users.reserved = Зарезервовано systemhooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки спрацьовуватимуть для всіх репозиторіїв системи, тож врахуйте всі ймовірні наслідки для швидкодії. Докладніше — в посібнику з вебхуків. @@ -3505,6 +3516,18 @@ users.details = Дані користувача auths.login_source_exist = Джерело автентифікації «%s» вже існує. dashboard.delete_old_system_notices = Видалити всі старі сповіщення системи з бази даних users.purge = Повністю видалити користувач_ку +auths.tips.oauth2.general.tip = При реєстрації нової програми OAuth2 URL-адреса зворотного виклику/переспрямування повинна бути: +auths.attribute_avatar = Атрибут аватара +auths.oauth2_required_claim_name = Необхідна назва заявки +auths.oauth2_admin_group = Значення групової заявки для адміністраторів. (Необов'язково — потрібна назва заявки вище) +auths.oauth2_required_claim_name_helper = Вкажіть назву, щоб обмежити вхід з цього джерела користувачами із заявкою з такою назвою +auths.map_group_to_team = Зіставити групи LDAP з командами організації (залиште поле порожнім, щоб пропустити) +auths.oauth2_required_claim_value = Необхідне значення заявки +auths.oauth2_map_group_to_team = Зіставити заявлені групи з командами організації. (Необов'язково — потрібна назва заявки вище) +auths.oauth2_required_claim_value_helper = Вкажіть значення, щоб обмежити вхід з цього джерела користувачами із заявкою з такими назвою і значенням +auths.oauth2_group_claim_name = Назва заявки, що надає назви груп для цього джерела. (Необов'язково) +auths.oauth2_restricted_group = Значення групової заявки для обмежених користувачів. (Необов'язково — потрібна назва заявки вище) +users.local_import.description = Дозволити імпорт репозиторіїв з локальної файлової системи сервера. Це може становити загрозу безпеці. [action] @@ -3724,7 +3747,7 @@ alpine.registry = Налаштуйте цей реєстр, додавши URL cran.registry = Налаштуйте цей реєстр у файлі Rprofile.site: npm.registry = Налаштуйте цей реєстр у файлі .npmrc свого проєкту: chef.registry = Налаштуйте цей реєстр у файлі ~/.chef/config.rb: -owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки публічний ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. +owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки відкритий ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. nuget.dependency.framework = Цільовий фреймворк owner.settings.cleanuprules.preview.overview = Заплановано видалити %d пакунків. owner.settings.cleanuprules.pattern_full_match = Застосувати шаблон до повної назви пакунка @@ -3754,6 +3777,8 @@ rpm.distros.redhat = у дистрибутивах на основі RedHat rpm.distros.suse = у дистрибутивах на основі SUSE owner.settings.cargo.rebuild.no_index = Неможливо перебудувати, індекс не ініціалізовано. alpine.registry.key = Завантажте відкритий RSA-ключ реєстру в папку /etc/apk/keys/ для перевірки підпису індексу: +rubygems.required.ruby = Необхідна версія Ruby +rubygems.required.rubygems = Необхідна версія RubyGem [secrets] deletion = Видалити секрет @@ -3861,6 +3886,7 @@ runs.invalid_workflow_helper = Недійсний файл конфігурац runs.no_job = Робочий потік повинен містити принаймні одне завдання workflow.dispatch.use_from = Використати робочий потік із runs.no_job_without_needs = Робочий потік повинен містити принаймні одне завдання без залежностей. +workflow.dispatch.trigger_found = Цей робочий потік спрацьовує на події workflow_dispatch. @@ -3930,6 +3956,8 @@ issues.read = Читати: дивитись і створювати за wiki.read = Читати: переглядати вбудовану вікі та її історію. actions.write = Писати: вручну запускати, перезапускати, скасовувати або схвалювати конвеєри CI/CD в очікуванні. projects.write = Писати: створювати проєкти і стовпчики та редагувати їх. +ext_issues = Доступ до посилання на зовнішню систему відстеження задач. Налаштування дозволів відбувається поза сайтом. +ext_wiki = Доступ до посилання на зовнішню вікі. Налаштування дозволів відбувається поза сайтом. [munits.data] pib = ПіБ diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 59901392c2..eb7c3c95e7 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -828,7 +828,7 @@ activations_pending=等待激活 can_not_add_email_activations_pending=有一个待处理的激活请求,请稍等几分钟后再尝试添加新的电子邮件地址。 delete_email=移除 email_deletion=移除电子邮件地址 -email_deletion_desc=电子邮件地址和相关信息将会被删除。使用此电子邮件地址发送的Git提交将会保留,继续? +email_deletion_desc=此电子邮件地址及相关信息将被删除。使用此电子邮件地址的Git提交将被保留。继续吗? email_deletion_success=您的电子邮件地址已被移除。 theme_update_success=您的主题已更新。 theme_update_error=所选主题不存在。 @@ -1156,7 +1156,7 @@ mirror_sync = 已同步 mirror_sync_on_commit=推送提交时同步 mirror_address=从 URL 克隆 mirror_address_desc=在授权框中输入必要的凭据。 -mirror_address_url_invalid=URL无效。请检查您所输入的URL是否正确。 +mirror_address_url_invalid=URL无效。请检查所输入URL的所有部分是否被正确地转译。 mirror_address_protocol_invalid=提供的URL无效。只能使用http(s)://或git://地址进行镜像操作。 mirror_lfs=大文件存储(LFS) mirror_lfs_desc=镜像 LFS 数据。 @@ -1245,7 +1245,7 @@ migrate_items_releases=版本发布 migrate_repo=迁移仓库 migrate.clone_address=从 URL 迁移/克隆 migrate.clone_address_desc=现有仓库的 HTTP(s) 或 Git “clone” URL -migrate.github_token_desc=由于 GitHub API 速率限制,您可以在此处放置一个或多个以逗号分隔的令牌,以加快迁移速度。 警告:滥用此功能可能会违反服务提供商的政策并导致帐户被封。 +migrate.github_token_desc=您可以在此填写一个或多个以逗号分隔的令牌,以规避 GitHub API 速率限制并加快迁移速度。 警告:滥用此功能可能会违反服务提供商的政策并导致您的帐户被封禁。 migrate.clone_local_path=或服务器本地路径 migrate.permission_denied=您没有获得导入本地仓库的权限。 migrate.permission_denied_blocked=您不能从不允许的主机导入,请询问管理员以检查 ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS 设置。 diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json index 6cca2187a0..aafb85cb4e 100644 --- a/options/locale_next/locale_ar.json +++ b/options/locale_next/locale_ar.json @@ -141,5 +141,6 @@ "discussion.sidebar.reference": "مرجع", "admin.moderation.moderation_reports": "تقارير الإشراف", "admin.moderation.reports": "التقارير", - "admin.moderation.no_open_reports": "لا يوجد حالياً أية تقارير مفتوحة." + "admin.moderation.no_open_reports": "لا يوجد حالياً أية تقارير مفتوحة.", + "admin.moderation.deleted_content_ref": "المحتوى المُبلغ عنه بالنوع %[1]v والمعرف %[2]d لم يعد موجوداً" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index e8b0976994..59e12c2ea8 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -110,5 +110,6 @@ "admin.moderation.deleted_content_ref": "Gemeldeter Inhalt vom Typ %[1]v und ID %[2]d existiert nicht mehr", "admin.moderation.moderation_reports": "Moderationsmeldungen", "admin.moderation.reports": "Meldungen", - "admin.moderation.no_open_reports": "Es gibt momentan keine offenen Meldungen." + "admin.moderation.no_open_reports": "Es gibt momentan keine offenen Meldungen.", + "admin.dashboard.remove_resolved_reports": "Erledigte Meldungen entfernen" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 6eb2039ab4..c07a6b152f 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -110,5 +110,6 @@ "admin.moderation.moderation_reports": "Mga ulat sa moderation", "admin.moderation.no_open_reports": "Kasalukuyang walang mga nakabukas na ulat.", "admin.moderation.reports": "Mga ulat", - "admin.moderation.deleted_content_ref": "Hindi na umiiral ang inulat na nilalaman na may uri na %[1]v at ID %[2]d" + "admin.moderation.deleted_content_ref": "Hindi na umiiral ang inulat na nilalaman na may uri na %[1]v at ID %[2]d", + "admin.dashboard.remove_resolved_reports": "Tanggalin ang mga naresolbang ulat" } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index 4a1a27ac0d..97b2f1733a 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -99,9 +99,16 @@ "repo.form.cannot_create": "Tous les espaces dans lesquels vous pouvez créer des dépôts ont atteint la limite de dépôts.", "admin.dashboard.cleanup_offline_runners": "Nettoyer les exécuteurs hors ligne", "mail.actions.run_info_trigger": "Déclenché parce que : %[1]s par : %[2]s", - "settings.visibility.description": "La visibilité du profil affecte la capacité des autres à accéder à vos dépôts non-privés. Voir plus", + "settings.visibility.description": "La visibilité du profil affecte la capacité des autres à accéder à vos dépôts non-privés. Voir plus.", "editor.textarea.shift_tab_hint": "Pas d'indentation sur cette ligne. Appuyez sur Maj + Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", "avatar.constraints_hint": "L'avatar personnalisé ne doit pas dépasser une taille de %[1]s ou être plus grand que %[2]dx%[3]d pixels", "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", - "discussion.sidebar.reference": "Référence" + "discussion.sidebar.reference": "Référence", + "repo.settings.push_mirror.branch_filter.label": "Filtre de branche (optionnel)", + "repo.diff.commit.next-short": "Suiv.", + "repo.diff.commit.previous-short": "Préc.", + "profile.actions.tooltip": "Plus d'actions", + "profile.edit.link": "Éditer le profil", + "keys.ssh.link": "Clé SSH", + "keys.gpg.link": "Clés GPG" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 0bf903f026..bf6e4729b3 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -118,5 +118,6 @@ "admin.moderation.no_open_reports": "Pašlaik nav atvērtu pārskatu.", "admin.moderation.moderation_reports": "Satura pārraudzības pārskati", "admin.moderation.reports": "Pārskati", - "admin.moderation.deleted_content_ref": "Saturs, par kuru ziņots, ar veidu %[1]v un Id %[2]d vairs nepastāv" + "admin.moderation.deleted_content_ref": "Saturs, par kuru ziņots, ar veidu %[1]v un Id %[2]d vairs nepastāv", + "admin.dashboard.remove_resolved_reports": "Noņemt atrisinātos ziņojumus" } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index f51077c610..66c70939f4 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -110,5 +110,6 @@ "admin.moderation.moderation_reports": "Moderatioons-Berichten", "admin.moderation.no_open_reports": "Dat gifft jüüst keene open Berichten.", "admin.moderation.reports": "Berichten", - "admin.moderation.deleted_content_ref": "Mellt Inholl mit Aard %[1]v un Kennteken %[2]d gifft dat nich mehr" + "admin.moderation.deleted_content_ref": "Mellt Inholl mit Aard %[1]v un Kennteken %[2]d gifft dat nich mehr", + "admin.dashboard.remove_resolved_reports": "Lööst Berichten wegdoon" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 67a9f79917..74dd2c4b83 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -118,5 +118,6 @@ "admin.moderation.deleted_content_ref": "Conteúdo denunciado do tipo %[1]v e ID %[2]d não existe mais", "admin.moderation.moderation_reports": "Denúncias para moderação", "admin.moderation.reports": "Denúncias", - "admin.moderation.no_open_reports": "Não há denúncias abertas atualmente." + "admin.moderation.no_open_reports": "Não há denúncias abertas atualmente.", + "admin.dashboard.remove_resolved_reports": "Remover denúncias resolvidas" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index c3b908ed07..0bdabfd431 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -101,7 +101,7 @@ "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Escape para sair do editor.", "stars.list.none": "Ninguém juntou este repositório aos favoritos.", "admin.dashboard.cleanup_offline_runners": "Limpeza de executores offline", - "settings.visibility.description": "A visibilidade do perfil afecta a capacidade de outros acederem aos seus repositórios não privados. Ler mais", + "settings.visibility.description": "A visibilidade do perfil afecta a capacidade de outros acederem aos seus repositórios não privados. Saiba mais.", "avatar.constraints_hint": "O avatar personalizado não pode exceder %[1]s de tamanho ou ser maior do que %[2]dx%[3]d pixéis", "repo.diff.commit.next-short": "Seg.", "profile.actions.tooltip": "Mais Actions", @@ -114,5 +114,10 @@ "repo.settings.push_mirror.branch_filter.label": "Filtro de ramos (opcional)", "repo.settings.push_mirror.branch_filter.description": "Ramos a serem espelhados. Deixe em branco para espelhar todos os ramos. Veja a %[2]s documentação sobre a sintaxe. Exemplos: main, release/*", "mail.actions.run_info_sha": "Cometimento: %[1]s", - "discussion.sidebar.reference": "Referência" + "discussion.sidebar.reference": "Referência", + "admin.dashboard.remove_resolved_reports": "Remover denúncias resolvidas", + "admin.moderation.no_open_reports": "Atualmente não há denúncias em aberto.", + "admin.moderation.deleted_content_ref": "O conteúdo denunciado do tipo %[1]v e id %[2]d já não existe", + "admin.moderation.moderation_reports": "Denúncias aos moderadores", + "admin.moderation.reports": "Denúncias" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index cc6d99ca80..e15c8ffa27 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -102,7 +102,7 @@ "editor.textarea.shift_tab_hint": "В строке нет отступов. Нажмите Shift + Tab снова или Escape, чтобы покинуть редактор.", "admin.dashboard.cleanup_offline_runners": "Удалить недоступных исполнителей", "avatar.constraints_hint": "Изображение профиля не может быть более %[1]s и крупнее %[2]dx%[3]d пикселей", - "settings.visibility.description": "Видимость профиля влияет на доступ других до ваших не частных репозиториев. Подробнее", + "settings.visibility.description": "Видимость профиля влияет на доступ других до ваших не частных репозиториев. Подробнее.", "repo.diff.commit.previous-short": "Пред.", "repo.diff.commit.next-short": "След.", "profile.actions.tooltip": "Показать действия", @@ -114,5 +114,10 @@ "mail.actions.run_info_sha": "Коммит: %[1]s", "repo.settings.push_mirror.branch_filter.description": "Синхронизируемые ветви. Оставьте пустым, чтобы синхронизировать все. Ознакомьтесь с синтаксисом в документации %[2]s. Примеры: main, release/*", "repo.settings.push_mirror.branch_filter.label": "Выбор ветвей (опционально)", - "discussion.sidebar.reference": "Ссылка" + "discussion.sidebar.reference": "Ссылка", + "admin.moderation.moderation_reports": "Жалобы модерации", + "admin.moderation.deleted_content_ref": "Содержимое типа %[1]v и ид. %[2]d, на которое пожаловались, более не существует", + "admin.moderation.reports": "Жалобы", + "admin.moderation.no_open_reports": "Нет открытых жалоб.", + "admin.dashboard.remove_resolved_reports": "Удалить разрешённые жалобы" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 046738e334..8c8ade33c9 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -117,5 +117,7 @@ "discussion.sidebar.reference": "Посилання", "admin.moderation.no_open_reports": "Відкритих скарг наразі немає.", "admin.moderation.reports": "Скарги", - "admin.moderation.deleted_content_ref": "Вміст типу %[1]v з ідентифікатором %[2]d, на який подано скаргу, більше не існує" + "admin.moderation.deleted_content_ref": "Вміст типу %[1]v з ідентифікатором %[2]d, на який подано скаргу, більше не існує", + "admin.moderation.moderation_reports": "Скарги модераторам", + "admin.dashboard.remove_resolved_reports": "Видалити закриті скарги" } From 3ff78d243a62e525d141238012ff554820b2f36e Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Aug 2025 07:55:49 +0200 Subject: [PATCH 212/297] Update linters (forgejo) (#8750) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 179 +++++++++++++++++++++++----------------------- package.json | 12 ++-- 2 files changed, 94 insertions(+), 97 deletions(-) diff --git a/package-lock.json b/package-lock.json index eface926ae..9f5286dd43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,25 +65,25 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.54.1", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "5.2.0", + "@stylistic/eslint-plugin": "5.2.2", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", - "eslint": "9.31.0", + "eslint": "9.32.0", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", "eslint-plugin-import-x": "4.16.1", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", - "eslint-plugin-playwright": "2.2.0", + "eslint-plugin-playwright": "2.2.2", "eslint-plugin-regexp": "2.9.0", "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "10.3.0", + "eslint-plugin-vue": "10.4.0", "eslint-plugin-vue-scoped-css": "2.11.0", "eslint-plugin-wc": "3.0.1", "globals": "16.3.0", @@ -92,13 +92,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.3", - "stylelint": "16.22.0", + "stylelint": "16.23.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.37.0", + "typescript-eslint": "8.38.0", "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, @@ -1162,9 +1162,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { @@ -3048,9 +3048,9 @@ } }, "node_modules/@stylistic/eslint-plugin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.0.tgz", - "integrity": "sha512-RCEdbREv9EBiToUBQTlRhVYKG093I6ZnnQ990j08eJ6uRZh71DXkOnoxtTLfDQ6utVCVQzrhZFHZP0zfrfOIjA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.2.tgz", + "integrity": "sha512-bE2DUjruqXlHYP3Q2Gpqiuj2bHq7/88FnuaS0FjeGGLCy+X6a07bGVuwtiOYnPSLHR6jmx5Bwdv+j7l8H+G97A==", "dev": true, "license": "MIT", "dependencies": { @@ -3544,17 +3544,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3568,7 +3568,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -3584,16 +3584,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3609,14 +3609,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3631,14 +3631,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3649,9 +3649,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -3666,15 +3666,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3691,9 +3691,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -3705,16 +3705,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3767,16 +3767,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3791,13 +3791,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -7290,9 +7290,9 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { @@ -7302,8 +7302,8 @@ "@eslint/config-helpers": "^0.3.0", "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -7506,14 +7506,11 @@ } }, "node_modules/eslint-plugin-playwright": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.2.0.tgz", - "integrity": "sha512-qSQpAw7RcSzE3zPp8FMGkthaCWovHZ/BsXtpmnGax9vQLIovlh1bsZHEa2+j2lv9DWhnyeLM/qZmp7ffQZfQvg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.2.2.tgz", + "integrity": "sha512-j0jKpndIPOXRRP9uMkwb9l/nSmModOU3452nrFdgFJoEv/435J1onk8+aITzjDW8DfypxgmVaDMdmVIa6F7I0w==", "dev": true, "license": "MIT", - "workspaces": [ - "examples" - ], "dependencies": { "globals": "^13.23.0" }, @@ -7709,9 +7706,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-vue": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.3.0.tgz", - "integrity": "sha512-A0u9snqjCfYaPnqqOaH6MBLVWDUIN4trXn8J3x67uDcXvR7X6Ut8p16N+nYhMCQ9Y7edg2BIRGzfyZsY0IdqoQ==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.4.0.tgz", + "integrity": "sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==", "dev": true, "license": "MIT", "dependencies": { @@ -13838,9 +13835,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.22.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.22.0.tgz", - "integrity": "sha512-SVEMTdjKNV4ollUrIY9ordZ36zHv2/PHzPjfPMau370MlL2VYXeLgSNMMiEbLGRO8RmD2R8/BVUeF2DfnfkC0w==", + "version": "16.23.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.23.0.tgz", + "integrity": "sha512-69T5aS2LUY306ekt1Q1oaSPwz/jaG9HjyMix3UMrai1iEbuOafBe2Dh8xlyczrxFAy89qcKyZWWtc42XLx3Bbw==", "dev": true, "funding": [ { @@ -13867,7 +13864,7 @@ "debug": "^4.4.1", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^10.1.1", + "file-entry-cache": "^10.1.3", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", @@ -14924,16 +14921,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index b0dc05c6d1..559c79b38f 100644 --- a/package.json +++ b/package.json @@ -64,25 +64,25 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.54.1", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin": "5.2.0", + "@stylistic/eslint-plugin": "5.2.2", "@stylistic/stylelint-plugin": "3.1.3", "@vitejs/plugin-vue": "6.0.0", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", - "eslint": "9.31.0", + "eslint": "9.32.0", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.0.2", "eslint-plugin-import-x": "4.16.1", "eslint-plugin-no-jquery": "3.1.1", "eslint-plugin-no-use-extend-native": "0.7.2", - "eslint-plugin-playwright": "2.2.0", + "eslint-plugin-playwright": "2.2.2", "eslint-plugin-regexp": "2.9.0", "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", "eslint-plugin-unicorn": "59.0.1", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "10.3.0", + "eslint-plugin-vue": "10.4.0", "eslint-plugin-vue-scoped-css": "2.11.0", "eslint-plugin-wc": "3.0.1", "globals": "16.3.0", @@ -91,13 +91,13 @@ "markdownlint-cli": "0.45.0", "postcss-html": "1.8.0", "sharp": "0.34.3", - "stylelint": "16.22.0", + "stylelint": "16.23.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", "typescript": "5.8.3", - "typescript-eslint": "8.37.0", + "typescript-eslint": "8.38.0", "vite-string-plugin": "1.4.6", "vitest": "3.2.4" }, From fb5726005619ff34986d1aacbeccec349230a977 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Aug 2025 07:56:31 +0200 Subject: [PATCH 213/297] Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.3.0 (forgejo) (#8752) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b46b23a44a..446e9e0546 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.2.2 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.3.0 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest From f185a4ce671e131c7d3c6952b9ad4a2253fdcde2 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Fri, 1 Aug 2025 10:55:05 +0200 Subject: [PATCH 214/297] feat(logger): rename settings for consistency and remove obsolete settings (#8667) - [x] rename logger settings: `logger..MODE` -> `LOGGER__MODE` - [x] dropped legacy logger settings - [ ] create a docs PR to update documentation I used Github Copilot for some auto completion of code. ## 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 - [x] 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. - [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/.md` to be be used for the release notes instead of the title. ## Release notes - Breaking features - [PR](https://codeberg.org/forgejo/forgejo/pulls/8667): feat(logger): rename settings for consistency and remove obsolete settings Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8667 Reviewed-by: Earl Warren Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- custom/conf/app.example.ini | 6 +-- modules/setting/config_provider.go | 8 +++ modules/setting/log.go | 80 +++++++++++++++++++++--------- modules/setting/log_test.go | 36 +++++++++++--- tests/mysql.ini.tmpl | 2 +- tests/pgsql.ini.tmpl | 2 +- tests/sqlite.ini.tmpl | 2 +- 7 files changed, 100 insertions(+), 36 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 8f7c4658ce..ec65b3382e 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -592,9 +592,9 @@ LEVEL = Info ;BUFFER_LEN = 10000 ;; ;; Sub logger modes, a single comma means use default MODE above, empty means disable it -;logger.access.MODE= -;logger.router.MODE=, -;logger.xorm.MODE=, +;LOGGER_ACCESS_MODE= +;LOGGER_ROUTER_MODE=, +;LOGGER_XORM_MODE=, ;; ;; Collect SSH logs (Creates log from ssh git request) ;; diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go index 19f3b9008a..a34859de56 100644 --- a/modules/setting/config_provider.go +++ b/modules/setting/config_provider.go @@ -331,6 +331,14 @@ func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, n } } +func deprecatedSettingWarning(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey string) { //nolint:unparam + if rootCfg.Section(oldSection).HasKey(oldKey) { + msg := fmt.Sprintf("Deprecated config option `[%s]` `%s` present. Use `[%s]` `%s` instead.", oldSection, oldKey, newSection, newKey) + log.Error("%v", msg) + DeprecatedWarnings = append(DeprecatedWarnings, msg) + } +} + // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) { if rootCfg.Section(oldSection).HasKey(oldKey) { diff --git a/modules/setting/log.go b/modules/setting/log.go index 6d069d0e9c..a23662239e 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -56,41 +56,73 @@ func loadLogGlobalFrom(rootCfg ConfigProvider) { func prepareLoggerConfig(rootCfg ConfigProvider) { sec := rootCfg.Section("log") - if !sec.HasKey("logger.default.MODE") { - sec.Key("logger.default.MODE").MustString(",") + // Priority: `LOGGER_DEFAULT_MODE` -> `logger.default.MODE` + deprecatedSettingWarning(rootCfg, "log", "logger.default.MODE", "log", "LOGGER_DEFAULT_MODE") + hasNoValue := !sec.HasKey("LOGGER_DEFAULT_MODE") + if hasNoValue && sec.HasKey("logger.default.MODE") { + sec.Key("LOGGER_DEFAULT_MODE").SetValue(sec.Key("logger.default.MODE").String()) + hasNoValue = false + } + if hasNoValue { + sec.Key("LOGGER_DEFAULT_MODE").SetValue(",") // use default logger } - deprecatedSetting(rootCfg, "log", "ACCESS", "log", "logger.access.MODE", "1.21") - deprecatedSetting(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "logger.access.MODE", "1.21") - if val := sec.Key("ACCESS").String(); val != "" { - sec.Key("logger.access.MODE").MustString(val) + // Priority: `ENABLE_ACCESS_LOG` -> `LOGGER_ACCESS_MODE` -> `logger.access.MODE` -> `ACCESS` + deprecatedSettingWarning(rootCfg, "log", "ACCESS", "log", "LOGGER_ACCESS_MODE") + deprecatedSettingWarning(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "LOGGER_ACCESS_MODE") + deprecatedSettingWarning(rootCfg, "log", "logger.access.MODE", "log", "LOGGER_ACCESS_MODE") + hasNoValue = !sec.HasKey("LOGGER_ACCESS_MODE") + if hasNoValue && sec.HasKey("logger.access.MODE") { + sec.Key("LOGGER_ACCESS_MODE").SetValue(sec.Key("logger.access.MODE").String()) + hasNoValue = false + } + if val := sec.Key("ACCESS").String(); hasNoValue && val != "" { + sec.Key("LOGGER_ACCESS_MODE").SetValue(val) } if sec.HasKey("ENABLE_ACCESS_LOG") && !sec.Key("ENABLE_ACCESS_LOG").MustBool() { - sec.Key("logger.access.MODE").SetValue("") + sec.Key("LOGGER_ACCESS_MODE").SetValue("") } - deprecatedSetting(rootCfg, "log", "ROUTER", "log", "logger.router.MODE", "1.21") - deprecatedSetting(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "logger.router.MODE", "1.21") - if val := sec.Key("ROUTER").String(); val != "" { - sec.Key("logger.router.MODE").MustString(val) + // Priority: `DISABLE_ROUTER_LOG` -> `LOGGER_ROUTER_MODE` -> `logger.router.MODE` -> `ROUTER` + deprecatedSettingWarning(rootCfg, "log", "ROUTER", "log", "LOGGER_ROUTER_MODE") + deprecatedSettingWarning(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "LOGGER_ROUTER_MODE") + deprecatedSettingWarning(rootCfg, "log", "logger.router.MODE", "log", "LOGGER_ROUTER_MODE") + hasNoValue = !sec.HasKey("LOGGER_ROUTER_MODE") + if hasNoValue && sec.HasKey("logger.router.MODE") { + sec.Key("LOGGER_ROUTER_MODE").SetValue(sec.Key("logger.router.MODE").String()) + hasNoValue = false } - if !sec.HasKey("logger.router.MODE") { - sec.Key("logger.router.MODE").MustString(",") // use default logger + if val := sec.Key("ROUTER").String(); hasNoValue && val != "" { + sec.Key("LOGGER_ROUTER_MODE").SetValue(val) + hasNoValue = false } if sec.HasKey("DISABLE_ROUTER_LOG") && sec.Key("DISABLE_ROUTER_LOG").MustBool() { - sec.Key("logger.router.MODE").SetValue("") + sec.Key("LOGGER_ROUTER_MODE").SetValue("") + hasNoValue = false + } + if hasNoValue { + sec.Key("LOGGER_ROUTER_MODE").SetValue(",") // use default logger } - deprecatedSetting(rootCfg, "log", "XORM", "log", "logger.xorm.MODE", "1.21") - deprecatedSetting(rootCfg, "log", "ENABLE_XORM_LOG", "log", "logger.xorm.MODE", "1.21") - if val := sec.Key("XORM").String(); val != "" { - sec.Key("logger.xorm.MODE").MustString(val) + // Priority: `ENABLE_XORM_LOG` -> `LOGGER_XORM_MODE` -> `logger.xorm.MODE` -> `XORM` + deprecatedSettingWarning(rootCfg, "log", "XORM", "log", "LOGGER_XORM_MODE") + deprecatedSettingWarning(rootCfg, "log", "ENABLE_XORM_LOG", "log", "LOGGER_XORM_MODE") + deprecatedSettingWarning(rootCfg, "log", "logger.xorm.MODE", "log", "LOGGER_XORM_MODE") + hasNoValue = !sec.HasKey("LOGGER_XORM_MODE") + if hasNoValue && sec.HasKey("logger.xorm.MODE") { + sec.Key("LOGGER_XORM_MODE").SetValue(sec.Key("logger.xorm.MODE").String()) + hasNoValue = false } - if !sec.HasKey("logger.xorm.MODE") { - sec.Key("logger.xorm.MODE").MustString(",") // use default logger + if val := sec.Key("XORM").String(); hasNoValue && val != "" { + sec.Key("LOGGER_XORM_MODE").SetValue(val) + hasNoValue = false } if sec.HasKey("ENABLE_XORM_LOG") && !sec.Key("ENABLE_XORM_LOG").MustBool() { - sec.Key("logger.xorm.MODE").SetValue("") + sec.Key("LOGGER_XORM_MODE").SetValue("") + hasNoValue = false + } + if hasNoValue { + sec.Key("LOGGER_XORM_MODE").SetValue(",") // use default logger } } @@ -217,14 +249,14 @@ func initManagedLoggers(manager *log.LoggerManager, cfg ConfigProvider) { func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, loggerName string) { sec := rootCfg.Section("log") - keyPrefix := "logger." + loggerName + key := "LOGGER_" + strings.ToUpper(loggerName) + "_MODE" - disabled := sec.HasKey(keyPrefix+".MODE") && sec.Key(keyPrefix+".MODE").String() == "" + disabled := sec.HasKey(key) && sec.Key(key).String() == "" if disabled { return } - modeVal := sec.Key(keyPrefix + ".MODE").String() + modeVal := sec.Key(key).String() if modeVal == "," { modeVal = Log.Mode } diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go index 528921c669..a37c8f07a9 100644 --- a/modules/setting/log_test.go +++ b/modules/setting/log_test.go @@ -251,8 +251,8 @@ ENABLE_ACCESS_LOG = false func TestLogConfigNewConfig(t *testing.T) { manager, managerClose := initLoggersByConfig(t, ` [log] -logger.access.MODE = console -logger.xorm.MODE = console, console-1 +LOGGER_ACCESS_MODE = console +LOGGER_XORM_MODE = console, console-1 [log.console] LEVEL = warn @@ -421,7 +421,7 @@ func TestLegacyLoggerMigrations(t *testing.T) { } t.Run("default", func(t *testing.T) { - runCases(t, "logger.default.MODE", Cases{ + runCases(t, "LOGGER_DEFAULT_MODE", Cases{ { "uses default value for default logger", "", @@ -438,7 +438,7 @@ logger.default.MODE = file }) t.Run("access", func(t *testing.T) { - runCases(t, "logger.access.MODE", Cases{ + runCases(t, "LOGGER_ACCESS_MODE", Cases{ { "uses default value for access logger", "", @@ -475,6 +475,14 @@ logger.access.MODE = console `, "console", }, + { + "LOGGER_ACCESS_MODE has precedence over logger.access.MODE for access logger", + `[log] +LOGGER_ACCESS_MODE = file +logger.access.MODE = console +`, + "file", + }, { "ENABLE_ACCESS_LOG doesn't enable access logger", `[log] @@ -486,7 +494,7 @@ ENABLE_ACCESS_LOG = true }) t.Run("router", func(t *testing.T) { - runCases(t, "logger.router.MODE", Cases{ + runCases(t, "LOGGER_ROUTER_MODE", Cases{ { "uses default value for router logger", "", @@ -523,11 +531,19 @@ logger.router.MODE = console `, "console", }, + { + "LOGGER_ROUTER_MODE has precedence over logger.router.MODE for router logger", + `[log] +LOGGER_ROUTER_MODE = file +logger.router.MODE = console +`, + "file", + }, }) }) t.Run("xorm", func(t *testing.T) { - runCases(t, "logger.xorm.MODE", Cases{ + runCases(t, "LOGGER_XORM_MODE", Cases{ { "uses default value for xorm logger", "", @@ -564,6 +580,14 @@ logger.xorm.MODE = console `, "console", }, + { + "LOGGER_XORM_MODE has precedence over logger.xorm.MODE for xorm logger", + `[log] +LOGGER_XORM_MODE = file +logger.xorm.MODE = console +`, + "file", + }, }) }) } diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index 3315d85a3f..674884cea5 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -77,7 +77,7 @@ PROVIDER_CONFIG = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/data/sessions MODE = {{TEST_LOGGER}} ROOT_PATH = {{REPO_TEST_DIR}}mysql-log ENABLE_SSH_LOG = true -logger.xorm.MODE = file +LOGGER_XORM_MODE = file [log.test] LEVEL = Info diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl index 1e9b981800..386137d219 100644 --- a/tests/pgsql.ini.tmpl +++ b/tests/pgsql.ini.tmpl @@ -82,7 +82,7 @@ PROVIDER_CONFIG = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-pgsql/data/sessions MODE = {{TEST_LOGGER}} ROOT_PATH = {{REPO_TEST_DIR}}pgsql-log ENABLE_SSH_LOG = true -logger.xorm.MODE = file +LOGGER_XORM_MODE = file [log.test] LEVEL = Info diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl index df6cea44ca..b3baa314a2 100644 --- a/tests/sqlite.ini.tmpl +++ b/tests/sqlite.ini.tmpl @@ -79,7 +79,7 @@ PROVIDER_CONFIG = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-sqlite/data/sessions MODE = {{TEST_LOGGER}} ROOT_PATH = {{REPO_TEST_DIR}}sqlite-log ENABLE_SSH_LOG = true -logger.xorm.MODE = file +LOGGER_XORM_MODE = file [log.test] LEVEL = Info From d539fe7bf773150b47478c4a52dbdf10d2f215f4 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Aug 2025 21:01:54 +0200 Subject: [PATCH 215/297] Update module github.com/mattn/go-sqlite3 to v1.14.30 (forgejo) (#8738) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8738 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 167fe117c1..b0df1e7480 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/lib/pq v1.10.9 github.com/markbates/goth v1.80.0 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-sqlite3 v1.14.29 + github.com/mattn/go-sqlite3 v1.14.30 github.com/meilisearch/meilisearch-go v0.31.0 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 diff --git a/go.sum b/go.sum index a8af308a15..adb10d19d2 100644 --- a/go.sum +++ b/go.sum @@ -395,8 +395,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.29 h1:1O6nRLJKvsi1H2Sj0Hzdfojwt8GiGKm+LOfLaBFaouQ= -github.com/mattn/go-sqlite3 v1.14.29/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.30 h1:bVreufq3EAIG1Quvws73du3/QgdeZ3myglJlrzSYYCY= +github.com/mattn/go-sqlite3 v1.14.30/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= From 1331a7f75cf74a89e5e6cac13d0bcf856e3ec28d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Aug 2025 21:01:58 +0200 Subject: [PATCH 216/297] Update module github.com/golang-jwt/jwt/v5 to v5.3.0 (forgejo) (#8739) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8739 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b0df1e7480..d664940e5a 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 - github.com/golang-jwt/jwt/v5 v5.2.3 + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/google/go-github/v64 v64.0.0 github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 diff --git a/go.sum b/go.sum index adb10d19d2..d358671e16 100644 --- a/go.sum +++ b/go.sum @@ -268,8 +268,8 @@ github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= -github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= From e4cd25057f04e45aabf27eb0f6092776936a30db Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 1 Aug 2025 22:33:27 +0200 Subject: [PATCH 217/297] Update dependency @vitejs/plugin-vue to v6.0.1 (forgejo) (#8749) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f5286dd43..b4bbf1f575 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,7 @@ "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.2.2", "@stylistic/stylelint-plugin": "3.1.3", - "@vitejs/plugin-vue": "6.0.0", + "@vitejs/plugin-vue": "6.0.1", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", @@ -2207,9 +2207,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.19", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", - "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", "dev": true, "license": "MIT" }, @@ -4078,13 +4078,13 @@ ] }, "node_modules/@vitejs/plugin-vue": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz", - "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.19" + "@rolldown/pluginutils": "1.0.0-beta.29" }, "engines": { "node": "^20.19.0 || >=22.12.0" diff --git a/package.json b/package.json index 559c79b38f..b8f5db1e78 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.2.2", "@stylistic/stylelint-plugin": "3.1.3", - "@vitejs/plugin-vue": "6.0.0", + "@vitejs/plugin-vue": "6.0.1", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", "@vue/test-utils": "2.4.6", From 648a75e687445f441e7233b51a505706e083b1e1 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 2 Aug 2025 13:06:04 +0200 Subject: [PATCH 218/297] fix: correctly get stats for API commits (#8756) - Instead of generating a patch and parsing its contents, use a faster and simple way to get it via `--shortstat`. - Resolves forgejo/forgejo#8725 - Regression of forgejo/forgejo#7682 - Adds unit test. - Adds integration test. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8756 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/git/repo_compare.go | 11 +++ modules/git/repo_compare_test.go | 81 +++++++++++++++++++ services/convert/git_commit.go | 11 +-- .../integration/api_repo_git_commits_test.go | 19 +++++ 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 373b5befb5..94f1911c4a 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -183,6 +183,17 @@ func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAddi return numFiles, totalAdditions, totalDeletions, err } +// GetCommitStat returns the number of files, total additions and total deletions the commit has. +func (repo *Repository) GetCommitShortStat(commitID string) (numFiles, totalAdditions, totalDeletions int, err error) { + cmd := NewCommand(repo.Ctx, "diff-tree", "--shortstat", "--no-commit-id", "--root").AddDynamicArguments(commitID) + stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) + if err != nil { + return 0, 0, 0, err + } + + return parseDiffStat(stdout) +} + // GetDiffShortStat counts number of changed files, number of additions and deletions func GetDiffShortStat(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) { // Now if we call: diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index 86bd6855a7..b1ebdf6177 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -1,4 +1,5 @@ // Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package git @@ -162,3 +163,83 @@ func TestGetCommitFilesChanged(t *testing.T) { assert.ElementsMatch(t, tc.files, changedFiles) } } + +func TestGetCommitShortStat(t *testing.T) { + t.Run("repo1_bare", func(t *testing.T) { + repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare")) + if err != nil { + require.NoError(t, err) + return + } + defer repo.Close() + + numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("ce064814f4a0d337b333e646ece456cd39fab612") + require.NoError(t, err) + assert.Equal(t, 0, numFiles) + assert.Equal(t, 0, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("feaf4ba6bc635fec442f46ddd4512416ec43c2c2") + require.NoError(t, err) + assert.Equal(t, 0, numFiles) + assert.Equal(t, 0, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("37991dec2c8e592043f47155ce4808d4580f9123") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("6fbd69e9823458e6c4a2fc5c0f6bc022b2f2acd1") + require.NoError(t, err) + assert.Equal(t, 2, numFiles) + assert.Equal(t, 2, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0") + require.NoError(t, err) + assert.Equal(t, 2, numFiles) + assert.Equal(t, 2, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("95bb4d39648ee7e325106df01a621c530863a653") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + }) + + t.Run("repo6_blame_sha256", func(t *testing.T) { + repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo6_blame_sha256")) + if err != nil { + require.NoError(t, err) + return + } + defer repo.Close() + + numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 1, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 6, totalAddition) + assert.Equal(t, 0, totalDeletions) + }) +} diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go index 4603cfac4d..6a691966b8 100644 --- a/services/convert/git_commit.go +++ b/services/convert/git_commit.go @@ -15,7 +15,6 @@ import ( api "forgejo.org/modules/structs" "forgejo.org/modules/util" ctx "forgejo.org/services/context" - "forgejo.org/services/gitdiff" ) // ToCommitUser convert a git.Signature to an api.CommitUser @@ -210,17 +209,15 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep // Get diff stats for commit if opts.Stat { - diff, _, err := gitdiff.GetDiffSimple(ctx, gitRepo, &gitdiff.DiffOptions{ - AfterCommitID: commit.ID.String(), - }) + _, totalAdditions, totalDeletions, err := gitRepo.GetCommitShortStat(commit.ID.String()) if err != nil { return nil, err } res.Stats = &api.CommitStats{ - Total: diff.TotalAddition + diff.TotalDeletion, - Additions: diff.TotalAddition, - Deletions: diff.TotalDeletion, + Total: totalAdditions + totalDeletions, + Additions: totalAdditions, + Deletions: totalDeletions, } } diff --git a/tests/integration/api_repo_git_commits_test.go b/tests/integration/api_repo_git_commits_test.go index 7a93029d4c..db73307653 100644 --- a/tests/integration/api_repo_git_commits_test.go +++ b/tests/integration/api_repo_git_commits_test.go @@ -231,3 +231,22 @@ func TestGetFileHistoryNotOnMaster(t *testing.T) { assert.Equal(t, "1", resp.Header().Get("X-Total")) } + +func TestAPIReposGitCommit(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + + req := NewRequest(t, "GET", "/api/v1/repos/user2/repo16/git/commits/f27c2b2b03dcab38beaf89b0ab4ff61f6de63441"). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiData api.Commit + DecodeJSON(t, resp, &apiData) + + assert.Equal(t, "f27c2b2b03dcab38beaf89b0ab4ff61f6de63441", apiData.CommitMeta.SHA) + assert.Equal(t, 1, apiData.Stats.Total) + assert.Equal(t, 1, apiData.Stats.Additions) + assert.Equal(t, 0, apiData.Stats.Deletions) +} From a2d6b791950f9aec56bad22036f6dc3b3abc5081 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 3 Aug 2025 03:36:45 +0200 Subject: [PATCH 219/297] Update dependency @playwright/test to v1.54.2 (forgejo) (#8761) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 24 ++++++++++++------------ package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4bbf1f575..d9e278262d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.54.1", + "@playwright/test": "1.54.2", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.2.2", "@stylistic/stylelint-plugin": "3.1.3", @@ -2172,13 +2172,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", - "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz", + "integrity": "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.54.1" + "playwright": "1.54.2" }, "bin": { "playwright": "cli.js" @@ -11987,13 +11987,13 @@ } }, "node_modules/playwright": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", - "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz", + "integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.54.1" + "playwright-core": "1.54.2" }, "bin": { "playwright": "cli.js" @@ -12006,9 +12006,9 @@ } }, "node_modules/playwright-core": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", - "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz", + "integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index b8f5db1e78..ed97d45ea7 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "devDependencies": { "@axe-core/playwright": "4.10.2", "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", - "@playwright/test": "1.54.1", + "@playwright/test": "1.54.2", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.2.2", "@stylistic/stylelint-plugin": "3.1.3", From 7af647025a3fd234adb8ff516b20645a17668d13 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 3 Aug 2025 03:37:09 +0200 Subject: [PATCH 220/297] Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.3.1 (forgejo) (#8763) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 446e9e0546..c9e3f17402 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go 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.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.11 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest From a8ef6af0fd05e3085cb6690bf9b131ec3d4ee0a8 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 3 Aug 2025 03:39:00 +0200 Subject: [PATCH 221/297] Update dependency @stylistic/stylelint-plugin to v4 (forgejo) (#8765) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 98 ++++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index d9e278262d..a9ee1b866b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "@playwright/test": "1.54.2", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.2.2", - "@stylistic/stylelint-plugin": "3.1.3", + "@stylistic/stylelint-plugin": "4.0.0", "@vitejs/plugin-vue": "6.0.1", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", @@ -481,9 +481,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", - "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", "dev": true, "funding": [ { @@ -500,8 +500,8 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@discoveryjs/json-ext": { @@ -3082,18 +3082,17 @@ } }, "node_modules/@stylistic/stylelint-plugin": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.3.tgz", - "integrity": "sha512-85fsmzgsIVmyG3/GFrjuYj6Cz8rAM7IZiPiXCMiSMfoDOC1lOrzrXPDk24WqviAghnPqGpx8b0caK2PuewWGFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-4.0.0.tgz", + "integrity": "sha512-CFwt3K4Y/7bygNCLCQ8Sy4Hzgbhxq3BsNW0FIuYxl17HD3ywptm54ocyeiLVRrk5jtz1Zwks7Xr9eiZt8SWHAw==", "dev": true, "license": "MIT", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1", - "@csstools/media-query-list-parser": "^3.0.1", - "is-plain-object": "^5.0.0", - "postcss": "^8.4.41", - "postcss-selector-parser": "^6.1.2", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3", + "postcss": "^8.5.6", + "postcss-selector-parser": "^7.1.0", "postcss-value-parser": "^4.2.0", "style-search": "^0.1.0" }, @@ -3101,7 +3100,50 @@ "node": "^18.12 || >=20.9" }, "peerDependencies": { - "stylelint": "^16.8.0" + "stylelint": "^16.22.0" + } + }, + "node_modules/@stylistic/stylelint-plugin/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@stylistic/stylelint-plugin/node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, "node_modules/@swc/helpers": { @@ -13940,30 +13982,6 @@ "stylelint": ">=16" } }, - "node_modules/stylelint/node_modules/@csstools/media-query-list-parser": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", - "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, "node_modules/stylelint/node_modules/@csstools/selector-specificity": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", diff --git a/package.json b/package.json index ed97d45ea7..b790414c22 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@playwright/test": "1.54.2", "@stoplight/spectral-cli": "6.15.0", "@stylistic/eslint-plugin": "5.2.2", - "@stylistic/stylelint-plugin": "3.1.3", + "@stylistic/stylelint-plugin": "4.0.0", "@vitejs/plugin-vue": "6.0.1", "@vitest/coverage-v8": "3.2.4", "@vitest/eslint-plugin": "1.3.4", From cdb6296d580aceeddc315f415e67b8c82676cc69 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 3 Aug 2025 03:41:36 +0200 Subject: [PATCH 222/297] Update dependency eslint-plugin-unicorn to v60 (forgejo) (#8766) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 61 ++++++++++++++++------------------------------- package.json | 2 +- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9ee1b866b..54cb1d7ac7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,7 +81,7 @@ "eslint-plugin-regexp": "2.9.0", "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", - "eslint-plugin-unicorn": "59.0.1", + "eslint-plugin-unicorn": "60.0.0", "eslint-plugin-vitest-globals": "1.5.0", "eslint-plugin-vue": "10.4.0", "eslint-plugin-vue-scoped-css": "2.11.0", @@ -5467,6 +5467,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -7679,65 +7686,39 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "59.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-59.0.1.tgz", - "integrity": "sha512-EtNXYuWPUmkgSU2E7Ttn57LbRREQesIP1BiLn7OZLKodopKfDXfBUkC/0j6mpw2JExwf43Uf3qLSvrSvppgy8Q==", + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", + "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "@eslint-community/eslint-utils": "^4.5.1", - "@eslint/plugin-kit": "^0.2.7", - "ci-info": "^4.2.0", + "@babel/helper-validator-identifier": "^7.27.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@eslint/plugin-kit": "^0.3.3", + "change-case": "^5.4.4", + "ci-info": "^4.3.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.41.0", + "core-js-compat": "^3.44.0", "esquery": "^1.6.0", "find-up-simple": "^1.0.1", - "globals": "^16.0.0", + "globals": "^16.3.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", "regexp-tree": "^0.1.27", "regjsparser": "^0.12.0", - "semver": "^7.7.1", + "semver": "^7.7.2", "strip-indent": "^4.0.0" }, "engines": { - "node": "^18.20.0 || ^20.10.0 || >=21.0.0" + "node": "^20.10.0 || >=21.0.0" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=9.22.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/eslint-plugin-unicorn/node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.13.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "eslint": ">=9.29.0" } }, "node_modules/eslint-plugin-vitest-globals": { diff --git a/package.json b/package.json index b790414c22..0966c570cf 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "eslint-plugin-regexp": "2.9.0", "eslint-plugin-sonarjs": "3.0.4", "eslint-plugin-toml": "0.12.0", - "eslint-plugin-unicorn": "59.0.1", + "eslint-plugin-unicorn": "60.0.0", "eslint-plugin-vitest-globals": "1.5.0", "eslint-plugin-vue": "10.4.0", "eslint-plugin-vue-scoped-css": "2.11.0", From 2269831c9fd87e2868e4131097744ababfd8c77b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 3 Aug 2025 04:17:41 +0200 Subject: [PATCH 223/297] Update module code.forgejo.org/forgejo/runner/v9 to v9.0.2 (forgejo) (#8762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Confidence | |---|---|---|---| | code.forgejo.org/forgejo/runner/v9 | `v9.0.1` -> `v9.0.2` | [![age](https://developer.mend.io/api/mc/badges/age/go/code.forgejo.org%2fforgejo%2frunner%2fv9/v9.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/code.forgejo.org%2fforgejo%2frunner%2fv9/v9.0.1/v9.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8762 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d664940e5a..8850e3e17c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( code.forgejo.org/forgejo/go-rpmutils v1.0.0 code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/reply v1.0.2 - code.forgejo.org/forgejo/runner/v9 v9.0.1 + code.forgejo.org/forgejo/runner/v9 v9.0.2 code.forgejo.org/go-chi/binding v1.0.1 code.forgejo.org/go-chi/cache v1.0.1 code.forgejo.org/go-chi/captcha v1.0.2 diff --git a/go.sum b/go.sum index d358671e16..475c1f345d 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/Uf code.forgejo.org/forgejo/levelqueue v1.0.0/go.mod h1:fmG6zhVuqim2rxSFOoasgXO8V2W/k9U31VVYqLIRLhQ= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= -code.forgejo.org/forgejo/runner/v9 v9.0.1 h1:Qdg7c0yvqgjXj9NMQ+Vsq+GuUoGshHFHGYF79Bz+/2g= -code.forgejo.org/forgejo/runner/v9 v9.0.1/go.mod h1:b6JWcpwHnBwLuWeQERM4pCRzFbnsEvC9gagQfxbZeJw= +code.forgejo.org/forgejo/runner/v9 v9.0.2 h1:ajTyjmn3tjJs3fkJ58498CgI4kvxifVY8gbh46SWAgc= +code.forgejo.org/forgejo/runner/v9 v9.0.2/go.mod h1:b6JWcpwHnBwLuWeQERM4pCRzFbnsEvC9gagQfxbZeJw= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY= From 4392dee96d7437ca3d47ed156883b43cb02385d3 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 3 Aug 2025 05:49:24 +0200 Subject: [PATCH 224/297] chore(ui): improve hashbox (#8721) Followup to https://codeberg.org/forgejo/forgejo/pulls/7822 * Fix signaturebox's background breaking out of hashbox and thus breaking it's rounded corners * Fix a bug where an extra right margin was applied to signbox's avatar on pull request overview by unrelated rule * Untangle hashbox's CSS from .label - it was not a good partnership between them. Some extra properties, some that we had to override * Move CSS out of "repo.css" to a separate file - it's clearly not only related to repos Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8721 Reviewed-by: Gusted --- templates/repo/commit_header.tmpl | 6 +- templates/repo/commits_table.tmpl | 4 +- templates/repo/editor/cherry_pick.tmpl | 2 +- templates/repo/settings/lfs_file_find.tmpl | 4 +- templates/repo/settings/options.tmpl | 4 +- templates/repo/settings/webhook/history.tmpl | 2 +- templates/repo/shabox.tmpl | 2 +- web_src/css/index.css | 1 + web_src/css/modules/hashbox.css | 104 +++++++++++++++++++ web_src/css/repo.css | 97 ----------------- 10 files changed, 117 insertions(+), 109 deletions(-) create mode 100644 web_src/css/modules/hashbox.css diff --git a/templates/repo/commit_header.tmpl b/templates/repo/commit_header.tmpl index 9604daf2b0..f0c094c9bd 100644 --- a/templates/repo/commit_header.tmpl +++ b/templates/repo/commit_header.tmpl @@ -181,11 +181,11 @@ {{ctx.Locale.Tr "repo.diff.parent"}} {{range .Parents}} {{if $.PageIsWiki}} - + {{ShortSha .}} {{else}} - + {{ShortSha .}} {{end}} @@ -200,7 +200,7 @@ {{ShortSha .CommitID}} {{else}} - + {{ShortSha .CommitID}} {{end}} diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index c0e4f5ca46..1c2f793d1c 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -10,11 +10,11 @@
    {{if .IsDiffCompare}} diff --git a/templates/repo/editor/cherry_pick.tmpl b/templates/repo/editor/cherry_pick.tmpl index 49b210f75c..362b8d52ee 100644 --- a/templates/repo/editor/cherry_pick.tmpl +++ b/templates/repo/editor/cherry_pick.tmpl @@ -11,7 +11,7 @@
    diff --git a/templates/repo/shabox.tmpl b/templates/repo/shabox.tmpl index 270eab7759..86413a5412 100644 --- a/templates/repo/shabox.tmpl +++ b/templates/repo/shabox.tmpl @@ -1,4 +1,4 @@ -{{$class := "ui sha label"}} +{{$class := "sha label"}} {{if .signature}} {{$class = (print $class " isSigned")}} {{if .verification.Verified}} diff --git a/web_src/css/index.css b/web_src/css/index.css index d86a784bcc..7b0fa45916 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -30,6 +30,7 @@ @import "./modules/svg.css"; @import "./modules/flexcontainer.css"; @import "./modules/user-cards.css"; +@import "./modules/hashbox.css"; @import "./shared/flex-list.css"; @import "./shared/milestone.css"; diff --git a/web_src/css/modules/hashbox.css b/web_src/css/modules/hashbox.css new file mode 100644 index 0000000000..316ac1f655 --- /dev/null +++ b/web_src/css/modules/hashbox.css @@ -0,0 +1,104 @@ +.sha.label { + display: inline-flex; + flex-shrink: 0; + gap: 0; + align-items: center; + margin: 0 6px; /* needs to go but looked into case-by-case */ + padding: 0; + min-width: 0; /* from .ui.label, may be bad actually */ + background: var(--color-label-bg); + color: var(--color-label-text); + border: 1px solid var(--color-light-border); + border-radius: var(--border-radius); + white-space: nowrap; + font-family: var(--fonts-monospace); + font-size: 13px; + font-weight: var(--font-weight-normal); + line-height: 1; +} + +.primary.sha.label { + border: none; + background: var(--color-primary); + color: var(--color-primary-contrast); +} + +.sha.label .shortsha { + padding: 0.33rem 0.5rem; +} + +.sha.label .signature { + color: var(--color-text); + background: var(--color-light); + padding: 0.25rem 0.33rem; + border-left: 1px solid var(--color-light-border); + border-top-right-radius: inherit; + border-bottom-right-radius: inherit; +} + +.sha.label .signature-author { + display: flex; + gap: 0.25rem; +} + +.sha.label .signature-author .avatar { + height: 16px; + width: 16px; + margin: 0 !important; /* In some areas selectors with .avatar are too broad */ +} + +.sha.label.isSigned.isWarning { + border: 1px solid var(--color-red-badge); + background: var(--color-red-badge-bg); +} + +.sha.label.isSigned.isWarning .signature { + border-left: 1px solid var(--color-red-badge); + color: var(--color-red-badge); +} + +.sha.label.isSigned.isWarning:hover { + background: var(--color-red-badge-hover-bg); +} + +.sha.label.isSigned.isVerified { + border: 1px solid var(--color-green-badge); + background: var(--color-green-badge-bg); +} + +.sha.label.isSigned.isVerified .signature { + border-left: 1px solid var(--color-green-badge); + color: var(--color-green-badge); +} + +.sha.label.isSigned.isVerified:hover { + background: var(--color-green-badge-hover-bg); +} + +.sha.label.isSigned.isVerifiedUntrusted { + border: 1px solid var(--color-yellow-badge); + background: var(--color-yellow-badge-bg); +} + +.sha.label.isSigned.isVerifiedUntrusted .signature { + border-left: 1px solid var(--color-yellow-badge); + color: var(--color-yellow-badge); +} + +.sha.label.isSigned.isVerifiedUntrusted:hover { + background: var(--color-yellow-badge-hover-bg); +} + +.sha.label.isSigned.isVerifiedUnmatched { + border: 1px solid var(--color-orange-badge); + background: var(--color-orange-badge-bg); +} + +.sha.label.isSigned.isVerifiedUnmatched .signature { + border-left: 1px solid var(--color-orange-badge); + color: var(--color-orange-badge); +} + +.sha.label.isSigned.isVerifiedUnmatched:hover { + background: var(--color-orange-badge-hover-bg); +} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index a3713a4c3d..4aa6faa46e 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1238,103 +1238,6 @@ pdf-object { background-color: var(--color-light) !important; } -.ui .sha.label { - font-family: var(--fonts-monospace); - font-size: 13px; - font-weight: var(--font-weight-normal); - margin: 0 6px; - padding: 0; - gap: 0; - flex-shrink: 0; -} - -.ui.ui .sha.label { - border: 1px solid var(--color-light-border); -} - -.ui.primary.sha.label { - border: none !important; - background: var(--color-primary) !important; -} - -.sha.label .shortsha { - padding: 0.33rem 0.5rem; -} - -.sha.label .signature { - color: var(--color-text); - background: var(--color-light); - padding: 0.25rem 0.33rem; - border-left: 1px solid var(--color-light-border); -} - -.sha.label .signature-author { - display: flex; - gap: 0.25rem; -} - -.sha.label .signature-author .avatar { - height: 16px; - margin-bottom: 0; - width: 16px; -} - -.sha.label.isSigned.isWarning { - border: 1px solid var(--color-red-badge); - background: var(--color-red-badge-bg); -} - -.sha.label.isSigned.isWarning .signature { - border-left: 1px solid var(--color-red-badge); - color: var(--color-red-badge); -} - -.sha.label.isSigned.isWarning:hover { - background: var(--color-red-badge-hover-bg) !important; -} - -.sha.label.isSigned.isVerified { - border: 1px solid var(--color-green-badge); - background: var(--color-green-badge-bg); -} - -.sha.label.isSigned.isVerified .signature { - border-left: 1px solid var(--color-green-badge); - color: var(--color-green-badge); -} - -.sha.label.isSigned.isVerified:hover { - background: var(--color-green-badge-hover-bg) !important; -} - -.sha.label.isSigned.isVerifiedUntrusted { - border: 1px solid var(--color-yellow-badge); - background: var(--color-yellow-badge-bg); -} - -.sha.label.isSigned.isVerifiedUntrusted .signature { - border-left: 1px solid var(--color-yellow-badge); - color: var(--color-yellow-badge); -} - -.sha.label.isSigned.isVerifiedUntrusted:hover { - background: var(--color-yellow-badge-hover-bg) !important; -} - -.sha.label.isSigned.isVerifiedUnmatched { - border: 1px solid var(--color-orange-badge); - background: var(--color-orange-badge-bg); -} - -.sha.label.isSigned.isVerifiedUnmatched .signature { - border-left: 1px solid var(--color-orange-badge); - color: var(--color-orange-badge); -} - -.sha.label.isSigned.isVerifiedUnmatched:hover { - background: var(--color-orange-badge-hover-bg) !important; -} - .repository .data-table { width: 100%; } From 64193310eeed0dd7e32a659c12a0ff0b230114df Mon Sep 17 00:00:00 2001 From: zokki Date: Sun, 3 Aug 2025 06:19:45 +0200 Subject: [PATCH 225/297] fix(ui): compare branches even with pull requests disabled (#8496) Resolves #8428 Its currently showing the diff of two branches, even if the PR-unit is disabled. But the POST to create the PR is still returning an error when the unit is disabled, so its only a change for UI. Preview: https://codeberg.org/attachments/610d6b81-a50f-4c43-91c2-db4c38e7b701 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8496 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: zokki Co-committed-by: zokki --- options/locale_next/locale_en-US.json | 1 + routers/web/repo/compare.go | 11 ----------- templates/repo/branch/list.tmpl | 6 +++++- templates/repo/diff/compare.tmpl | 11 ++++++++--- tests/integration/compare_test.go | 11 +++++++---- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index d861c42d66..8542114100 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -111,5 +111,6 @@ "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more.", "avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels", "og.repo.summary_card.alt_description": "Summary card of repository %[1]s, described as: %[2]s", + "compare.branches.title": "Compare branches", "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." } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 90ce2e0a71..feedeef945 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -520,17 +520,6 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch } - if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) { - if log.IsTrace() { - log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v", - ctx.Doer, - baseRepo, - permBase) - } - ctx.NotFound("ParseCompareInfo", nil) - return nil - } - baseBranchRef := ci.BaseBranch if baseIsBranch { baseBranchRef = git.BranchPrefix + ci.BaseBranch diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index fe945324ea..7ee53deb84 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -121,7 +121,11 @@ {{end}} - {{if not .LatestPullRequest}} + {{if not ($.Permission.CanRead $.UnitTypePullRequests)}} + + + + {{else if not .LatestPullRequest}} {{if .IsIncluded}} {{svg "octicon-git-pull-request"}} {{ctx.Locale.Tr "repo.branch.included"}} diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index c1b00c5f9e..9dadb13510 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -2,11 +2,16 @@
    {{template "repo/header" .}} {{$showDiffBox := false}} + {{$canReadPullRequests := .Permission.CanRead $.UnitTypePullRequests}}

    {{if and $.PageIsComparePull $.IsSigned (not .Repository.IsArchived)}} - {{ctx.Locale.Tr "repo.pulls.compare_changes"}} -
    {{ctx.Locale.Tr "repo.pulls.compare_changes_desc"}}
    + {{if $canReadPullRequests}} + {{ctx.Locale.Tr "repo.pulls.compare_changes"}} +
    {{ctx.Locale.Tr "repo.pulls.compare_changes_desc"}}
    + {{else}} + {{ctx.Locale.Tr "compare.branches.title"}} + {{end}} {{else}} {{ctx.Locale.Tr "action.compare_commits_general"}} {{end}} @@ -166,7 +171,7 @@ {{else}}
    {{ctx.Locale.Tr "repo.pulls.nothing_to_compare_have_tag"}}
    {{end}} - {{else if and .PageIsComparePull (gt .CommitCount 0)}} + {{else if and .PageIsComparePull (gt .CommitCount 0) $canReadPullRequests}} {{if .HasPullRequest}}
    diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index 3c3e8adf94..581aa67659 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -267,13 +267,14 @@ func TestCompareWithPRsDisabled(t *testing.T) { []unit_model.Type{unit_model.TypePullRequests}) require.NoError(t, err) - t.Run("branch view doesn't offer creating PRs", func(t *testing.T) { + t.Run("branch view offer comparing branches", func(t *testing.T) { defer tests.PrintCurrentTest(t)() req := NewRequest(t, "GET", "/user1/repo1/branches") resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - htmlDoc.AssertElement(t, "a[href='/user1/repo1/compare/master...recent-push']", false) + compareLink := htmlDoc.Find("a[href='/user1/repo1/compare/master...recent-push']") + assert.Equal(t, "Compare branches", strings.TrimSpace(compareLink.Text())) }) t.Run("compare doesn't offer local branches", func(t *testing.T) { @@ -290,11 +291,13 @@ func TestCompareWithPRsDisabled(t *testing.T) { } }) - t.Run("comparing against a disabled-PR repo is 404", func(t *testing.T) { + t.Run("comparing against a disabled-PR repo", func(t *testing.T) { defer tests.PrintCurrentTest(t)() req := NewRequest(t, "GET", "/user1/repo1/compare/master...recent-push") - session.MakeRequest(t, req, http.StatusNotFound) + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Equal(t, "Compare branches", strings.TrimSpace(htmlDoc.Find("h2.header").Text())) }) }) } From 7566ebfba7a144a224705fb5a63922d297afc78e Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Sun, 3 Aug 2025 11:55:01 +0200 Subject: [PATCH 226/297] Add ActivityPub Person follow from distant (#8720) This PR is part of #4767. It 1. adds the ability to follow a local person from a distant federation server (see tests/integration/api_activitypub_person_inbox_follow_test.go) 2. streamlines the router code (refactor the person conversion & handling of inbox requests in service direction, unifies service call signature & error handling) 3. introduces queues for decoupling outgoing communication (delivery retry to cope network issues or distant service downtimes) and 4. adds minor fixes to integration tests (test timeout & invalid inbox activities) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8720 Reviewed-by: Earl Warren Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- .deadcode-out | 16 +-- routers/api/v1/activitypub/person.go | 54 +++------ routers/init.go | 2 + services/convert/activitypub_person.go | 62 +++++++++++ services/federation/delivery_queue.go | 76 +++++++++++++ services/federation/federation_service.go | 5 +- services/federation/person_inbox_follow.go | 73 ++++++++++++ services/federation/person_inbox_undo.go | 47 ++++++++ services/federation/person_service.go | 25 +++++ templates/swagger/v1_json.tmpl | 2 +- tests/integration/activitypub_client_test.go | 17 +-- ...pi_activitypub_person_inbox_follow_test.go | 105 ++++++++++++++++++ .../api_activitypub_person_test.go | 4 +- 13 files changed, 422 insertions(+), 66 deletions(-) create mode 100644 services/convert/activitypub_person.go create mode 100644 services/federation/delivery_queue.go create mode 100644 services/federation/person_inbox_follow.go create mode 100644 services/federation/person_inbox_undo.go create mode 100644 services/federation/person_service.go create mode 100644 tests/integration/api_activitypub_person_inbox_follow_test.go diff --git a/.deadcode-out b/.deadcode-out index 31b04687dc..87dc416ff1 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -59,16 +59,12 @@ forgejo.org/models/user IsErrExternalLoginUserAlreadyExist IsErrExternalLoginUserNotExist NewFederatedUser - NewFederatedUserFollower IsErrUserSettingIsNotExist GetUserAllSettings DeleteUserSetting GetFederatedUser GetFederatedUserByUserID GetFollowersForUser - AddFollower - RemoveFollower - IsFollowingAp forgejo.org/modules/activitypub NewContext @@ -99,11 +95,7 @@ forgejo.org/modules/eventsource Event.String forgejo.org/modules/forgefed - NewForgeFollowFromAp NewForgeFollow - ForgeFollow.MarshalJSON - ForgeFollow.UnmarshalJSON - ForgeFollow.Validate NewForgeUndoLike ForgeUndoLike.UnmarshalJSON ForgeUndoLike.Validate @@ -228,7 +220,6 @@ forgejo.org/modules/util/filebuffer forgejo.org/modules/validation IsErrNotValid - ValidateIDExists forgejo.org/modules/web RouteMock @@ -245,11 +236,8 @@ forgejo.org/routers/web/org forgejo.org/services/context GetPrivateContext -forgejo.org/services/federation - NewErrInternalf - ErrInternal.Error - Init - NewServiceResultWithBytes +forgejo.org/services/convert + ToActivityPubPersonFeedItem forgejo.org/services/repository IsErrForkAlreadyExist diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go index 1da7933418..6120a078af 100644 --- a/routers/api/v1/activitypub/person.go +++ b/routers/api/v1/activitypub/person.go @@ -4,14 +4,14 @@ package activitypub import ( - "fmt" "net/http" - "strings" "forgejo.org/modules/activitypub" "forgejo.org/modules/log" - "forgejo.org/modules/setting" + "forgejo.org/modules/web" "forgejo.org/services/context" + "forgejo.org/services/convert" + "forgejo.org/services/federation" ap "github.com/go-ap/activitypub" "github.com/go-ap/jsonld" @@ -34,45 +34,12 @@ func Person(ctx *context.APIContext) { // "200": // "$ref": "#/responses/ActivityPub" - // TODO: the setting.AppURL during the test doesn't follow the definition: "It always has a '/' suffix" - link := fmt.Sprintf("%s/api/v1/activitypub/user-id/%d", strings.TrimSuffix(setting.AppURL, "/"), ctx.ContextUser.ID) - person := ap.PersonNew(ap.IRI(link)) - - person.Name = ap.NaturalLanguageValuesNew() - err := person.Name.Set("en", ap.Content(ctx.ContextUser.FullName)) + person, err := convert.ToActivityPubPerson(ctx, ctx.ContextUser) if err != nil { - ctx.ServerError("Set Name", err) + ctx.ServerError("convert.ToActivityPubPerson", err) return } - person.PreferredUsername = ap.NaturalLanguageValuesNew() - err = person.PreferredUsername.Set("en", ap.Content(ctx.ContextUser.Name)) - if err != nil { - ctx.ServerError("Set PreferredUsername", err) - return - } - - person.URL = ap.IRI(ctx.ContextUser.HTMLURL()) - - person.Icon = ap.Image{ - Type: ap.ImageType, - MediaType: "image/png", - URL: ap.IRI(ctx.ContextUser.AvatarLink(ctx)), - } - - person.Inbox = ap.IRI(link + "/inbox") - person.Outbox = ap.IRI(link + "/outbox") - - person.PublicKey.ID = ap.IRI(link + "#main-key") - person.PublicKey.Owner = ap.IRI(link) - - publicKeyPem, err := activitypub.GetPublicKey(ctx, ctx.ContextUser) - if err != nil { - ctx.ServerError("GetPublicKey", err) - return - } - person.PublicKey.PublicKeyPem = publicKeyPem - binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(person) if err != nil { ctx.ServerError("MarshalJSON", err) @@ -99,8 +66,15 @@ func PersonInbox(ctx *context.APIContext) { // type: integer // required: true // responses: - // "204": + // "202": // "$ref": "#/responses/empty" - ctx.Status(http.StatusNoContent) + form := web.GetForm(ctx) + activity := form.(*ap.Activity) + result, err := federation.ProcessPersonInbox(ctx, ctx.ContextUser, activity) + if err != nil { + ctx.Error(federation.HTTPStatus(err), "PersonInbox", err) + return + } + responseServiceResult(ctx, result) } diff --git a/routers/init.go b/routers/init.go index 90a1cb1e89..9a304527fa 100644 --- a/routers/init.go +++ b/routers/init.go @@ -38,6 +38,7 @@ import ( "forgejo.org/services/auth/source/oauth2" "forgejo.org/services/automerge" "forgejo.org/services/cron" + federation_service "forgejo.org/services/federation" feed_service "forgejo.org/services/feed" indexer_service "forgejo.org/services/indexer" "forgejo.org/services/mailer" @@ -122,6 +123,7 @@ func InitWebInstalled(ctx context.Context) { mailer.NewContext(ctx) mustInit(cache.Init) mustInit(feed_service.Init) + mustInit(federation_service.Init) mustInit(uinotification.Init) mustInitCtx(ctx, archiver.Init) diff --git a/services/convert/activitypub_person.go b/services/convert/activitypub_person.go new file mode 100644 index 0000000000..2c05f8c1c0 --- /dev/null +++ b/services/convert/activitypub_person.go @@ -0,0 +1,62 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "context" + + "forgejo.org/models/activities" + user_model "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + + ap "github.com/go-ap/activitypub" +) + +func ToActivityPubPersonFeedItem(item *activities.FederatedUserActivity) ap.Note { + return ap.Note{ + AttributedTo: ap.IRI(item.ActorURI), + Content: ap.NaturalLanguageValues{{Value: ap.Content(item.NoteContent), Ref: ap.NilLangRef}}, + ID: ap.IRI(item.NoteURL), + URL: ap.IRI(item.OriginalNote), + } +} + +func ToActivityPubPerson(ctx context.Context, user *user_model.User) (*ap.Person, error) { + link := user.APActorID() + person := ap.PersonNew(ap.IRI(link)) + + person.Name = ap.NaturalLanguageValuesNew() + err := person.Name.Set("en", ap.Content(user.FullName)) + if err != nil { + return nil, err + } + + person.PreferredUsername = ap.NaturalLanguageValuesNew() + err = person.PreferredUsername.Set("en", ap.Content(user.Name)) + if err != nil { + return nil, err + } + + person.URL = ap.IRI(user.HTMLURL()) + + person.Icon = ap.Image{ + Type: ap.ImageType, + MediaType: "image/png", + URL: ap.IRI(user.AvatarLink(ctx)), + } + + person.Inbox = ap.IRI(link + "/inbox") + person.Outbox = ap.IRI(link + "/outbox") + + person.PublicKey.ID = ap.IRI(link + "#main-key") + person.PublicKey.Owner = ap.IRI(link) + + publicKeyPem, err := activitypub.GetPublicKey(ctx, user) + if err != nil { + return nil, err + } + person.PublicKey.PublicKeyPem = publicKeyPem + + return person, nil +} diff --git a/services/federation/delivery_queue.go b/services/federation/delivery_queue.go new file mode 100644 index 0000000000..f71467e9f0 --- /dev/null +++ b/services/federation/delivery_queue.go @@ -0,0 +1,76 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "fmt" + "io" + + "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/graceful" + "forgejo.org/modules/log" + "forgejo.org/modules/process" + "forgejo.org/modules/queue" +) + +type deliveryQueueItem struct { + Doer *user.User + InboxURL string + Payload []byte + DeliveryCount int +} + +var deliveryQueue *queue.WorkerPoolQueue[deliveryQueueItem] + +func initDeliveryQueue() error { + deliveryQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "activitypub_inbox_delivery", deliveryQueueHandler) + if deliveryQueue == nil { + return fmt.Errorf("unable to create activitypub_inbox_delivery queue") + } + go graceful.GetManager().RunWithCancel(deliveryQueue) + + return nil +} + +func deliveryQueueHandler(items ...deliveryQueueItem) (unhandled []deliveryQueueItem) { + for _, item := range items { + item.DeliveryCount++ + err := deliverToInbox(item) + if err != nil && item.DeliveryCount < 10 { + unhandled = append(unhandled, item) + } + } + return unhandled +} + +func deliverToInbox(item deliveryQueueItem) error { + ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), + fmt.Sprintf("Delivering an Activity via user[%d] (%s), to %s", item.Doer.ID, item.Doer.Name, item.InboxURL)) + defer finished() + + clientFactory, err := activitypub.GetClientFactory(ctx) + if err != nil { + return err + } + apclient, err := clientFactory.WithKeys(ctx, item.Doer, item.Doer.APActorID()+"#main-key") + if err != nil { + return err + } + + log.Debug("Delivering %s to %s", item.Payload, item.InboxURL) + res, err := apclient.Post(item.Payload, item.InboxURL) + if err != nil { + return err + } + if res.StatusCode >= 400 { + defer res.Body.Close() + body, _ := io.ReadAll(io.LimitReader(res.Body, 16*1024)) + + log.Warn("Delivering to %s failed: %d %s, %v times", item.InboxURL, res.StatusCode, string(body), item.DeliveryCount) + return fmt.Errorf("delivery failed") + } + + return nil +} diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index 36788e725a..ccdb9bbab0 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -23,7 +23,10 @@ import ( ) func Init() error { - return nil + if !setting.Federation.Enabled { + return nil + } + return initDeliveryQueue() } func FindOrCreateFederationHost(ctx context.Context, actorURI string) (*forgefed.FederationHost, error) { diff --git a/services/federation/person_inbox_follow.go b/services/federation/person_inbox_follow.go new file mode 100644 index 0000000000..baa7934ad5 --- /dev/null +++ b/services/federation/person_inbox_follow.go @@ -0,0 +1,73 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "fmt" + "net/http" + + "forgejo.org/models/user" + "forgejo.org/modules/forgefed" + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" + "github.com/go-ap/jsonld" +) + +func processPersonFollow(ctx context.Context, ctxUser *user.User, activity *ap.Activity) (ServiceResult, error) { + follow, err := forgefed.NewForgeFollowFromAp(*activity) + if err != nil { + log.Error("Invalid follow activity: %s", err) + return ServiceResult{}, NewErrNotAcceptablef("Invalid follow activity: %v", err) + } + + actorURI := follow.Actor.GetLink().String() + _, federatedUser, federationHost, err := FindOrCreateFederatedUser(ctx, actorURI) + if err != nil { + log.Error("Error finding or creating federated user (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("Federated user not found: %v", err) + } + + following, err := user.IsFollowingAp(ctx, ctxUser, federatedUser) + if err != nil { + log.Error("forgefed.IsFollowing: %v", err) + return ServiceResult{}, NewErrNotAcceptablef("forgefed.IsFollowing: %v", err) + } + if following { + // If the user is already following, we're good, nothing to do. + log.Trace("Local user[%d] is already following federated user[%d]", ctxUser.ID, federatedUser.ID) + return NewServiceResultStatusOnly(http.StatusNoContent), nil + } + + follower, err := user.AddFollower(ctx, ctxUser, federatedUser) + if err != nil { + log.Error("Unable to add follower: %v", err) + return ServiceResult{}, NewErrNotAcceptablef("Unable to add follower: %v", err) + } + + accept := ap.AcceptNew(ap.IRI(fmt.Sprintf( + "%s#accepts/follow/%d", ctxUser.APActorID(), follower.ID, + )), follow) + accept.Actor = ap.IRI(ctxUser.APActorID()) + payload, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI)).Marshal(accept) + if err != nil { + log.Error("Unable to Marshal JSON: %v", err) + return ServiceResult{}, NewErrInternalf("MarshalJSON: %v", err) + } + + hostURL := federationHost.AsURL() + if err := deliveryQueue.Push(deliveryQueueItem{ + InboxURL: hostURL.JoinPath(federatedUser.InboxPath).String(), + Doer: ctxUser, + Payload: payload, + }); err != nil { + log.Error("Unable to push to pending queue: %v", err) + return ServiceResult{}, NewErrInternalf("Unable to push to pending queue: %v", err) + } + + // Respond back with an accept + result := NewServiceResultWithBytes(http.StatusAccepted, []byte(`{"status":"Accepted"}`)) + return result, nil +} diff --git a/services/federation/person_inbox_undo.go b/services/federation/person_inbox_undo.go new file mode 100644 index 0000000000..4379cf242a --- /dev/null +++ b/services/federation/person_inbox_undo.go @@ -0,0 +1,47 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "net/http" + + "forgejo.org/models/user" + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" +) + +func processPersonInboxUndo(ctx context.Context, ctxUser *user.User, activity *ap.Activity) (ServiceResult, error) { + if activity.Object.GetType() != ap.FollowType { + log.Error("Invalid object type for Undo activity: %v", activity.Object.GetType()) + return ServiceResult{}, NewErrNotAcceptablef("Invalid object type for Undo activity: %v", activity.Object.GetType()) + } + + actorURI := activity.Actor.GetLink().String() + _, federatedUser, _, err := findFederatedUser(ctx, actorURI) + if err != nil { + log.Error("User not found: %v", err) + return ServiceResult{}, NewErrInternalf("User not found: %v", err) + } + + if federatedUser != nil { + following, err := user.IsFollowingAp(ctx, ctxUser, federatedUser) + if err != nil { + log.Error("forgefed.IsFollowing: %v", err) + return ServiceResult{}, NewErrInternalf("forgefed.IsFollowing: %v", err) + } + if !following { + // The local user is not following the federated one, nothing to do. + log.Trace("Local user[%d] is not following federated user[%d]", ctxUser.ID, federatedUser.ID) + return NewServiceResultStatusOnly(http.StatusNoContent), nil + } + if err := user.RemoveFollower(ctx, ctxUser, federatedUser); err != nil { + log.Error("Unable to remove follower", err) + return ServiceResult{}, NewErrInternalf("Unable to remove follower: %v", err) + } + } + + return NewServiceResultStatusOnly(http.StatusNoContent), nil +} diff --git a/services/federation/person_service.go b/services/federation/person_service.go new file mode 100644 index 0000000000..f67d2b492d --- /dev/null +++ b/services/federation/person_service.go @@ -0,0 +1,25 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + + "forgejo.org/models/user" + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" +) + +func ProcessPersonInbox(ctx context.Context, user *user.User, activity *ap.Activity) (ServiceResult, error) { + switch activity.Type { + case ap.FollowType: + return processPersonFollow(ctx, user, activity) + case ap.UndoType: + return processPersonInboxUndo(ctx, user, activity) + } + + log.Error("Unsupported PersonInbox activity: %v", activity.Type) + return ServiceResult{}, NewErrNotAcceptablef("unsupported activity: %v", activity.Type) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index a610620eab..492f9487f3 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -162,7 +162,7 @@ } ], "responses": { - "204": { + "202": { "$ref": "#/responses/empty" } } diff --git a/tests/integration/activitypub_client_test.go b/tests/integration/activitypub_client_test.go index 2adb8304c2..67482a7277 100644 --- a/tests/integration/activitypub_client_test.go +++ b/tests/integration/activitypub_client_test.go @@ -6,14 +6,15 @@ package integration import ( "net/url" "testing" + "time" - "forgejo.org/models/db" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/activitypub" "forgejo.org/modules/setting" "forgejo.org/modules/test" "forgejo.org/routers" + "forgejo.org/services/contexttest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -25,15 +26,15 @@ func TestActivityPubClientBodySize(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - - clientFactory, err := activitypub.GetClientFactory(db.DefaultContext) - require.NoError(t, err) - - apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.KeyID()) - require.NoError(t, err) - url := u.JoinPath("/api/v1/nodeinfo").String() + ctx, _ := contexttest.MockAPIContext(t, url) + clientFactory, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + apClient, err := clientFactory.WithKeys(ctx, user1, user1.KeyID()) + require.NoError(t, err) + // Request with normal MaxSize t.Run("NormalMaxSize", func(t *testing.T) { resp, err := apClient.GetBody(url) diff --git a/tests/integration/api_activitypub_person_inbox_follow_test.go b/tests/integration/api_activitypub_person_inbox_follow_test.go new file mode 100644 index 0000000000..f171b8951f --- /dev/null +++ b/tests/integration/api_activitypub_person_inbox_follow_test.go @@ -0,0 +1,105 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + "forgejo.org/services/federation" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Flow of this test is documented at: https://codeberg.org/forgejo-contrib/federation/src/branch/main/doc/user-activity-following.md +func TestActivityPubPersonInboxFollow(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + federation.Init() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, localUrl *url.URL) { + defer test.MockVariableValue(&setting.AppURL, localUrl.String())() + + distantURL := federatedSrv.URL + distantUser15URL := fmt.Sprintf("%s/api/v1/activitypub/user-id/15", distantURL) + + localUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + localUser2URL := localUrl.JoinPath("/api/v1/activitypub/user-id/2").String() + localUser2Inbox := localUrl.JoinPath("/api/v1/activitypub/user-id/2/inbox").String() + + ctx, _ := contexttest.MockAPIContext(t, localUser2Inbox) + + // distant follows local + followActivity := []byte(fmt.Sprintf( + `{"type":"Follow",`+ + `"actor":"%s",`+ + `"object":"%s"}`, + distantUser15URL, + localUser2URL, + )) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + c, err := cf.WithKeysDirect(ctx, mock.ApActor.PrivKey, + mock.ApActor.KeyID(federatedSrv.URL)) + require.NoError(t, err) + resp, err := c.Post(followActivity, localUser2Inbox) + require.NoError(t, err) + assert.Equal(t, http.StatusAccepted, resp.StatusCode) + + // local follow exists + distantFederatedUser := unittest.AssertExistsAndLoadBean(t, &user_model.FederatedUser{ExternalID: "15"}) + unittest.AssertExistsAndLoadBean(t, + &user_model.FederatedUserFollower{ + FollowedUserID: localUser.ID, + FollowingUserID: distantFederatedUser.UserID, + }, + ) + + // distant is informed about accepting follow + assert.Contains(t, mock.LastPost, "\"type\":\"Accept\"") + + // distant undoes follow + undoFollowActivity := []byte(fmt.Sprintf( + `{"type":"Undo",`+ + `"actor":"%s",`+ + `"object":{"type":"Follow",`+ + `"actor":"%s",`+ + `"object":"%s"}}`, + distantUser15URL, + distantUser15URL, + localUser2URL, + )) + c, err = cf.WithKeysDirect(ctx, mock.ApActor.PrivKey, + mock.ApActor.KeyID(federatedSrv.URL)) + require.NoError(t, err) + resp, err = c.Post(undoFollowActivity, localUser2Inbox) + require.NoError(t, err) + assert.Equal(t, http.StatusNoContent, resp.StatusCode) + + // local follow removed + unittest.AssertNotExistsBean(t, + &user_model.FederatedUserFollower{ + FollowedUserID: localUser.ID, + FollowingUserID: distantFederatedUser.UserID, + }, + ) + }) +} diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go index 277b150a1e..04d1fb1648 100644 --- a/tests/integration/api_activitypub_person_test.go +++ b/tests/integration/api_activitypub_person_test.go @@ -105,9 +105,9 @@ func TestActivityPubPersonInbox(t *testing.T) { c, err := cf.WithKeys(ctx, user1, user1url) require.NoError(t, err) - // Signed request "succeeds" + // invalid request is rejected resp, err := c.Post([]byte{}, user2inboxurl) require.NoError(t, err) - assert.Equal(t, http.StatusNoContent, resp.StatusCode) + assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) }) } From e50cfc849936e6ea25d13c43a9c85add9f009542 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 3 Aug 2025 22:14:13 +0200 Subject: [PATCH 227/297] fix: migrate new Github release assets (#8771) - It seems that mid-May (according to some system notices in Codeberg) Github started (or converted all) release asssets to be made available under `https://release-assets.githubusercontent.com/`. - Update the migration code to allow this baseURL for Github release assets. - Resolves Codeberg/Community#2061 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8771 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- services/migrations/github.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/migrations/github.go b/services/migrations/github.go index 9721c86180..317eb568b5 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -364,7 +364,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease) // Prevent open redirect if !hasBaseURL(redirectURL, g.baseURL) && - !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") { + !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") && + !hasBaseURL(redirectURL, "https://release-assets.githubusercontent.com/") { WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL) return io.NopCloser(strings.NewReader(redirectURL)), nil From 15231669e6e85ef1c1d300ebe7f93d1311f136d1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 4 Aug 2025 07:24:16 +0200 Subject: [PATCH 228/297] Update renovate to v41.51.1 (forgejo) (#8775) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index c2923f7d0e..1b5d05ceef 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:41.43.5 + image: data.forgejo.org/renovate/renovate:41.51.1 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index c9e3f17402..8e3341b33b 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.35.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@41.43.5 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@41.51.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From f2ffae12cfd2ac4be0f151ba3c8f4c21af6021cb Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 4 Aug 2025 09:16:38 +0200 Subject: [PATCH 229/297] Update https://data.forgejo.org/forgejo/forgejo-build-publish action to v5.4.1 (forgejo) (#8776) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [https://data.forgejo.org/forgejo/forgejo-build-publish](https://data.forgejo.org/forgejo/forgejo-build-publish) | action | minor | `v5.3.5` -> `v5.4.1` | --- ### Release Notes
    forgejo/forgejo-build-publish (https://data.forgejo.org/forgejo/forgejo-build-publish) ### [`v5.4.1`](https://data.forgejo.org/forgejo/forgejo-build-publish/compare/v5.4.0...v5.4.1) [Compare Source](https://data.forgejo.org/forgejo/forgejo-build-publish/compare/v5.4.0...v5.4.1) ### [`v5.4.0`](https://data.forgejo.org/forgejo/forgejo-build-publish/compare/v5.3.5...v5.4.0) [Compare Source](https://data.forgejo.org/forgejo/forgejo-build-publish/compare/v5.3.5...v5.4.0)
    --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), 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. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8776 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/build-release.yml | 4 ++-- .forgejo/workflows/publish-release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index 944fd68fb7..c0948781c1 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -164,7 +164,7 @@ jobs: - name: build container & release if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.4.1 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" @@ -183,7 +183,7 @@ jobs: - name: build rootless container if: ${{ secrets.TOKEN != '' }} - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.3.5 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/build@v5.4.1 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 3aec46fb03..91fa03ab8e 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -44,7 +44,7 @@ jobs: - uses: https://data.forgejo.org/actions/checkout@v4 - name: copy & sign - uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.5 + uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.4.1 with: from-forgejo: ${{ vars.FORGEJO }} to-forgejo: ${{ vars.FORGEJO }} From eb18fccac57137e9f99ef687427284494a8d708b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 4 Aug 2025 09:21:41 +0200 Subject: [PATCH 230/297] Lock file maintenance (forgejo) (#8777) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on Monday ( * 0-3 * * 1 ) (UTC), 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. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8777 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 428 ++++++++++++----------------- web_src/fomantic/package-lock.json | 12 +- 2 files changed, 188 insertions(+), 252 deletions(-) diff --git a/package-lock.json b/package-lock.json index 54cb1d7ac7..1ab1271cae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -504,6 +504,50 @@ "@csstools/css-tokenizer": "^3.0.4" } }, + "node_modules/@csstools/selector-resolve-nested": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", + "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", @@ -2261,9 +2305,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.0.tgz", - "integrity": "sha512-9f3nSTFI2ivfxc7/tHBHcJ8pRnp8ROrELvsVprlQPVvcZ+j5zztYd+PTJGpyIOAdTvNwNrpCXswKSeoQcyGjMQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", "cpu": [ "arm" ], @@ -2275,9 +2319,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.0.tgz", - "integrity": "sha512-tFZSEhqJ8Yrpe50TzOdeoYi72gi/jsnT7y8Qrozf3cNu28WX+s6I3XzEPUAqoaT9SAS8Xz9AzGTFlxxCH/w20w==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", "cpu": [ "arm64" ], @@ -2289,9 +2333,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.0.tgz", - "integrity": "sha512-+DikIIs+p6yU2hF51UaWG8BnHbq90X0QIOt5zqSKSZxY+G3qqdLih214e9InJal21af2PuuxkDectetGfbVPJw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", "cpu": [ "arm64" ], @@ -2303,9 +2347,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.0.tgz", - "integrity": "sha512-5a+NofhdEB/WimSlFMskbFQn1vqz1FWryYpA99trmZGO6qEmiS0IsX6w4B3d91U878Q2ZQdiaFF1gxX4P147og==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", "cpu": [ "x64" ], @@ -2317,9 +2361,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.0.tgz", - "integrity": "sha512-igr/RlKPS3OCy4jD3XBmAmo3UAcNZkJSubRsw1JeM8bAbwf15k/3eMZXD91bnjheijJiOJcga3kfCLKjV8IXNg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", "cpu": [ "arm64" ], @@ -2331,9 +2375,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.0.tgz", - "integrity": "sha512-MdigWzPSHlQzB1xZ+MdFDWTAH+kcn7UxjEBoOKuaso7z1DRlnAnrknB1mTtNOQ+GdPI8xgExAGwHeqQjntR0Cg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", "cpu": [ "x64" ], @@ -2345,9 +2389,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.0.tgz", - "integrity": "sha512-dmZseE0ZwA/4yy1+BwFrDqFTjjNg24GO9xSrb1weVbt6AFkhp5pz1gVS7IMtfIvoWy8yp6q/zN0bKnefRUImvQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", "cpu": [ "arm" ], @@ -2359,9 +2403,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.0.tgz", - "integrity": "sha512-fzhfn6p9Cfm3W8UrWKIa4l7Wfjs/KGdgaswMBBE3KY3Ta43jg2XsPrAtfezHpsRk0Nx+TFuS3hZk/To2N5kFPQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", "cpu": [ "arm" ], @@ -2373,9 +2417,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.0.tgz", - "integrity": "sha512-vVDD+iPDPmJQ5nAQ5Tifq3ywdv60FartglFI8VOCK+hcU9aoG0qlQTsDJP97O5yiTaTqlneZWoARMcVC5nyUoQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", "cpu": [ "arm64" ], @@ -2387,9 +2431,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.0.tgz", - "integrity": "sha512-0d0jx08fzDHCzXqrtCMEEyxKU0SvJrWmUjUDE2/KDQ2UDJql0tfiwYvEx1oHELClKO8CNdE+AGJj+RqXscZpdQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", "cpu": [ "arm64" ], @@ -2401,9 +2445,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.0.tgz", - "integrity": "sha512-XBYu9oW9eKJadWn8M7hkTZsD4yG+RrsTrVEgyKwb4L72cpJjRbRboTG9Lg9fec8MxJp/cfTHAocg4mnismQR8A==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", "cpu": [ "loong64" ], @@ -2415,9 +2459,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.0.tgz", - "integrity": "sha512-wJaRvcT17PoOK6Ggcfo3nouFlybHvARBS4jzT0PC/lg17fIJHcDS2fZz3sD+iA4nRlho2zE6OGbU0HvwATdokQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", "cpu": [ "ppc64" ], @@ -2429,9 +2473,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.0.tgz", - "integrity": "sha512-GZ5bkMFteAGkcmh8x0Ok4LSa+L62Ez0tMsHPX6JtR0wl4Xc3bQcrFHDiR5DGLEDFtGrXih4Nd/UDaFqs968/wA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", "cpu": [ "riscv64" ], @@ -2443,9 +2487,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.0.tgz", - "integrity": "sha512-7CjPw6FflFsVOUfWOrVrREiV3IYXG4RzZ1ZQUaT3BtSK8YXN6x286o+sruPZJESIaPebYuFowmg54ZdrkVBYog==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", "cpu": [ "riscv64" ], @@ -2457,9 +2501,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.0.tgz", - "integrity": "sha512-nmvnl0ZiuysltcB/cKjUh40Rx4FbSyueERDsl2FLvLYr6pCgSsvGr3SocUT84svSpmloS7f1DRWqtRha74Gi1w==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", "cpu": [ "s390x" ], @@ -2471,9 +2515,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.0.tgz", - "integrity": "sha512-Cv+moII5C8RM6gZbR3cb21o6rquVDZrN2o81maROg1LFzBz2dZUwIQSxFA8GtGZ/F2KtsqQ2z3eFPBb6akvQNg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", "cpu": [ "x64" ], @@ -2485,9 +2529,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.0.tgz", - "integrity": "sha512-PHcMG8DZTM9RCIjp8QIfN0VYtX0TtBPnWOTRurFhoCDoi9zptUZL2k7pCs+5rgut7JAiUsYy+huyhVKPcmxoog==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", "cpu": [ "x64" ], @@ -2499,9 +2543,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.0.tgz", - "integrity": "sha512-1SI/Rd47e8aQJeFWMDg16ET+fjvCcD/CzeaRmIEPmb05hx+3cCcwIF4ebUag4yTt/D1peE+Mgp0+Po3M358cAA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", "cpu": [ "arm64" ], @@ -2513,9 +2557,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.0.tgz", - "integrity": "sha512-JwOCYxmumFDfDhx4kNyz6kTVK3gWzBIvVdMNzQMRDubcoGRDniOOmo6DDNP42qwZx3Bp9/6vWJ+kNzNqXoHmeA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", "cpu": [ "ia32" ], @@ -2527,9 +2571,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.0.tgz", - "integrity": "sha512-IPMIfrfkG1GaEXi+JSsQEx8x9b4b+hRZXO7KYc2pKio3zO2/VDXDs6B9Ts/nnO+25Fk1tdAVtUn60HKKPPzDig==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", "cpu": [ "x64" ], @@ -3132,20 +3176,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/@stylistic/stylelint-plugin/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@swc/helpers": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.2.14.tgz", @@ -3273,9 +3303,9 @@ "license": "MIT" }, "node_modules/@types/d3-dispatch": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", - "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", "license": "MIT" }, "node_modules/@types/d3-drag": { @@ -5041,13 +5071,13 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.29", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } @@ -5337,9 +5367,9 @@ } }, "node_modules/cacheable/node_modules/keyv": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.4.0.tgz", - "integrity": "sha512-TMckyVjEoacG5IteUpUrOBsFORtheqziVyyY2dLUwg1jwTb8u48LX4TgmtogkNl9Y9unaEJ1luj10fGyjMGFOQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.0.tgz", + "integrity": "sha512-QG7qR2tijh1ftOvClut4YKKg1iW6cx3GZsKoGyJPxHkGWK9oJhG9P3j5deP0QQOGDowBMVQFaP+Vm4NpGYvmIQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5415,9 +5445,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "funding": [ { "type": "opencollective", @@ -6068,9 +6098,9 @@ "license": "MIT" }, "node_modules/cytoscape": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.1.tgz", - "integrity": "sha512-dbeqFTLYEwlFg7UGtcZhCCG/2WayX72zK3Sq323CEX29CY81tYfVhw1MIdduCtpstB0cTOhJswWlM/OEB3Xp+Q==", + "version": "3.33.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.0.tgz", + "integrity": "sha512-2d2EwwhaxLWC8ahkH1PpQwCyu6EY3xDRdcEJXrLTb4fOUtVc+YWQalHU67rFS1a6ngj1fgv9dQLtJxP/KAFZEw==", "license": "MIT", "engines": { "node": ">=0.10" @@ -6992,9 +7022,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.191", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz", - "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==", + "version": "1.5.194", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz", + "integrity": "sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7783,10 +7813,10 @@ "vue-eslint-parser": ">=7.1.0" } }, - "node_modules/eslint-plugin-vue-scoped-css/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "node_modules/eslint-plugin-vue/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { @@ -12227,19 +12257,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-scope": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", @@ -12255,19 +12272,6 @@ "postcss": "^8.1.0" } }, - "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-modules-values": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", @@ -12308,6 +12312,19 @@ "postcss": "^8.2.14" } }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-nesting": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", @@ -12335,63 +12352,6 @@ "postcss": "^8.4" } }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", - "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/postcss-resolve-nested-selector": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", @@ -12444,9 +12404,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -13963,29 +13923,6 @@ "stylelint": ">=16" } }, - "node_modules/stylelint/node_modules/@csstools/selector-specificity": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", - "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^7.0.0" - } - }, "node_modules/stylelint/node_modules/file-entry-cache": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.3.tgz", @@ -14074,20 +14011,6 @@ "postcss": "^8.4.31" } }, - "node_modules/stylelint/node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/stylelint/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -14414,6 +14337,19 @@ } } }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/tapable": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", @@ -14944,9 +14880,9 @@ } }, "node_modules/typo-js": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.5.tgz", - "integrity": "sha512-F45vFWdGX8xahIk/sOp79z2NJs8ETMYsmMChm9D5Hlx3+9j7VnCyQyvij5MOCrNY3NNe8noSyokRjQRfq+Bc7A==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.3.0.tgz", + "integrity": "sha512-ERVoYp5PG7jpj7+kzKmFZfp5+IYMOOQM3etNnlYu06Z2CL5UmC9ekT8Z/JVYpR7e7sXB4bOFq1fz/budv++Z0g==", "license": "BSD-3-Clause" }, "node_modules/uc.micro": { @@ -15326,9 +15262,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.46.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.0.tgz", - "integrity": "sha512-ONmkT3Ud3IfW15nl7l4qAZko5/2iZ5ALVBDh02ZSZ5IGVLJSYkRcRa3iB58VyEIyoofs9m2xdVrm+lTi97+3pw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "dev": true, "license": "MIT", "dependencies": { @@ -15342,26 +15278,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.46.0", - "@rollup/rollup-android-arm64": "4.46.0", - "@rollup/rollup-darwin-arm64": "4.46.0", - "@rollup/rollup-darwin-x64": "4.46.0", - "@rollup/rollup-freebsd-arm64": "4.46.0", - "@rollup/rollup-freebsd-x64": "4.46.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.46.0", - "@rollup/rollup-linux-arm-musleabihf": "4.46.0", - "@rollup/rollup-linux-arm64-gnu": "4.46.0", - "@rollup/rollup-linux-arm64-musl": "4.46.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.46.0", - "@rollup/rollup-linux-ppc64-gnu": "4.46.0", - "@rollup/rollup-linux-riscv64-gnu": "4.46.0", - "@rollup/rollup-linux-riscv64-musl": "4.46.0", - "@rollup/rollup-linux-s390x-gnu": "4.46.0", - "@rollup/rollup-linux-x64-gnu": "4.46.0", - "@rollup/rollup-linux-x64-musl": "4.46.0", - "@rollup/rollup-win32-arm64-msvc": "4.46.0", - "@rollup/rollup-win32-ia32-msvc": "4.46.0", - "@rollup/rollup-win32-x64-msvc": "4.46.0", + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" } }, diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index 073339afc6..1351785107 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -1249,9 +1249,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "funding": [ { "type": "opencollective", @@ -2005,9 +2005,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.191", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz", - "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==", + "version": "1.5.194", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz", + "integrity": "sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==", "license": "ISC" }, "node_modules/emoji-regex": { From cc31b744d18820b5e709cebafa8c57220ed475b0 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 5 Aug 2025 02:41:36 +0200 Subject: [PATCH 231/297] Update dependency mini-css-extract-plugin to v2.9.3 (forgejo) (#8782) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8782 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ab1271cae..93396a9ed5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "jquery": "3.7.1", "katex": "0.16.22", "mermaid": "11.8.1", - "mini-css-extract-plugin": "2.9.2", + "mini-css-extract-plugin": "2.9.3", "minimatch": "10.0.3", "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", @@ -11274,9 +11274,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", - "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.3.tgz", + "integrity": "sha512-tRA0+PsS4kLVijnN1w9jUu5lkxBwUk9E8SbgEB5dBJqchE6pVYdawROG6uQtpmAri7tdCK9i7b1bULeVWqS6Ag==", "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", diff --git a/package.json b/package.json index 0966c570cf..22b8b3386c 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "jquery": "3.7.1", "katex": "0.16.22", "mermaid": "11.8.1", - "mini-css-extract-plugin": "2.9.2", + "mini-css-extract-plugin": "2.9.3", "minimatch": "10.0.3", "monaco-editor": "0.52.2", "monaco-editor-webpack-plugin": "7.1.0", From 0a444a374ed37b15e5fd3a21d9a1a86f9d889819 Mon Sep 17 00:00:00 2001 From: John Moon Date: Tue, 5 Aug 2025 17:00:35 +0200 Subject: [PATCH 232/297] fix: add .forgejo/CODEOWNERS support (#8773) Currently, the documentation mention that a CODEOWNERS file can be located in .forgejo for code owner PR review assignment, but this does not work. Add support for this location. Resolves #8746 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8773 Reviewed-by: Gusted Co-authored-by: John Moon Co-committed-by: John Moon --- routers/web/repo/view.go | 2 +- services/issue/pull.go | 2 +- tests/integration/codeowner_test.go | 297 +++++++++++++++------------- 3 files changed, 159 insertions(+), 142 deletions(-) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 36791fc770..f317f1e549 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -438,7 +438,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { if workFlowErr != nil { ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) } - } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { + } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"}, ctx.Repo.TreePath) { if rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err == nil { _, warnings := issue_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize) if len(warnings) > 0 { diff --git a/services/issue/pull.go b/services/issue/pull.go index 2eef1fbfa8..6245344ccb 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -71,7 +71,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, } var rules []*issues_model.CodeOwnerRule - for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} { + for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"} { if blob, err := commit.GetBlobByPath(file); err == nil { rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize) if err == nil { diff --git a/tests/integration/codeowner_test.go b/tests/integration/codeowner_test.go index e2eeb843d8..b85a5f213d 100644 --- a/tests/integration/codeowner_test.go +++ b/tests/integration/codeowner_test.go @@ -26,175 +26,192 @@ import ( "github.com/stretchr/testify/require" ) -func TestCodeOwner(t *testing.T) { - onGiteaRun(t, func(t *testing.T, u *url.URL) { - user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) +func CodeOwnerTestCommon(t *testing.T, u *url.URL, codeownerTest CodeownerTest) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - // Create the repo. - repo, _, f := tests.CreateDeclarativeRepo(t, user2, "", - []unit_model.Type{unit_model.TypePullRequests}, nil, - []*files_service.ChangeRepoFile{ - { - Operation: "create", - TreePath: "CODEOWNERS", - ContentReader: strings.NewReader("README.md @user5\ntest-file @user4"), - }, + // Create the repo. + repo, _, f := tests.CreateDeclarativeRepo(t, user2, codeownerTest.Name, + []unit_model.Type{unit_model.TypePullRequests}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: codeownerTest.Path, + ContentReader: strings.NewReader("README.md @user5\ntest-file @user4"), }, - ) - defer f() + }, + ) + defer f() - dstPath := t.TempDir() - r := fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name) - cloneURL, _ := url.Parse(r) - cloneURL.User = url.UserPassword("user2", userPassword) - require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + dstPath := t.TempDir() + r := fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name) + cloneURL, _ := url.Parse(r) + cloneURL.User = url.UserPassword("user2", userPassword) + require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) - t.Run("Normal", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - err := os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o666) - require.NoError(t, err) + err := os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o666) + require.NoError(t, err) - err = git.AddChanges(dstPath, true) - require.NoError(t, err) + err = git.AddChanges(dstPath, true) + require.NoError(t, err) - err = git.CommitChanges(dstPath, git.CommitChangesOptions{ - Committer: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Author: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Message: "Add README.", - }) - require.NoError(t, err) - - err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-normal").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) - - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-normal"}) - unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add README.", }) + require.NoError(t, err) - t.Run("Forked repository", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-normal").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - session := loginUser(t, "user1") - testRepoFork(t, session, user2.Name, repo.Name, "user1", "repo1") + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-normal"}) + unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"}) + t.Run("Forked repository", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - r := fmt.Sprintf("%suser1/repo1.git", u.String()) - remoteURL, _ := url.Parse(r) - remoteURL.User = url.UserPassword("user2", userPassword) - doGitAddRemote(dstPath, "forked", remoteURL)(t) + session := loginUser(t, "user1") + testRepoFork(t, session, user2.Name, repo.Name, "user1", codeownerTest.Name) - err := git.NewCommand(git.DefaultContext, "push", "forked", "HEAD:refs/for/main", "-o", "topic=codeowner-forked").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: codeownerTest.Name}) - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-forked"}) - unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + r := fmt.Sprintf("%suser1/%s.git", u.String(), codeownerTest.Name) + remoteURL, _ := url.Parse(r) + remoteURL.User = url.UserPassword("user2", userPassword) + doGitAddRemote(dstPath, "forked", remoteURL)(t) + + err := git.NewCommand(git.DefaultContext, "push", "forked", "HEAD:refs/for/main", "-o", "topic=codeowner-forked").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-forked"}) + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) + + t.Run("Out of date", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Push the changes made from the previous subtest. + require.NoError(t, git.NewCommand(git.DefaultContext, "push", "origin").Run(&git.RunOpts{Dir: dstPath})) + + // Reset the tree to the previous commit. + require.NoError(t, git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})) + + err := os.WriteFile(path.Join(dstPath, "test-file"), []byte("## test content"), 0o666) + require.NoError(t, err) + + err = git.AddChanges(dstPath, true) + require.NoError(t, err) + + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add test-file.", }) + require.NoError(t, err) - t.Run("Out of date", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-out-of-date").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - // Push the changes made from the previous subtest. - require.NoError(t, git.NewCommand(git.DefaultContext, "push", "origin").Run(&git.RunOpts{Dir: dstPath})) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-out-of-date"}) + unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) + t.Run("From a forked repository", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - // Reset the tree to the previous commit. - require.NoError(t, git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})) + session := loginUser(t, "user1") - err := os.WriteFile(path.Join(dstPath, "test-file"), []byte("## test content"), 0o666) - require.NoError(t, err) + r := fmt.Sprintf("%suser1/%s.git", u.String(), codeownerTest.Name) + remoteURL, _ := url.Parse(r) + remoteURL.User = url.UserPassword("user1", userPassword) + doGitAddRemote(dstPath, "forked-2", remoteURL)(t) - err = git.AddChanges(dstPath, true) - require.NoError(t, err) + err := git.NewCommand(git.DefaultContext, "push", "forked-2", "HEAD:branch").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - err = git.CommitChanges(dstPath, git.CommitChangesOptions{ - Committer: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Author: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Message: "Add test-file.", - }) - require.NoError(t, err) - - err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-out-of-date").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) - - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-out-of-date"}) - unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) - unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + req := NewRequestWithValues(t, "POST", repo.FullName()+"/compare/main...user1/"+codeownerTest.Name+":branch", map[string]string{ + "_csrf": GetCSRF(t, session, repo.FullName()+"/compare/main...user1/"+codeownerTest.Name+":branch"), + "title": "pull request", }) - t.Run("From a forked repository", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + session.MakeRequest(t, req, http.StatusOK) - session := loginUser(t, "user1") + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "branch"}) + unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) + }) - r := fmt.Sprintf("%suser1/repo1.git", u.String()) - remoteURL, _ := url.Parse(r) - remoteURL.User = url.UserPassword("user1", userPassword) - doGitAddRemote(dstPath, "forked-2", remoteURL)(t) + t.Run("Codeowner user with no permission", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - err := git.NewCommand(git.DefaultContext, "push", "forked-2", "HEAD:branch").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) + // Make repository private, only user2 (owner of repository) has now access to this repository. + repo.IsPrivate = true + _, err := db.GetEngine(db.DefaultContext).Cols("is_private").Update(repo) + require.NoError(t, err) - req := NewRequestWithValues(t, "POST", repo.FullName()+"/compare/main...user1/repo1:branch", map[string]string{ - "_csrf": GetCSRF(t, session, repo.FullName()+"/compare/main...user1/repo1:branch"), - "title": "pull request", - }) - session.MakeRequest(t, req, http.StatusOK) + err = os.WriteFile(path.Join(dstPath, "README.md"), []byte("## very sensitive info"), 0o666) + require.NoError(t, err) - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "branch"}) - unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) + err = git.AddChanges(dstPath, true) + require.NoError(t, err) + + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add secrets to the README.", }) + require.NoError(t, err) - t.Run("Codeowner user with no permission", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-private").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - // Make repository private, only user2 (owner of repository) has now access to this repository. - repo.IsPrivate = true - _, err := db.GetEngine(db.DefaultContext).Cols("is_private").Update(repo) - require.NoError(t, err) - - err = os.WriteFile(path.Join(dstPath, "README.md"), []byte("## very sensitive info"), 0o666) - require.NoError(t, err) - - err = git.AddChanges(dstPath, true) - require.NoError(t, err) - - err = git.CommitChanges(dstPath, git.CommitChangesOptions{ - Committer: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Author: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Message: "Add secrets to the README.", - }) - require.NoError(t, err) - - err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-private").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) - - // In CODEOWNERS file the codeowner for README.md is user5, but does not have access to this private repository. - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-private"}) - unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) - }) + // In CODEOWNERS file the codeowner for README.md is user5, but does not have access to this private repository. + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-private"}) + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) +} + +type CodeownerTest struct { + Name string + Path string +} + +func TestCodeOwner(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + tests := []CodeownerTest{ + {Name: "root", Path: "CODEOWNERS"}, + {Name: "docs", Path: "docs/CODEOWNERS"}, + {Name: "gitea", Path: ".gitea/CODEOWNERS"}, + {Name: "forgejo", Path: ".forgejo/CODEOWNERS"}, + } + for _, test := range tests { + CodeOwnerTestCommon(t, u, test) + } }) } From 5294cff95f43a1ded4e8d160605e35793a9aa5a8 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 6 Aug 2025 01:30:20 +0200 Subject: [PATCH 233/297] Update module github.com/alecthomas/chroma/v2 to v2.20.0 (forgejo) (#8783) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8783 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 8 +++--- modules/indexer/code/search_test.go | 12 ++++---- modules/markup/html_test.go | 40 +++++++++++++------------- modules/markup/orgmode/orgmode_test.go | 8 +++--- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index 8850e3e17c..d1a6e17532 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/ProtonMail/go-crypto v1.3.0 github.com/PuerkitoBio/goquery v1.10.3 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 - github.com/alecthomas/chroma/v2 v2.19.0 + github.com/alecthomas/chroma/v2 v2.20.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.5.2 github.com/buildkite/terminal-to-html/v3 v3.16.8 diff --git a/go.sum b/go.sum index 475c1f345d..2231ea9fa6 100644 --- a/go.sum +++ b/go.sum @@ -62,11 +62,11 @@ github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/chroma/v2 v2.19.0 h1:Im+SLRgT8maArxv81mULDWN8oKxkzboH07CHesxElq4= -github.com/alecthomas/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= +github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= diff --git a/modules/indexer/code/search_test.go b/modules/indexer/code/search_test.go index e542b38c24..2413ddec0b 100644 --- a/modules/indexer/code/search_test.go +++ b/modules/indexer/code/search_test.go @@ -86,9 +86,9 @@ func TestHighlightSearchResultCode(t *testing.T) { Range: [][3]int{{1, 14, 23}}, Code: "func main() {\n\tfmt.Println(\"mark this\")\n}", Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this")", - "}", + "func main() {", + "\tfmt.Println("mark this")", + "}", }, }, { @@ -97,9 +97,9 @@ func TestHighlightSearchResultCode(t *testing.T) { Range: [][3]int{{1, 14, 28}}, Code: "func main() {\n\tfmt.Println(\"mark this 😊\")\n}", Result: []template.HTML{ - "func main() {", - "\tfmt.Println("mark this 😊")", - "}", + "func main() {", + "\tfmt.Println("mark this 😊")", + "}", }, }, } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 2bc929bb04..6bc0f7e56c 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -743,11 +743,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -777,11 +777,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -856,11 +856,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -887,11 +887,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -920,11 +920,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -945,11 +945,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -976,11 +976,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1001,11 +1001,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1026,11 +1026,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ @@ -1129,11 +1129,11 @@ func TestRender_FilePreview(t *testing.T) { ``+ ``+ ``+ - `B`+"\n"+``+ + `B`+"\n"+``+ ``+ ``+ ``+ - `C`+"\n"+``+ + `C`+"\n"+``+ ``+ ``+ ``+ diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index cdaa9f18ce..71157dc7c7 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -152,9 +152,9 @@ func HelloWorld() { } #+end_src `, `
    -
    // HelloWorld prints "Hello World"
    -func HelloWorld() {
    -	fmt.Println("Hello World")
    -}
    +
    // HelloWorld prints "Hello World"
    +func HelloWorld() {
    +	fmt.Println("Hello World")
    +}
    `) } From 56f4671c82a73b2af13aaa2b8f7aca020848bb95 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 6 Aug 2025 00:04:22 +0000 Subject: [PATCH 234/297] Update module code.forgejo.org/forgejo/runner/v9 to v9.0.3 --- go.mod | 17 +++++++++-------- go.sum | 40 ++++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index d1a6e17532..8ded0c003a 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( code.forgejo.org/forgejo/go-rpmutils v1.0.0 code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/reply v1.0.2 - code.forgejo.org/forgejo/runner/v9 v9.0.2 + code.forgejo.org/forgejo/runner/v9 v9.0.3 code.forgejo.org/go-chi/binding v1.0.1 code.forgejo.org/go-chi/cache v1.0.1 code.forgejo.org/go-chi/captcha v1.0.2 @@ -144,13 +144,14 @@ require ( github.com/blevesearch/zapx/v14 v14.4.2 // indirect github.com/blevesearch/zapx/v15 v15.4.2 // indirect github.com/blevesearch/zapx/v16 v16.2.4 // indirect + github.com/bmatcuk/doublestar/v4 v4.8.0 // indirect github.com/boombuler/barcode v1.0.1 // indirect github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect github.com/caddyserver/zerossl v0.1.3 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect - github.com/cyphar/filepath-securejoin v0.3.6 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -165,7 +166,7 @@ require ( github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect - github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-git/go-git/v5 v5.16.2 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect @@ -173,7 +174,7 @@ require ( github.com/go-webauthn/x v0.1.23 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // 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 github.com/google/btree v1.1.2 // indirect @@ -194,6 +195,7 @@ require ( github.com/markbates/going v1.0.3 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mholt/acmez/v3 v3.1.2 // indirect github.com/miekg/dns v1.1.63 // indirect github.com/minio/crc64nvme v1.0.1 // indirect @@ -217,12 +219,11 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/rhysd/actionlint v1.6.27 // indirect + github.com/rhysd/actionlint v1.7.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/skeema/knownhosts v1.3.0 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/tinylib/msgp v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -230,7 +231,7 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/zeebo/assert v1.3.0 // indirect github.com/zeebo/blake3 v0.2.4 // indirect - go.etcd.io/bbolt v1.4.0 // indirect + go.etcd.io/bbolt v1.4.2 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect diff --git a/go.sum b/go.sum index 2231ea9fa6..343088ce1b 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/Uf code.forgejo.org/forgejo/levelqueue v1.0.0/go.mod h1:fmG6zhVuqim2rxSFOoasgXO8V2W/k9U31VVYqLIRLhQ= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= -code.forgejo.org/forgejo/runner/v9 v9.0.2 h1:ajTyjmn3tjJs3fkJ58498CgI4kvxifVY8gbh46SWAgc= -code.forgejo.org/forgejo/runner/v9 v9.0.2/go.mod h1:b6JWcpwHnBwLuWeQERM4pCRzFbnsEvC9gagQfxbZeJw= +code.forgejo.org/forgejo/runner/v9 v9.0.3 h1:BijUSIuPCDNKE3xfq7Ilj0JvK/IcFmP8oarg9dX/QPE= +code.forgejo.org/forgejo/runner/v9 v9.0.3/go.mod h1:0/xEl3Fndpb7g28/4xmmInsugp2V6sXBePJuERPT+S8= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= code.forgejo.org/go-chi/binding v1.0.1 h1:coKNI+X1NzRN7X85LlrpvBRqk0TXpJ+ja28vusQWEuY= @@ -123,6 +123,8 @@ github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFx github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww= github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs= +github.com/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ= +github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= @@ -153,8 +155,8 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38 github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= -github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -181,8 +183,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp 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= github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c= -github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= -github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA= github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY= github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4= @@ -231,8 +233,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= -github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= +github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= @@ -272,8 +274,8 @@ 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/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -395,6 +397,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.30 h1:bVreufq3EAIG1Quvws73du3/QgdeZ3myglJlrzSYYCY= github.com/mattn/go-sqlite3 v1.14.30/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= @@ -484,8 +488,8 @@ github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhi github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rhysd/actionlint v1.6.27 h1:xxwe8YmveBcC8lydW6GoHMGmB6H/MTqUU60F2p10wjw= -github.com/rhysd/actionlint v1.6.27/go.mod h1:m2nFUjAnOrxCMXuOMz9evYBRCLUsMnKY2IJl/N5umbk= +github.com/rhysd/actionlint v1.7.7 h1:0KgkoNTrYY7vmOCs9BW2AHxLvvpoY9nEUzgBHiPUr0k= +github.com/rhysd/actionlint v1.7.7/go.mod h1:AE6I6vJEkNaIfWqC2GNE5spIJNhxf8NCtLEKU4NnUXg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -493,8 +497,8 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= @@ -505,8 +509,8 @@ github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepq github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= -github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -560,8 +564,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/gitlab-org/api/client-go v0.130.1 h1:1xF5C5Zq3sFeNg3PzS2z63oqrxifne3n/OnbI7nptRc= gitlab.com/gitlab-org/api/client-go v0.130.1/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I= +go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= From b6046c17a19afd4a62c91f199179c3f3ce4609fa Mon Sep 17 00:00:00 2001 From: pat-s Date: Wed, 6 Aug 2025 14:47:51 +0200 Subject: [PATCH 235/297] feat: add tag label to commit list view (#8759) Mainly a port of https://github.com/go-gitea/gitea/pull/31082. closes #3573 ## Screenshots ![image](/attachments/a1ba8729-e7f3-4d3d-ab20-04593b119c3f) ![image](/attachments/13790703-8a71-41a0-a875-9c087da989f8) --- ## Checklist ### 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/.md` to be be used for the release notes instead of the title. ## Release notes - User Interface features - [PR](https://codeberg.org/forgejo/forgejo/pulls/8759): add tag label to commit list view Co-authored-by: Lunny Xiao Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8759 Reviewed-by: Gusted Co-authored-by: pat-s Co-committed-by: pat-s --- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v40.go | 13 ++++ models/repo/release.go | 16 +++- models/repo/release_test.go | 13 ++++ options/locale_next/locale_en-US.json | 1 + routers/web/repo/commit.go | 11 +++ templates/repo/commits_list.tmpl | 8 ++ templates/repo/graph/commits.tmpl | 4 +- templates/repo/tag/name.tmpl | 3 + tests/integration/repo_commits_tags_test.go | 73 +++++++++++++++++++ .../integration/repo_commits_template_test.go | 61 ++++++++++++++++ 11 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 models/forgejo_migrations/v40.go create mode 100644 templates/repo/tag/name.tmpl create mode 100644 tests/integration/repo_commits_tags_test.go create mode 100644 tests/integration/repo_commits_template_test.go diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index c5553388ea..71fcf16e7a 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -117,6 +117,8 @@ var migrations = []*Migration{ NewMigration("Add `resolved_unix` column to `abuse_report` table", AddResolvedUnixToAbuseReport), // v38 -> v39 NewMigration("Migrate `data` column of `secret` table to store keying material", MigrateActionSecretsToKeying), + // v39 -> v40 + NewMigration("Add index for release sha1", AddIndexForReleaseSha1), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v40.go b/models/forgejo_migrations/v40.go new file mode 100644 index 0000000000..11e8fbd85e --- /dev/null +++ b/models/forgejo_migrations/v40.go @@ -0,0 +1,13 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations + +import "xorm.io/xorm" + +func AddIndexForReleaseSha1(x *xorm.Engine) error { + type Release struct { + Sha1 string `xorm:"INDEX VARCHAR(64)"` + } + return x.Sync(new(Release)) +} diff --git a/models/repo/release.go b/models/repo/release.go index 10e9bb259f..b39a1de971 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -77,7 +77,7 @@ type Release struct { Target string TargetBehind string `xorm:"-"` // to handle non-existing or empty target Title string - Sha1 string `xorm:"VARCHAR(64)"` + Sha1 string `xorm:"INDEX VARCHAR(64)"` HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"` NumCommits int64 NumCommitsBehind int64 `xorm:"-"` @@ -618,3 +618,17 @@ func InsertReleases(ctx context.Context, rels ...*Release) error { return committer.Commit() } + +func FindTagsByCommitIDs(ctx context.Context, repoID int64, commitIDs ...string) (map[string][]*Release, error) { + releases := make([]*Release, 0, len(commitIDs)) + if err := db.GetEngine(ctx).Where("repo_id=?", repoID). + In("sha1", commitIDs). + Find(&releases); err != nil { + return nil, err + } + res := make(map[string][]*Release, len(releases)) + for _, r := range releases { + res[r.Sha1] = append(res[r.Sha1], r) + } + return res, nil +} diff --git a/models/repo/release_test.go b/models/repo/release_test.go index 94dbd6d9d5..69f9333589 100644 --- a/models/repo/release_test.go +++ b/models/repo/release_test.go @@ -49,3 +49,16 @@ func TestReleaseDisplayName(t *testing.T) { release.Title = "Title" assert.Equal(t, "Title", release.DisplayName()) } + +func Test_FindTagsByCommitIDs(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + sha1Rels, err := FindTagsByCommitIDs(db.DefaultContext, 1, "65f1bf27bc3bf70f64657658635e66094edbcb4d") + require.NoError(t, err) + assert.Len(t, sha1Rels, 1) + rels := sha1Rels["65f1bf27bc3bf70f64657658635e66094edbcb4d"] + assert.Len(t, rels, 3) + assert.Equal(t, "v1.1", rels[0].TagName) + assert.Equal(t, "delete-tag", rels[1].TagName) + assert.Equal(t, "v1.0", rels[2].TagName) +} diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 8542114100..e730c84726 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -111,6 +111,7 @@ "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more.", "avatar.constraints_hint": "Custom avatar may not exceed %[1]s in size or be larger than %[2]dx%[3]d pixels", "og.repo.summary_card.alt_description": "Summary card of repository %[1]s, described as: %[2]s", + "repo.commit.load_tags_failed": "Load tags failed because of internal error", "compare.branches.title": "Compare branches", "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." } diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index f3192266ad..408a2844de 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -85,6 +85,17 @@ func Commits(ctx *context.Context) { } ctx.Data["Commits"] = git_model.ParseCommitsWithStatus(ctx, commits, ctx.Repo.Repository) + commitIDs := make([]string, 0, len(commits)) + for _, c := range commits { + commitIDs = append(commitIDs, c.ID.String()) + } + commitTagsMap, err := repo_model.FindTagsByCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs...) + if err != nil { + log.Error("FindTagsByCommitIDs: %v", err) + ctx.Flash.Error(ctx.Tr("repo.commit.load_tags_failed")) + } else { + ctx.Data["CommitTagsMap"] = commitTagsMap + } ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["CommitCount"] = commitsCount diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 69837bfc1a..2c0a93f2ba 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -59,6 +59,14 @@ {{end}} {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}} + {{if $.CommitTagsMap}} + {{$tags := index $.CommitTagsMap .ID.String}} + {{if $tags}} + {{range $tags}} + {{- template "repo/tag/name" dict "RepoLink" $.Repository.Link "TagName" .TagName "IsRelease" (not .IsTag) -}} + {{end}} + {{end}} + {{end}} {{if IsMultilineCommitMessage .Message}}
    {{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}
    {{end}} diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl index 51392e3470..0c4cb49e9d 100644 --- a/templates/repo/graph/commits.tmpl +++ b/templates/repo/graph/commits.tmpl @@ -27,9 +27,7 @@ {{end}} {{else if eq $refGroup "tags"}} - - {{svg "octicon-tag"}} {{.ShortName}} - + {{- template "repo/tag/name" dict "RepoLink" $.Repository.Link "TagName" .ShortName -}} {{else if eq $refGroup "remotes"}} {{svg "octicon-cross-reference"}} {{.ShortName}} diff --git a/templates/repo/tag/name.tmpl b/templates/repo/tag/name.tmpl new file mode 100644 index 0000000000..a0445545e6 --- /dev/null +++ b/templates/repo/tag/name.tmpl @@ -0,0 +1,3 @@ + +{{svg "octicon-tag"}} {{.TagName}} + diff --git a/tests/integration/repo_commits_tags_test.go b/tests/integration/repo_commits_tags_test.go new file mode 100644 index 0000000000..a6797fce94 --- /dev/null +++ b/tests/integration/repo_commits_tags_test.go @@ -0,0 +1,73 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "strings" + "testing" + + "forgejo.org/tests" + + "github.com/PuerkitoBio/goquery" + "github.com/stretchr/testify/assert" +) + +// TestRepoCommitsWithTags tests that tags are displayed inline with commit messages +// in the commits list, and not in a separate column +func TestRepoCommitsWithTags(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master") + resp := session.MakeRequest(t, req, http.StatusOK) + + doc := NewHTMLParser(t, resp.Body) + + // Find the commit with SHA 65f1bf27bc3bf70f64657658635e66094edbcb4d + // This commit should have tags v1.1 + commitRow := doc.doc.Find(`#commits-table tbody tr`).FilterFunction(func(i int, s *goquery.Selection) bool { + shaLink := s.Find("td.sha a") + href, _ := shaLink.Attr("href") + return strings.Contains(href, "65f1bf27bc3bf70f64657658635e66094edbcb4d") + }) + + // 1. Check for tag labels within the message cell + messageCell := commitRow.Find("td.message") + tagLabels := messageCell.Find("a.ui.label.basic") + assert.GreaterOrEqual(t, tagLabels.Length(), 1, "Should find tag label") + + // 2. tag has proper HTML attr and links to the correct tag + tagFound := false + tagLabels.Each(func(i int, s *goquery.Selection) { + if strings.Contains(s.Text(), "v1.1") { + tagFound = true + href, exists := s.Attr("href") + assert.True(t, exists, "Tag should have href") + assert.Contains(t, href, "/src/tag/v1.1", "Tag link should point to tag page") + assert.Equal(t, 1, s.Find("svg.octicon-tag").Length(), "Tag should have octicon-tag icon") + } + }) + assert.True(t, tagFound, "Should find v1.1 tag") + + // 3. tags appear after the commit messsage and status indicators + messageHTML, _ := messageCell.Html() + messageWrapperPos := strings.Index(messageHTML, "message-wrapper") + ellipsisButtonPos := strings.Index(messageHTML, "ellipsis-button") + commitStatusPos := strings.Index(messageHTML, "commit-status") + tagLabelPos := strings.Index(messageHTML, "ui label basic") + + // 4. Tags should appear after the message wrapper + assert.Greater(t, tagLabelPos, messageWrapperPos, "Tags should appear after message wrapper") + + // 5. If ellipsis button exists, tags should appear after that one + if ellipsisButtonPos > 0 { + assert.Greater(t, tagLabelPos, ellipsisButtonPos, "Tags should appear after ellipsis button") + } + + // 6. If commit status exists, tags should appear after that one + if commitStatusPos > 0 { + assert.Greater(t, tagLabelPos, commitStatusPos, "Tags should appear after commit status") + } +} diff --git a/tests/integration/repo_commits_template_test.go b/tests/integration/repo_commits_template_test.go new file mode 100644 index 0000000000..8243ecc62a --- /dev/null +++ b/tests/integration/repo_commits_template_test.go @@ -0,0 +1,61 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "strings" + "testing" + + "forgejo.org/tests" + + "github.com/stretchr/testify/assert" +) + +// TestRepoCommitsTemplateVariables ensures that template variables in commits_list.tmpl are correctly referenced +func TestRepoCommitsTemplateVariables(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + + // Test the main commits page + req := NewRequest(t, "GET", "/user2/repo1/commits/branch/master") + resp := session.MakeRequest(t, req, http.StatusOK) + assert.Equal(t, http.StatusOK, resp.Code, "Template should render without errors") + + doc := NewHTMLParser(t, resp.Body) + + // 1. Repository.Link is used in tag template + tagLinks := doc.doc.Find("a.ui.label.basic[href*='/src/tag/']") + if tagLinks.Length() > 0 { + href, _ := tagLinks.First().Attr("href") + assert.Contains(t, href, "/user2/repo1/src/tag/", "Repository link should be correctly rendered in tag URLs") + } + + // 2. Repository.ObjectFormatName is used in the SHA column header + shaHeader := doc.doc.Find("#commits-table thead tr th.sha") + assert.Equal(t, 1, shaHeader.Length(), "SHA column header should exist") + headerText := strings.TrimSpace(shaHeader.Text()) + assert.NotEmpty(t, headerText, "SHA column header should have text (ObjectFormatName)") + // Should be uppercase SHA1 or SHA256 depending on the repository format + assert.True(t, headerText == "SHA1" || headerText == "SHA256", "ObjectFormatName should be rendered correctly, got: %s", headerText) + + // 3. Repository.ComposeMetas is used for rendering commit messages + commitMessages := doc.doc.Find("#commits-table tbody tr td.message .commit-summary") + assert.Positive(t, commitMessages.Length(), "Should have commit messages rendered") + + // 4. RepoLink variable is used throughout + commitLinks := doc.doc.Find("#commits-table tbody tr td.sha a[href*='/commit/']") + assert.Positive(t, commitLinks.Length(), "Should have commit links") + firstCommitLink, _ := commitLinks.First().Attr("href") + assert.Contains(t, firstCommitLink, "/user2/repo1/commit/", "RepoLink should be correctly used in commit URLs") + + // 5. CommitTagsMap is used for tag rendering + // If $.CommitTagsMap is mistyped, the template would fail with a 500 error + // (for detailed tag rendering tests see repo_commits_tags_test.go) + tagLabels := doc.doc.Find("#commits-table tbody tr td.message a.ui.label.basic") + if tagLabels.Length() > 0 { + assert.NotContains(t, tagLabels.First().Text(), "{{", "Tags should be properly rendered without template syntax") + } +} From 73dac787a374f166ad47c761134f8e09ec51b26a Mon Sep 17 00:00:00 2001 From: hazycora Date: Wed, 6 Aug 2025 14:50:51 +0200 Subject: [PATCH 236/297] fix: trim trailing slash in WebFinger OIDC issuer link (#8794) As stated in a comment: https://codeberg.org/forgejo/forgejo/issues/8634#issuecomment-6136933 > `routers/web/webfinger.go` was left unchanged, so it still includes the trailing slash, no longer matching the issuer specified in other endpoints. > > ... > > From the [OpenID Connect Discovery specification](https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery): > > > The Issuer location MUST be returned in the WebFinger response as the value of the href member of a links array element with rel member value http://openid.net/specs/connect/1.0/issuer. > > This sounds to me like the `href` should be the issuer location exactly. > > Using Forgejo for OIDC for auth with Tailscale is one instance of this change breaking something - signing up to Tailscale with OIDC now gives an error. Unsure what happens for existing accounts. In summary, since !8028, trailing slashes have been removed from the OIDC issuer locations specified by Forgejo everywhere except in WebFinger responses at `/.well-known/webfinger`, which still includes a trailing slash and so no longer matches the issuer as specified elsewhere (such as at `/.well-known/openid-configuration`). ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [ ] 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/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8794 Reviewed-by: Gusted Reviewed-by: Michael Kriese Co-authored-by: hazycora Co-committed-by: hazycora --- routers/web/webfinger.go | 2 +- tests/integration/webfinger_test.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index 372c08f7d8..5f67e436bf 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -179,7 +179,7 @@ func WebfingerQuery(ctx *context.Context) { }, { Rel: "http://openid.net/specs/connect/1.0/issuer", - Href: appURL.String(), + Href: strings.TrimSuffix(appURL.String(), "/"), }, } diff --git a/tests/integration/webfinger_test.go b/tests/integration/webfinger_test.go index 9708fc1627..bc94d8a59a 100644 --- a/tests/integration/webfinger_test.go +++ b/tests/integration/webfinger_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "net/url" + "strings" "testing" "forgejo.org/models/unittest" @@ -43,6 +44,8 @@ func TestWebfinger(t *testing.T) { session := loginUser(t, "user1") + ctx := t.Context() + req := NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:%s@%s", user.LowerName, appURL.Host)) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "application/jrd+json", resp.Header().Get("Content-Type")) @@ -50,6 +53,26 @@ func TestWebfinger(t *testing.T) { var jrd webfingerJRD DecodeJSON(t, resp, &jrd) assert.Equal(t, "acct:user2@"+appURL.Host, jrd.Subject) + assert.ElementsMatch(t, []*webfingerLink{ + { + Rel: "http://webfinger.net/rel/profile-page", + Type: "text/html", + Href: user.HTMLURL(), + }, + { + Rel: "http://webfinger.net/rel/avatar", + Href: user.AvatarLink(ctx), + }, + { + Rel: "self", + Type: "application/activity+json", + Href: appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(user.ID), + }, + { + Rel: "http://openid.net/specs/connect/1.0/issuer", + Href: strings.TrimSuffix(appURL.String(), "/"), + }, + }, jrd.Links) assert.ElementsMatch(t, []string{user.HTMLURL(), appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(user.ID)}, jrd.Aliases) instanceReq := NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:ghost@%s", appURL.Host)) From 7b76e5510bd9d08be64232a0d0e1fc18695207bc Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 6 Aug 2025 14:59:10 +0200 Subject: [PATCH 237/297] Update module code.forgejo.org/forgejo/runner/v9 to v9.0.3 (update licenses) --- assets/go-licenses.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 59d933a6d6..0a82cdcdff 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -249,6 +249,11 @@ "path": "github.com/blevesearch/zapx/v16/LICENSE", "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License." }, + { + "name": "github.com/bmatcuk/doublestar/v4", + "path": "github.com/bmatcuk/doublestar/v4/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Bob Matcuk\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" + }, { "name": "github.com/boombuler/barcode", "path": "github.com/boombuler/barcode/LICENSE", @@ -709,6 +714,11 @@ "path": "github.com/mattn/go-runewidth/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Yasuhiro Matsumoto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, + { + "name": "github.com/mattn/go-shellwords", + "path": "github.com/mattn/go-shellwords/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2017 Yasuhiro Matsumoto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" + }, { "name": "github.com/meilisearch/meilisearch-go", "path": "github.com/meilisearch/meilisearch-go/LICENSE", From 6b6fa21b2536275ff084a5adf800ec126f37c6d9 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 6 Aug 2025 16:10:19 +0200 Subject: [PATCH 238/297] Update module github.com/caddyserver/certmagic to v0.24.0 (forgejo) (#8796) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8796 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8ded0c003a..289a3e4da0 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.5.2 github.com/buildkite/terminal-to-html/v3 v3.16.8 - github.com/caddyserver/certmagic v0.23.0 + github.com/caddyserver/certmagic v0.24.0 github.com/chi-middleware/proxy v1.1.1 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -190,7 +190,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/libdns/libdns v1.0.0-beta.1 // indirect + github.com/libdns/libdns v1.0.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect github.com/mattn/go-colorable v0.1.14 // indirect diff --git a/go.sum b/go.sum index 343088ce1b..12642b5df1 100644 --- a/go.sum +++ b/go.sum @@ -136,8 +136,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buildkite/terminal-to-html/v3 v3.16.8 h1:QN/daUob6cmK8GcdKnwn9+YTlPr1vNj+oeAIiJK6fPc= github.com/buildkite/terminal-to-html/v3 v3.16.8/go.mod h1:+k1KVKROZocrTLsEQ9PEf9A+8+X8uaVV5iO1ZIOwKYM= -github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= -github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4= +github.com/caddyserver/certmagic v0.24.0 h1:EfXTWpxHAUKgDfOj6MHImJN8Jm4AMFfMT6ITuKhrDF0= +github.com/caddyserver/certmagic v0.24.0/go.mod h1:xPT7dC1DuHHnS2yuEQCEyks+b89sUkMENh8dJF+InLE= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= @@ -382,8 +382,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= -github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/libdns/libdns v1.0.0 h1:IvYaz07JNz6jUQ4h/fv2R4sVnRnm77J/aOuC9B+TQTA= +github.com/libdns/libdns v1.0.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= From c081f2077679aabf1d744de20dca182d5cec8a70 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Wed, 6 Aug 2025 16:16:13 +0200 Subject: [PATCH 239/297] Sent user activities to distant federated server (#8792) This PR is part of #4767. It contains * a refactoring of validation error messages * adds the ability to send user-activities to distant federated servers Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8792 Reviewed-by: Earl Warren Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- .deadcode-out | 19 +- .gitignore | 3 + modules/forgefed/activity_follow_test.go | 11 +- modules/forgefed/activity_like_test.go | 75 ++++---- modules/forgefed/activity_undo_like_test.go | 2 +- .../forgefed/activity_user_activity_test.go | 6 +- modules/forgefed/actor_person_test.go | 72 +++---- modules/forgefed/actor_test.go | 2 +- .../object_user_activity_note_test.go | 6 +- modules/validation/validatable.go | 6 +- routers/api/v1/activitypub/person.go | 162 ++++++++++++++++ routers/api/v1/api.go | 5 + routers/api/v1/swagger/activitypub.go | 7 + services/convert/activitypub_user_action.go | 177 ++++++++++++++++++ services/federation/person_inbox_accept.go | 22 +++ services/federation/person_inbox_create.go | 55 ++++++ services/federation/person_service.go | 35 ++++ services/federation/user_activity.go | 83 ++++++++ services/feed/action.go | 11 +- templates/swagger/v1_json.tmpl | 126 +++++++++++++ ...ivitypub_person_inbox_useractivity_test.go | 100 ++++++++++ .../api_activitypub_person_test.go | 30 ++- 22 files changed, 885 insertions(+), 130 deletions(-) create mode 100644 services/convert/activitypub_user_action.go create mode 100644 services/federation/person_inbox_accept.go create mode 100644 services/federation/person_inbox_create.go create mode 100644 services/federation/user_activity.go create mode 100644 tests/integration/api_activitypub_person_inbox_useractivity_test.go diff --git a/.deadcode-out b/.deadcode-out index 87dc416ff1..24facdf12e 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -13,13 +13,6 @@ forgejo.org/models IsErrSHANotFound IsErrMergeDivergingFastForwardOnly -forgejo.org/models/activities - GetActivityByID - NewFederatedUserActivity - CreateUserActivity - GetFollowingFeeds - FederatedUserActivity.loadActor - forgejo.org/models/auth WebAuthnCredentials @@ -63,8 +56,6 @@ forgejo.org/models/user GetUserAllSettings DeleteUserSetting GetFederatedUser - GetFederatedUserByUserID - GetFollowersForUser forgejo.org/modules/activitypub NewContext @@ -99,16 +90,10 @@ forgejo.org/modules/forgefed NewForgeUndoLike ForgeUndoLike.UnmarshalJSON ForgeUndoLike.Validate - NewForgeUserActivityFromAp - NewForgeUserActivity - ForgeUserActivity.Validate NewPersonIDFromModel GetItemByType JSONUnmarshalerFn NotEmpty - NewForgeUserActivityNoteFromAp - newNote - ForgeUserActivityNote.Validate ToRepository OnRepository @@ -236,8 +221,8 @@ forgejo.org/routers/web/org forgejo.org/services/context GetPrivateContext -forgejo.org/services/convert - ToActivityPubPersonFeedItem +forgejo.org/services/federation + FollowRemoteActor forgejo.org/services/repository IsErrForkAlreadyExist diff --git a/.gitignore b/.gitignore index 744e24a09a..e1200ce4e8 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,8 @@ _testmain.go *coverage.out coverage.all +coverage.html +coverage.html.gz coverage/ cpu.out @@ -129,3 +131,4 @@ prime/ # Manpage /man +tests/integration/api_activitypub_person_inbox_useractivity_test.go diff --git a/modules/forgefed/activity_follow_test.go b/modules/forgefed/activity_follow_test.go index bb0c1de2f7..8ba31d5f6f 100644 --- a/modules/forgefed/activity_follow_test.go +++ b/modules/forgefed/activity_follow_test.go @@ -9,6 +9,7 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) func Test_NewForgeFollowValidation(t *testing.T) { @@ -17,15 +18,13 @@ func Test_NewForgeFollowValidation(t *testing.T) { sut.Actor = ap.IRI("example.org/alice") sut.Object = ap.IRI("example.org/bob") - if err, _ := validation.IsValid(sut); !err { - t.Errorf("sut is invalid: %v\n", err) - } + valid, err := validation.IsValid(sut) + assert.True(t, valid, "sut is invalid: %v\n", err) sut = ForgeFollow{} sut.Actor = ap.IRI("example.org/alice") sut.Object = ap.IRI("example.org/bob") - if err, _ := validation.IsValid(sut); err { - t.Errorf("sut is valid: %v\n", err) - } + valid, err = validation.IsValid(sut) + assert.False(t, valid, "sut is valid: %v\n", err) } diff --git a/modules/forgefed/activity_like_test.go b/modules/forgefed/activity_like_test.go index 6b252d5960..eef5563d8b 100644 --- a/modules/forgefed/activity_like_test.go +++ b/modules/forgefed/activity_like_test.go @@ -13,6 +13,8 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_NewForgeLike(t *testing.T) { @@ -22,21 +24,14 @@ func Test_NewForgeLike(t *testing.T) { objectIRI := "https://codeberg.org/api/v1/activitypub/repository-id/1" startTime, _ := time.Parse("2006-Jan-02", "2024-Mar-07") sut, err := NewForgeLike(actorIRI, objectIRI, startTime) - if err != nil { - t.Errorf("unexpected error: %v\n", err) - } - if valid, _ := validation.IsValid(sut); !valid { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + require.NoError(t, err, "unexpected error: %v\n", err) + + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) got, err := sut.MarshalJSON() - if err != nil { - t.Errorf("MarshalJSON() error = \"%v\"", err) - return - } - if !reflect.DeepEqual(got, want) { - t.Errorf("MarshalJSON() got = %q, want %q", got, want) - } + require.NoError(t, err, "MarshalJSON() error = %q", err) + assert.True(t, reflect.DeepEqual(got, want), "MarshalJSON()\n got: %q,\n want: %q", got, want) } func Test_LikeMarshalJSON(t *testing.T) { @@ -66,13 +61,8 @@ func Test_LikeMarshalJSON(t *testing.T) { for name, tt := range tests { t.Run(name, func(t *testing.T) { got, err := tt.item.MarshalJSON() - if (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error() { - t.Errorf("MarshalJSON() error = \"%v\", wantErr \"%v\"", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MarshalJSON() got = %q, want %q", got, tt.want) - } + assert.False(t, (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error(), "MarshalJSON()\n got: %v,\n want: %v", err, tt.wantErr) + assert.True(t, reflect.DeepEqual(got, tt.want), "MarshalJSON()\n got: %q\n want: %q", got, tt.want) }) } } @@ -89,8 +79,8 @@ func Test_LikeUnmarshalJSON(t *testing.T) { item: []byte(`{"type":"Like","actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1","object":"https://codeberg.org/api/activitypub/repository-id/1"}`), want: &ForgeLike{ Activity: ap.Activity{ - Actor: ap.IRI("https://repo.prod.meissa.de/api/activitypub/user-id/1"), Type: "Like", + Actor: ap.IRI("https://repo.prod.meissa.de/api/activitypub/user-id/1"), Object: ap.IRI("https://codeberg.org/api/activitypub/repository-id/1"), }, }, @@ -107,12 +97,10 @@ func Test_LikeUnmarshalJSON(t *testing.T) { t.Run(name, func(t *testing.T) { got := new(ForgeLike) err := got.UnmarshalJSON(test.item) - if (err != nil || test.wantErr != nil) && !strings.Contains(err.Error(), test.wantErr.Error()) { - t.Errorf("UnmarshalJSON() error = \"%v\", wantErr \"%v\"", err, test.wantErr) - return - } + assert.False(t, (err != nil || test.wantErr != nil) && !strings.Contains(err.Error(), test.wantErr.Error()), "UnmarshalJSON()\n error: %v\n wantErr: %v", err, test.wantErr) + if !reflect.DeepEqual(got, test.want) { - t.Errorf("UnmarshalJSON() got = %q, want %q, err %q", got, test.want, err.Error()) + assert.Errorf(t, err, "UnmarshalJSON() got = %q, want %q, err %q", got, test.want, err.Error()) } }) } @@ -120,46 +108,47 @@ func Test_LikeUnmarshalJSON(t *testing.T) { func Test_ForgeLikeValidation(t *testing.T) { // Successful - sut := new(ForgeLike) sut.UnmarshalJSON([]byte(`{"type":"Like", "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "2014-12-31T23:00:00-08:00"}`)) - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) // Errors - sut.UnmarshalJSON([]byte(`{"actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "2014-12-31T23:00:00-08:00"}`)) - if err := validateAndCheckError(sut, "type should not be empty"); err != nil { - t.Error(err) - } + validate := sut.Validate() + assert.Len(t, validate, 2) + assert.Equal(t, + "Field type contains the value , which is not in allowed subset [Like]", + validate[1]) sut.UnmarshalJSON([]byte(`{"type":"bad-type", "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "2014-12-31T23:00:00-08:00"}`)) - if err := validateAndCheckError(sut, "Field type contains the value bad-type, which is not in allowed subset [Like]"); err != nil { - t.Error(err) - } + validate = sut.Validate() + assert.Len(t, validate, 1) + assert.Equal(t, + "Field type contains the value bad-type, which is not in allowed subset [Like]", + validate[0]) sut.UnmarshalJSON([]byte(`{"type":"Like", "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "not a date"}`)) - if err := validateAndCheckError(sut, "StartTime was invalid."); err != nil { - t.Error(err) - } + validate = sut.Validate() + assert.Len(t, validate, 1) + assert.Equal(t, + "StartTime was invalid.", + validate[0]) } func TestActivityValidation_Attack(t *testing.T) { sut := new(ForgeLike) sut.UnmarshalJSON([]byte(`{rubbish}`)) - if len(sut.Validate()) != 5 { - t.Errorf("5 validation errors expected but was: %v\n", len(sut.Validate())) - } + assert.Len(t, sut.Validate(), 5) } diff --git a/modules/forgefed/activity_undo_like_test.go b/modules/forgefed/activity_undo_like_test.go index 5867a84e7b..76358b1669 100644 --- a/modules/forgefed/activity_undo_like_test.go +++ b/modules/forgefed/activity_undo_like_test.go @@ -173,7 +173,7 @@ func TestActivityValidationUndo(t *testing.T) { "startTime":"2024-03-27T00:00:00Z", "actor":"https://repo.prod.meissa.de/api/v1/activitypub/user-id/1", "object":"https://codeberg.org/api/v1/activitypub/repository-id/1"}}`)) - if err := validateAndCheckError(sut, "type should not be empty"); err != nil { + if err := validateAndCheckError(sut, "Value type should not be empty"); err != nil { t.Error(*err) } diff --git a/modules/forgefed/activity_user_activity_test.go b/modules/forgefed/activity_user_activity_test.go index 9cb9f133b9..107ae51204 100644 --- a/modules/forgefed/activity_user_activity_test.go +++ b/modules/forgefed/activity_user_activity_test.go @@ -9,6 +9,7 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) func Test_ForgeUserActivityValidation(t *testing.T) { @@ -34,7 +35,6 @@ func Test_ForgeUserActivityValidation(t *testing.T) { sut.Note = note - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) } diff --git a/modules/forgefed/actor_person_test.go b/modules/forgefed/actor_person_test.go index f466ddb964..e4f1734a9d 100644 --- a/modules/forgefed/actor_person_test.go +++ b/modules/forgefed/actor_person_test.go @@ -115,7 +115,7 @@ func TestPersonIdValidation(t *testing.T) { result, err := validation.IsValid(sut) assert.False(t, result) - require.EqualError(t, err, "Validation Error: forgefed.PersonID: path should not be empty\npath: \"\" has to be a person specific api path") + require.EqualError(t, err, "Validation Error: forgefed.PersonID: Value path should not be empty\npath: \"\" has to be a person specific api path") sut = PersonID{} sut.ID = "1" @@ -166,38 +166,28 @@ func TestWebfingerId(t *testing.T) { } func TestShouldThrowErrorOnInvalidInput(t *testing.T) { - var err any - _, err = NewPersonID("", "forgejo") - if err == nil { - t.Errorf("empty input should be invalid.") + tests := []struct { + input string + username string + expectErr bool + }{ + {"", "forgejo", true}, + {"http://localhost:3000/api/v1/something", "forgejo", true}, + {"./api/v1/something", "forgejo", true}, + {"http://1.2.3.4/api/v1/something", "forgejo", true}, + {"http:///[fe80::1ff:fe23:4567:890a%25eth0]/api/v1/something", "forgejo", true}, + {"https://codeberg.org/api/v1/activitypub/../activitypub/user-id/12345", "forgejo", true}, + {"https://myuser@an.other.host/api/v1/activitypub/user-id/1", "forgejo", true}, + {"https://an.other.host/api/v1/activitypub/user-id/1", "forgejo", false}, } - _, err = NewPersonID("http://localhost:3000/api/v1/something", "forgejo") - if err == nil { - t.Errorf("localhost uris are not external") - } - _, err = NewPersonID("./api/v1/something", "forgejo") - if err == nil { - t.Errorf("relative uris are not allowed") - } - _, err = NewPersonID("http://1.2.3.4/api/v1/something", "forgejo") - if err == nil { - t.Errorf("uri may not be ip-4 based") - } - _, err = NewPersonID("http:///[fe80::1ff:fe23:4567:890a%25eth0]/api/v1/something", "forgejo") - if err == nil { - t.Errorf("uri may not be ip-6 based") - } - _, err = NewPersonID("https://codeberg.org/api/v1/activitypub/../activitypub/user-id/12345", "forgejo") - if err == nil { - t.Errorf("uri may not contain relative path elements") - } - _, err = NewPersonID("https://myuser@an.other.host/api/v1/activitypub/user-id/1", "forgejo") - if err == nil { - t.Errorf("uri may not contain unparsed elements") - } - _, err = NewPersonID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo") - if err != nil { - t.Errorf("this uri should be valid but was: %v", err) + + for _, tt := range tests { + _, err := NewPersonID(tt.input, tt.username) + if tt.expectErr { + assert.Error(t, err, "Expected an error for input: %s", tt.input) + } else { + assert.NoError(t, err, "Expected no error for input: %s, but got: %v", tt.input, err) + } } } @@ -221,14 +211,11 @@ func Test_PersonUnmarshalJSON(t *testing.T) { } sut := new(ForgePerson) err := sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`)) - if err != nil { - t.Errorf("UnmarshalJSON() unexpected error: %v", err) - } + require.NoError(t, err, "UnmarshalJSON() unexpected error: %q", err) + x, _ := expected.MarshalJSON() y, _ := sut.MarshalJSON() - if !reflect.DeepEqual(x, y) { - t.Errorf("UnmarshalJSON() expected: %q got: %q", x, y) - } + assert.True(t, reflect.DeepEqual(x, y), "UnmarshalJSON()\n got: %q,\n want: %q", x, y) expectedStr := strings.ReplaceAll(strings.ReplaceAll(`{ "id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/10", @@ -244,9 +231,7 @@ func Test_PersonUnmarshalJSON(t *testing.T) { "\n", ""), "\t", "") err = sut.UnmarshalJSON([]byte(expectedStr)) - if err != nil { - t.Errorf("UnmarshalJSON() unexpected error: %v", err) - } + require.NoError(t, err, "UnmarshalJSON() unexpected error: %q", err) result, _ := sut.MarshalJSON() assert.JSONEq(t, expectedStr, string(result), "Expected string is not equal") } @@ -254,9 +239,8 @@ func Test_PersonUnmarshalJSON(t *testing.T) { func TestForgePersonValidation(t *testing.T) { sut := new(ForgePerson) sut.UnmarshalJSON([]byte(`{"type":"Person","preferredUsername":"MaxMuster"}`)) - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) } func TestAsloginName(t *testing.T) { diff --git a/modules/forgefed/actor_test.go b/modules/forgefed/actor_test.go index 48d773c5b9..a32114616c 100644 --- a/modules/forgefed/actor_test.go +++ b/modules/forgefed/actor_test.go @@ -58,7 +58,7 @@ func TestActorIdValidation(t *testing.T) { sut.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/" result := sut.Validate() assert.Len(t, result, 1) - assert.Equal(t, "ID should not be empty", result[0]) + assert.Equal(t, "Value ID should not be empty", result[0]) sut = ActorID{} sut.ID = "1" diff --git a/modules/forgefed/object_user_activity_note_test.go b/modules/forgefed/object_user_activity_note_test.go index 20c3666bb1..02aebd58d3 100644 --- a/modules/forgefed/object_user_activity_note_test.go +++ b/modules/forgefed/object_user_activity_note_test.go @@ -9,6 +9,7 @@ import ( "forgejo.org/modules/validation" ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) func Test_UserActivityNoteValidation(t *testing.T) { @@ -22,7 +23,6 @@ func Test_UserActivityNoteValidation(t *testing.T) { } sut.URL = ap.IRI("example.org/user-id/57") - if res, _ := validation.IsValid(sut); !res { - t.Errorf("sut expected to be valid: %v\n", sut.Validate()) - } + valid, _ := validation.IsValid(sut) + assert.True(t, valid, "sut expected to be valid: %v\n", sut.Validate()) } diff --git a/modules/validation/validatable.go b/modules/validation/validatable.go index 4500f6e53d..7bcca03bf8 100644 --- a/modules/validation/validatable.go +++ b/modules/validation/validatable.go @@ -45,7 +45,7 @@ func IsValid(v Validateable) (bool, error) { func ValidateIDExists(value ap.Item, name string) []string { if value == nil { - return []string{fmt.Sprintf("%v should not be nil", name)} + return []string{fmt.Sprintf("Field %v must not be nil", name)} } return ValidateNotEmpty(value.GetID().String(), name) } @@ -76,12 +76,12 @@ func ValidateNotEmpty(value any, name string) []string { if isValid { return []string{} } - return []string{fmt.Sprintf("%v should not be empty", name)} + return []string{fmt.Sprintf("Value %v should not be empty", name)} } func ValidateMaxLen(value string, maxLen int, name string) []string { if utf8.RuneCountInString(value) > maxLen { - return []string{fmt.Sprintf("Value %v was longer than %v", name, maxLen)} + return []string{fmt.Sprintf("Value %v is longer than expected length %v", name, maxLen)} } return []string{} } diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go index 6120a078af..72f9680b9b 100644 --- a/routers/api/v1/activitypub/person.go +++ b/routers/api/v1/activitypub/person.go @@ -6,9 +6,12 @@ package activitypub import ( "net/http" + "forgejo.org/models/activities" "forgejo.org/modules/activitypub" + "forgejo.org/modules/forgefed" "forgejo.org/modules/log" "forgejo.org/modules/web" + "forgejo.org/routers/api/v1/utils" "forgejo.org/services/context" "forgejo.org/services/convert" "forgejo.org/services/federation" @@ -78,3 +81,162 @@ func PersonInbox(ctx *context.APIContext) { } responseServiceResult(ctx, result) } + +// PersonFeed returns the recorded activities in the user's feed +func PersonFeed(ctx *context.APIContext) { + // swagger:operation GET /activitypub/user-id/{user-id}/outbox activitypub activitypubPersonFeed + // --- + // summary: List the user's recorded activity + // produces: + // - application/json + // parameters: + // - name: user-id + // in: path + // description: user ID of the user + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/PersonFeed" + // "403": + // "$ref": "#/responses/forbidden" + + listOptions := utils.GetListOptions(ctx) + opts := activities.GetFollowingFeedsOptions{ + ListOptions: listOptions, + } + items, count, err := activities.GetFollowingFeeds(ctx, ctx.ContextUser.ID, opts) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetFollowingFeeds", err) + return + } + ctx.SetTotalCountHeader(count) + + feed := ap.OrderedCollectionNew(ap.IRI(ctx.ContextUser.APActorID() + "/outbox")) + feed.AttributedTo = ap.IRI(ctx.ContextUser.APActorID()) + for _, item := range items { + if err := feed.OrderedItems.Append(convert.ToActivityPubPersonFeedItem(item)); err != nil { + ctx.Error(http.StatusInternalServerError, "OrderedItems.Append", err) + return + } + } + + binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(feed) + if err != nil { + ctx.ServerError("MarshalJSON", err) + return + } + + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + ctx.Resp.WriteHeader(http.StatusOK) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } +} + +func getActivity(ctx *context.APIContext, id int64) (*forgefed.ForgeUserActivity, error) { + action, err := activities.GetActivityByID(ctx, id) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetActivityByID", err.Error()) + return nil, err + } + + if action.UserID != action.ActUserID || action.ActUserID != ctx.ContextUser.ID { + ctx.NotFound() + return nil, err + } + + actions := activities.ActionList{action} + if err := actions.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "action.LoadAttributes", err.Error()) + return nil, err + } + + activity, err := convert.ActionToForgeUserActivity(ctx, actions[0]) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ActionToForgeUserActivity", err.Error()) + return nil, err + } + + return &activity, nil +} + +// PersonActivity returns a user's given activity +func PersonActivity(ctx *context.APIContext) { + // swagger:operation GET /activitypub/user-id/{user-id}/activities/{activity-id}/activity activitypub activitypubPersonActivity + // --- + // summary: Get a specific activity of the user + // produces: + // - application/json + // parameters: + // - name: user-id + // in: path + // description: user ID of the user + // type: integer + // required: true + // - name: activity-id + // in: path + // description: activity ID of the sought activity + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/ActivityPub" + + id := ctx.ParamsInt64("activity-id") + activity, err := getActivity(ctx, id) + if err != nil { + return + } + + binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(activity) + if err != nil { + ctx.ServerError("MarshalJSON", err) + return + } + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + ctx.Resp.WriteHeader(http.StatusOK) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } +} + +// PersonActivity returns the Object part of a user's given activity +func PersonActivityNote(ctx *context.APIContext) { + // swagger:operation GET /activitypub/user-id/{user-id}/activities/{activity-id} activitypub activitypubPersonActivityNote + // --- + // summary: Get a specific activity object of the user + // produces: + // - application/json + // parameters: + // - name: user-id + // in: path + // description: user ID of the user + // type: integer + // required: true + // - name: activity-id + // in: path + // description: activity ID of the sought activity + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/ActivityPub" + + id := ctx.ParamsInt64("activity-id") + activity, err := getActivity(ctx, id) + if err != nil { + return + } + + binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(activity.Object) + if err != nil { + ctx.ServerError("MarshalJSON", err) + return + } + ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType) + ctx.Resp.WriteHeader(http.StatusOK) + if _, err = ctx.Resp.Write(binary); err != nil { + log.Error("write to resp err: %v", err) + } +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 2f806ba35d..26a2c0ffe3 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -844,6 +844,11 @@ func Routes() *web.Route { activitypub.ReqHTTPUserSignature(), bind(ap.Activity{}), activitypub.PersonInbox) + m.Group("/activities/{activity-id}", func() { + m.Get("", activitypub.PersonActivityNote) + m.Get("/activity", activitypub.PersonActivity) + }) + m.Get("/outbox", activitypub.ReqHTTPUserSignature(), activitypub.PersonFeed) }, context.UserIDAssignmentAPI(), checkTokenPublicOnly()) m.Group("/actor", func() { m.Get("", activitypub.Actor) diff --git a/routers/api/v1/swagger/activitypub.go b/routers/api/v1/swagger/activitypub.go index 6235009572..a11fc4098c 100644 --- a/routers/api/v1/swagger/activitypub.go +++ b/routers/api/v1/swagger/activitypub.go @@ -13,3 +13,10 @@ type swaggerResponseActivityPub struct { // in:body Body api.ActivityPub `json:"body"` } + +// Personfeed +// swagger:response PersonFeed +type swaggerResponsePersonFeed struct { + // in:body + Body []api.APPersonFollowItem `json:"body"` +} diff --git a/services/convert/activitypub_user_action.go b/services/convert/activitypub_user_action.go new file mode 100644 index 0000000000..9c62e6f25c --- /dev/null +++ b/services/convert/activitypub_user_action.go @@ -0,0 +1,177 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "context" + "fmt" + "html" + "net/url" + "time" + + activities_model "forgejo.org/models/activities" + issues_model "forgejo.org/models/issues" + fm "forgejo.org/modules/forgefed" + "forgejo.org/modules/json" + "forgejo.org/modules/markup" + "forgejo.org/modules/markup/markdown" +) + +func ActionToForgeUserActivity(ctx context.Context, action *activities_model.Action) (fm.ForgeUserActivity, error) { + render := func(format string, args ...any) string { + return fmt.Sprintf(`%s %s`, action.ActUser.HTMLURL(), action.GetActDisplayName(ctx), fmt.Sprintf(format, args...)) + } + renderIssue := func(issue *issues_model.Issue) string { + return fmt.Sprintf(`%s#%d`, + issue.HTMLURL(), + action.GetRepoPath(ctx), + issue.Index, + ) + } + renderRepo := func() string { + return fmt.Sprintf(`%s`, action.Repo.HTMLURL(), action.GetRepoPath(ctx)) + } + renderBranch := func() string { + return fmt.Sprintf(`%s`, action.GetRefLink(ctx), action.GetBranch()) + } + renderTag := func() string { + return fmt.Sprintf(`%s`, action.GetRefLink(ctx), action.GetTag()) + } + + makeUserActivity := func(format string, args ...any) (fm.ForgeUserActivity, error) { + return fm.NewForgeUserActivity(action.ActUser, action.ID, render(format, args...)) + } + + switch action.OpType { + case activities_model.ActionCreateRepo: + return makeUserActivity("created a new repository: %s", renderRepo()) + case activities_model.ActionRenameRepo: + return makeUserActivity("renamed a repository: %s", renderRepo()) + case activities_model.ActionStarRepo: + return makeUserActivity("starred a repository: %s", renderRepo()) + case activities_model.ActionWatchRepo: + return makeUserActivity("started watching a repository: %s", renderRepo()) + case activities_model.ActionCommitRepo: + type PushCommit struct { + Sha1 string + Message string + AuthorEmail string + AuthorName string + CommitterEmail string + CommitterName string + Timestamp time.Time + } + type PushCommits struct { + Commits []*PushCommit + HeadCommit *PushCommit + CompareURL string + Len int + } + + commits := &PushCommits{} + if err := json.Unmarshal([]byte(action.GetContent()), commits); err != nil { + return fm.ForgeUserActivity{}, err + } + commitsHTML := "" + renderCommit := func(commit *PushCommit) string { + return fmt.Sprintf(`
  • %s
    %s
  • `, + fmt.Sprintf("%s/commit/%s", action.GetRepoAbsoluteLink(ctx), url.PathEscape(commit.Sha1)), + commit.Sha1, + html.EscapeString(commit.Message), + ) + } + for _, commit := range commits.Commits { + commitsHTML += renderCommit(commit) + } + return makeUserActivity("pushed to %s at %s:
      %s
    ", renderBranch(), renderRepo(), commitsHTML) + case activities_model.ActionCreateIssue: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("opened issue %s", renderIssue(action.Issue)) + case activities_model.ActionCreatePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("opened pull request %s", renderIssue(action.Issue)) + case activities_model.ActionTransferRepo: + return makeUserActivity("transferred %s", renderRepo()) + case activities_model.ActionPushTag: + return makeUserActivity("pushed %s at %s", renderTag(), renderRepo()) + case activities_model.ActionCommentIssue: + renderedComment, err := markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + }, action.Comment.Content) + if err != nil { + return fm.ForgeUserActivity{}, err + } + + return makeUserActivity(`commented on %s:
    %s
    `, + action.GetCommentHTMLURL(ctx), + renderIssue(action.Comment.Issue), + renderedComment, + ) + case activities_model.ActionMergePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("merged pull request %s", renderIssue(action.Issue)) + case activities_model.ActionCloseIssue: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("closed issue %s", renderIssue(action.Issue)) + case activities_model.ActionReopenIssue: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("reopened issue %s", renderIssue(action.Issue)) + case activities_model.ActionClosePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("closed pull request %s", renderIssue(action.Issue)) + case activities_model.ActionReopenPullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("reopened pull request %s", renderIssue(action.Issue)) + case activities_model.ActionDeleteTag: + return makeUserActivity("deleted tag %s at %s", action.GetTag(), renderRepo()) + case activities_model.ActionDeleteBranch: + return makeUserActivity("deleted branch %s at %s", action.GetBranch(), renderRepo()) + case activities_model.ActionApprovePullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("approved pull request %s", renderIssue(action.Issue)) + case activities_model.ActionRejectPullRequest: + if err := action.LoadIssue(ctx); err != nil { + return fm.ForgeUserActivity{}, err + } + return makeUserActivity("rejected pull request %s", renderIssue(action.Issue)) + case activities_model.ActionCommentPull: + renderedComment, err := markdown.RenderString(&markup.RenderContext{ + Ctx: ctx, + }, action.Comment.Content) + if err != nil { + return fm.ForgeUserActivity{}, err + } + + return makeUserActivity(`commented on %s:
    %s
    `, + action.GetCommentHTMLURL(ctx), + renderIssue(action.Comment.Issue), + renderedComment, + ) + case activities_model.ActionMirrorSyncPush: + case activities_model.ActionMirrorSyncCreate: + case activities_model.ActionMirrorSyncDelete: + case activities_model.ActionPublishRelease: + case activities_model.ActionPullReviewDismissed: + case activities_model.ActionPullRequestReadyForReview: + case activities_model.ActionAutoMergePullRequest: + } + + return makeUserActivity("performed an unrecognised action: %s", action.OpType.String()) +} diff --git a/services/federation/person_inbox_accept.go b/services/federation/person_inbox_accept.go new file mode 100644 index 0000000000..d0a840bd2d --- /dev/null +++ b/services/federation/person_inbox_accept.go @@ -0,0 +1,22 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "net/http" + + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" +) + +func processPersonInboxAccept(activity *ap.Activity) (ServiceResult, error) { + if activity.Object.GetType() != ap.FollowType { + log.Error("Invalid object type for Accept activity: %v", activity.Object.GetType()) + return ServiceResult{}, NewErrNotAcceptablef("invalid object type for Accept activity: %v", activity.Object.GetType()) + } + + // We currently do not do anything here, we just drop it. + return NewServiceResultStatusOnly(http.StatusNoContent), nil +} diff --git a/services/federation/person_inbox_create.go b/services/federation/person_inbox_create.go new file mode 100644 index 0000000000..2132c7ede1 --- /dev/null +++ b/services/federation/person_inbox_create.go @@ -0,0 +1,55 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + "net/http" + + "forgejo.org/models/activities" + "forgejo.org/models/user" + fm "forgejo.org/modules/forgefed" + "forgejo.org/modules/log" + + ap "github.com/go-ap/activitypub" +) + +func processPersonInboxCreate(ctx context.Context, user *user.User, activity *ap.Activity) (ServiceResult, error) { + createAct, err := fm.NewForgeUserActivityFromAp(*activity) + if err != nil { + log.Error("Invalid user activity: %v, %v", activity, err) + return ServiceResult{}, NewErrNotAcceptablef("Invalid user activity: %v", err) + } + + actorURI := createAct.Actor.GetLink().String() + federatedBaseUser, _, _, err := findFederatedUser(ctx, actorURI) + if err != nil { + log.Error("Federated user not found (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("federated user not found (%s): %v", actorURI, err) + } + if federatedBaseUser == nil { + log.Error("Federated user not found (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("federated user not found (%s): %v", actorURI, err) + } + + federatedUserActivity, err := activities.NewFederatedUserActivity( + user.ID, + federatedBaseUser.ID, + createAct.Actor.GetLink().String(), + createAct.Note.Content.String(), + createAct.Note.URL.GetID().String(), + *activity, + ) + if err != nil { + log.Error("Error creating federatedUserActivity (%s): %v", actorURI, err) + return ServiceResult{}, NewErrNotAcceptablef("Error creating federatedUserActivity: %v", err) + } + + if err := activities.CreateUserActivity(ctx, &federatedUserActivity); err != nil { + log.Error("Unable to record activity: %v", err) + return ServiceResult{}, NewErrNotAcceptablef("Unable to record activity: %v", err) + } + + return NewServiceResultStatusOnly(http.StatusNoContent), nil +} diff --git a/services/federation/person_service.go b/services/federation/person_service.go index f67d2b492d..d6482d013c 100644 --- a/services/federation/person_service.go +++ b/services/federation/person_service.go @@ -5,21 +5,56 @@ package federation import ( "context" + "net/http" "forgejo.org/models/user" + "forgejo.org/modules/forgefed" "forgejo.org/modules/log" + context_service "forgejo.org/services/context" ap "github.com/go-ap/activitypub" + "github.com/go-ap/jsonld" ) func ProcessPersonInbox(ctx context.Context, user *user.User, activity *ap.Activity) (ServiceResult, error) { switch activity.Type { + case ap.CreateType: + return processPersonInboxCreate(ctx, user, activity) case ap.FollowType: return processPersonFollow(ctx, user, activity) case ap.UndoType: return processPersonInboxUndo(ctx, user, activity) + case ap.AcceptType: + return processPersonInboxAccept(activity) } log.Error("Unsupported PersonInbox activity: %v", activity.Type) return ServiceResult{}, NewErrNotAcceptablef("unsupported activity: %v", activity.Type) } + +func FollowRemoteActor(ctx *context_service.APIContext, localUser *user.User, actorURI string) error { + _, federatedUser, federationHost, err := FindOrCreateFederatedUser(ctx.Base, actorURI) + if err != nil { + log.Error("Federated user not found (%s): %v", actorURI, err) + ctx.Error(http.StatusNotAcceptable, "Federated user not found", err) + return err + } + + followReq, err := forgefed.NewForgeFollow(localUser.APActorID(), actorURI) + if err != nil { + return err + } + + payload, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI)). + Marshal(followReq) + if err != nil { + return err + } + + hostURL := federationHost.AsURL() + return deliveryQueue.Push(deliveryQueueItem{ + InboxURL: hostURL.JoinPath(federatedUser.InboxPath).String(), + Doer: localUser, + Payload: payload, + }) +} diff --git a/services/federation/user_activity.go b/services/federation/user_activity.go new file mode 100644 index 0000000000..0db2aee4ec --- /dev/null +++ b/services/federation/user_activity.go @@ -0,0 +1,83 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package federation + +import ( + "context" + + activities_model "forgejo.org/models/activities" + "forgejo.org/models/forgefed" + "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/services/convert" + + ap "github.com/go-ap/activitypub" + "github.com/go-ap/jsonld" +) + +func SendUserActivity(ctx context.Context, doer *user.User, activity *activities_model.Action) error { + followers, err := user.GetFollowersForUser(ctx, doer) + if err != nil { + return err + } + + userActivity, err := convert.ActionToForgeUserActivity(ctx, activity) + if err != nil { + return err + } + + payload, err := jsonld.WithContext( + jsonld.IRI(ap.ActivityBaseURI), + ).Marshal(userActivity) + if err != nil { + return err + } + + for _, follower := range followers { + _, federatedUserFollower, err := user.GetFederatedUserByUserID(ctx, follower.FollowingUserID) + if err != nil { + return err + } + + federationHost, err := forgefed.GetFederationHost(ctx, federatedUserFollower.FederationHostID) + if err != nil { + return err + } + + hostURL := federationHost.AsURL() + if err := deliveryQueue.Push(deliveryQueueItem{ + InboxURL: hostURL.JoinPath(federatedUserFollower.InboxPath).String(), + Doer: doer, + Payload: payload, + }); err != nil { + return err + } + } + + return nil +} + +func NotifyActivityPubFollowers(ctx context.Context, actions []activities_model.Action) error { + if !setting.Federation.Enabled { + return nil + } + for _, act := range actions { + if act.Repo != nil { + if act.Repo.IsPrivate { + continue + } + if act.Repo.Owner.KeepActivityPrivate || act.Repo.Owner.Visibility != structs.VisibleTypePublic { + continue + } + } + if act.ActUser.KeepActivityPrivate || act.ActUser.Visibility != structs.VisibleTypePublic { + continue + } + if err := SendUserActivity(ctx, act.ActUser, &act); err != nil { + return err + } + } + return nil +} diff --git a/services/feed/action.go b/services/feed/action.go index 7d179bd1c8..c708ae5404 100644 --- a/services/feed/action.go +++ b/services/feed/action.go @@ -19,6 +19,7 @@ import ( "forgejo.org/modules/repository" "forgejo.org/modules/setting" "forgejo.org/modules/util" + federation_service "forgejo.org/services/federation" notify_service "forgejo.org/services/notify" ) @@ -40,21 +41,19 @@ func NewNotifier() notify_service.Notifier { } func notifyAll(ctx context.Context, action *activities_model.Action) error { - _, err := activities_model.NotifyWatchers(ctx, action) + out, err := activities_model.NotifyWatchers(ctx, action) if err != nil { return err } - return err - // return federation_service.NotifyActivityPubFollowers(ctx, out) + return federation_service.NotifyActivityPubFollowers(ctx, out) } func notifyAllActions(ctx context.Context, acts []*activities_model.Action) error { - _, err := activities_model.NotifyWatchersActions(ctx, acts) + out, err := activities_model.NotifyWatchersActions(ctx, acts) if err != nil { return err } - return nil - // return federation_service.NotifyActivityPubFollowers(ctx, out) + return federation_service.NotifyActivityPubFollowers(ctx, out) } func (a *actionNotifier) NewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 492f9487f3..353abe76c0 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -142,6 +142,72 @@ } } }, + "/activitypub/user-id/{user-id}/activities/{activity-id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "activitypub" + ], + "summary": "Get a specific activity object of the user", + "operationId": "activitypubPersonActivityNote", + "parameters": [ + { + "type": "integer", + "description": "user ID of the user", + "name": "user-id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "activity ID of the sought activity", + "name": "activity-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/ActivityPub" + } + } + } + }, + "/activitypub/user-id/{user-id}/activities/{activity-id}/activity": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "activitypub" + ], + "summary": "Get a specific activity of the user", + "operationId": "activitypubPersonActivity", + "parameters": [ + { + "type": "integer", + "description": "user ID of the user", + "name": "user-id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "activity ID of the sought activity", + "name": "activity-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/ActivityPub" + } + } + } + }, "/activitypub/user-id/{user-id}/inbox": { "post": { "produces": [ @@ -168,6 +234,35 @@ } } }, + "/activitypub/user-id/{user-id}/outbox": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "activitypub" + ], + "summary": "List the user's recorded activity", + "operationId": "activitypubPersonFeed", + "parameters": [ + { + "type": "integer", + "description": "user ID of the user", + "name": "user-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/PersonFeed" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + } + }, "/admin/cron": { "get": { "produces": [ @@ -21121,6 +21216,28 @@ }, "x-go-package": "forgejo.org/services/context" }, + "APPersonFollowItem": { + "type": "object", + "properties": { + "actor_id": { + "type": "string", + "x-go-name": "ActorID" + }, + "note": { + "type": "string", + "x-go-name": "Note" + }, + "original_item": { + "type": "string", + "x-go-name": "OriginalItem" + }, + "original_url": { + "type": "string", + "x-go-name": "OriginalURL" + } + }, + "x-go-package": "forgejo.org/modules/structs" + }, "AccessToken": { "type": "object", "title": "AccessToken represents an API access token.", @@ -29566,6 +29683,15 @@ } } }, + "PersonFeed": { + "description": "Personfeed", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/APPersonFollowItem" + } + } + }, "PublicKey": { "description": "PublicKey", "schema": { diff --git a/tests/integration/api_activitypub_person_inbox_useractivity_test.go b/tests/integration/api_activitypub_person_inbox_useractivity_test.go new file mode 100644 index 0000000000..39f08f4d9c --- /dev/null +++ b/tests/integration/api_activitypub_person_inbox_useractivity_test.go @@ -0,0 +1,100 @@ +// Copyright 2022, 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + "time" + + auth_model "forgejo.org/models/auth" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/activitypub" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/modules/test" + "forgejo.org/routers" + "forgejo.org/services/contexttest" + "forgejo.org/services/federation" + "forgejo.org/tests" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestActivityPubPersonInboxNoteToDistant(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + federation.Init() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, localUrl *url.URL) { + defer test.MockVariableValue(&setting.AppURL, localUrl.String())() + + distantURL := federatedSrv.URL + distantUser15URL := fmt.Sprintf("%s/api/v1/activitypub/user-id/15", distantURL) + + localUser2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + localUser2URL := localUrl.JoinPath("/api/v1/activitypub/user-id/2").String() + localUser2Inbox := fmt.Sprintf("%v/inbox", localUser2URL) + localSession2 := loginUser(t, localUser2.LoginName) + localSecssion2Token := getTokenForLoggedInUser(t, localSession2, auth_model.AccessTokenScopeWriteIssue) + + repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, localUser2, tests.DeclarativeRepoOptions{}) + defer f() + + // follow (distant follows local) + followActivity := []byte(fmt.Sprintf( + `{"type":"Follow",`+ + `"actor":"%s",`+ + `"object":"%s"}`, + distantUser15URL, + localUser2URL, + )) + ctx, _ := contexttest.MockAPIContext(t, localUser2Inbox) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + c, err := cf.WithKeysDirect(ctx, mock.ApActor.PrivKey, + mock.ApActor.KeyID(federatedSrv.URL)) + require.NoError(t, err) + resp, err := c.Post(followActivity, localUser2Inbox) + require.NoError(t, err) + assert.Equal(t, http.StatusAccepted, resp.StatusCode) + + // local action which triggers a user activity + IssueURL := fmt.Sprintf("/api/v1/repos/%s/issues?state=all", repo.FullName()) + req := NewRequestWithJSON(t, "POST", IssueURL, &structs.CreateIssueOption{ + Title: "ActivityFeed test", + Body: "Nothing to see here!", + }).AddTokenAuth(localSecssion2Token) + MakeRequest(t, req, http.StatusCreated) + + // distant request outbox + localUser2Outbox := fmt.Sprintf("%v/outbox", localUser2URL) + resp, err = c.Get(localUser2Outbox) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // distant request activity & activity note + localUser2ActivityNote := fmt.Sprintf("%v/activities/1", localUser2URL) + localUser2Activity := fmt.Sprintf("%v/activities/1/activity", localUser2URL) + resp, err = c.Get(localUser2ActivityNote) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + resp, err = c.Get(localUser2Activity) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + // check for activity on distant inbox + assert.Contains(t, mock.LastPost, "user2 opened issue") + }) +} diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go index 04d1fb1648..bd21c13612 100644 --- a/tests/integration/api_activitypub_person_test.go +++ b/tests/integration/api_activitypub_person_test.go @@ -39,9 +39,6 @@ func TestActivityPubPerson(t *testing.T) { localUserName := "user2" localUserURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", localUrl, localUserID) - // distantURL := federatedSrv.URL - // distantUser15URL := fmt.Sprintf("%s/api/v1/activitypub/user-id/15", distantURL) - // Unsigned request t.Run("UnsignedRequest", func(t *testing.T) { req := NewRequest(t, "GET", localUserURL) @@ -111,3 +108,30 @@ func TestActivityPubPersonInbox(t *testing.T) { assert.Equal(t, http.StatusNotAcceptable, resp.StatusCode) }) } + +func TestActivityPubPersonOutbox(t *testing.T) { + defer test.MockVariableValue(&setting.Federation.Enabled, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) + defer federatedSrv.Close() + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + defer test.MockVariableValue(&setting.AppURL, u.String())() + user2outboxurl := u.JoinPath("/api/v1/activitypub/user-id/2/outbox").String() + + ctx, _ := contexttest.MockAPIContext(t, user2outboxurl) + cf, err := activitypub.NewClientFactoryWithTimeout(60 * time.Second) + require.NoError(t, err) + + c, err := cf.WithKeysDirect(ctx, mock.Persons[0].PrivKey, + mock.Persons[0].KeyID(federatedSrv.URL)) + require.NoError(t, err) + + // request outbox + resp, err := c.Get(user2outboxurl) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) +} From 16a0c97fbf0255e632d0ea2244bf02a4dad0703a Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 6 Aug 2025 17:51:36 +0200 Subject: [PATCH 240/297] fix: correct release link in feed (#8802) Resolves forgejo/forgejo#8793 ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. ### 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/.md` to be be used for the release notes instead of the title. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8802): correct release link in feed Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8802 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- routers/web/feed/release.go | 2 +- tests/integration/release_feed_test.go | 41 +++++++++++++++----------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go index 646241c021..d24fa6ecc7 100644 --- a/routers/web/feed/release.go +++ b/routers/web/feed/release.go @@ -29,7 +29,7 @@ func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleas if isReleasesOnly { title = ctx.Locale.TrString("repo.release.releases_for", repo.FullName()) - link = &feeds.Link{Href: repo.HTMLURL() + "/release"} + link = &feeds.Link{Href: repo.HTMLURL() + "/releases"} } else { title = ctx.Locale.TrString("repo.release.tags_for", repo.FullName()) link = &feeds.Link{Href: repo.HTMLURL() + "/tags"} diff --git a/tests/integration/release_feed_test.go b/tests/integration/release_feed_test.go index e1781e343e..eded0459c8 100644 --- a/tests/integration/release_feed_test.go +++ b/tests/integration/release_feed_test.go @@ -4,6 +4,7 @@ package integration import ( + "fmt" "net/http" "regexp" "testing" @@ -27,50 +28,56 @@ func TestReleaseFeed(t *testing.T) { t.Run("RSS feed", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/releases.rss"), http.StatusOK) - assert.Equal(t, ` + releasesPath := "/user2/repo1/releases" + MakeRequest(t, NewRequest(t, "GET", releasesPath), http.StatusOK) + + resp := MakeRequest(t, NewRequest(t, "GET", releasesPath+".rss"), http.StatusOK) + assert.Equal(t, fmt.Sprintf(` Releases for user2/repo1 - http://localhost/user2/repo1/release + http://localhost%[1]s pre-release - http://localhost/user2/repo1/releases/tag/v1.0 + http://localhost%[1]s/tag/v1.0 some text for a pre release

    ]]>
    user2 - 5: http://localhost/user2/repo1/releases/tag/v1.0 + 5: http://localhost%[1]s/tag/v1.0
    testing-release - http://localhost/user2/repo1/releases/tag/v1.1 + http://localhost%[1]s/tag/v1.1 user2 - 1: http://localhost/user2/repo1/releases/tag/v1.1 + 1: http://localhost%[1]s/tag/v1.1
    -
    `, normalize(resp.Body.String())) +
    `, releasesPath), normalize(resp.Body.String())) }) t.Run("Atom feed", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/releases.atom"), http.StatusOK) - assert.Equal(t, ` + releasesPath := "/user2/repo1/releases" + MakeRequest(t, NewRequest(t, "GET", releasesPath), http.StatusOK) + + resp := MakeRequest(t, NewRequest(t, "GET", releasesPath+".atom"), http.StatusOK) + assert.Equal(t, fmt.Sprintf(` Releases for user2/repo1 - http://localhost/user2/repo1/release + http://localhost%[1]s - + pre-release - 5: http://localhost/user2/repo1/releases/tag/v1.0 + 5: http://localhost%[1]s/tag/v1.0 <p dir="auto">some text for a pre release</p> - + user2 user2@noreply.example.org @@ -79,13 +86,13 @@ func TestReleaseFeed(t *testing.T) { testing-release - 1: http://localhost/user2/repo1/releases/tag/v1.1 - + 1: http://localhost%[1]s/tag/v1.1 + user2 user2@noreply.example.org -`, normalize(resp.Body.String())) +`, releasesPath), normalize(resp.Body.String())) }) } From b51f97e97d55a8d55a2975d2ebcadff0d26638f1 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 6 Aug 2025 20:25:13 +0200 Subject: [PATCH 241/297] feat: add option to allow non-local users to change usernames (#8714) Add a new config option for OAuth2 authentication sources: allow users to change their username. In the case where OAuth2 is more like a social OAuth2 login there's no need to not allow users to change their username. The information how the user is linked to the authentication source is stored in different fields. Resolves forgejo/forgejo#687 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8714 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- cmd/admin_auth_oauth.go | 9 +++++ cmd/admin_auth_oauth_test.go | 2 ++ models/user/fixtures/login_source.yml | 8 +++++ models/user/fixtures/user.yml | 1 + options/locale_next/locale_en-US.json | 2 ++ routers/web/admin/auths.go | 1 + services/auth/source/oauth2/source.go | 1 + services/forms/auth_form.go | 1 + services/user/user.go | 25 +++++++++++-- services/user/user_test.go | 40 +++++++++++++-------- templates/admin/auth/edit.tmpl | 7 ++++ templates/admin/auth/source/oauth.tmpl | 7 ++++ tests/integration/admin_auth_source_test.go | 32 +++++++++++++++++ 13 files changed, 119 insertions(+), 17 deletions(-) create mode 100644 models/user/fixtures/login_source.yml create mode 100644 tests/integration/admin_auth_source_test.go diff --git a/cmd/admin_auth_oauth.go b/cmd/admin_auth_oauth.go index 8a756480cd..ef5d9116e3 100644 --- a/cmd/admin_auth_oauth.go +++ b/cmd/admin_auth_oauth.go @@ -125,6 +125,10 @@ func oauthCLIFlags() []cli.Flag { Name: "group-team-map-removal", Usage: "Activate automatic team membership removal depending on groups", }, + &cli.BoolFlag{ + Name: "allow-username-change", + Usage: "Allow users to change their username", + }, } } @@ -176,6 +180,7 @@ func parseOAuth2Config(_ context.Context, c *cli.Command) *oauth2.Source { RestrictedGroup: c.String("restricted-group"), GroupTeamMap: c.String("group-team-map"), GroupTeamMapRemoval: c.Bool("group-team-map-removal"), + AllowUsernameChange: c.Bool("allow-username-change"), } } @@ -277,6 +282,10 @@ func (a *authService) updateOauth(ctx context.Context, c *cli.Command) error { oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal") } + if c.IsSet("allow-username-change") { + oAuth2Config.AllowUsernameChange = c.Bool("allow-username-change") + } + // update custom URL mapping customURLMapping := &oauth2.CustomURLMapping{} diff --git a/cmd/admin_auth_oauth_test.go b/cmd/admin_auth_oauth_test.go index ea5442e62d..3430ad1f56 100644 --- a/cmd/admin_auth_oauth_test.go +++ b/cmd/admin_auth_oauth_test.go @@ -55,6 +55,7 @@ func TestAddOauth(t *testing.T) { "--restricted-group", "restricted", "--group-team-map", `{"org_a_team_1": {"organization-a": ["Team 1"]}, "org_a_all_teams": {"organization-a": ["Team 1", "Team 2", "Team 3"]}}`, "--group-team-map-removal", + "--allow-username-change", }, source: &auth.Source{ Type: auth.OAuth2, @@ -83,6 +84,7 @@ func TestAddOauth(t *testing.T) { GroupTeamMapRemoval: true, RestrictedGroup: "restricted", SkipLocalTwoFA: true, + AllowUsernameChange: true, }, }, }, diff --git a/models/user/fixtures/login_source.yml b/models/user/fixtures/login_source.yml new file mode 100644 index 0000000000..3950f85964 --- /dev/null +++ b/models/user/fixtures/login_source.yml @@ -0,0 +1,8 @@ +- + id: 1001 + type: 6 # OAuth2 + name: OAuth2 authentication source + is_active: 1 + cfg: '{"Provider":"invalid","ClientID":"invalid","ClientSecret":"invalid","AllowUsernameChange":true}' + created_unix: 1753740851 + updated_unix: 1753740851 diff --git a/models/user/fixtures/user.yml b/models/user/fixtures/user.yml index b1892f331b..137064a368 100644 --- a/models/user/fixtures/user.yml +++ b/models/user/fixtures/user.yml @@ -11,6 +11,7 @@ must_change_password: false login_source: 1001 login_name: 123 + login_type: 6 type: 5 salt: ZogKvWdyEx max_repo_creation: -1 diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index e730c84726..aa8d811bd3 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -106,6 +106,8 @@ "discussion.sidebar.reference": "Reference", "editor.textarea.tab_hint": "Line already indented. Press Tab again or Escape to leave the editor.", "editor.textarea.shift_tab_hint": "No indentation on this line. Press Shift + Tab again or Escape to leave the editor.", + "admin.auths.allow_username_change": "Allow username change", + "admin.auths.allow_username_change.description": "Allow users to change their username in the profile settings", "admin.dashboard.cleanup_offline_runners": "Cleanup offline runners", "admin.dashboard.remove_resolved_reports": "Remove resolved reports", "settings.visibility.description": "Profile visibility affects others' ability to access your non-private repositories. Learn more.", diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 2c6dc76305..c352b6ad1a 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -190,6 +190,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source { AdminGroup: form.Oauth2AdminGroup, GroupTeamMap: form.Oauth2GroupTeamMap, GroupTeamMapRemoval: form.Oauth2GroupTeamMapRemoval, + AllowUsernameChange: form.AllowUsernameChange, } } diff --git a/services/auth/source/oauth2/source.go b/services/auth/source/oauth2/source.go index 5245f88270..a3126cf353 100644 --- a/services/auth/source/oauth2/source.go +++ b/services/auth/source/oauth2/source.go @@ -29,6 +29,7 @@ type Source struct { GroupTeamMapRemoval bool RestrictedGroup string SkipLocalTwoFA bool `json:",omitempty"` + AllowUsernameChange bool // reference to the authSource authSource *auth.Source diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index e665ca0d19..b89e87f749 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -79,6 +79,7 @@ type AuthenticationForm struct { SkipLocalTwoFA bool GroupTeamMap string `binding:"ValidGroupTeamMap"` GroupTeamMapRemoval bool + AllowUsernameChange bool } // Validate validates fields diff --git a/services/user/user.go b/services/user/user.go index d682d5a434..9cb6858f0c 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -13,6 +13,7 @@ import ( "forgejo.org/models" asymkey_model "forgejo.org/models/asymkey" + "forgejo.org/models/auth" "forgejo.org/models/db" "forgejo.org/models/organization" packages_model "forgejo.org/models/packages" @@ -25,6 +26,7 @@ import ( "forgejo.org/modules/storage" "forgejo.org/modules/util" "forgejo.org/services/agit" + "forgejo.org/services/auth/source/oauth2" org_service "forgejo.org/services/org" "forgejo.org/services/packages" container_service "forgejo.org/services/packages/container" @@ -49,9 +51,26 @@ func renameUser(ctx context.Context, u *user_model.User, newUserName string, doe // Non-local users are not allowed to change their username. // If the doer is an admin, then allow the rename - they know better. if !doerIsAdmin && !u.IsOrganization() && !u.IsLocal() { - return user_model.ErrUserIsNotLocal{ - UID: u.ID, - Name: u.Name, + // If the user's authentication source is OAuth2 and that source allows for + // username changes then don't make a fuzz about it. + + if !u.IsOAuth2() { + return user_model.ErrUserIsNotLocal{ + UID: u.ID, + Name: u.Name, + } + } + + source, err := auth.GetSourceByID(ctx, u.LoginSource) + if err != nil { + return err + } + sourceCfg := source.Cfg.(*oauth2.Source) + if !sourceCfg.AllowUsernameChange { + return user_model.ErrUserIsNotLocal{ + UID: u.ID, + Name: u.Name, + } } } diff --git a/services/user/user_test.go b/services/user/user_test.go index f1cab60a6d..0747833557 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -25,6 +25,7 @@ import ( "forgejo.org/modules/setting" "forgejo.org/modules/test" "forgejo.org/modules/timeutil" + "forgejo.org/services/auth/source/oauth2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -140,23 +141,10 @@ func TestCreateUser(t *testing.T) { } func TestRenameUser(t *testing.T) { + defer unittest.OverrideFixtures("models/user/fixtures/")() require.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 21}) - t.Run("Non-Local", func(t *testing.T) { - u := &user_model.User{ - ID: 2, - Name: "old-name", - Type: user_model.UserTypeIndividual, - LoginType: auth.OAuth2, - } - require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename2"), user_model.ErrUserIsNotLocal{UID: 2, Name: "old-name"}) - - t.Run("Admin", func(t *testing.T) { - require.NoError(t, AdminRenameUser(t.Context(), u, "user_rename2")) - }) - }) - t.Run("Same username", func(t *testing.T) { require.NoError(t, RenameUser(db.DefaultContext, user, user.Name)) }) @@ -225,6 +213,30 @@ func TestRenameUser(t *testing.T) { unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-1"}) unittest.AssertExistsIf(t, true, &user_model.Redirect{LowerName: "redirect-2"}) }) + + t.Run("Non-local", func(t *testing.T) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1041, LoginSource: 1001}) + authSource := unittest.AssertExistsAndLoadBean(t, &auth.Source{ID: user.LoginSource}) + assert.False(t, user.IsLocal()) + assert.True(t, user.IsOAuth2()) + + t.Run("Allowed", func(t *testing.T) { + require.NoError(t, RenameUser(t.Context(), user, "I-am-a-local-username")) + }) + + t.Run("Not allowed", func(t *testing.T) { + authSourceCfg := authSource.Cfg.(*oauth2.Source) + authSourceCfg.AllowUsernameChange = false + authSource.Cfg = authSourceCfg + _, err := db.GetEngine(t.Context()).Cols("cfg").ID(authSource.ID).Update(authSource) + require.NoError(t, err) + + require.ErrorIs(t, RenameUser(t.Context(), user, "Another-username-change"), user_model.ErrUserIsNotLocal{UID: user.ID, Name: user.Name}) + t.Run("Admin", func(t *testing.T) { + require.NoError(t, AdminRenameUser(t.Context(), user, "Another-username-change")) + }) + }) + }) } func TestCreateUser_Issue5882(t *testing.T) { diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 1ca5573cae..11be03acc5 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -299,6 +299,13 @@

    {{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}

    +
    +
    + + +

    {{ctx.Locale.Tr "admin.auths.allow_username_change.description"}}

    +
    +
    diff --git a/templates/admin/auth/source/oauth.tmpl b/templates/admin/auth/source/oauth.tmpl index 7d0a64d269..0cb9ea56bc 100644 --- a/templates/admin/auth/source/oauth.tmpl +++ b/templates/admin/auth/source/oauth.tmpl @@ -35,6 +35,13 @@

    {{ctx.Locale.Tr "admin.auths.skip_local_two_fa_helper"}}

    +
    +
    + + +

    {{ctx.Locale.Tr "admin.auths.allow_username_change.description"}}

    +
    +
    diff --git a/tests/integration/admin_auth_source_test.go b/tests/integration/admin_auth_source_test.go new file mode 100644 index 0000000000..4b46541318 --- /dev/null +++ b/tests/integration/admin_auth_source_test.go @@ -0,0 +1,32 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later. + +package integration + +import ( + "fmt" + "net/http" + "testing" + + "forgejo.org/models/auth" + "forgejo.org/tests" +) + +func TestAdminAuthAllowUsernameChangeSetting(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user1") + + source := addAuthSource(t, map[string]string{ + "type": fmt.Sprintf("%d", auth.OAuth2), + "name": "some-name", + "is_active": "on", + "allow_username_change": "on", + "oauth2_provider": "gitlab", + }) + + response := session.MakeRequest(t, NewRequestf(t, "GET", "/admin/auths/%d", source.ID), http.StatusOK) + htmlDoc := NewHTMLParser(t, response.Body) + + htmlDoc.AssertElement(t, "#allow_username_change[checked]", true) +} From bc0c2b7d0d9a6af413da980a3449011b492cb693 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 7 Aug 2025 06:46:53 +0200 Subject: [PATCH 242/297] chore: add MariaDB to daily tests (#8810) - Add the oldest and newest supported version of MariaDB to the daily tests to be run against the integration tests. - Ref: https://codeberg.org/forgejo/forgejo/pulls/8691 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8810 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- .forgejo/workflows/testing-integration.yml | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 518b56c463..bf0ebbf0e9 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -66,3 +66,34 @@ jobs: RACE_ENABLED: true TEST_TAGS: sqlite sqlite_unlock_notify USE_REPO_TEST_DIR: 1 + test-mariadb: +# if: vars.ROLE == 'forgejo-coding' + if: vars.ROLE == 'forgejo-integration' + runs-on: docker + name: ${{ format('test-mariadb (v{0})', matrix.version) }} + strategy: + matrix: + version: ['10.6', '11.8'] + container: + image: 'data.forgejo.org/oci/node:22-bookworm' + options: --tmpfs /tmp:exec,noatime + services: + mysql: + image: ${{ format('data.forgejo.org/oci/mariadb:{0}', matrix.version) }} + env: + MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes + MARIADB_DATABASE: testgitea + options: --tmpfs /mariadb/data:noatime + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + - uses: ./.forgejo/workflows-composite/setup-env + - name: install dependencies & git >= 2.42 + uses: ./.forgejo/workflows-composite/apt-install-from + with: + packages: git git-lfs + - uses: ./.forgejo/workflows-composite/build-backend + - run: | + su forgejo -c 'make test-mysql-migration test-mysql' + timeout-minutes: 120 + env: + USE_REPO_TEST_DIR: 1 From 6faaf807ff66cd98c8834bb259b3dfa2121b630a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 7 Aug 2025 06:49:30 +0200 Subject: [PATCH 243/297] Update dependency go to v1.24.6 (forgejo) (#8807) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [go](https://go.dev/) ([source](https://github.com/golang/go)) | toolchain | patch | `1.24.5` -> `1.24.6` | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8807 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 289a3e4da0..1aeeb7ee63 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module forgejo.org go 1.24 -toolchain go1.24.5 +toolchain go1.24.6 require ( code.forgejo.org/f3/gof3/v3 v3.11.0 From a1dd77d1156f1b57d027ab0d0afa8e17a90f7299 Mon Sep 17 00:00:00 2001 From: Shiny Nematoda Date: Thu, 7 Aug 2025 14:28:52 +0200 Subject: [PATCH 244/297] chore(dep): Update module meilisearch to v0.33 (#8788) Fixes the breakages with the version update... Co-authored-by: Renovate Bot Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8788 Reviewed-by: Gusted Co-authored-by: Shiny Nematoda Co-committed-by: Shiny Nematoda --- go.mod | 2 +- go.sum | 5 +-- .../indexer/issues/meilisearch/meilisearch.go | 14 +++--- .../issues/meilisearch/meilisearch_test.go | 44 ++++++++++--------- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 1aeeb7ee63..5c6bd8e22d 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( github.com/markbates/goth v1.80.0 github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-sqlite3 v1.14.30 - github.com/meilisearch/meilisearch-go v0.31.0 + github.com/meilisearch/meilisearch-go v0.33.0 github.com/mholt/archiver/v3 v3.5.1 github.com/microcosm-cc/bluemonday v1.0.27 github.com/minio/minio-go/v7 v7.0.94 diff --git a/go.sum b/go.sum index 12642b5df1..192384aee5 100644 --- a/go.sum +++ b/go.sum @@ -267,7 +267,6 @@ github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7w github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= @@ -401,8 +400,8 @@ github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebG github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.14.30 h1:bVreufq3EAIG1Quvws73du3/QgdeZ3myglJlrzSYYCY= github.com/mattn/go-sqlite3 v1.14.30/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= -github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= +github.com/meilisearch/meilisearch-go v0.33.0 h1:vDJtB5Ba5X0XaDmrazDiX89QEpi3zuee5Lvt7FJu7Q0= +github.com/meilisearch/meilisearch-go v0.33.0/go.mod h1:dY4nxhVc0Ext8Kn7u2YohJCsEjirg80DdcOmfNezUYg= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 17a8ba2452..8c83a7f417 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -13,6 +13,7 @@ import ( indexer_internal "forgejo.org/modules/indexer/internal" inner_meilisearch "forgejo.org/modules/indexer/internal/meilisearch" "forgejo.org/modules/indexer/issues/internal" + "forgejo.org/modules/json" "github.com/meilisearch/meilisearch-go" ) @@ -100,7 +101,7 @@ func (b *Indexer) Index(_ context.Context, issues ...*internal.IndexerData) erro return nil } for _, issue := range issues { - _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).AddDocuments(issue) + _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).AddDocuments(issue, nil) if err != nil { return err } @@ -305,18 +306,13 @@ func doubleQuoteKeyword(k string) string { func convertHits(searchRes *meilisearch.SearchResponse) ([]internal.Match, error) { hits := make([]internal.Match, 0, len(searchRes.Hits)) for _, hit := range searchRes.Hits { - hit, ok := hit.(map[string]any) - if !ok { - return nil, ErrMalformedResponse - } - - issueID, ok := hit["id"].(float64) - if !ok { + var issueID int64 + if err := json.Unmarshal(hit["id"], &issueID); err != nil { return nil, ErrMalformedResponse } hits = append(hits, internal.Match{ - ID: int64(issueID), + ID: issueID, }) } return hits, nil diff --git a/modules/indexer/issues/meilisearch/meilisearch_test.go b/modules/indexer/issues/meilisearch/meilisearch_test.go index 7637e8d6b4..810de77046 100644 --- a/modules/indexer/issues/meilisearch/meilisearch_test.go +++ b/modules/indexer/issues/meilisearch/meilisearch_test.go @@ -46,30 +46,34 @@ func TestMeilisearchIndexer(t *testing.T) { } func TestConvertHits(t *testing.T) { - _, err := convertHits(&meilisearch.SearchResponse{ - Hits: []any{"aa", "bb", "cc", "dd"}, - }) - require.ErrorIs(t, err, ErrMalformedResponse) + for _, invalidID := range []string{"\"aa\"", "{\"aa\":\"123\"}", "[\"aa\"]"} { + _, err := convertHits(&meilisearch.SearchResponse{ + Hits: meilisearch.Hits{ + meilisearch.Hit{"id": []byte(invalidID)}, + }, + }) + require.ErrorIs(t, err, ErrMalformedResponse) + } validResponse := &meilisearch.SearchResponse{ - Hits: []any{ - map[string]any{ - "id": float64(11), - "title": "a title", - "content": "issue body with no match", - "comments": []any{"hey what's up?", "I'm currently bowling", "nice"}, + Hits: meilisearch.Hits{ + meilisearch.Hit{ + "id": []byte("11"), + "title": []byte("\"a title\""), + "content": []byte("\"issue body with no match\""), + "comments": []byte("[\"hey what's up?\", \"I'm currently bowling\", \"nice\"]"), }, - map[string]any{ - "id": float64(22), - "title": "Bowling as title", - "content": "", - "comments": []any{}, + meilisearch.Hit{ + "id": []byte("22"), + "title": []byte("\"Bowling as title\""), + "content": []byte("\"\""), + "comments": []byte("[]"), }, - map[string]any{ - "id": float64(33), - "title": "Bowl-ing as fuzzy match", - "content": "", - "comments": []any{}, + meilisearch.Hit{ + "id": []byte("33"), + "title": []byte("\"Bowl-ing as fuzzy match\""), + "content": []byte("\"\""), + "comments": []byte("[]"), }, }, } From a2b73b7b1126fbdfe2722cf6fcd6173a9557f550 Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Thu, 7 Aug 2025 14:32:55 +0200 Subject: [PATCH 245/297] fix(ui): wrong org dashboard links when switching dashboard context (#8688) Regression of !8239 Closes #8685 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8688 Reviewed-by: floss4good Reviewed-by: Gusted Co-authored-by: Robert Wolff Co-committed-by: Robert Wolff --- models/organization/org_list.go | 8 +++----- models/organization/org_list_test.go | 12 ++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/models/organization/org_list.go b/models/organization/org_list.go index e387936473..371993cdee 100644 --- a/models/organization/org_list.go +++ b/models/organization/org_list.go @@ -71,11 +71,8 @@ func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organiz Find(&orgs) } -// MinimalOrg represents a simple organization with only the needed columns -type MinimalOrg = Organization - // GetUserOrgsList returns all organizations the given user has access to -func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) { +func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*Organization, error) { schema, err := db.TableInfo(new(user_model.User)) if err != nil { return nil, err @@ -100,7 +97,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, } columnsStr := selectColumns.String() - var orgs []*MinimalOrg + var orgs []*Organization if err := db.GetEngine(ctx).Select(columnsStr). Table("user"). Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))). @@ -138,6 +135,7 @@ func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, for _, org := range orgs { org.NumRepos = orgCountMap[org.ID] + org.Type = user_model.UserTypeOrganization } return orgs, nil diff --git a/models/organization/org_list_test.go b/models/organization/org_list_test.go index 170e2bf131..6e8c0bac26 100644 --- a/models/organization/org_list_test.go +++ b/models/organization/org_list_test.go @@ -85,11 +85,11 @@ func TestGetUserOrgsList(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 4}) require.NoError(t, err) - if assert.Len(t, orgs, 1) { - assert.EqualValues(t, 3, orgs[0].ID) - // repo_id: 3 is in the team, 32 is public, 5 is private with no team - assert.Equal(t, 2, orgs[0].NumRepos) - } + assert.Len(t, orgs, 1) + assert.EqualValues(t, 3, orgs[0].ID) + // repo_id: 3 is in the team, 32 is public, 5 is private with no team + assert.Equal(t, 2, orgs[0].NumRepos) + assert.Equal(t, user_model.UserTypeOrganization, orgs[0].Type) } func TestGetUserOrgsListSorting(t *testing.T) { @@ -97,7 +97,7 @@ func TestGetUserOrgsListSorting(t *testing.T) { orgs, err := organization.GetUserOrgsList(db.DefaultContext, &user_model.User{ID: 1}) require.NoError(t, err) - isSorted := slices.IsSortedFunc(orgs, func(a, b *organization.MinimalOrg) int { + isSorted := slices.IsSortedFunc(orgs, func(a, b *organization.Organization) int { return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name)) }) From c0a1a604e6fce51c1770b7fbb8c97d62f3b4f1ad Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Thu, 7 Aug 2025 14:01:05 +0000 Subject: [PATCH 246/297] i18n: update of translations from Codeberg Translate Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Dirk Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Ikuyo Co-authored-by: Juno Takano Co-authored-by: Lzebulon Co-authored-by: SomeTr Co-authored-by: Vaibhav Sunder Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: earl-warren Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/hi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/nl/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_fr-FR.ini | 4 +- options/locale/locale_hi.ini | 60 ++++++++++++++++++++++++++- options/locale/locale_nl-NL.ini | 12 +++--- options/locale_next/locale_cs-CZ.json | 7 +++- options/locale_next/locale_de-DE.json | 6 ++- options/locale_next/locale_fil.json | 6 ++- options/locale_next/locale_fr-FR.json | 6 ++- options/locale_next/locale_lv-LV.json | 6 ++- options/locale_next/locale_nds.json | 6 ++- options/locale_next/locale_pt-BR.json | 3 +- options/locale_next/locale_ru-RU.json | 6 ++- options/locale_next/locale_uk-UA.json | 6 ++- 12 files changed, 110 insertions(+), 18 deletions(-) diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 90b7048df7..d1f9b805cf 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -773,7 +773,7 @@ language=Langue ui=Thème hidden_comment_types=Catégories de commentaires masqués hidden_comment_types_description=Cochez les catégories suivantes pour masquer les commentaires correspondants des fils d'activité. Par exemple, « Label » cache les commentaires du genre « Cerise a attribué le label Bug il y a 2 heures». -hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket, révision, etc. +hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket/révision/… hidden_comment_types.issue_ref_tooltip=Commentaires où l’utilisateur change la branche/étiquette associée au ticket comment_type_group_reference=Référence comment_type_group_label=Label @@ -2027,7 +2027,7 @@ milestones.filter_sort.most_issues=Le plus de tickets milestones.filter_sort.least_issues=Le moins de tickets signing.will_sign=Cette révision sera signée avec la clé « %s ». -signing.wont_sign.error=Impossible de vérifier la signature de la révision. +signing.wont_sign.error=Une erreur s'est produite lors des vérifications préliminaires à la signature de la révision. signing.wont_sign.nokey=Aucune clé n’est disponible sur cette instance pour signer cette révision. signing.wont_sign.never=Les révisions ne sont jamais signées. signing.wont_sign.always=Les révisions sont toujours signées. diff --git a/options/locale/locale_hi.ini b/options/locale/locale_hi.ini index 2f75c8d13e..c95c13df4d 100644 --- a/options/locale/locale_hi.ini +++ b/options/locale/locale_hi.ini @@ -39,4 +39,62 @@ organization = संगठन mirror = छवि settings = सेटिंग्स your_settings = आपकी सेटिंग्स -return_to_forgejo = फ़ोर्जेगो पे वापस जाएं \ No newline at end of file +return_to_forgejo = फ़ोर्जेगो पे वापस जाएं +access_token = एक्सेस टोकन +webauthn_insert_key = अपनी सिक्योरिटी की डालें +webauthn_press_button = अपनी सिक्योरिटी की पर बटन दबाएं… +webauthn_use_twofa = अपने फ़ोन से दो फैक्टर कोड लाएं +webauthn_unsupported_browser = आपका ब्राउज़र वेबौथ सपोर्ट नहीं करता। +webauthn_error_unknown = कोई अंजान एरर हुई, फिर से कोशिश करें। +webauthn_error_unable_to_process = सर्वर आपकी रिक्वेस्ट प्रोसेस नहीं कर पाया। +webauthn_error_empty = कृपया इस चाभी का नाम रखें। +webauthn_error_timeout = आपकी की पढ़ने से पहले टाइमआउट हो गया। पृष्ट रीलोड करें और फिर कोशिश करें। +new_project = नया प्रोजेक्ट +test = टेस्ट +locked = लॉक्ड +copy = कॉपी +copy_generic = क्लिपबोर्ड पर कॉपी करें +copy_url = URL कॉपी करें +copy_hash = कॉपी हैश +copy_path = राह कॉपी करें +admin_panel = वेबसाइट प्रबंधन +your_profile = प्रोफाइल +your_starred = सीतारित +new_mirror = नया मिरर +new_fork = नयी रिपॉजिटरी फोर्क +all = सब +sources = स्रोत +new_project_column = नया स्तम्भ +mirrors = मिर्रोर्स +toggle_menu = चालू-बंद करें +add_all = सब जोड़ें +remove_all = सारा हटाएं +new_org.title = नई संस्था +new_repo.link = नई रिपॉजिटरी +new_migrate.link = नया प्रवासन +new_org.link = नई संस्था +collaborative = सहयोगी +forks = फोर्क्स +issues = इश्यूज +milestones = महत्वपूर्ण +ok = ओके +cancel = रद्द करें +retry = फिर से करें +rerun = फिर से +save = सेव करें +add = जोड़ें +remove = हटाएं +remove_label_str = हटाएं आइटम “%s” +edit = संपादित करना +view = देखें +disabled = असक्षम किया गया +webauthn_sign_in = सिक्योरिटी की का बटन दबाएं, नहीं है तो फिर से प्लग करें। +webauthn_error_insecure = वेबौथ पर सिर्फ सुरक्षित कनेक्शन हो. HTTP टेस्ट के लिए ओरिजिन “लोकलहोस्ट” या “127.0.0.1” +new_repo.title = नई रिपॉजिटरी +pull_requests = पुल्ल करें +enabled = सक्षम किया गया +webauthn_error = सिक्योरिटी की रीड नहीं हो पा रही। +webauthn_error_duplicated = सिक्योरिटी की इस रिक्वेस्ट के लिए नहीं है। कृपया देखें की पहले से रजिस्टर्ड तो नहीं। +new_migrate.title = नया प्रवासन +activities = गतिविधियाँ +rerun_all = फिर से सारे काम करें \ No newline at end of file diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index d547b5bf77..3f354fe163 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -343,7 +343,7 @@ invalid_db_table = De database tabel "%s" is ongeldig: %v allow_dots_in_usernames = Sta gebruikers toe om punten te gebruiken in hun gebruikersnaam. Heeft geen invloed op bestaande accounts. enable_update_checker = Updatecontrole inschakelen invalid_password_algorithm = Ongeldig wachtwoord hash-algoritme -password_algorithm_helper = Stel het hashing-algoritme voor wachtwoorden in. De algoritmes hebben verschillende vereisten en sterkte. Het argon2-algoritme is tamelijk veilig, maar gebruikt veel geheugen en kan ongeschikt zijn voor kleine systemen. +password_algorithm_helper = Stel het hashing-algoritme voor wachtwoorden in. De algoritmes hebben verschillende vereisten en sterktes. Het argon2-algoritme is tamelijk veilig, maar gebruikt veel geheugen en kan ongeschikt zijn voor kleine systemen. run_user_helper = De gebruikersnaam van het besturingssysteem waaronder Forgejo draait. Merk op dat deze gebruiker toegang moet hebben tot de hoofdmap van de repository. require_sign_in_view.description = Beperk de inhoudstoegang tot aangemelde gebruikers. Bezoekers kunnen alleen de verificatiepagina's bezoeken. enable_update_checker_helper_forgejo = Het zal periodiek controleren op nieuwe Forgejo-versies door een TXT DNS-record op release.forgejo.org te controleren. @@ -815,7 +815,7 @@ activate_email=Stuur activatie activations_pending=Activaties in behandeling delete_email=Verwijder email_deletion=Verwijder e-mailadres -email_deletion_desc=Het e-mailadres en verwante informatie worden verwijderd uit je account. Git commits van dit e-mailadres blijven ongewijzigd. Wil je doorgaan? +email_deletion_desc=Dit e-mailadres en verwante informatie worden verwijderd uit je account. Git commits van dit e-mailadres blijven ongewijzigd. Wil je doorgaan? email_deletion_success=Het e-mailadres is verwijderd. theme_update_success=Je thema is bijgewerkt. theme_update_error=Het geselecteerde thema bestaat niet. @@ -1222,7 +1222,7 @@ migrate_items_releases=Releases migrate_repo=Migreer repository migrate.clone_address=Migreer / kloon van URL migrate.clone_address_desc=De HTTP(S) of Git "kloon" URL van een bestaande repository -migrate.github_token_desc=Je kunt hier een of meerdere tokens met komma gescheiden plaatsen om sneller te migreren door de GitHub API limiet te beperken. WAARSCHUWING: Het misbruik van deze functie kan in strijd zijn met het beleid van de serviceprovider en leiden tot het blokkeren van rekeningen. +migrate.github_token_desc=U kunt hier een of meer tokens plaatsen, gescheiden door komma's, om de migratie te versnellen door de GitHub API-limiet te omzeilen. WAARSCHUWING: Misbruik van deze functie kan in strijd zijn met het beleid van de serviceprovider en kan ertoe leiden dat uw account(s) worden geblokkeerd. migrate.clone_local_path=of een lokaal pad migrate.permission_denied=U bent niet gemachtigd om deze lokale repositories te importeren. migrate.permission_denied_blocked=Je kunt niet importeren uit niet-toegestane hosts, vraag de beheerder om de instellingen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS te controleren. @@ -2495,7 +2495,7 @@ issues.label_archive = Label archiveren issues.label_exclusive_warning = Eventuele conflicterende scoped labels worden verwijderd bij het bewerken van de labels van een issue of pull request. issues.unpin_comment = ontpind dit %s pulls.show_changes_since_your_last_review = Wijzigingen weergeven sinds je laatste beoordeling -mirror_address_url_invalid = De opgegeven URL is ongeldig. Je moet alle componenten van de URL correct escapen. +mirror_address_url_invalid = De opgegeven URL is ongeldig. Zorg ervoor dat de onderdelen van de URL correct worden geëscape. desc.sha256 = SHA256 form.name_reserved = De repository naam "%s" is gereserveerd. form.name_pattern_not_allowed = Het patroon "%s" is niet toegestaan in een repository naam. @@ -2538,7 +2538,7 @@ editor.directory_is_a_file = Mapnaam "%s" wordt al gebruikt als bestandsnaam in commits.renamed_from = Hernoemd van %s projects.card_type.desc = Kaart voorbeeld pulls.filter_changes_by_commit = Filter op commit -pulls.nothing_to_compare_have_tag = De geselecteerde branch/tag zijn gelijk. +pulls.nothing_to_compare_have_tag = De geselecteerde branches/tags zijn gelijk. pulls.merged_success = Pull request succesvol samengevoegd en gesloten pulls.closed = Pull request gesloten pulls.merged_info_text = De branch %s kan nu verwijderd worden. @@ -2557,7 +2557,7 @@ signing.wont_sign.parentsigned = De vastlegging zal niet ondertekend worden, omd signing.wont_sign.headsigned = De samenvoeging zal niet ondertekend worden, omdat de hoofd commit niet ondertekend is. signing.wont_sign.commitssigned = De samenvoeging zal niet getekend worden, omdat alle geassocieerde commits niet getekend zijn. signing.wont_sign.never = Commits worden nooit ondertekend. -signing.wont_sign.error = Er is een fout opgetreden tijdens het controleren of de commit ondertekend kon worden. +signing.wont_sign.error = Er is een fout opgetreden bij het controleren of de commit kon worden ondertekend. signing.will_sign = Deze commit wordt ondertekend met sleutel "%s". milestones.filter_sort.latest_due_date = Uiterste vervaldatum milestones.filter_sort.earliest_due_data = Dichtstbijzijnde vervaldatum diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 8de6c46ad8..e7ea7df21b 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -118,5 +118,10 @@ "admin.moderation.moderation_reports": "Hlášení moderace", "admin.moderation.reports": "Hlášení", "admin.moderation.deleted_content_ref": "Nahlášený obsah s typem %[1]v a ID %[2]d již neexistuje", - "admin.moderation.no_open_reports": "Momentálně nejsou otevřena žádná hlášení." + "admin.moderation.no_open_reports": "Momentálně nejsou otevřena žádná hlášení.", + "admin.dashboard.remove_resolved_reports": "Odstranit vyřešená hlášení", + "compare.branches.title": "Porovnat větve", + "repo.commit.load_tags_failed": "Načtení značek selhalo z důvodu interní chyby", + "admin.auths.allow_username_change": "Povolit změnu uživatelského jména", + "admin.auths.allow_username_change.description": "Povolit uživatelům změnit své uživatelské jméno v nastavení profilu" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 59e12c2ea8..1e4d843001 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -111,5 +111,9 @@ "admin.moderation.moderation_reports": "Moderationsmeldungen", "admin.moderation.reports": "Meldungen", "admin.moderation.no_open_reports": "Es gibt momentan keine offenen Meldungen.", - "admin.dashboard.remove_resolved_reports": "Erledigte Meldungen entfernen" + "admin.dashboard.remove_resolved_reports": "Erledigte Meldungen entfernen", + "compare.branches.title": "Branches vergleichen", + "repo.commit.load_tags_failed": "Laden von Tags aufgrund eines internen Fehlers fehlgeschlagen", + "admin.auths.allow_username_change": "Erlaube Benutzernamenswechsel", + "admin.auths.allow_username_change.description": "Erlaube Benutzern, ihren Benutzernamen in den Profileinstellungen zu ändern" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index c07a6b152f..1b9eb4c85a 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -111,5 +111,9 @@ "admin.moderation.no_open_reports": "Kasalukuyang walang mga nakabukas na ulat.", "admin.moderation.reports": "Mga ulat", "admin.moderation.deleted_content_ref": "Hindi na umiiral ang inulat na nilalaman na may uri na %[1]v at ID %[2]d", - "admin.dashboard.remove_resolved_reports": "Tanggalin ang mga naresolbang ulat" + "admin.dashboard.remove_resolved_reports": "Tanggalin ang mga naresolbang ulat", + "compare.branches.title": "Ikumpara ang mga branch", + "repo.commit.load_tags_failed": "Nabigo ang pag-load ng mga tag dahil sa isang panloob na error", + "admin.auths.allow_username_change": "Payagan ang pagpalit ng username", + "admin.auths.allow_username_change.description": "Payagan ang mga user na palitan ang kanilang username sa mga setting ng profile" } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index 97b2f1733a..9c2af655a0 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -110,5 +110,9 @@ "profile.actions.tooltip": "Plus d'actions", "profile.edit.link": "Éditer le profil", "keys.ssh.link": "Clé SSH", - "keys.gpg.link": "Clés GPG" + "keys.gpg.link": "Clés GPG", + "compare.branches.title": "Comparer les branches", + "repo.settings.push_mirror.branch_filter.description": "Branches a répliquer. Laisser vide pour répliquer toutes les branches. Voir la documentation %[2] pour la syntaxe. Exemples: main, release/*", + "mail.actions.run_info_sha": "Commit: %[1]s", + "admin.moderation.deleted_content_ref": "Le signalement avec le type %[1]v et l'identifiant %[2]d n'existe plus." } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index bf6e4729b3..e0ded3a53b 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -119,5 +119,9 @@ "admin.moderation.moderation_reports": "Satura pārraudzības pārskati", "admin.moderation.reports": "Pārskati", "admin.moderation.deleted_content_ref": "Saturs, par kuru ziņots, ar veidu %[1]v un Id %[2]d vairs nepastāv", - "admin.dashboard.remove_resolved_reports": "Noņemt atrisinātos ziņojumus" + "admin.dashboard.remove_resolved_reports": "Noņemt atrisinātos ziņojumus", + "compare.branches.title": "Salīdzināt zarus", + "admin.auths.allow_username_change": "Atļaut lietotājvārda mainīšanu", + "repo.commit.load_tags_failed": "Birku ielādēšana neizdevās iekšējas kļūdas dēļ", + "admin.auths.allow_username_change.description": "Ļaut lietotājiem profila iestatījumos mainīt savu lietotājvārdu" } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 66c70939f4..9c6505b49e 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -111,5 +111,9 @@ "admin.moderation.no_open_reports": "Dat gifft jüüst keene open Berichten.", "admin.moderation.reports": "Berichten", "admin.moderation.deleted_content_ref": "Mellt Inholl mit Aard %[1]v un Kennteken %[2]d gifft dat nich mehr", - "admin.dashboard.remove_resolved_reports": "Lööst Berichten wegdoon" + "admin.dashboard.remove_resolved_reports": "Lööst Berichten wegdoon", + "compare.branches.title": "Twiegen verglieken", + "repo.commit.load_tags_failed": "Kunn de Markens um eenen binnern Fehler nich laden", + "admin.auths.allow_username_change.description": "Verlööv Brukers, hör Brukernaam in de Profil-Instellens to ännern", + "admin.auths.allow_username_change": "Brukernaam-Ännern verlöven" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 74dd2c4b83..a2fb841c39 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -119,5 +119,6 @@ "admin.moderation.moderation_reports": "Denúncias para moderação", "admin.moderation.reports": "Denúncias", "admin.moderation.no_open_reports": "Não há denúncias abertas atualmente.", - "admin.dashboard.remove_resolved_reports": "Remover denúncias resolvidas" + "admin.dashboard.remove_resolved_reports": "Remover denúncias resolvidas", + "compare.branches.title": "Comparar branches" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index e15c8ffa27..0838e9063e 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -119,5 +119,9 @@ "admin.moderation.deleted_content_ref": "Содержимое типа %[1]v и ид. %[2]d, на которое пожаловались, более не существует", "admin.moderation.reports": "Жалобы", "admin.moderation.no_open_reports": "Нет открытых жалоб.", - "admin.dashboard.remove_resolved_reports": "Удалить разрешённые жалобы" + "admin.dashboard.remove_resolved_reports": "Удалить разрешённые жалобы", + "compare.branches.title": "Сравнение ветвей", + "repo.commit.load_tags_failed": "Загрузка тегов не удалась из-за внутренней ошибки", + "admin.auths.allow_username_change.description": "Пользователи смогут изменять свои имена после регистрации", + "admin.auths.allow_username_change": "Разрешить смену имён" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 8c8ade33c9..dc410f29a2 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -119,5 +119,9 @@ "admin.moderation.reports": "Скарги", "admin.moderation.deleted_content_ref": "Вміст типу %[1]v з ідентифікатором %[2]d, на який подано скаргу, більше не існує", "admin.moderation.moderation_reports": "Скарги модераторам", - "admin.dashboard.remove_resolved_reports": "Видалити закриті скарги" + "admin.dashboard.remove_resolved_reports": "Видалити закриті скарги", + "compare.branches.title": "Порівняти гілки", + "repo.commit.load_tags_failed": "Завантаження тегів не вдалося через внутрішню помилку", + "admin.auths.allow_username_change.description": "Дозволити користувачам змінювати ім'я користувача в налаштуваннях профілю", + "admin.auths.allow_username_change": "Дозволити змінювати імена" } From 4e2908ff9d3643c40eb4c85a36c9cfa83b7dadc8 Mon Sep 17 00:00:00 2001 From: viceice Date: Fri, 8 Aug 2025 01:43:32 +0200 Subject: [PATCH 247/297] ci: fix mariadb path (#8815) Ref: forgejo/forgejo#8810 https://hub.docker.com/_/mariadb#where-to-store-data Signed-off-by: viceice Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8815 Reviewed-by: Gusted Co-authored-by: viceice Co-committed-by: viceice --- .forgejo/workflows/testing-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index bf0ebbf0e9..6822c45f39 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -83,7 +83,7 @@ jobs: env: MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes MARIADB_DATABASE: testgitea - options: --tmpfs /mariadb/data:noatime + options: --tmpfs /var/lib/mysql:noatime steps: - uses: https://data.forgejo.org/actions/checkout@v4 - uses: ./.forgejo/workflows-composite/setup-env From c872758c054b6a04b90fdb307c996aeb746d3684 Mon Sep 17 00:00:00 2001 From: BtbN Date: Fri, 8 Aug 2025 01:51:55 +0200 Subject: [PATCH 248/297] feat: improve `mysqlGetNextResourceIndex` to use modern MariaDB features (#8691) MariaDB supports "INSERT ... RETURNING ..." since 10.5.0, so this patch makes `mysqlGetNextResourceIndex` use that, provided the query is run on MariaDB of a sufficient version. If it's not supported it proceeds as it always did. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8691 Reviewed-by: Gusted Co-authored-by: BtbN Co-committed-by: BtbN --- models/db/index.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/models/db/index.go b/models/db/index.go index 4c15dbe8a1..c069f0febd 100644 --- a/models/db/index.go +++ b/models/db/index.go @@ -72,14 +72,19 @@ func postgresGetNextResourceIndex(ctx context.Context, tableName string, groupID } func mysqlGetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) { - if _, err := GetEngine(ctx).Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ - "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", - tableName), groupID); err != nil { + res, err := GetEngine(ctx).Query(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ + "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1 /*M!100500 RETURNING max_index */", + tableName), groupID) + if err != nil { return 0, err } + if len(res) > 0 { + return strconv.ParseInt(string(res[0]["max_index"]), 10, 64) + } + var idx int64 - _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&idx) + _, err = GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&idx) if err != nil { return 0, err } From 72bac983657c561b0318d908ecbaae3959d7a81e Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 8 Aug 2025 01:53:23 +0200 Subject: [PATCH 249/297] feat: require `data-modal-id` for delete buttons (#8711) All instances should have a `data-modal-id` now. Throw a user-friendly error if this is not the case (custom templates, or missed cases). Checked via `rg -P -e '^(?=.*delete-button)' | grep -v "data-modal-id"` Removed two instances of delete modals and one case of simplified logic. ## Rationale I am currently surveying the existing modals in Forgejo in the context of eventually replacing the modals implementation with our own modal implementation. This refactor fixes one of the many inconsistencies that the current usage of modals has. It should explicitly specify which modal should be used to avoids any problems if new modals are introduced on the page (for example via https://codeberg.org/forgejo/forgejo/pulls/8662). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8711 Reviewed-by: oliverpool Co-authored-by: Gusted Co-committed-by: Gusted --- templates/admin/auth/edit.tmpl | 4 ++-- templates/admin/emails/list.tmpl | 4 ++-- templates/admin/packages/list.tmpl | 4 ++-- templates/admin/repo/list.tmpl | 4 ++-- templates/admin/stacktrace-row.tmpl | 2 +- templates/admin/stacktrace.tmpl | 2 +- templates/org/settings/delete.tmpl | 4 ++-- templates/org/team/new.tmpl | 4 ++-- templates/projects/list.tmpl | 4 ++-- templates/projects/view.tmpl | 4 ++-- templates/repo/branch/list.tmpl | 4 ++-- templates/repo/issue/labels/edit_delete_label.tmpl | 2 +- templates/repo/issue/labels/label_list.tmpl | 7 ++----- templates/repo/issue/milestones.tmpl | 4 ++-- templates/repo/issue/view_content.tmpl | 2 +- templates/repo/issue/view_content/pull.tmpl | 4 ++-- templates/repo/release/list.tmpl | 13 ------------- templates/repo/release/new.tmpl | 4 ++-- templates/repo/settings/branches.tmpl | 4 ++-- templates/repo/settings/collaboration.tmpl | 6 +++--- templates/repo/settings/deploy_keys.tmpl | 4 ++-- templates/repo/settings/webhook/base_list.tmpl | 2 +- templates/repo/settings/webhook/delete_modal.tmpl | 2 +- templates/repo/tag/list.tmpl | 4 ++-- templates/repo/wiki/view.tmpl | 4 ++-- templates/shared/actions/runner_edit.tmpl | 2 +- templates/user/dashboard/milestones.tmpl | 11 ----------- templates/webhook/shared-settings.tmpl | 2 +- web_src/js/features/common-global.js | 9 +++++---- 29 files changed, 50 insertions(+), 76 deletions(-) diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 11be03acc5..d4eaa29117 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -404,7 +404,7 @@
    - +
    @@ -421,7 +421,7 @@

    -