Template
1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo synced 2024-11-22 09:54:24 +01:00

Compare commits

..

23 commits

Author SHA1 Message Date
SomeTr 6a2b45ab8f
i18n: translated using Weblate (Ukrainian)
Currently translated at 73.3% (2680 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/
2024-11-16 17:04:39 +00:00
artnay 4d75ce4f76
i18n: translated using Weblate (Finnish)
Currently translated at 57.2% (2094 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fi/
2024-11-16 17:04:39 +00:00
xtex b2ff717263
i18n: translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (3655 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/
2024-11-16 17:04:39 +00:00
SomeTr bd117a617f
i18n: translated using Weblate (Ukrainian)
Currently translated at 73.2% (2676 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/
2024-11-16 17:04:39 +00:00
artnay c5c2f62f5b
i18n: translated using Weblate (Finnish)
Currently translated at 56.3% (2059 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fi/
2024-11-16 17:04:39 +00:00
SomeTr 8879e02af6
i18n: translated using Weblate (Ukrainian)
Currently translated at 73.1% (2675 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/
2024-11-16 17:04:39 +00:00
artnay c71510dae1
i18n: translated using Weblate (Finnish)
Currently translated at 55.8% (2040 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fi/
2024-11-16 17:04:39 +00:00
SomeTr 2db03d0d02
i18n: translated using Weblate (Ukrainian)
Currently translated at 72.8% (2661 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/
2024-11-16 17:04:39 +00:00
xtex 25bf32c838
i18n: translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (3655 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/
2024-11-16 17:04:39 +00:00
xtex 1ece127e55
i18n: translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (3655 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/
2024-11-16 17:04:38 +00:00
Edgarsons 3f49b7e81f
i18n: translated using Weblate (Latvian)
Currently translated at 72.3% (2644 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/lv/
2024-11-16 17:04:38 +00:00
xtex 5d3db32a45
i18n: translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (3655 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/
2024-11-16 17:04:38 +00:00
Edgarsons cf9826c678
i18n: translated using Weblate (Latvian)
Currently translated at 71.9% (2628 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/lv/
2024-11-16 17:04:38 +00:00
yumechi b666a1ee93
i18n: translated using Weblate (Japanese)
Currently translated at 91.7% (3353 of 3655 strings)

Translation: Forgejo/forgejo
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ja/
2024-11-16 17:04:38 +00:00
Earl Warren 569a67327c Merge pull request 'bug: correctly generate oauth2 jwt signing key' (#5986) from gusted/improve-oauth2-jwt into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5986
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
2024-11-16 17:04:31 +00:00
Gusted 146824badc Merge pull request 'feat: improve GetLatestCommitStatusForPairs' (#5983) from gusted/improve-commit-pairs into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5983
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Otto <otto@codeberg.org>
2024-11-16 15:54:40 +00:00
Earl Warren eaa66f85f6 Merge pull request '[gitea] week 2024-46 cherry pick (gitea/main -> forgejo)' (#5988) from earl-warren/wcp/2024-46 into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5988
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2024-11-16 15:49:01 +00:00
Earl Warren 969a6ab24a
chore(release-notes): notes for the week 2024-46 weekly cherry pick 2024-11-16 15:25:37 +01:00
Gusted 7d59060dc6
bug: correctly generate oauth2 jwt signing key
- When RS256, RS384, ES384, ES512 was specified as the JWT signing
algorithm they would generate RS512 and ES256 respectively.
- Added unit test.
2024-11-16 15:17:19 +01:00
silverwind 308812a82e
Fix mermaid diagram height when initially hidden (#32457)
In a hidden iframe, `document.body.clientHeight` is not reliable. Use
`IntersectionObserver` to detect the visibility change and update the
height there.

Fixes: https://github.com/go-gitea/gitea/issues/32392

<img width="885" alt="image"
src="https://github.com/user-attachments/assets/a95ef6aa-27e7-443f-9d06-400ef27919ae">

(cherry picked from commit b55a31eb6a894feb5508e350ff5e9548b2531bd6)
2024-11-16 15:12:25 +01:00
Zettat123 fc26becba4
Fix broken releases when re-pushing tags (#32435)
Fix #32427

(cherry picked from commit 35bcd667b23de29a7b0d0bf1090fb10961d3aca3)

Conflicts:
	- tests/integration/repo_tag_test.go
	  Resolved by manually copying the added test, and also manually
	  adjusting the imported Go modules.
2024-11-16 15:12:25 +01:00
Lunny Xiao 013cc1dee4
Only query team tables if repository is under org when getting assignees (#32414)
It's unnecessary to query the team table if the repository is not under
organization when getting assignees.

(cherry picked from commit 1887c75c35c1d16372b1dbe2b792e374b558ce1f)
2024-11-16 14:57:11 +01:00
Gusted 2cccc02e76
feat: improve GetLatestCommitStatusForPairs
- Simplify the function into a single SQL query. This may or may not
help with a monster query we are seeing in Codeberg that is using 400MiB
and takes 50MiB to simply log the query. The result is now capped to the
actual latest index,
- Add unit test.
2024-11-16 13:23:40 +01:00
13 changed files with 465 additions and 71 deletions

View file

@ -7,6 +7,7 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@ -18,6 +19,7 @@
target_url: https://example.com/coverage/
description: My awesome Coverage service
context: cov/awesomeness
context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@ -29,6 +31,7 @@
target_url: https://example.com/coverage/
description: My awesome Coverage service
context: cov/awesomeness
context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
creator_id: 2
-
@ -40,6 +43,7 @@
target_url: https://example.com/builds/
description: My awesome CI-service
context: ci/awesomeness
context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
creator_id: 2
-
@ -51,15 +55,41 @@
target_url: https://example.com/builds/
description: My awesome deploy service
context: deploy/awesomeness
context_hash: ae9547713a6665fc4261d0756904932085a41cf2
creator_id: 2
-
id: 6
index: 6
index: 1
repo_id: 62
state: "failure"
sha: "774f93df12d14931ea93259ae93418da4482fcc1"
target_url: "/user2/test_workflows/actions"
description: My awesome deploy service
context: deploy/awesomeness
context_hash: ae9547713a6665fc4261d0756904932085a41cf2
creator_id: 2
-
id: 7
index: 6
repo_id: 1
state: "pending"
sha: "1234123412341234123412341234123412341234"
target_url: https://example.com/builds/
description: My awesome deploy service
context: deploy/awesomeness
context_hash: ae9547713a6665fc4261d0756904932085a41cf2
creator_id: 2
-
id: 8
index: 2
repo_id: 62
state: "error"
sha: "774f93df12d14931ea93259ae93418da4482fcc1"
target_url: "/user2/test_workflows/actions"
description: "My awesome deploy service - v2"
context: deploy/awesomeness
context_hash: ae9547713a6665fc4261d0756904932085a41cf2
creator_id: 2

View file

@ -288,27 +288,18 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map[int64][]*CommitStatus, error) {
type result struct {
Index int64
RepoID int64
SHA string
}
results := make([]result, 0, len(repoSHAs))
getBase := func() *xorm.Session {
return db.GetEngine(ctx).Table(&CommitStatus{})
}
results := []*CommitStatus{}
// Create a disjunction of conditions for each repoID and SHA pair
conds := make([]builder.Cond, 0, len(repoSHAs))
for _, repoSHA := range repoSHAs {
conds = append(conds, builder.Eq{"repo_id": repoSHA.RepoID, "sha": repoSHA.SHA})
}
sess := getBase().Where(builder.Or(conds...)).
Select("max( `index` ) as `index`, repo_id, sha").
GroupBy("context_hash, repo_id, sha").OrderBy("max( `index` ) desc")
sess := db.GetEngine(ctx).Table(&CommitStatus{}).
Select("MAX(`index`) AS `index`, *").
Where(builder.Or(conds...)).
GroupBy("context_hash, repo_id, sha").OrderBy("MAX(`index`) DESC")
err := sess.Find(&results)
if err != nil {
return nil, err
@ -316,27 +307,9 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map
repoStatuses := make(map[int64][]*CommitStatus)
if len(results) > 0 {
statuses := make([]*CommitStatus, 0, len(results))
conds = make([]builder.Cond, 0, len(results))
for _, result := range results {
cond := builder.Eq{
"`index`": result.Index,
"repo_id": result.RepoID,
"sha": result.SHA,
}
conds = append(conds, cond)
}
err = getBase().Where(builder.Or(conds...)).Find(&statuses)
if err != nil {
return nil, err
}
// Group the statuses by repo ID
for _, status := range statuses {
repoStatuses[status.RepoID] = append(repoStatuses[status.RepoID], status)
}
// Group the statuses by repo ID
for _, status := range results {
repoStatuses[status.RepoID] = append(repoStatuses[status.RepoID], status)
}
return repoStatuses, nil

View file

@ -35,8 +35,8 @@ func TestGetCommitStatuses(t *testing.T) {
SHA: sha1,
})
require.NoError(t, err)
assert.Equal(t, 5, int(maxResults))
assert.Len(t, statuses, 5)
assert.EqualValues(t, 6, maxResults)
assert.Len(t, statuses, 6)
assert.Equal(t, "ci/awesomeness", statuses[0].Context)
assert.Equal(t, structs.CommitStatusPending, statuses[0].State)
@ -58,13 +58,17 @@ func TestGetCommitStatuses(t *testing.T) {
assert.Equal(t, structs.CommitStatusError, statuses[4].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext))
assert.Equal(t, "deploy/awesomeness", statuses[5].Context)
assert.Equal(t, structs.CommitStatusPending, statuses[5].State)
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[5].APIURL(db.DefaultContext))
statuses, maxResults, err = db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
ListOptions: db.ListOptions{Page: 2, PageSize: 50},
RepoID: repo1.ID,
SHA: sha1,
})
require.NoError(t, err)
assert.Equal(t, 5, int(maxResults))
assert.EqualValues(t, 6, maxResults)
assert.Empty(t, statuses)
}
@ -265,3 +269,148 @@ func TestCommitStatusesHideActionsURL(t *testing.T) {
assert.Empty(t, statuses[0].TargetURL)
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
}
func TestGetLatestCommitStatusForPairs(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
t.Run("All", func(t *testing.T) {
pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, nil)
require.NoError(t, err)
assert.EqualValues(t, map[int64][]*git_model.CommitStatus{
1: {
{
ID: 7,
Index: 6,
RepoID: 1,
State: structs.CommitStatusPending,
SHA: "1234123412341234123412341234123412341234",
TargetURL: "https://example.com/builds/",
Description: "My awesome deploy service",
ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2",
Context: "deploy/awesomeness",
CreatorID: 2,
},
{
ID: 4,
Index: 4,
State: structs.CommitStatusFailure,
TargetURL: "https://example.com/builds/",
Description: "My awesome CI-service",
Context: "ci/awesomeness",
CreatorID: 2,
RepoID: 1,
SHA: "1234123412341234123412341234123412341234",
ContextHash: "c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7",
},
{
ID: 3,
Index: 3,
State: structs.CommitStatusSuccess,
TargetURL: "https://example.com/coverage/",
Description: "My awesome Coverage service",
Context: "cov/awesomeness",
CreatorID: 2,
RepoID: 1,
SHA: "1234123412341234123412341234123412341234",
ContextHash: "3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe",
},
},
62: {
{
ID: 8,
Index: 2,
RepoID: 62,
State: structs.CommitStatusError,
TargetURL: "/user2/test_workflows/actions",
Description: "My awesome deploy service - v2",
Context: "deploy/awesomeness",
SHA: "774f93df12d14931ea93259ae93418da4482fcc1",
ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2",
CreatorID: 2,
},
},
}, pairs)
})
t.Run("Repo 1", func(t *testing.T) {
pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{1, "1234123412341234123412341234123412341234"}})
require.NoError(t, err)
assert.EqualValues(t, map[int64][]*git_model.CommitStatus{
1: {
{
ID: 7,
Index: 6,
RepoID: 1,
State: structs.CommitStatusPending,
SHA: "1234123412341234123412341234123412341234",
TargetURL: "https://example.com/builds/",
Description: "My awesome deploy service",
ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2",
Context: "deploy/awesomeness",
CreatorID: 2,
},
{
ID: 4,
Index: 4,
State: structs.CommitStatusFailure,
TargetURL: "https://example.com/builds/",
Description: "My awesome CI-service",
Context: "ci/awesomeness",
CreatorID: 2,
RepoID: 1,
SHA: "1234123412341234123412341234123412341234",
ContextHash: "c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7",
},
{
ID: 3,
Index: 3,
State: structs.CommitStatusSuccess,
TargetURL: "https://example.com/coverage/",
Description: "My awesome Coverage service",
Context: "cov/awesomeness",
CreatorID: 2,
RepoID: 1,
SHA: "1234123412341234123412341234123412341234",
ContextHash: "3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe",
},
},
}, pairs)
})
t.Run("Repo 62", func(t *testing.T) {
pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{62, "774f93df12d14931ea93259ae93418da4482fcc1"}})
require.NoError(t, err)
assert.EqualValues(t, map[int64][]*git_model.CommitStatus{
62: {
{
ID: 8,
Index: 2,
RepoID: 62,
State: structs.CommitStatusError,
TargetURL: "/user2/test_workflows/actions",
Description: "My awesome deploy service - v2",
Context: "deploy/awesomeness",
SHA: "774f93df12d14931ea93259ae93418da4482fcc1",
ContextHash: "ae9547713a6665fc4261d0756904932085a41cf2",
CreatorID: 2,
},
},
}, pairs)
})
t.Run("Repo 62 nonexistant sha", func(t *testing.T) {
pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{62, "774f93df12d14931ea93259ae93418da4482fcc"}})
require.NoError(t, err)
assert.EqualValues(t, map[int64][]*git_model.CommitStatus{}, pairs)
})
t.Run("SHA with non existant repo id", func(t *testing.T) {
pairs, err := git_model.GetLatestCommitStatusForPairs(db.DefaultContext, []git_model.RepoSHA{{1, "774f93df12d14931ea93259ae93418da4482fcc1"}})
require.NoError(t, err)
assert.EqualValues(t, map[int64][]*git_model.CommitStatus{}, pairs)
})
}

View file

@ -75,26 +75,28 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
return nil, err
}
additionalUserIDs := make([]int64, 0, 10)
if err = e.Table("team_user").
Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))",
repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests).
Distinct("`team_user`.uid").
Select("`team_user`.uid").
Find(&additionalUserIDs); err != nil {
return nil, err
}
uniqueUserIDs := make(container.Set[int64])
uniqueUserIDs.AddMultiple(userIDs...)
uniqueUserIDs.AddMultiple(additionalUserIDs...)
if repo.Owner.IsOrganization() {
additionalUserIDs := make([]int64, 0, 10)
if err = e.Table("team_user").
Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))",
repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests).
Distinct("`team_user`.uid").
Select("`team_user`.uid").
Find(&additionalUserIDs); err != nil {
return nil, err
}
uniqueUserIDs.AddMultiple(additionalUserIDs...)
}
// Leave a seat for owner itself to append later, but if owner is an organization
// and just waste 1 unit is cheaper than re-allocate memory once.
users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
if len(userIDs) > 0 {
if len(uniqueUserIDs) > 0 {
if err = e.In("id", uniqueUserIDs.Values()).
Where(builder.Eq{"`user`.is_active": true}).
OrderBy(user_model.GetOrderByName()).

View file

@ -197,6 +197,12 @@ buttons.indent.tooltip = Sisennä yhden tason verran
buttons.quote.tooltip = Lainaa tekstiä
buttons.enable_monospace_font = Käytä tasalevyistä fonttia
buttons.ref.tooltip = Viittaa ongelmaa tai vetopyyntöä
buttons.new_table.tooltip = Lisää taulukko
table_modal.header = Lisää taulukko
table_modal.placeholder.header = Otsikko
table_modal.placeholder.content = Sisältö
table_modal.label.rows = Rivit
table_modal.label.columns = Sarakkeet
[filter]
string.asc = A - Ö
@ -246,7 +252,7 @@ err_empty_db_path=SQLite3-tietokannan polku ei voi olla tyhjä.
no_admin_and_disable_registration=Et voi kytkeä rekisteröintiä pois luomatta sitä ennen ylläpitotiliä.
err_empty_admin_password=Ylläpitäjän salasana ei voi olla tyhjä.
err_empty_admin_email=Ylläpitäjän sähköpostiosoite ei voi olla tyhjä.
err_admin_name_is_reserved=Ylläpitäjän käyttäjätunnus on virheellinen: käyttäjätunnus on varattu
err_admin_name_is_reserved=Ylläpitäjän käyttäjätunnus on virheellinen; käyttäjätunnus on varattu
err_admin_name_is_invalid=Ylläpitäjän käyttäjätunnus on virheellinen
general_title=Yleiset asetukset
@ -336,6 +342,7 @@ allow_dots_in_usernames = Salli pisteiden käyttö käyttäjänimissä. Ei vaiku
enable_update_checker = Ota päivitystentarkistus käyttöön
app_slogan = Instanssin tunnuslause
app_slogan_helper = Syötä instanssin tunnuslause tähän. Jätä tyhjäksi poistaaksesi käytöstä.
domain_helper = Palvelimen verkkotunnus tai isäntänimi.
[home]
uname_holder=Käyttäjätunnus tai sähköpostiosoite
@ -381,6 +388,7 @@ stars_few = %d tähteä
relevant_repositories = Vain relevantit repositoriot näytetään, <a href="%s">näytä suodattamattomat tulokset</a>.
forks_one = %d forkki
forks_few = %d forkkia
go_to = Siirry
[auth]
create_new_account=Rekisteröi tili
@ -418,7 +426,7 @@ twofa_scratch_token_incorrect=Kertakäyttökoodisi on virheellinen.
login_userpass=Kirjaudu sisään
tab_openid=OpenID
oauth_signup_tab=Rekisteröi uusi tili
oauth_signup_title=Viimeistele tili
oauth_signup_title=Viimeistele uusi tili
oauth_signup_submit=Viimeistele tili
oauth_signin_tab=Linkitä olemassa olevaan tiliin
oauth_signin_title=Kirjaudu sisään valtuuttaaksesi linkitetyn tilin
@ -452,8 +460,8 @@ activate_account=Ole hyvä ja aktivoi tilisi
activate_email=Vahvista sähköpostiosoitteesi
register_notify=Tervetuloa %san
register_notify.text_2=Voit nyt kirjautua käyttäjätunnuksella: %s.
register_notify=Tervetuloa %s-palveluun
register_notify.text_2=Voit nyt kirjautua tilillesi käyttäjätunnuksella: %s
reset_password=Palauta käyttäjätili
reset_password.title=%s, olet pyytänyt tilisi palauttamista
@ -478,6 +486,14 @@ password_change.text_1 = Tilisi salasana vaihdettiin juuri hetki sitten.
removed_security_key.subject = Turva-avain on poistettu
removed_security_key.text_1 = Turva-avain "%[1]s" on poistettu tililtäsi.
team_invite.text_2 = Napsauta seuraavaa linkkiä liittyäksesi tiimiin:
activate_account.text_1 = Hei <b>%[1]s</b>, kiitos kun rekisteröidyit palveluun %[2]s!
activate_account.text_2 = Aktivoidaksesi tilin, napsauta alla olevaa linkkiä aikaikkunan <b>%s</b> sisällä:
totp_disabled.subject = TOTP on poistettu käytöstä
primary_mail_change.subject = Ensisijainen sähköpostiosoitteesi on vaihdettu
admin.new_user.user_info = Käyttäjätiedot
activate_email.text = Vahvista sähköpostiosoitteesi napsauttamalla linkkiä aikaikkunan <b>%s</b> sisällä:
admin.new_user.subject = Uusi käyttäjä %s rekisteröityi juuri
register_notify.text_3 = Jos joku muu teki tämän tilin puolestasi, <a href="%s">aseta salasana</a> ensin.
@ -486,13 +502,14 @@ yes=Kyllä
no=Ei
cancel=Peruuta
modify=Päivitys
confirm = Vahvista
[form]
UserName=Käyttäjätunnus
RepoName=Repon nimi
Email=Sähköposti osoite
Password=Salasana
Retype=Varmista salasana
Retype=Vahvista salasana
SSHTitle=SSH avain nimi
HttpsUrl=HTTPS-osoite
TeamName=Tiimin nimi
@ -762,7 +779,7 @@ orgs_none=Et ole minkään organisaation jäsen.
delete_account=Poista tilisi
delete_prompt=Tämä toiminto poistaa käyttäjätilisi pysyvästi. Toimintoa <strong>EI VOI</strong> kumota.
confirm_delete_account=Varmista poisto
confirm_delete_account=Vahvista poisto
delete_account_title=Poista käyttäjätili
email_notifications.enable=Ota käyttöön sähköposti-ilmoitukset
@ -807,6 +824,7 @@ email_desc = Ensisijaista sähköpostiosoitettasi käytetään ilmoituksiin, sal
tokens_desc = Nämä poletit mahdollistavat pääsyn tilillesi Forgejon rajapintaa vasten.
keep_email_private_popup = Tämä piilottaa sähköpostiosoitteesi profiilistasi. Se ei ole enää oletus verkkosivukäyttöliittymän kautta tehdyissä kommiteissa, kuten tiedostojen lähetyksissä ja muokkauksissa, eikä sitä käytetä yhdistämiskommiteissa. Sen sijaan erikoisosoitetta %s voidaan käyttää kommittien liittämisessä tiliisi. Ota huomioon, ettei tämän asetuksen muuttaminen vaikuta olemassa oleviin kommitteihin.
added_on = Lisätty %s
additional_repo_units_hint = Ehdota repositorion lisäyksiköiden käyttöönottoa
[repo]
owner=Omistaja
@ -1801,6 +1819,13 @@ milestones.filter_sort.latest_due_date = Kaukaisin määräpäivä
license_helper_desc = Lisenssi määrää, mitä muut voivat ja eivät voi tehdä koodillasi. Etkö ole varma, mikä lisenssi soveltuu projektillesi? Lue <a target="_blank" rel="noopener noreferrer" href="%s">ohje lisenssin valinnasta.</a>
milestones.filter_sort.earliest_due_data = Lähin määräpäivä
issues.filter_type.reviewed_by_you = Katselmoitu toimestasi
settings.units.overview = Yleisnäkymä
settings.remove_team_success = Tiimin pääsy repositorioon on poistettu.
migrate.cancel_migrating_confirm = Haluatko perua tämän migraation?
settings.units.units = Yksiköt
settings.update_settings_no_unit = Repositorion tulisi sallia edes jonkinlainen vuorovaikutus.
settings.units.add_more = Ota lisää käyttöön
settings.add_team_success = Tiimillä on nyt pääsy repositorioon.
@ -1903,6 +1928,9 @@ code = Koodi
teams.remove_all_repos_title = Poista kaikki tiimin repot
form.name_reserved = Organisaation nimi "%s" on varattu.
settings.delete_org_desc = Organisaatio poistetaan pysyvästi. Jatketaanko?
team_access_desc = Repositorion käyttö
teams.specific_repositories = Määritetyt repositoriot
open_dashboard = Avaa kojelauta
[admin]
dashboard=Kojelauta
@ -2301,6 +2329,7 @@ error.extract_sign = Allekirjoituksen purkaminen epäonnistui
default_key = Allekirjoitettu oletusavaimella
[units]
unit = Yksikkö
[packages]
title=Paketit
@ -2397,6 +2426,10 @@ conda.install = Asenna paketti Condalla suorittamalla seuraava komento:
helm.registry = Määritä tämä rekisteri komentoriviltä:
pub.install = Asenna paketti Dartilla suorittamalla seuraava komento:
owner.settings.cargo.title = Cargon rekisteri-indeksi
settings.delete.description = Paketin poistaminen on peruuttamaton toimenpide, sitä ei voi perua.
settings.link.success = Repositorion linkki päivitettiin onnistuneesti.
settings.link.button = Päivitä repositorion linkki
owner.settings.cleanuprules.preview.overview = %d pakettia on ajastettu poistettavaksi.
[secrets]
creation.failed = Salaisuuden lisääminen epäonnistui.
@ -2409,6 +2442,7 @@ deletion.failed = Salaisuuden poistaminen epäonnistui.
secrets = Salaisuudet
deletion.description = Salaisuuden poistaminen on pysyvä toimenpide, eikä sitä voi perua. Jatketaanko?
deletion.success = Salaisuus on poistettu.
description = Salaisuudet välitetään tietyille toimenpiteille, eikä niitä voi muuten lukea.
[actions]
@ -2496,6 +2530,8 @@ workflow.disable = Poista työnkulku käytöstä
workflow.disable_success = Työnkulku "%s" on poistettu käytöstä.
runs.no_job = Työnkulun tulee sisältää vähintään yksi työ
runs.invalid_workflow_helper = Työnkulun asetustiedosto on virheellinen. Tarkista asetustiedosto: %s
runners = Ajajat
actions = Toimenpiteet
@ -2539,4 +2575,5 @@ issue_kind = Etsi ongelmia...
milestone_kind = Etsi merkkipaaluja...
pull_kind = Etsi pull-vetoja...
commit_kind = Etsi kommitteja...
fuzzy = Sumea
fuzzy = Sumea
runner_kind = Etsi ajajia...

View file

@ -3195,6 +3195,11 @@ workflow.disabled = Робочий потік вимкнено.
workflow.enable = Увімкнути робочий потік
runs.no_workflows = Робочих потоків ще немає.
runs.all_workflows = Усі робочі потоки
runs.no_results = Не знайдено відповідних результатів.
status.failure = Помилка
status.running = Працює
status.success = Успіх
status.skipped = Пропущено

View file

@ -1103,7 +1103,7 @@ object_format_helper=仓库的对象格式。之后无法更改。SHA1 是最兼
readme=自述
readme_helper=选择自述文件模板
readme_helper_desc=这是您可以为您的项目撰写完整描述的地方。
auto_init=初始化仓库(添加. gitignore、许可证和自述文件)
auto_init=初始化仓库(添加 .gitignore、许可证和自述文件)
trust_model_helper=选择签名验证的“信任模型”。可能的选项是:
trust_model_helper_collaborator=协作者:信任协作者的签名
trust_model_helper_committer=提交者:信任与提交者相符的签名

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

@ -0,0 +1 @@
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/fc26becba4b08877a726f2e7e453992310245fe5) when a tag was removed and a release existed for that tag, it would be broken. The release is no longer broken the tag can be added again.

View file

@ -347,12 +347,30 @@ func loadOrCreateAsymmetricKey() (any, error) {
key, err := func() (any, error) {
switch {
case strings.HasPrefix(setting.OAuth2.JWTSigningAlgorithm, "RS"):
return rsa.GenerateKey(rand.Reader, 4096)
var bits int
switch setting.OAuth2.JWTSigningAlgorithm {
case "RS256":
bits = 2048
case "RS384":
bits = 3072
case "RS512":
bits = 4096
}
return rsa.GenerateKey(rand.Reader, bits)
case setting.OAuth2.JWTSigningAlgorithm == "EdDSA":
_, pk, err := ed25519.GenerateKey(rand.Reader)
return pk, err
default:
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
var curve elliptic.Curve
switch setting.OAuth2.JWTSigningAlgorithm {
case "ES256":
curve = elliptic.P256()
case "ES384":
curve = elliptic.P384()
case "ES512":
curve = elliptic.P521()
}
return ecdsa.GenerateKey(curve, rand.Reader)
}
}()
if err != nil {

View file

@ -0,0 +1,116 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package oauth2
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"os"
"path/filepath"
"testing"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLoadOrCreateAsymmetricKey(t *testing.T) {
loadKey := func(t *testing.T) any {
t.Helper()
loadOrCreateAsymmetricKey()
fileContent, err := os.ReadFile(setting.OAuth2.JWTSigningPrivateKeyFile)
require.NoError(t, err)
block, _ := pem.Decode(fileContent)
assert.NotNil(t, block)
assert.EqualValues(t, "PRIVATE KEY", block.Type)
parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
require.NoError(t, err)
return parsedKey
}
t.Run("RSA-2048", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-rsa-2048.priv"))()
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "RS256")()
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen())
t.Run("Load key with differ specified algorithm", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "EdDSA")()
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
assert.EqualValues(t, 2048, rsaPrivateKey.N.BitLen())
})
})
t.Run("RSA-3072", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-rsa-3072.priv"))()
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "RS384")()
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
assert.EqualValues(t, 3072, rsaPrivateKey.N.BitLen())
})
t.Run("RSA-4096", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-rsa-4096.priv"))()
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "RS512")()
parsedKey := loadKey(t)
rsaPrivateKey := parsedKey.(*rsa.PrivateKey)
assert.EqualValues(t, 4096, rsaPrivateKey.N.BitLen())
})
t.Run("ECDSA-256", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-ecdsa-256.priv"))()
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "ES256")()
parsedKey := loadKey(t)
ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey)
assert.EqualValues(t, 256, ecdsaPrivateKey.Params().BitSize)
})
t.Run("ECDSA-384", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-ecdsa-384.priv"))()
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "ES384")()
parsedKey := loadKey(t)
ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey)
assert.EqualValues(t, 384, ecdsaPrivateKey.Params().BitSize)
})
t.Run("ECDSA-512", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-ecdsa-512.priv"))()
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "ES512")()
parsedKey := loadKey(t)
ecdsaPrivateKey := parsedKey.(*ecdsa.PrivateKey)
assert.EqualValues(t, 521, ecdsaPrivateKey.Params().BitSize)
})
t.Run("EdDSA", func(t *testing.T) {
defer test.MockVariableValue(&setting.OAuth2.JWTSigningPrivateKeyFile, filepath.Join(t.TempDir(), "jwt-eddsa.priv"))()
defer test.MockVariableValue(&setting.OAuth2.JWTSigningAlgorithm, "EdDSA")()
parsedKey := loadKey(t)
assert.NotNil(t, parsedKey.(ed25519.PrivateKey))
})
}

View file

@ -307,9 +307,10 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
}
releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{
RepoID: repo.ID,
TagNames: tags,
IncludeTags: true,
RepoID: repo.ID,
TagNames: tags,
IncludeDrafts: true,
IncludeTags: true,
})
if err != nil {
return fmt.Errorf("db.Find[repo_model.Release]: %w", err)
@ -394,13 +395,17 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
}
newReleases = append(newReleases, rel)
} else {
rel.Title = parts[0]
rel.Note = note
rel.Sha1 = commit.ID.String()
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
rel.NumCommits = commitsCount
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
if rel.IsTag {
rel.Title = parts[0]
rel.Note = note
if author != nil {
rel.PublisherID = author.ID
}
} else {
rel.IsDraft = false
}
if err = repo_model.UpdateRelease(ctx, rel); err != nil {
return fmt.Errorf("Update: %w", err)

View file

@ -5,17 +5,20 @@
package integration
import (
"fmt"
"net/http"
"net/url"
"strings"
"testing"
"code.gitea.io/gitea/models"
auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
repo_module "code.gitea.io/gitea/modules/repository"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/release"
"code.gitea.io/gitea/tests"
@ -159,3 +162,47 @@ func TestSyncRepoTags(t *testing.T) {
})
})
}
func TestRepushTag(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
session := loginUser(t, owner.LowerName)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
httpContext := NewAPITestContext(t, owner.Name, repo.Name)
dstPath := t.TempDir()
u.Path = httpContext.GitPath()
u.User = url.UserPassword(owner.Name, userPassword)
doGitClone(dstPath, u)(t)
// create and push a tag
_, _, err := git.NewCommand(git.DefaultContext, "tag", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
_, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
// create a release for the tag
createdRelease := createNewReleaseUsingAPI(t, token, owner, repo, "v2.0", "", "Release of v2.0", "desc")
assert.False(t, createdRelease.IsDraft)
// delete the tag
_, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--delete", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
// query the release by API and it should be a draft
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0"))
resp := MakeRequest(t, req, http.StatusOK)
var respRelease *api.Release
DecodeJSON(t, resp, &respRelease)
assert.True(t, respRelease.IsDraft)
// re-push the tag
_, _, err = git.NewCommand(git.DefaultContext, "push", "origin", "--tags", "v2.0").RunStdString(&git.RunOpts{Dir: dstPath})
require.NoError(t, err)
// query the release by API and it should not be a draft
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, "v2.0"))
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &respRelease)
assert.False(t, respRelease.IsDraft)
})
}

View file

@ -56,10 +56,21 @@ export async function renderMermaid() {
btn.setAttribute('data-clipboard-text', source);
mermaidBlock.append(btn);
const updateIframeHeight = () => {
iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`;
};
// update height when element's visibility state changes, for example when the diagram is inside
// a <details> + <summary> block and the <details> block becomes visible upon user interaction, it
// would initially set a incorrect height and the correct height is set during this callback.
(new IntersectionObserver(() => {
updateIframeHeight();
}, {root: document.documentElement})).observe(iframe);
iframe.addEventListener('load', () => {
pre.replaceWith(mermaidBlock);
mermaidBlock.classList.remove('tw-hidden');
iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`;
updateIframeHeight();
setTimeout(() => { // avoid flash of iframe background
mermaidBlock.classList.remove('is-loading');
iframe.classList.remove('tw-invisible');