From f1928b49ad85a1c7a1d743a3d4deacf3f8433714 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Wed, 13 Apr 2022 23:32:54 -0400 Subject: [PATCH 0001/1287] add k/v store for repo settings --- models/repo/setting.go | 148 ++++++++++++++++++++++++++++++++++++ models/repo/setting_keys.go | 7 ++ models/repo/setting_test.go | 59 ++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 models/repo/setting.go create mode 100644 models/repo/setting_keys.go create mode 100644 models/repo/setting_test.go diff --git a/models/repo/setting.go b/models/repo/setting.go new file mode 100644 index 0000000000000..f64abbc0d0967 --- /dev/null +++ b/models/repo/setting.go @@ -0,0 +1,148 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "context" + "fmt" + "strings" + + "code.gitea.io/gitea/models/db" + + "xorm.io/builder" +) + +// Setting is a key value store of repo settings +type Setting struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"index unique(key_repoid)"` // to load all of someone's settings + SettingKey string `xorm:"varchar(255) index unique(key_repoid)"` // ensure key is always lowercase + SettingValue string `xorm:"text"` +} + +// TableName sets the table name for the settings struct +func (s *Setting) TableName() string { + return "repo_setting" +} + +func init() { + db.RegisterModel(new(Setting)) +} + +// GetRepoSettings returns specific settings from repo +func GetRepoSettings(rid int64, keys []string) (map[string]*Setting, error) { + settings := make([]*Setting, 0, len(keys)) + if err := db.GetEngine(db.DefaultContext). + Where("repo_id=?", rid). + And(builder.In("setting_key", keys)). + Find(&settings); err != nil { + return nil, err + } + settingsMap := make(map[string]*Setting) + for _, s := range settings { + settingsMap[s.SettingKey] = s + } + return settingsMap, nil +} + +// GetRepoAllSettings returns all settings from repo +func GetRepoAllSettings(rid int64) (map[string]*Setting, error) { + settings := make([]*Setting, 0, 5) + if err := db.GetEngine(db.DefaultContext). + Where("repo_id=?", rid). + Find(&settings); err != nil { + return nil, err + } + settingsMap := make(map[string]*Setting) + for _, s := range settings { + settingsMap[s.SettingKey] = s + } + return settingsMap, nil +} + +func validateRepoSettingKey(key string) error { + if len(key) == 0 { + return fmt.Errorf("setting key must be set") + } + if strings.ToLower(key) != key { + return fmt.Errorf("setting key should be lowercase") + } + return nil +} + +// GetRepoSetting gets a specific setting for a repo +func GetRepoSetting(repoID int64, key string, def ...string) (string, error) { + if err := validateRepoSettingKey(key); err != nil { + return "", err + } + setting := &Setting{RepoID: repoID, SettingKey: key} + has, err := db.GetEngine(db.DefaultContext).Get(setting) + if err != nil { + return "", err + } + if !has { + if len(def) == 1 { + return def[0], nil + } + return "", nil + } + return setting.SettingValue, nil +} + +// DeleteRepoSetting deletes a specific setting for a repo +func DeleteRepoSetting(repoID int64, key string) error { + if err := validateRepoSettingKey(key); err != nil { + return err + } + _, err := db.GetEngine(db.DefaultContext).Delete(&Setting{RepoID: repoID, SettingKey: key}) + return err +} + +// SetRepoSetting updates a repos' setting for a specific key +func SetRepoSetting(repoID int64, key, value string) error { + if err := validateRepoSettingKey(key); err != nil { + return err + } + return upsertRepoSettingValue(repoID, key, value) +} + +func upsertRepoSettingValue(repoID int64, key, value string) error { + return db.WithTx(func(ctx context.Context) error { + e := db.GetEngine(ctx) + + // here we use a general method to do a safe upsert for different databases (and most transaction levels) + // 1. try to UPDATE the record and acquire the transaction write lock + // if UPDATE returns non-zero rows are changed, OK, the setting is saved correctly + // if UPDATE returns "0 rows changed", two possibilities: (a) record doesn't exist (b) value is not changed + // 2. do a SELECT to check if the row exists or not (we already have the transaction lock) + // 3. if the row doesn't exist, do an INSERT (we are still protected by the transaction lock, so it's safe) + // + // to optimize the SELECT in step 2, we can use an extra column like `revision=revision+1` + // to make sure the UPDATE always returns a non-zero value for existing (unchanged) records. + + res, err := e.Exec("UPDATE repo_setting SET setting_value=? WHERE setting_key=? AND repo_id=?", value, key, repoID) + if err != nil { + return err + } + rows, _ := res.RowsAffected() + if rows > 0 { + // the existing row is updated, so we can return + return nil + } + + // in case the value isn't changed, update would return 0 rows changed, so we need this check + has, err := e.Exist(&Setting{RepoID: repoID, SettingKey: key}) + if err != nil { + return err + } + if has { + return nil + } + + // if no existing row, insert a new row + _, err = e.Insert(&Setting{RepoID: repoID, SettingKey: key, SettingValue: value}) + return err + }) +} diff --git a/models/repo/setting_keys.go b/models/repo/setting_keys.go new file mode 100644 index 0000000000000..651e458cbd22e --- /dev/null +++ b/models/repo/setting_keys.go @@ -0,0 +1,7 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +const () diff --git a/models/repo/setting_test.go b/models/repo/setting_test.go new file mode 100644 index 0000000000000..9f74b1826fb1e --- /dev/null +++ b/models/repo/setting_test.go @@ -0,0 +1,59 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestSettings(t *testing.T) { + keyName := "test_repo_setting" + assert.NoError(t, unittest.PrepareTestDatabase()) + + newSetting := &Setting{RepoID: 99, SettingKey: keyName, SettingValue: "Gitea Repo Setting Test"} + + // create setting + err := SetRepoSetting(newSetting.RepoID, newSetting.SettingKey, newSetting.SettingValue) + assert.NoError(t, err) + // test about saving unchanged values + err = SetRepoSetting(newSetting.RepoID, newSetting.SettingKey, newSetting.SettingValue) + assert.NoError(t, err) + + // get specific setting + settings, err := GetRepoSettings(99, []string{keyName}) + assert.NoError(t, err) + assert.Len(t, settings, 1) + assert.EqualValues(t, newSetting.SettingValue, settings[keyName].SettingValue) + + settingValue, err := GetRepoSetting(99, keyName) + assert.NoError(t, err) + assert.EqualValues(t, newSetting.SettingValue, settingValue) + + settingValue, err = GetRepoSetting(99, "no_such") + assert.NoError(t, err) + assert.EqualValues(t, "", settingValue) + + // updated setting + updatedSetting := &Setting{RepoID: 99, SettingKey: keyName, SettingValue: "Updated"} + err = SetRepoSetting(updatedSetting.RepoID, updatedSetting.SettingKey, updatedSetting.SettingValue) + assert.NoError(t, err) + + // get all settings + settings, err = GetRepoAllSettings(99) + assert.NoError(t, err) + assert.Len(t, settings, 1) + assert.EqualValues(t, updatedSetting.SettingValue, settings[updatedSetting.SettingKey].SettingValue) + + // delete setting + err = DeleteRepoSetting(99, keyName) + assert.NoError(t, err) + settings, err = GetRepoAllSettings(99) + assert.NoError(t, err) + assert.Len(t, settings, 0) +} From e56fe34a101cd980a364eec9f844c0e233a511c4 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Wed, 13 Apr 2022 23:42:34 -0400 Subject: [PATCH 0002/1287] migration --- models/migrations/migrations.go | 2 ++ models/migrations/v213.go | 24 ++++++++++++++++++++++++ models/repo/setting_keys.go | 2 -- 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 models/migrations/v213.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index de1d41e71a848..c161c7daa42fd 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -380,6 +380,8 @@ var migrations = []Migration{ NewMigration("Create ForeignReference table", createForeignReferenceTable), // v212 -> v213 NewMigration("Add package tables", addPackageTables), + // v213 -> v214 + NewMigration("Create key/value table for repo settings", createRepoSettingsTable), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v213.go b/models/migrations/v213.go new file mode 100644 index 0000000000000..b7ad8900f056b --- /dev/null +++ b/models/migrations/v213.go @@ -0,0 +1,24 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "fmt" + + "xorm.io/xorm" +) + +func createRepoSettingsTable(x *xorm.Engine) error { + type RepoSetting struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"index unique(key_repoid)"` // to load all of someone's settings + SettingKey string `xorm:"varchar(255) index unique(key_repoid)"` // ensure key is always lowercase + SettingValue string `xorm:"text"` + } + if err := x.Sync2(new(RepoSetting)); err != nil { + return fmt.Errorf("sync2: %v", err) + } + return nil +} diff --git a/models/repo/setting_keys.go b/models/repo/setting_keys.go index 651e458cbd22e..71f8a1c341a2a 100644 --- a/models/repo/setting_keys.go +++ b/models/repo/setting_keys.go @@ -3,5 +3,3 @@ // license that can be found in the LICENSE file. package repo - -const () From 9053096f1f62d155a60431093aea062ac784eba6 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 15 Apr 2022 11:43:38 +0000 Subject: [PATCH 0003/1287] Fix double blob-hunk (#19404) - Don't show the blob-hunk twice. --- templates/repo/diff/section_unified.tmpl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl index dbd0ca269f33c..2fe3f6490389a 100644 --- a/templates/repo/diff/section_unified.tmpl +++ b/templates/repo/diff/section_unified.tmpl @@ -31,9 +31,7 @@ {{if eq .GetType 4}} {{/* */}}{{$inlineDiff.Content}}{{/* - */}} - {{$line.Content}} - + */}} {{else}} {{/* */}}{{if and $.root.SignedUserID $.root.PageIsPullFiles}}{{/* From deffe9e5258bac233472b57166be17bc48a527c9 Mon Sep 17 00:00:00 2001 From: chavacava Date: Fri, 15 Apr 2022 16:50:09 +0200 Subject: [PATCH 0004/1287] Fix datarace in gitea_uploader.go (#19409) --- services/migrations/gitea_uploader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 8529f24895c64..275b7026a0f6e 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -754,13 +754,13 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error { _ = reader.Close() _ = writer.Close() }() - go func() { + go func(comment *base.ReviewComment) { if err := git.GetRepoRawDiffForFile(g.gitRepo, pr.MergeBase, headCommitID, git.RawDiffNormal, comment.TreePath, writer); err != nil { // We should ignore the error since the commit maybe removed when force push to the pull request log.Warn("GetRepoRawDiffForFile failed when migrating [%s, %s, %s, %s]: %v", g.gitRepo.Path, pr.MergeBase, headCommitID, comment.TreePath, err) } _ = writer.Close() - }() + }(comment) patch, _ = git.CutDiffAroundLine(reader, int64((&models.Comment{Line: int64(line + comment.Position - 1)}).UnsignedLine()), line < 0, setting.UI.CodeCommentLines) From e5d09e9868e5e6d65277cd22a927d58c3c4141f5 Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Fri, 15 Apr 2022 18:16:33 -0400 Subject: [PATCH 0005/1287] Update models/migrations/v213.go Co-authored-by: delvh --- models/migrations/v213.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/models/migrations/v213.go b/models/migrations/v213.go index b7ad8900f056b..0887149668c83 100644 --- a/models/migrations/v213.go +++ b/models/migrations/v213.go @@ -17,8 +17,5 @@ func createRepoSettingsTable(x *xorm.Engine) error { SettingKey string `xorm:"varchar(255) index unique(key_repoid)"` // ensure key is always lowercase SettingValue string `xorm:"text"` } - if err := x.Sync2(new(RepoSetting)); err != nil { - return fmt.Errorf("sync2: %v", err) - } - return nil + return x.Sync2(new(RepoSetting)) } From 695c4b4b0ecb65c87fd7e9c8f77e612b99b8abe3 Mon Sep 17 00:00:00 2001 From: "Stephen J. Fuhry" Date: Sat, 16 Apr 2022 11:59:56 -0400 Subject: [PATCH 0006/1287] upgrade postgres refrence to 14 (#19416) --- docs/content/doc/installation/with-docker-rootless.en-us.md | 2 +- docs/content/doc/installation/with-docker.en-us.md | 2 +- docs/content/doc/installation/with-docker.zh-cn.md | 2 +- integrations/README_ZH.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/content/doc/installation/with-docker-rootless.en-us.md b/docs/content/doc/installation/with-docker-rootless.en-us.md index e3de969122ed2..634e08a72ec42 100644 --- a/docs/content/doc/installation/with-docker-rootless.en-us.md +++ b/docs/content/doc/installation/with-docker-rootless.en-us.md @@ -147,7 +147,7 @@ services: + - db + + db: -+ image: postgres:13 ++ image: postgres:14 + restart: always + environment: + - POSTGRES_USER=gitea diff --git a/docs/content/doc/installation/with-docker.en-us.md b/docs/content/doc/installation/with-docker.en-us.md index 6322e0b953a98..66e596ea4de68 100644 --- a/docs/content/doc/installation/with-docker.en-us.md +++ b/docs/content/doc/installation/with-docker.en-us.md @@ -187,7 +187,7 @@ services: + - db + + db: -+ image: postgres:13 ++ image: postgres:14 + restart: always + environment: + - POSTGRES_USER=gitea diff --git a/docs/content/doc/installation/with-docker.zh-cn.md b/docs/content/doc/installation/with-docker.zh-cn.md index 8461951f726bb..77577736f26b5 100644 --- a/docs/content/doc/installation/with-docker.zh-cn.md +++ b/docs/content/doc/installation/with-docker.zh-cn.md @@ -172,7 +172,7 @@ services: + - db + + db: -+ image: postgres:13 ++ image: postgres:14 + restart: always + environment: + - POSTGRES_USER=gitea diff --git a/integrations/README_ZH.md b/integrations/README_ZH.md index 39639f9b89adf..eebb14de737df 100644 --- a/integrations/README_ZH.md +++ b/integrations/README_ZH.md @@ -36,7 +36,7 @@ TEST_MYSQL_HOST=localhost:3306 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=root T ## 如何使用 pgsql 数据库进行集成测试 同上,首先在 docker 容器里部署一个 pgsql 数据库 ``` -docker run -e "POSTGRES_DB=test" -p 5432:5432 --rm --name pgsql postgres:13 #(just ctrl-c to stop db and clean the container) +docker run -e "POSTGRES_DB=test" -p 5432:5432 --rm --name pgsql postgres:14 #(just ctrl-c to stop db and clean the container) ``` 之后便可以基于这个数据库进行集成测试 ``` From 490065b93b5fbff02d2c90efbe7f45ee95934fa9 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sun, 17 Apr 2022 00:10:24 +0000 Subject: [PATCH 0007/1287] [skip ci] Updated licenses and gitignores --- options/gitignore/Python | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/options/gitignore/Python b/options/gitignore/Python index de2d5e086e265..68bc17f9ff210 100644 --- a/options/gitignore/Python +++ b/options/gitignore/Python @@ -101,7 +101,15 @@ ipython_config.py # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock -# PEP 582; used by e.g. github.com/David-OConnor/pyflow +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff From b74322dfce8d15e9204900913bb815a7b0ba073f Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Mon, 18 Apr 2022 00:10:09 +0000 Subject: [PATCH 0008/1287] [skip ci] Updated translations via Crowdin --- options/locale/locale_is-IS.ini | 1372 +++++++++++++++++++++++++++++++ 1 file changed, 1372 insertions(+) create mode 100644 options/locale/locale_is-IS.ini diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini new file mode 100644 index 0000000000000..03589d3ae4f38 --- /dev/null +++ b/options/locale/locale_is-IS.ini @@ -0,0 +1,1372 @@ +home=Forsíða +dashboard=Stjórnborð +explore=Vafra +help=Hjálp +sign_in=Skrá Inn +sign_in_with=Skrá Inn Með +sign_out=Skrá Út +sign_up=Nýskráning +link_account=Tengja Notanda +register=Nýskráning +website=Vefsíða +version=Útgáfa +powered_by=Keyrt af %s +page=Síða +template=Sniðmát +language=Tungumál +notifications=Tilkynningar +active_stopwatch=Virk Tímamæling +create_new=Skapa… +user_profile_and_more=Notandasíða og Stillingar… +signed_in_as=Skráð(ur) inn sem +enable_javascript=Þessi vefsíða virkar betur með JavaScript virkt. +toc=Efnisyfirlit +licenses=Hugbúnaðarleyfi +return_to_gitea=Til baka að Gitea + +username=Notandanafn +email=Netfang +password=Lykilorð +access_token=Aðgangslykill +re_type=Endurtaktu Lykilorðið +captcha=CAPTCHA +twofa=Tvíþætt Auðkenning +twofa_scratch=Tveggja-Þátta Skrapkóði +passcode=Aðgangstala + +webauthn_insert_key=Settu öryggislykilinn þinn inn +webauthn_sign_in=Ýttu á hnappinn á öryggislyklinum þínum. Ef öryggislykillinn þinn hefur engan hnapp skaltu setja hann aftur inn. +webauthn_press_button=Vinsamlegast ýttu á hnappinn á öryggislyklinum þínum… +webauthn_use_twofa=Notaðu tveggja-þátta kóða úr símanum þínum +webauthn_error=Gat ekki lesið öryggislykilinn þinn. +webauthn_unsupported_browser=Vafrinn þinn styður ekki WebAuthn eins og er. +webauthn_error_unknown=Óþekkt villa kom upp. Vinsamlegast reyndu aftur. +webauthn_error_insecure=WebAuthn styður aðeins öruggar tengingar. Til að prófa yfir HTTP geturðu notað upprunann „localhost“ eða „127.0.0.1“ +webauthn_error_unable_to_process=Netþjónninn gat ekki ráðið við beiðni þína. +webauthn_error_duplicated=Öryggislykillinn er ekki leyfður fyrir þessa beiðni. Gakktu úr skugga um að lykillinn sé ekki þegar skráður. +webauthn_error_empty=Þú verður að setja nafn fyrir þennan lykil. +webauthn_error_timeout=Tímamörk náð áður en hægt var að lesa lykilinn þinn. Vinsamlegast endurhlaðið þessa síðu og reyndu aftur. +webauthn_u2f_deprecated=Lykillinn: „%s“ auðkennir með því að nota úrelta U2F aðferð. Þú ættir að endurskrá þennan lykil og fjarlægja gömlu skráninguna. +webauthn_reload=Endurhlaða + +repository=Hugbúnaðarsafn +organization=Stofnun +mirror=Speglun +new_repo=Nýtt Hugbúnaðarsafn +new_migrate=Nýr Flutningur +new_mirror=Ný Speglun +new_fork=Ný Hugbúnaðarskipting +new_org=Ný Stofnun +new_project=Nýtt Verkefni +new_project_board=Stjórn Nýs Verkefnis +manage_org=Stjórna Stofnunum +admin_panel=Stjórnborð +account_settings=Notandastillingar +settings=Stillingar +your_profile=Notandasíða +your_starred=Eftirlæti +your_settings=Stillingar + +all=Allt +sources=Eigin +mirrors=Speglanir +collaborative=Samstörf +forks=Skiptingar + +activities=Virkni +pull_requests=Sameiningarbeiðnir +issues=Vandamál +milestones=Tímamót + +ok=Í lagi +cancel=Hætta við +save=Vista +add=Bæta við +add_all=Bæta Öllu Við +remove=Fjarlægja +remove_all=Fjarlægja Allt +edit=Breyta + +copy=Afrita +copy_url=Afrita vefslóð +copy_branch=Afritaðu heiti greinar +copy_success=Afritað! +copy_error=Afritun mistókst + +write=Skrifa +preview=Forskoða +loading=Hleður… + +step1=Skref 1: +step2=Skref 2: + +error=Villa +error404=Síðan sem þú ert að reyna að fá annað hvort er ekki til eða þú hefur ekki heimild til að skoða hana. + +never=Aldrei + +[error] +occurred=Villa kom upp +report_message=Ef þú ert viss um að þetta sé villa í Gitea þá skaltu leita að vandamálum á GitHub eða opna nýtt vandamál ef þörf krefst. +missing_csrf=Slæm beiðni: enginn CSRF lykill +invalid_csrf=Slæm beiðni: ógildur CSRF lykill +not_found=Markmiðið fannst ekki. +network_error=Netkerfisvilla + +[startpage] +app_desc=Þrautalaus og sjálfhýst Git þjónusta +install=Einföld uppsetning +install_desc=Einfaldlega keyrðu forritiðfyrir vettvanginn þinn, Docker, eða fáðu það í pakka. +platform=Fjölvettvangur +platform_desc=Gitea virkar hvar sem að Go gerir: Linux, macOS, Windows, ARM o. s. frv. Veldu það sem þú vilt! +lightweight=Létt +lightweight_desc=Gitea hefur lágar lágmarkskröfur og getur keyrt á ódýrum Raspberry Pi. Sparaðu orku! +license=Frjáls Hugbúnaður +license_desc=Sæktu code.gitea.io/gitea! Gakktu til liðs með því að taka þátt til þess að gera þetta verkefni jafnvel betra! Vertu ekki feimin(n) við að verða þátttakandi! + +[install] +install=Uppsetning +title=Upphafleg Uppsetning +docker_helper=Ef þú keyrir Gitea inni í Docker þá viltu vinsamlegast lesa leiðbeiningaritið áður en þú breytir stillingum. +require_db_desc=Gitea krefst MySQL, PostgreSQL, MSSQL, SQLite3 eða TiDB (MySQL samskiptareglur). +db_title=Gagnagrunnsstillingar +db_type=Tegund Gagnagrunns +host=Hýsill +user=Notandanafn +password=Lykilorð +db_name=Gagnagrunnsheiti +db_helper=Athugið MySQL notendur: vinsamlegast notið InnoDB geymsluvélina og ef þið notið „utf8mb4,“ verður InnoDB útgáfan ykkar að vera yfir 5.6 . +db_schema=Uppdráttur +db_schema_helper=Skildu eftir autt fyrir sjálfgefinn gagnagrunn („public“). +ssl_mode=SSL +charset=Stafatafla +path=Slóð +sqlite_helper=Skráarslóð fyrir SQLite3 gagnagrunninn.
Sláðu inn algjöra slóð ef þú keyrir Gitea sem þjónustu. +reinstall_error=Þú ert að reyna að setja upp í núverandi Gitea gagnagrunn +reinstall_confirm_message=Enduruppsetning með núverandi Gitea gagnagrunni getur valdið mörgum vandamálum. Í flestum tilfellum ættir þú að nota núverandi "app.ini" til að keyra Gitea. Ef þú veist hvað þú ert að gera skaltu staðfesta eftirfarandi: +reinstall_confirm_check_1=Gögnin sem eru dulkóðuð með SECRET_KEY í app.ini gætu glatast: notendur gætu hugsanlega ekki skráð sig inn með 2FA/OTP og speglar virka kannski ekki rétt. Með því að haka við þennan reit staðfestirðu að núverandi app.ini skrá inniheldur réttan SECRET_KEY. +reinstall_confirm_check_2=Hugbúnaðarsöfn og stillingar gætu þurft að endursamstilla. Með því að haka við þennan reit staðfestir þú að þú endursamstillir krókana fyrir hugbúnaðarsöfn og authorized_keys skrána handvirkt. Þú staðfestir að þú tryggir að hugbúnaðarsafns- og spegilstillingar séu réttar. +reinstall_confirm_check_3=Þú staðfestir að þú sért alveg viss um að þetta Gitea sé í gangi með réttri app.ini staðsetningu og að þú sért viss um að þú þurfir að setja það upp aftur. Þú staðfestir að þú viðurkennir ofangreindar áhættur. +err_empty_db_path=SQLite3 gagnagrunnsslóðin má ekki vera tóm. +no_admin_and_disable_registration=Þú getur ekki slökkt á sjálfsskráningu notenda án þess að búa til stjórnandanotanda. +err_empty_admin_password=Lykilorð stjórnanda má ekki vera tómt. +err_empty_admin_email=Netfang stjórnanda má ekki vera tómt. +err_admin_name_is_reserved=Notandanafn stjórnanda er ógilt. Notandanafnið er frátekið +err_admin_name_pattern_not_allowed=Notandanafn stjórnanda er ógilt. Notandanafnið passar við frátekið mynstur +err_admin_name_is_invalid=Notandanafn Stjórnanda er ógilt + +general_title=Almennar Stillingar +app_name=Heiti vefsvæðis +app_name_helper=Þú getur slegið inn nafn fyrirtækis þíns hér. +repo_path=Grunnsslóð Hugbúnaðarsafns +repo_path_helper=Fjarlægar Git hugbúnaðarsöfn verða vistaðar í þessari möppu. +lfs_path=Git LFS Grunnsslóð +lfs_path_helper=Skrár sem Git LFS rekur verða geymdar í þessari möppu. Skildu eftir tómt til að slökkva á. +run_user=Keyra Sem Notandanafn +run_user_helper=Sláðu inn notandanafn stýrikerfisins sem Gitea keyrir sem. Athugaðu að þessi notandi verður að hafa aðgang að grunnsslóð gugbúnaðarsafna. +domain=Lén Netþjóns +domain_helper=Lén eða hýsilfang fyrir netþjóninn. +ssh_port=SSH Netþjónsgátt +ssh_port_helper=Gátt sem SSH þjónninn þinn hlustar á. Skildu eftir tómt til að slökkva á. +http_port=Gitea HTTP Hlustunargátt +http_port_helper=Gátt sem Gitea vefþjónninn mun hlusta á. +app_url=Grunnvefslóð Gitea +app_url_helper=Grunnvistfang fyrir HTTP(S) afrit slóð og tölvupósttilkynningar. +log_root_path=Slóð Annáls +log_root_path_helper=Annálaskrár verða skrifaðar í þessa möppu. + +optional_title=Valfrjálsar Stillingar +email_title=Tölvupóstsstillingar +smtp_host=SMTP Hýsill +smtp_from=Senda Tölvupóst Sem +smtp_from_helper=Netfang sem Gitea mun nota. Sláðu inn venjulegt netfang eða notaðu „Nafn“ sniðið. +mailer_user=SMTP Notandanafn +mailer_password=SMTP Lykilorð +register_confirm=Krefjast Staðfestingar Tölvupósts Til Að Nýskrá +mail_notify=Virkja Tölvupósttilkynningar +server_service_title=Stillingar Netþjóns og Þriðja Aðila +offline_mode=Virkjaðu Staðbundin Ham +offline_mode_popup=Slökktu á efnisafhendingarnetum þriðja aðila og þjónaðu öllum gögnum á staðnum. +disable_gravatar=Óvirkja Gravatar +disable_gravatar_popup=Slökkva á Gravatar og notandamyndar þjónustum. Sjálfgefin notandamynd verður notuð ef notandi hleður ekki upp sína eigin. +federated_avatar_lookup=Virkja Samtök Notandamyndar +openid_signin=Virkja OpenID Innskráningu +openid_signin_popup=Virkja OpenID innskráningu notenda. +enable_captcha=Virkja CAPTCHA innskráningu +admin_name=Notandanafn Stjórnanda +admin_password=Lykilorð +confirm_password=Staðfestu Lykilorðið +admin_email=Netfang +install_btn_confirm=Setja upp Gitea +test_git_failed=Gat ekki prófað „git“ skipunina: %v +sqlite3_not_available=Þessi Gitea útgáfa styður ekki SQLite3. Vinsamlegast sæktu útgáfunni okkar frá %s (ekki „gobuild“ útgáfunna). +invalid_db_setting=Gagnagrunnsstillingarnar eru ógildar: %v +invalid_db_table=Gagnagrunnstaflan „%s“ er ógild: %v +invalid_repo_path=Grunnsslóð hugbúnaðarsafns er ógild: %v +invalid_log_root_path=Slóð annáls er ógild: %v +default_keep_email_private_popup=Fela sjálfgefið netföng nýrra notendareikninga. +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 +password_holder=Lykilorð +my_repos=Hugbúnaðarsöfn +show_more_repos=Sýna fleiri hugbúnaðarsöfn… +my_orgs=Stofnanir Mínar +my_mirrors=Speglanir Mínar +view_home=Skoða %s +search_repos=Finna hugbúnaðarsafn… +filter=Aðrar Síur + +show_archived=Safnvistað + +show_private=Einka +show_only_private=Að sýna aðeins einka +show_only_public=Að sýna aðeins opinber + +issues.in_your_repos=Í hugbúnaðarsöfnum þínum + +[explore] +repos=Hugbúnaðarsöfn +users=Notendur +organizations=Stofnanir +search=Leita +code=Kóði +search.fuzzy=Óljóst +code_search_unavailable=Sem stendur er kóðaleit ekki í boði. Vinsamlegast hafðu samband við síðustjórann þinn. +repo_no_results=Engin samsvarandi hugbúnaðarsöfn fundust. +user_no_results=Engir samsvarandi notendur fundust. +org_no_results=Engar samsvarandi stofnanir fundust. +code_no_results=Enginn samsvarandi frumkóði fannst eftur þínum leitarorðum. +code_search_results=Leitarniðurstöður fyrir „%s“ + +[auth] +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. +remember_me=Muna eftir þessu Tæki +forgot_password_title=Gleymt Lykilorð +forgot_password=Gleymdirðu Lykilorðinu? +sign_up_now=Vantar þig notanda? Nýskráðu núna! +sign_up_successful=Sköpun notanda tókst. +must_change_password=Uppfærðu lykilorðið þitt +active_your_account=Virkjaðu Aðganginn Þinn +account_activated=Aðgangur hefur verið virkjaður +prohibit_login=Nýskráningar Óheimilar +has_unconfirmed_mail=Halló, %s, þú ert með óstaðfest netfang (%s). Ef þú hefur ekki fengið staðfestingarpóst eða þarft nýjan, vinsamlegast smelltu á hnappinn hér að neðan. +resend_mail=Smelltu hér til að endursenda virkjunarpóstinn þinn +send_reset_mail=Senda Tölvupóst Til að Endurheimta Reikning +reset_password=Endurheimt Reiknings +reset_password_helper=Endurheimta Reikning +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_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 +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. +disable_forgot_password_mail_admin=Endurheimting reiknings er aðeins virk þegar tölvupóstur er uppsettur. Vinsamlegast settu upp tölvupóst til að virkja endurheimting reikningar. +authorize_application=Heimilda Forrit +authorize_application_created_by=Þetta forrit var stofnað af %s. +authorize_title=Veita „%s“ aðgang að reikningnum þínum? +authorization_failed=Heimild mistókst +authorization_failed_desc=Heimildin mistókst vegna þess að við fundum ógilda beiðni. Vinsamlegast hafðu samband við umsjónarmann forritsins sem þú hefur reynt að heimila. +sspi_auth_failed=SSPI auðkenning mistókst +password_pwned=Lykilorðið sem þú valdir er á lista yfir stolin lykilorð sem áður hafa verið afhjúpuð í opinberum gagnabrotum. Vinsamlegast reyndu aftur með öðru lykilorði. +password_pwned_err=Gat ekki klárað beiðni til HaveIBeenPwned + +[mail] +view_it_on=Skoða þetta á %s +link_not_working_do_paste=Virkar ekki? Prófaðu að afrita og líma slóðina í vafrann þinn. +hi_user_x=Halló, %s, + +activate_account=Vinsamlegast virkjaðu aðganginn þinn +activate_account.title=%s, vinsamlegast virkjaðu aðganginn þinn +activate_account.text_1=Halló, %[1]s, takk fyrir að nýskrá á %[2]s! +activate_account.text_2=Vinsamlegast smelltu á eftirfarandi tengil til að virkja reikninginn þinn innan %s: + +activate_email=Staðfestu netfangið þitt +activate_email.title=%s, vinsamlegast staðfestu netfangið þitt +activate_email.text=Vinsamlegast smelltu á eftirfarandi tengil til að staðfesta netfangið þitt innan %s: + +register_notify=Velkomin(n) í Gitea +register_notify.title=%[1]s, velkomin(n) í %[2]s +register_notify.text_1=þetta er staðfestingarpóstur þinn fyrir skráningu á %s! +register_notify.text_2=Þú getur nú skráð þig inn með notandanafni: %s. +register_notify.text_3=Ef þessi reikningur hefur verið búinn til fyrir þig, vinsamlegast stilltu lykilorðið þitt fyrst. + +reset_password=Endurheimta reikning þinn +reset_password.title=%s, þú hefur beðið um að endurheimta reikninginn þinn +reset_password.text=Vinsamlegast smelltu á eftirfarandi tengil til að endurheimta reikninginn þinn innan %s: + +register_success=Nýskráning tókst + +issue_assigned.pull=@%[1]s úthlutaði þér að sameiningarbeiðni %[2]s í hugbúnaðarsafni %[3]s. +issue_assigned.issue=@%[1]s úthlutaði þér að vandamáli %[2]s í hugbúnaðarsafni %[3]s. + +issue.x_mentioned_you=@%s minntist á þig: +issue.action.push_1=@%[1]s bætti við %[3]d framlag í %[2]s +issue.action.push_n=@%[1]s bætti við %[3]d framlög í %[2]s +issue.action.close=@%[1]s lokaði #%[2]d. +issue.action.reopen=@%[1]s enduropnaði #%[2]d. +issue.action.merge=@%[1]s sameinaði #%[2]d inni í %[3]s. +issue.action.approve=@%[1]s samþykkti þessa sameiningarbeiðni. +issue.action.reject=@%[1]s óskaði eftir breytingum á þessa sameiningarbeiðni. +issue.action.review=@%[1]s gerði ummæli á þessa sameiningarbeiðni. +issue.action.new=@%[1]s skapaði #%[2]d. +issue.in_tree_path=Í %s: + +release.new.subject=%s í %s útgefið +release.new.text=@%[1]s gaf út %[2]s í %[3]s +release.title=Heiti: %s +release.note=Athugasemd: +release.downloads=Niðurhöl: +release.download.zip=Frumkóði (ZIP) +release.download.targz=Frumkóði (TAR.GZ) + +repo.transfer.subject_to=%s langar að flytja „%s“ til %s +repo.transfer.subject_to_you=%s langar að flytja „%s“ til þín +repo.transfer.to_you=þig +repo.transfer.body=Til að samþykkja eða hafna því skaltu fara á %s eða hunsa það bara. + +repo.collaborator.added.subject=%s bætti þér við í %s +repo.collaborator.added.text=Þér hefur verið bætt við sem aðila hugbúnaðarsafns: + +[modal] +yes=Já +no=Nei +modify=Uppfæra + +[form] +UserName=Notandanafn +RepoName=Heiti Hugbúnaðarsafns +Email=Netfang +Password=Lykilorð +Retype=Endurtaktu Lykilorðið +HttpsUrl=HTTPS vefslóð +TeamName=Liðsheiti +AdminEmail=Netfang stjórnanda + +NewBranchName=Heiti nýjar greinar +CommitSummary=Framlagsútdráttur +CommitMessage=Framlagsskilaboð +CommitChoice=Framlagsval +TreeName=Skráarslóð +Content=Innihald + +SSPISeparatorReplacement=Aðgreinir +SSPIDefaultLanguage=Sjálfgefið Tungumál + +require_error=` Getur ekki verið tómt.` +alpha_dash_dot_error=` ætti aðeins að innihalda tölustafi, strik ('-'), undirstrik ('_') og punkta ('.').` +size_error=` þarf að vera stærð %s.` +email_error=` er ekki gilt netfang.` +url_error=` er ekki gild vefslóð.` +unknown_error=Óþekkt villa: +captcha_incorrect=CAPTCHA kóðinn er rangur. +password_not_match=Lykilorðin passa ekki saman. +lang_select_error=Veldu tungumál af listanum. + +username_been_taken=Notandanafnið er þegar í notkun. +username_change_not_local_user=Notendum utan staðarins er ekki heimilt að breyta notendanafni sínu. +repo_name_been_taken=Hugbúnaðarsafnsheiti er þegar notað. +org_name_been_taken=Stofnunarinnarheiti er þegar tekið. +team_name_been_taken=Liðsheiti er þegar í notkun. +email_been_used=Netfangið er þegar í notkun. +email_invalid=Netfang ógilt. +openid_been_used=OpenID vistfangið „%s“ er þegar notað. +username_password_incorrect=Notandanafn eða lykilorð er rangt. +password_complexity=Lykilorðið er ekki nógu flókið: +password_lowercase_one=Að minnsta kosti einn lágstafur +password_uppercase_one=Að minnsta kosti einn hástafur +password_digit_one=Að minnsta kosti einn tölustafur +password_special_one=Að minnsta kosti einn sérstafur (greinarmerki, sviga, gæsalappir, o. s. frv.) +enterred_invalid_repo_name=Hugbúnaðarsafnsheitið sem þú slóst inn er rangt. +enterred_invalid_org_name=Stofnunarinnarheitið sem þú slóst inn er rangt. +user_not_exist=Notandinn er ekki til. +team_not_exist=Liðið er ekki til. + + +org_still_own_repo=Þessi stofnun á enn eina eða fleiri hugbúnaðarsöfn; eyddu þeim eða flyttu þær fyrst. + + +[user] +change_avatar=Breyttu notandamyndinni þinni… +join_on=Gerðist meðlimi +repositories=Hugbúnaðarsöfn +activity=Opinber Virkni +followers=Fylgjendur +starred=Hugbúnaðarsöfn í Eftirlæti +watched=Hugbúnaðarsöfn í Áhorfi +projects=Verkefni +following=Fylgir +follow=Fylgja +unfollow=Affylgja +heatmap.loading=Hleð Hitakorti… +user_bio=Lífssaga +disabled_public_activity=Þessi notandi hefur slökkt á opinberum sýnileika virkninnar. + +form.name_reserved=Notandanafnið „%s“ er frátekið. +form.name_pattern_not_allowed=Mynstrið „%s“ er ekki leyft í notandanafni. +form.name_chars_not_allowed=Notandanafnið „%s“ inniheldur ógilda stafi. + +[settings] +profile=Notandasíða +account=Reikningur +appearance=Útlit +password=Lykilorð +security=Öryggi +avatar=Notandamynd +ssh_gpg_keys=SSH og GPG Lyklar +social=Félagsreikningar +applications=Forrit +orgs=Stjórna Stofnunum +repos=Hugbúnaðarsöfn +delete=Eyða Reikningi +twofa=Tvíþætt Auðkenning +account_link=Tengdir Reikningar +organization=Stofnanir +uid=Notandaauðkenni +webauthn=Öryggislyklar + +public_profile=Opinber Notandasíða +biography_placeholder=Segðu okkur svolítið um þig +profile_desc=Netfangið þitt verður notað fyrir tilkynningar og aðrar aðgerðir. +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 +location=Staðsetning +update_theme=Uppfæra Þemu +update_profile=Uppfæra Notandasíðu +update_language=Uppfæra Tungumál +update_language_not_found=Tungumálið „%s“ er ekki í boði. +update_language_success=Tungumálið hefur verið uppfært. +update_profile_success=Notandasíða þín hefur verið uppfærð. +change_username=Notandanafninu þínu hefur verið breytt. +change_username_prompt=Athugaðu: breytingar á notendanafni breyta einnig vefslóð reikningsins þíns. +change_username_redirect_prompt=Gamla notendanafnið mun áframsenda á meðan það er í boði. +continue=Halda áfram +cancel=Hætta við +language=Tungumál +ui=Þema +comment_type_group_reference=Tilvísun +comment_type_group_label=Lýsing +comment_type_group_milestone=Tímamót +comment_type_group_assignee=Úthlutað að +comment_type_group_title=Heiti +comment_type_group_branch=Grein +comment_type_group_time_tracking=Tímamæling +comment_type_group_deadline=Frestur +comment_type_group_pull_request_push=Bætti við framlögum +comment_type_group_project=Verkefni +privacy=Friðhelgi +keep_activity_private=Fela virkni frá notandasíðu + +lookup_avatar_by_mail=Leita Efitr Notandamynd Með Netfangi +enable_custom_avatar=Nota Sérsniða Notandamynd +choose_new_avatar=Veldu nýja notandamynd +update_avatar=Uppfæra Notandamynd +delete_current_avatar=Eyða Núverandi Notandamynd +uploaded_avatar_not_a_image=Skráin sem hlaðin var upp er ekki mynd. +uploaded_avatar_is_too_big=Skráin sem hlaðin var upp er yfir hámarksstærð. +update_avatar_success=Notandamynd þín hefur verið uppfærð. +update_user_avatar_success=Notandamynd þessara notanda hefur verið uppfærð. + +change_password=Uppfæra Lykilorð +old_password=Núverandi Lykilorð +new_password=Nýtt Lykilorð +retype_new_password=Endurtaktu Nýja Lykilorðið +password_incorrect=Núverandi lykilorðið er rangt. + +emails=Netföng +manage_emails=Stjórna Netföngum +email_desc=Aðal netfangið þitt verður notað fyrir tilkynningar og aðrar aðgerðir. +primary=Aðal +requires_activation=Krefst virkjunar +primary_email=Gerа Аðal +activate_email=Senda Virkjun +activations_pending=Virkjanir í Bið +delete_email=Fjarlægja +email_deletion=Fjarlægja Netfang +email_deletion_desc=Netfangið og tengdar upplýsingar verða fjarlægðar af reikningnum þínum. Git framlög með þessu netfangi verða óbreyttar. Halda áfram? +email_deletion_success=Netfangið hefur verið fjarlægt. +theme_update_success=Þeman þín var uppfærð. +theme_update_error=Valin þema er ekki til. +openid_deletion=Fjarlægja OpenID Netfang +add_new_email=Bæta við Nýju Netfangi +add_email=Bæta við Netfangi +add_email_confirmation_sent=Staðfestingarpóstur hefur verið sendur á „%s“. Vinsamlegast athugaðu pósthólfið þitt innan næstu %s til að staðfesta netfangið þitt. +add_email_success=Nýja netfangið hefur verið bætt við. +email_preference_set_success=Val á tölvupósti uppfært. +keep_email_private=Fela Netfang +keep_email_private_popup=Netfangið þitt verður falið öðrum notendum. + +manage_ssh_keys=Stjórna SSH Lyklum +manage_gpg_keys=Stjórna GPG Lyklum +add_key=Bæta við lykli +ssh_helper=Vantar þér aðstoð? Skoðaðu leiðbeiningarnar frá GitHub um að skapa þína eigin SSH lykla eða um að laga algeng vandamál þú getur rekist á við þegar þú ert að vinna með SSH. +gpg_helper=Vantar þér aðstoð? Skoðaðu leiðbeiningarnar frá GitHub um GPG. +add_new_key=Bæta við SSH lykli +key_content_ssh_placeholder=Byrjar með 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com' eða 'sk-ssh-ed25519@openssh.com' +gpg_key_verify=Staðfesta +gpg_token=Táknlykill +ssh_key_verified=Staðfestur Lykill +ssh_key_verify=Staðfesta +ssh_token=Táknlykill +key_signature_ssh_placeholder=Byrjar með „-----BEGIN SSH SIGNATURE-----“ +verify_ssh_key_success=SSH lykill „%s“ hefur verið staðfestur. +key_id=Lykilauðkenni +key_content=Innihald +principal_content=Innihald +add_key_success=SSH lyklinum „%s“ hefur verið bætt við. +add_gpg_key_success=GPG lyklinum „%s“ hefur verið bætt við. +delete_key=Fjarlægja +ssh_key_deletion=Fjarlæga SSH Lykil +gpg_key_deletion=Fjarlæga GPG Lykil +ssh_key_deletion_success=SSH lykillinn hefur verið fjarlægður. +gpg_key_deletion_success=GPG lykillinn hefur verið fjarlægður. +add_on=Bætt við +valid_until=Gildur til +valid_forever=Gildur að eilífu +last_used=Síðast notað +can_read_info=Lesa +can_write_info=Skrifa +show_openid=Sýna á notandasíðu +hide_openid=Fela frá notandasíðu +ssh_disabled=SSH Óvirkt +unbind=Aftengja + +token_name=Táknlykills Heiti +generate_token=Mynda Táknlykil +delete_token=Eyða +access_token_deletion=Eyða Aðgangslykli + +create_oauth2_application_button=Skapa Forrit +update_oauth2_application_success=Þú hefur uppfært OAuth2 forritið. +oauth2_application_name=Forritsheiti +oauth2_select_type=Hvaða forritsgerð passar? +oauth2_type_web=Net (t.d. Node.JS, Tomcat, Go) +oauth2_type_native=Á kerfi (t.d. síma, tölvu, vafra) +oauth2_redirect_uri=Áframsendingar Vefslóð +save_application=Vista +oauth2_client_id=Auðkenni Notanda +oauth2_client_secret=Leyndarmál Notanda +oauth2_application_edit=Breyta + +authorized_oauth2_applications_description=Þú hefur veitt þessum forritum aðgang að þínum Gitea reikningi. Vinsamlegast afturkallaðu aðgang fyrir forrit sem ekki er lengur þörf á. +revoke_key=Afturkalla +revoke_oauth2_grant=Afturkalla Aðgang + +twofa_disable=Óvirkja Tveggja-Þátta Auðkenningu +twofa_scratch_token_regenerate=Endurgera Skrapkóða +or_enter_secret=Eða sláðu inn leyndarmálið: %s + +webauthn_nickname=Gælunafn + + + + +email_notifications.enable=Virkja Tölvupósttilkynningar +email_notifications.onmention=Aðeins Tölvupóst Þegar Minnst Er á Mig +email_notifications.disable=Óvirkja Tölvupósttilkynningar +email_notifications.submit=Stilla Val á Tölvupósti + +visibility=Sýnileiki notanda +visibility.public=Opinbert +visibility.private=Einka + +[repo] +new_repo_helper=Hugbúnaðarsafn inniheldur allar verkefnaskrár, þar á meðal útgáfuferil. Ertu nú þegar með það annars staðar? Flytja hugbúnaðarsafn +owner=Eigandi +template=Sniðmát +visibility=Sýnileiki +visibility_helper=Gera Hugbúnaðarsafn Einka +visibility_fork_helper=(Að breyta þessu mun hafa áhrif á allar skiptingar.) +clone_helper=Þarftu hjálp við afritun? Fáðu aðstoð. +fork_repo=Tvískipta Hugbúnaðarsafni +download_zip=Sækja ZIP +generate_repo=Mynda Hugbúnaðarsafn +repo_desc=Lýsing +repo_desc_helper=Sláðu inn stutta lýsingu (valfrjálst) +repo_lang=Tungumál +repo_gitignore_helper=Velja .gitignore sniðmát. +repo_gitignore_helper_desc=Veldu hvaða skrár á ekki að rekja af lista sniðmáta fyrir algeng tungumál. Dæmagert rusl sem myndast af byggingarverkfærum hvers tungumáls er sjálfgefið í .gitignore. +issue_labels=Vandamálslýsingar +issue_labels_helper=Veldu vandamálslýsingarsett. +license=Hugbúnaðarleyfi +license_helper=Veldu hugbúnaðarleyfisskrá. +license_helper_desc=Hugbúnaðarleyfi stjórnar því hvað aðrir mega og mega ekki gera við frumkóðann þinn. Ertu ekki viss um hvað er rétt fyrir verkefnið þitt? Sjáðu Veldu leyfi. +readme=LESTUMIG +readme_helper=Veldu LESTUMIG skráarsniðmát. +auto_init=Frumstilla Hugbúnaðarsafn. Bætir við skránum: .gitignore, License og README +create_repo=Skapa Hugbúnaðarsafn +default_branch=Sjálfgefin Grein +mirror_prune=Snyrta +mirror_address=Afrita Frá Vefslóð +mirror_address_protocol_invalid=Uppgefin vefslóð er ógild. Aðeins http(s):// eða git:// staðsetningar er hægt að spegla frá. +mirror_lfs=Stór Skráargeymsla (LFS) +mirror_lfs_endpoint=LFS Endapunktur +mirror_last_synced=Síðast Samstillt +mirror_password_placeholder=(Óbreytt) +mirror_password_blank_placeholder=(Óstillt) +mirror_password_help=Breyttu notandanafninu til að eyða vistuðu lykilorði. +watchers=Fylgjendur +forks=Skiptingar +reactions_more=og %d fleiri +language_other=Annað +delete_preexisting_label=Eyða +delete_preexisting_content=Eyða skrám í %s + + +desc.private=Einka +desc.public=Opinbert +desc.private_template=Einka sniðmát +desc.public_template=Sniðmát +desc.internal=Innra +desc.internal_template=Innra sniðmát +desc.archived=Safnvistað + +template.git_content=Git Innihald (Sjálfgefin Grein) +template.git_hooks=Git krókar +template.webhooks=Vefkrókar +template.topics=Viðfangsefni +template.avatar=Auðkennismynd +template.issue_labels=Vandamálslýsingar + + + +migrate_options_lfs=Flytja LFS skrár +migrate_options_lfs_endpoint.label=LFS Endapunktur +migrate_items_wiki=Handbók +migrate_items_milestones=Tímamót +migrate_items_labels=Skýringar +migrate_items_issues=Vandamál +migrate_items_pullrequests=Sameiningarbeiðnir +migrate_items_merge_requests=Sameiningarbeiðnir +migrate_items_releases=Útgáfur +migrate_repo=Flytja Hugbúnaðarsafn +migrate.migrate=Flytja Frá %s +migrate.migrating_failed.error=Villa: %s +migrate.git.description=Flytja hugbúnaðarsafn aðeins frá Git þjónustu. +migrate.migrating_labels=Að færa Lýsingar + +mirror_from=speglun af +forked_from=tvískipt frá +generated_from=myndað frá +unwatch=Hætta að fylgjast með +watch=Fylgjast með +unstar=Fjarlægja eftirlæti +star=Bæta við eftirlæti +fork=Tvískipta +download_archive=Hlaða Miður Geymslu + +no_desc=Engin Lýsing +quick_guide=Stuttar Leiðbeiningar +clone_this_repo=Afrita þetta hugbúnaðarsafn +create_new_repo_command=Að búa til nýja geymslu með skipanalínu +push_exist_repo=Að senda inn núverandi geymslu með skipanalínu + +code=Kóði +branch=Grein +tree=Tré +find_tag=Finna merki +branches=Greinar +tags=Merki +issues=Vandamál +pulls=Sameiningarbeiðnir +project_board=Verkefni +labels=Skýringar + +milestones=Tímamót +commits=Framlög +commit=Framlag +release=Útgáfa +releases=Útgáfur +tag=Merki +file.title=%s í %s +file_raw=Hrátt +file_history=Saga +file_view_source=Skoða Frumkóða +file_view_rendered=Skoða Unnið + +file_copy_permalink=Afrita Varanlega Slóð +stored_lfs=Geymt með Git LFS +commit_graph.hide_pr_refs=Fela Sameiningarbeiðnir +commit_graph.monochrome=Einlitað +commit_graph.color=Litað +blame=Ásaka +download_file=Sækja skrá +line=lína +lines=línur + +editor.new_file=Ný Skrá +editor.upload_file=Uppfæra Skrá +editor.edit_file=Breyta Skrá +editor.preview_changes=Forskoða Breytingar +editor.edit_this_file=Breyta Skrá +editor.this_file_locked=Skrá er læst +editor.delete_this_file=Eyða Skrá +editor.name_your_file=Nefndu skrána þína… +editor.or=eða +editor.cancel_lower=Hætta við +editor.add_tmpl=Bæta við „“ +editor.add=Bæta við „%s“ +editor.update=Uppfæra „%s“ +editor.delete=Eyða „%s“ +editor.create_new_branch=Búðu til nýja grein og sameiningarbeiðni fyrir þetta framlag. +editor.create_new_branch_np=Búðu til nýja grein fyrir þetta framlag. +editor.new_branch_name_desc=Heiti nýjar greinar… +editor.cancel=Hætta við +editor.fail_to_update_file=Skrá „%s“ mistókst að skapa eða uppfæra. +editor.fail_to_update_file_summary=Villuskilaboð: + +commits.commits=Framlög +commits.find=Leita +commits.author=Höfundur +commits.message=Skilaboð +commits.date=Dagsetning +commits.older=Eldri +commits.newer=Nýrri + + + +projects=Verkefni +projects.description=Lýsing (valfrjálst) +projects.description_placeholder=Lýsing +projects.create=Stofna Verkefni +projects.title=Heiti +projects.new=Nýtt verkefni +projects.deletion=Eyða Verkefni +projects.deletion_success=Verkefninu hefur verið eytt. +projects.edit=Breyta Verkefnum +projects.modify=Uppfæra Verkefni +projects.type.none=Ekkert +projects.type.uncategorized=Óflokkuð +projects.board.new_submit=Staðfesta +projects.board.set_default=Stilla Sjálfgildi +projects.board.color=Litur +projects.open=Opna +projects.close=Loka +projects.board.assigned_to=Úthlutað til + +issues.filter_projects=Sía Verkefni +issues.filter_labels=Sía Lýsingar +issues.new=Nýtt Vandamál +issues.new.labels=Lýsingar +issues.new.no_label=Engin Lýsing +issues.new.clear_labels=Tæma lýsingar +issues.new.projects=Verkefni +issues.new.clear_projects=Tæma Verkefnum +issues.new.no_projects=Ekkert verkefni +issues.new.open_projects=Opin Verkefni +issues.new.closed_projects=Lokuð Verkefni +issues.new.milestone=Tímamót +issues.choose.get_started=Hefjast Handa +issues.choose.blank=Sjálfgefið +issues.no_ref=Engin Grein eða Merki Tilgreint +issues.create=Skapa Vandamálsumræðu +issues.new_label=Ný Lýsing +issues.new_label_placeholder=Lýsingarheiti +issues.new_label_desc_placeholder=Lýsing +issues.create_label=Skapa Lýsingu +issues.label_templates.info=Engin merki eru til ennþá. Búðu til merki með „Nýtt merki“ eða notaðu fyrirfram skilgreint merkisett: +issues.open_tab=%d Opin +issues.close_tab=%d Lokuð +issues.filter_label=Lýsing +issues.filter_label_no_select=Allar lýsingar +issues.filter_milestone=Tímamót +issues.filter_milestone_no_select=Öll tímamót +issues.filter_type=Tegund +issues.filter_type.all_issues=Öll vandamál +issues.filter_type.assigned_to_you=Úthlutuð til þín +issues.filter_type.created_by_you=Búin til af þér +issues.filter_type.mentioning_you=Minnast á þig +issues.filter_sort=Raða +issues.filter_sort.latest=Nýjustu +issues.filter_sort.oldest=Elstu +issues.filter_sort.recentupdate=Nýlega uppfærð +issues.filter_sort.leastupdate=Síðast uppfærð +issues.filter_sort.mostcomment=Flest ummæli +issues.filter_sort.leastcomment=Fæst ummæli +issues.filter_sort.mostforks=Flestar skiptingar +issues.filter_sort.fewestforks=Fæstar skiptingar +issues.action_open=Opna +issues.action_close=Loka +issues.action_label=Lýsing +issues.action_milestone=Tímamót +issues.action_milestone_no_select=Ekkert tímamót +issues.opened_by=opnað %[1]s af %[3]s +issues.opened_by_fake=opnað %[1] af %[2]s +issues.previous=Fyrri +issues.next=Áfram +issues.open_title=Opið +issues.closed_title=Lokað +issues.num_comments=%d ummæli +issues.commented_at=`gerði ummæli %s` +issues.context.edit=Breyta +issues.context.delete=Eyða +issues.close_issue=Loka +issues.manually_pull_merged_at=`sameinaði framlag %[2]s inni í %[3]s handvirkt %[4]s` +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.ref_reopened_from=`enduropnaði þetta vandamál %[4]s %[2]s` +issues.owner=Eigandi +issues.edit=Breyta +issues.cancel=Hætta við +issues.save=Vista +issues.label_title=Lýsingarheiti +issues.label_description=Lýsingarskýring +issues.label_color=Lýsingarlitur +issues.label_count=%d lýsingar +issues.label_open_issues=%d opin vandamál +issues.label_edit=Breyta +issues.label_delete=Eyða +issues.label_modify=Breytta Lýsingu +issues.label_deletion=Eyða Lýsingu +issues.label_deletion_desc=Ef lýsing er eytt er hún fjarlægð af öllum vandamálum. Halda áfram? +issues.label_deletion_success=Þessi lýsing hefur verið eytt. +issues.label.filter_sort.alphabetically=Stafrófsröð +issues.label.filter_sort.by_size=Minnsta stærð +issues.label.filter_sort.reverse_by_size=Stærsta stærð +issues.subscribe=Gerast áskrifandi +issues.unsubscribe=Afturkalla áskrift +issues.lock=Læsa umræðu +issues.unlock=Aflæsa umræðu +issues.unlock_comment=aflæsti þessa umræðu %s +issues.lock_confirm=Læsa +issues.unlock_confirm=Aflæsa +issues.delete=Eyða +issues.start_tracking_short=Ræsa Tímamælir +issues.add_time=Bæta Bið Tíma Handvirkt +issues.add_time_short=Bæta Bið Tíma +issues.add_time_cancel=Hætta við +issues.add_time_history=`bætti við eyddum tíma %s` +issues.del_time_history=`fjarlægði eyddum tíma %s` +issues.add_time_hours=Klukkutímar +issues.add_time_minutes=Mínútur +issues.add_time_sum_to_small=Enginn tími var sleginn inn. +issues.time_spent_total=Heildartíma Eytt +issues.time_spent_from_all_authors=`Heildartíma Eytt: %s` +issues.due_date=Eindagi +issues.push_commit_1=bætti við %d framlag %s +issues.push_commits_n=bætti við %d framlög %s +issues.due_date_form=áááá-mm-dd +issues.due_date_form_add=Bæta við eindaga +issues.due_date_form_edit=Breyta +issues.due_date_form_remove=Fjarlægja +issues.due_date_not_set=Enginn eindagi settur. +issues.due_date_added=bætti við eindagi %s %s +issues.due_date_remove=fjarlægði eindagi %s %s +issues.due_date_overdue=Gjaldfallið +issues.dependency.title=Kröfur +issues.dependency.issue_no_dependencies=Engar kröfur innsettar. +issues.dependency.pr_no_dependencies=Engar kröfur innsettar. +issues.dependency.add=Bæta við kröfu… +issues.dependency.cancel=Hætta við +issues.dependency.remove=Fjarlægja +issues.dependency.remove_info=Fjarlægja þessa kröfu +issues.dependency.added_dependency=`bætti við nýja kröfu %s` +issues.dependency.removed_dependency=`fjarlægði kröfu %s` +issues.dependency.blocked_by_short=Krefur +issues.dependency.remove_header=Fjarlægja Kröfu +issues.dependency.add_error_dep_not_exist=Krafa er ekki til. +issues.dependency.add_error_dep_exists=Krafa er nú þegar til. +issues.review.approve=samþykkti þessar breytingar %s +issues.review.comment=fór yfir %s +issues.review.dismissed_label=Hunsað +issues.review.left_comment=gerði ummæli +issues.review.pending=Í bið +issues.review.outdated=Úrelt +issues.review.show_outdated=Sýna úrelt +issues.review.hide_outdated=Fela úreld +issues.reference_issue.body=Meginmál +issues.content_history.deleted=eytt +issues.content_history.edited=breytt +issues.content_history.created=skapað +issues.content_history.delete_from_history=Eyða úr ferlinum +issues.content_history.delete_from_history_confirm=Eyða úr ferlinum? +issues.content_history.options=Valkostir + +compare.compare_base=grunnur +compare.compare_head=bera saman + +pulls.new=Ný Sameiningarbeiðni +pulls.view=Skoða Sameiningarbeiðni +pulls.compare_changes=Ný Sameiningarbeiðni +pulls.create=Skapa Sameiningarbeiðni +pulls.title_desc=vill sameina %[1]d framlög frá %[2]s í %[3]s +pulls.tab_conversation=Umræða +pulls.tab_commits=Framlög +pulls.tab_files=Skráum Breytt +pulls.merged=Sameinað +pulls.manually_merged=Sameinað handvirkt +pulls.is_closed=Sameiningarbeiðnin hefur verið lokuð. +pulls.blocked_by_approvals=Þessi Sameiningarbeiðni hefur ekki nóg samþykki ennþá. %d/%d samþykki. +pulls.num_conflicting_files_1=%d skrá í átökum +pulls.num_conflicting_files_n=%d skrár í átökum +pulls.approve_count_1=%d samþykki +pulls.approve_count_n=%d samþykki +pulls.reject_count_1=%d breytingarbeiðni +pulls.reject_count_n=%d breytingarbeiðnir +pulls.waiting_count_1=%d bíður endurskoðunar +pulls.waiting_count_n=%d bíða endurskoðunar + +pulls.merge_manually=Sameinað handvirkt +; %[2]s
%[3]s
+pulls.status_checks_requested=Nauðsynlegt +pulls.status_checks_details=Nánar + + +milestones.new=Nýtt tímamót +milestones.open_tab=%d Opin +milestones.close_tab=%d Lokuð +milestones.closed=Lokaði %s +milestones.update_ago=Uppfært fyrir %s +milestones.no_due_date=Enginn eindagi +milestones.open=Opna +milestones.close=Loka +milestones.title=Heiti +milestones.desc=Lýsing +milestones.due_date=Eindagi (valfrjálst) +milestones.clear=Hreinsa +milestones.cancel=Hætta við +milestones.filter_sort.most_issues=Flest vandamál +milestones.filter_sort.least_issues=Fæst vandamál + + + +wiki=Handbók +wiki.welcome=Velkomin(n) í handbókina. +wiki.create_first_page=Skapa Fyrstu Síðu +wiki.page=Síða +wiki.new_page=Síða +wiki.save_page=Vista Síðu +wiki.last_commit_info=%s breytti þessari síðu %s +wiki.edit_page_button=Breyta +wiki.new_page_button=Ný Síða +wiki.back_to_wiki=Aftur að handbókssíðu +wiki.delete_page_button=Eyða Síðu +wiki.page_already_exists=Handbókssíða með sömu nafni er þegar til. +wiki.reserved_page=Handbókssíðuheiti „%s“ er frátekið. +wiki.pages=Síður +wiki.last_updated=Síðast uppfært: %s + +activity=Virkni +activity.period.filter_label=Tímabil: +activity.period.daily=1 dagur +activity.period.halfweekly=3 dagar +activity.period.weekly=1 vika +activity.period.monthly=1 mánuður +activity.period.quarterly=3 mánuðir +activity.period.semiyearly=6 mánuðir +activity.period.yearly=1 ár +activity.overview=Yfirlit +activity.opened_prs_count_1=Fyrirhugað Sameiningarbeiðni +activity.opened_prs_count_n=Fyrirhuguð Sameiningarbeiðnir +activity.title.user_1=%d notandi +activity.title.user_n=%d notandar +activity.title.prs_1=%d Sameiningarbeiðni +activity.title.prs_n=%d Sameiningarbeiðnir +activity.title.prs_opened_by=%s lagt til af %s +activity.merged_prs_label=Sameinað +activity.opened_prs_label=Tillaga +activity.closed_issues_count_1=Lokað Vandamál +activity.closed_issues_count_n=Lokuð Vandamál +activity.title.issues_1=%d Vandamál +activity.title.issues_n=%d Vandamál +activity.closed_issue_label=Lokað +activity.new_issues_count_1=Nýtt Vandamál +activity.new_issues_count_n=Ný Vandamál +activity.new_issue_label=Opnað +activity.unresolved_conv_label=Opið +activity.title.releases_1=%d Útgáfa +activity.title.releases_n=%d Útgáfur +activity.git_stats_author_1=%d höfundur +activity.git_stats_author_n=%d höfundar +activity.git_stats_commit_1=%d framlag +activity.git_stats_commit_n=%d framlög +activity.git_stats_on_default_branch=Á %s, +activity.git_stats_file_1=%d skrá +activity.git_stats_file_n=%d skrár +activity.git_stats_files_changed_1=hefur breyst +activity.git_stats_files_changed_n=hafa breyst +activity.git_stats_addition_1=%d viðbót +activity.git_stats_addition_n=%d viðbætur +activity.git_stats_and_deletions=og +activity.git_stats_deletion_1=%d eyðing +activity.git_stats_deletion_n=%d eyðingar + +search=Leita +search.code_no_results=Enginn samsvarandi frumkóði fannst eftur þínum leitarorðum. + +settings=Stillingar +settings.options=Hugbúnaðarsafn +settings.collaboration.write=Skrifa +settings.collaboration.read=Lesa +settings.collaboration.owner=Eigandi +settings.collaboration.undefined=Óskilgreint +settings.hooks=Vefkrókar +settings.githooks=Git Krókar +settings.basic_settings=Grunnstillingar +settings.mirror_settings=Speglunarstillingar +settings.mirror_settings.mirrored_repository=Speglað hugbúnaðarsafn +settings.mirror_settings.direction=Stefna +settings.mirror_settings.direction.pull=Pull +settings.mirror_settings.direction.push=Push +settings.mirror_settings.last_update=Síðasta uppfærsla +settings.mirror_settings.push_mirror.remote_url=Vefslóð Git Fjarhugbúnaðarsafns +settings.email_notifications.enable=Virkja Tölvupósttilkynningar +settings.email_notifications.onmention=Aðeins Tölvupóst Þegar Minnst Er á Mig +settings.email_notifications.disable=Óvirkja Tölvupósttilkynningar +settings.email_notifications.submit=Stilla Val á Tölvupósti +settings.site=Vefsíða +settings.update_settings=Uppfæra Stillingar +settings.branches.update_default_branch=Uppfæra Sjálfgefna Grein +settings.wiki_desc=Virkja Handbók Hugbúnaðarsafns +settings.use_internal_wiki=Nota Innbyggða Handbók +settings.use_external_wiki=Nota Utanaðkomandi Handbók +settings.tracker_issue_style.numeric=Tölugildi +settings.danger_zone=Hættusvæði +settings.convert_notices_1=Þessi aðgerð mun breyta speglinum í venjulegt hugbúnaðarsafn og ekki er hægt að afturkalla hana. +settings.transfer=Flytja Eignarhald +settings.trust_model.collaboratorcommitter.desc=Gildar undirskriftir frá samstarfsaðilum hugbúnaðarsafnsins verða merktar „traust“ ef þær passa við framlagandan. Að öðrum kosti verða gildar undirskriftir merktar „ótraust“ ef undirskriftin passar við framlagandan og „ósamþykkt“ að öðru leyti. Þetta mun neyða Gitea til að vera merkt sem framlagandi á undirrituðum framlögum með raunverulega framlagandan merktan sem Co-Authored-By: og Co-Committed-By: í framlaginu. Sjálfgefinn Gitea lykill verður að passa við notanda í gagnagrunninum. +settings.wiki_delete_desc=Að eyða handbókargögn er varanlegt og ekki er hægt að afturkalla það. +settings.delete=Eyða Þetta Hugbúnaðarsafn +settings.delete_desc=Að eyða hugbúnaðarsafni er varanlegt og ekki er hægt að afturkalla það. +settings.delete_notices_fork_1=— Skiptingar þessara hugbúnaðarsafns verða sjálfstæðar eftir eyðingu. +settings.delete_collaborator=Fjarlægja +settings.teams=Lið +settings.add_webhook=Bæta við Vefkróki +settings.webhook.request=Beiðni +settings.webhook.headers=Hausar +settings.webhook.body=Meginmál +settings.update_githook=Uppfæra Krók +settings.slack_username=Notandanafn +settings.slack_icon_url=Táknmyndarvefslóð +settings.slack_color=Litur +settings.discord_username=Notandanafn +settings.discord_icon_url=Táknmyndarvefslóð +settings.event_delete=Eyða +settings.event_fork=Tvískipta +settings.event_release=Útgáfa +settings.event_repository=Hugbúnaðarsafn +settings.event_issues=Vandamál +settings.event_issues_desc=Vandamál opið, lokað, enduropnað eða breytt. +settings.event_issue_label=Vandamál Lýst +settings.event_issue_comment=Ummæli um Vandamál +settings.event_pull_request=Sameiningarbeiðni +settings.event_pull_request_desc=Sameiningarbeiðni opnuð, lokuð, enduropnuð eða breytt. +settings.active=Virkt +settings.update_webhook=Uppfæra Vefkrók +settings.slack_token=Táknlykill +settings.slack_channel=Rás +settings.web_hook_name_gitea=Gitea +settings.web_hook_name_discord=Discord +settings.web_hook_name_dingtalk=DingTalk +settings.web_hook_name_telegram=Telegram +settings.web_hook_name_msteams=Microsoft Teams +settings.web_hook_name_feishu=Feishu +settings.title=Heiti +settings.deploy_key_content=Innihald +settings.branches=Greinar +settings.edit_protected_branch=Breyta +settings.tags=Merki +settings.tags.protection.allowed.users=Leyfðir notendur +settings.tags.protection.allowed.teams=Leyfð lið +settings.tags.protection.allowed.noone=Enginn +settings.lfs=LFS +settings.lfs_locks=Lásar +settings.lfs_lock=Læsa +settings.lfs_pointers.oid=OID + +diff.commit=framlag +diff.git-notes=Athugasemd +diff.whitespace_button=Hvítbil +diff.whitespace_ignore_all_whitespace=Hunsa hvítbil þegar línur eru bornar saman +diff.stats_desc= %d breyttar skrár með %d viðbætur og %d eyðingar +diff.bin=Tvíundarkóði +diff.view_file=Skoða Skrá +diff.file_before=Fyrir +diff.file_after=Eftir +diff.file_image_width=Breidd +diff.file_image_height=Hæð +diff.file_byte_size=Stærð +diff.comment.add_single_comment=Bæta við eitt ummæli +diff.comment.add_review_comment=Leggja inn ummæli +diff.comment.reply=Svara +diff.review.comment=Senda Ummæli +diff.image.overlay=Yfirleggja + +release.releases=Útgáfur +release.tags=Merki +release.draft=Uppkast +release.compare=Bera saman +release.edit=breyta +release.tag_name=Merkisheiti +release.title=Heiti +release.content=Innihald +release.cancel=Hætta við +release.delete_tag=Eyða Merki +release.downloads=Niðurhöl + +branch.delete_head=Eyða +branch.tag_collision=Ekki er hægt að skapa grein »%s“ þar sem merki með sama nafni er þegar til í hugbúnaðarsafninu. + +tag.create_tag_operation=Skapa merki +tag.confirm_create_tag=Skapa merki + + +topic.done=Í lagi + + +[org] +repo_updated=Uppfært +people=Fólk +teams=Lið +lower_members=meðlimar +lower_repositories=hugbúnaðarsöfn +create_new_team=Nýtt Lið +org_desc=Lýsing +team_name=Liðsheiti +team_desc=Lýsing + + +settings=Stillingar +settings.full_name=Fullt nafn +settings.website=Vefsíða +settings.location=Staðsetning +settings.visibility=Sýnileiki +settings.visibility.public=Opinbert +settings.visibility.private_shortname=Einka + +settings.update_settings=Uppfæra Stillingar + + +members.private=Faldir +members.owner=Eigandi +members.member=Meðlimur +members.remove=Fjarlægja +members.leave=Yfirgefa +members.leave.detail=Viltu yfirgefa %s? +members.invite_desc=Bæta nýjum meðlimi við í %s: +members.invite_now=Bjóða Núna + +teams.join=Gerast meðlimur +teams.leave=Yfirgefa +teams.leave.detail=Viltu yfirgefa %s? +teams.can_create_org_repo=Skapa Hugbúnaðarsöfn +teams.none_access=Engin Aðgangur +teams.read_access=Lesa +teams.write_access=Skrifa +teams.settings=Stillingar +teams.update_settings=Uppfæra Stillingar +teams.all_repositories=Öll hugbúnaðarsöfn + +[admin] +repositories=Hugbúnaðarsöfn +config=Stilling +first_page=Byrjun +last_page=Síðasta +total=Samtals: %d + +dashboard.new_version_hint=Gitea %s er nú í boði en þú ert að keyra %s. Athugaðu bloggsíðuna fyrir frekari upplýsingar. +dashboard.statistic=Yfirlit +dashboard.statistic_info=Gagnasafn Gitea inniheldur %d notendur, %d stofnanir, %d dreifilykla, %d hugbúnaðarsöfn, %d áhorfir, %d eftirlæti, %d aðgerðir, %d aðganga, %d vandamál, %d ummæli, %d félagsreikningum, %d fylgjanir, %d speglanir, %d útgæfur, %d auðkenningarheimildir, %d vefkrókar, %d tímamót, %d skýringar, %d krókaverkefni, %d lið, %d uppfærsluaðgerðir, %d viðhengi. +dashboard.operation_switch=Skipta +dashboard.operation_run=Keyra +dashboard.update_mirrors=Uppfæra Speglanir +dashboard.server_uptime=Uppitími Netþjóns +dashboard.total_memory_allocated=Heildarminni úthlutað + +users.name=Notandanafn +users.full_name=Fullt Nafn +users.admin=Stjórnandi +users.2fa=Tvíþætt auðkenning +users.repos=Söfn +users.created=Búið til +users.edit=Breyta +users.local=Staðbundið +users.list_status_filter.menu_text=Sía +users.list_status_filter.reset=Endurstilla +users.list_status_filter.is_prohibit_login=Stöðva Innskráningu +users.list_status_filter.not_prohibit_login=Leyfa Innskráningu +users.list_status_filter.not_2fa_enabled=Tvíþætt Auðkenning Óvirk + +emails.primary=Aðal +emails.filter_sort.email=Tölvupóstur +emails.filter_sort.name=Notandanafn +emails.updated=Netfang uppfært + +orgs.name=Heiti +orgs.teams=Lið +orgs.members=Meðlimar + +repos.owner=Eigandi +repos.name=Heiti +repos.watches=Fylgist með +repos.stars=Eftirlæti +repos.forks=Skiptingar +repos.issues=Vandamál +repos.size=Stærð + +packages.total_size=Heildarstærð: %s +packages.name=Heiti +packages.version=Útgáfa +packages.type=Tegund +packages.repository=Hugbúnaðarsafn +packages.size=Stærð + +defaulthooks.desc=Vefkrókar senda sjálfkrafa HTTP POST beiðnir til netþjóns þegar ákveðnir Gitea atburðir koma af stað. Vefkrókar sem eru skilgreindir hér eru sjálfgefnir og verða afritaðir í allar nýjar geymslur. Frekari upplýsingar eru í handbókini. + + +auths.name=Heiti +auths.type=Tegund +auths.updated=Uppfært +auths.domain=Lén +auths.host=Hýsill +auths.port=Gátt +auths.search_page_size=Síðustærð +auths.smtphost=SMTP Hýsill +auths.smtpport=SMTP Gátt +auths.oauth2_icon_url=Táknmyndarvefslóð +auths.oauth2_profileURL=Notandasíðuslóð +auths.tips=Ábendingar +auths.tip.dropbox=Búðu til nýtt forrit á https://www.dropbox.com/developers/apps +auths.tip.yandex=Búðu til nýja umsókn á https://oauth.yandex.com/client/new. Veldu eftirfarandi heimildir úr „Yandex.Passport API“ kaflanum: "Aðgangur að netfangi", "Aðgangur að notandamynd" og "Aðgangur að notendanafni, fornafni og eftirnafni, kyni" + +config.app_name=Heiti Vefsvæðis +config.app_ver=Útgáfu Gitea +config.git_version=Útgáfa Git +config.repo_root_path=Grunnsslóð Hugbúnaðarsafns +config.lfs_root_path=LFS Grunnsslóð +config.log_file_root_path=Slóð Annáls + +config.ssh_port=Gátt +config.ssh_listen_port=Hlustunargátt +config.ssh_root_path=Grunnsslóð + +config.lfs_enabled=Virkt + +config.db_type=Tegund +config.db_host=Hýsill +config.db_name=Heiti +config.db_user=Notandanafn +config.db_schema=Uppdráttur +config.db_ssl_mode=SSL +config.db_path=Slóð + + + +config.mailer_name=Heiti +config.mailer_host=Hýsill +config.mailer_user=Notandi + + + +config.https_only=Aðeins HTTPS + + + + +monitor.name=Heiti +monitor.goroutines=%d Górútínur +monitor.desc=Lýsing +monitor.process.children=Börn +monitor.queues=Raðir +monitor.queue.name=Heiti +monitor.queue.type=Tegund + +monitor.queue.settings.submit=Uppfæra Stillingar +monitor.queue.settings.changed=Stillingar Uppfærðar +monitor.queue.settings.blocktimeout.value=%[1]v + + +notices.type=Tegund +notices.type_1=Hugbúnaðarsafn +notices.type_2=Verkefni +notices.desc=Lýsing + +[action] +create_issue=`opnaði vandamál %[3]s#%[2]s` +reopen_issue=`enduropnaði vandamál %[3]s#%[2]s` +reopen_pull_request=`enduropnaði sameiningarbeiðni %[3]s#%[2]s` +comment_issue=`gerði ummæli á vandamál %[3]s#%[2]s` +comment_pull=`gerði ummæli á sameiningarbeiðni %[3]s#%[2]s` +review_dismissed_reason=Ástæða: + +[tool] +ago=%s síðan +now=núna +future=í framtíð +1s=1 sekúnda +1m=1 mínúta +1h=1 klukkutími +1d=1 dagur +1w=1 vika +1mon=1 mánuður +1y=1 ár +seconds=%d sekúndur +minutes=%d mínútur +hours=%d klukkutímar +days=%d dagar +weeks=%d vikur +months=%d mánuðir +years=%d ár +raw_seconds=sekúndur +raw_minutes=mínútur + +[dropzone] + +[notification] +notifications=Tilkynningar +unread=Ólesnar +read=Lesnar + +[gpg] + +[units] + +[packages] +title=Pakkar +empty.documentation=Frekari upplýsingar um pakka skrána er hægt að finna hér. +filter.type=Tegund +filter.type.all=Allir +installation=Uppsetning +keywords=Stikkorð +details=Nánar +details.author=Höfundur +details.license=Hugbúnaðarleyfi +versions=Útgáfur +versions.on=á +versions.view_all=Sjá allar +dependency.id=Auðkenni +dependency.version=Útgáfa +composer.documentation=Frekari upplýsingar um Composer skrána er hægt að finna hér. +conan.details.repository=Hugbúnaðarsafn +conan.documentation=Frekari upplýsingar um Conan skrána er hægt að finna hér. +container.details.platform=Vettvangur +container.documentation=Frekari upplýsingar um Container skrána er hægt að finna hér. +container.labels=Lýsingar +container.labels.key=Lykill +container.labels.value=Gildi +generic.documentation=Frekari upplýsingar um almennu skrána er hægt að finna hér. +maven.documentation=Frekari upplýsingar um Maven skrána er hægt að finna hér. +nuget.documentation=Frekari upplýsingar um NuGet skrána er hægt að finna hér. +npm.documentation=Frekari upplýsingar um npm skrána er hægt að finna hér. +npm.details.tag=Merki +pypi.requires=Þarfnast Python +pypi.documentation=Frekari upplýsingar um PyPI skrána er hægt að finna hér. +rubygems.documentation=Frekari upplýsingar um RubyGems skrána er hægt að finna hér. + From 18727df73a00e7d05d3e1a68d6dae521cca7ff49 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Tue, 19 Apr 2022 18:55:35 +0200 Subject: [PATCH 0009/1287] Add Helm Chart registry (#19406) --- docs/content/doc/packages/helm.en-us.md | 67 +++++++ docs/content/doc/packages/maven.en-us.md | 2 +- docs/content/doc/packages/npm.en-us.md | 2 +- docs/content/doc/packages/nuget.en-us.md | 2 +- docs/content/doc/packages/overview.en-us.md | 1 + docs/content/doc/packages/pypi.en-us.md | 2 +- docs/content/doc/packages/rubygems.en-us.md | 2 +- integrations/api_packages_helm_test.go | 166 ++++++++++++++++ models/packages/descriptor.go | 3 + models/packages/package.go | 25 ++- modules/packages/helm/metadata.go | 131 +++++++++++++ options/locale/locale_en-US.ini | 3 + public/img/svg/gitea-helm.svg | 1 + routers/api/packages/api.go | 6 + routers/api/packages/helm/helm.go | 205 ++++++++++++++++++++ routers/api/v1/packages/package.go | 2 +- templates/admin/packages/list.tmpl | 1 + templates/package/content/helm.tmpl | 57 ++++++ templates/package/content/npm.tmpl | 2 +- templates/package/metadata/helm.tmpl | 4 + templates/package/shared/list.tmpl | 1 + templates/package/view.tmpl | 10 +- templates/swagger/v1_json.tmpl | 2 + web_src/svg/gitea-helm.svg | 3 + 24 files changed, 679 insertions(+), 21 deletions(-) create mode 100644 docs/content/doc/packages/helm.en-us.md create mode 100644 integrations/api_packages_helm_test.go create mode 100644 modules/packages/helm/metadata.go create mode 100644 public/img/svg/gitea-helm.svg create mode 100644 routers/api/packages/helm/helm.go create mode 100644 templates/package/content/helm.tmpl create mode 100644 templates/package/metadata/helm.tmpl create mode 100644 web_src/svg/gitea-helm.svg diff --git a/docs/content/doc/packages/helm.en-us.md b/docs/content/doc/packages/helm.en-us.md new file mode 100644 index 0000000000000..9c43b08bf4298 --- /dev/null +++ b/docs/content/doc/packages/helm.en-us.md @@ -0,0 +1,67 @@ +--- +date: "2022-04-14T00:00:00+00:00" +title: "Helm Chart Registry" +slug: "packages/helm" +draft: false +toc: false +menu: + sidebar: + parent: "packages" + name: "Helm" + weight: 50 + identifier: "helm" +--- + +# Helm Chart Registry + +Publish [Helm](https://helm.sh/) charts for your user or organization. + +**Table of Contents** + +{{< toc >}} + +## Requirements + +To work with the Helm Chart registry use a simple HTTP client like `curl` or the [`helm cm-push`](https://github.com/chartmuseum/helm-push/) plugin. + +## Publish a package + +Publish a package by running the following command: + +```shell +curl --user {username}:{password} -X POST --upload-file ./{chart_file}.tgz https://gitea.example.com/api/packages/{owner}/helm/api/charts +``` + +or with the `helm cm-push` plugin: + +```shell +helm repo add --username {username} --password {password} {repo} https://gitea.example.com/api/packages/{owner}/helm +helm cm-push ./{chart_file}.tgz {repo} +``` + +| Parameter | Description | +| ------------ | ----------- | +| `username` | Your Gitea username. | +| `password` | Your Gitea password or a personal access token. | +| `repo` | The name for the repository. | +| `chart_file` | The Helm Chart archive. | +| `owner` | The owner of the package. | + +## Install a package + +To install a Helm char from the registry, execute the following command: + +```shell +helm repo add --username {username} --password {password} {repo} https://gitea.example.com/api/packages/{owner}/helm +helm repo update +helm install {name} {repo}/{chart} +``` + +| Parameter | Description | +| ---------- | ----------- | +| `username` | Your Gitea username. | +| `password` | Your Gitea password or a personal access token. | +| `repo` | The name for the repository. | +| `owner` | The owner of the package. | +| `name` | The local name. | +| `chart` | The name Helm Chart. | diff --git a/docs/content/doc/packages/maven.en-us.md b/docs/content/doc/packages/maven.en-us.md index 78288a9e42faf..837c8434ae4d5 100644 --- a/docs/content/doc/packages/maven.en-us.md +++ b/docs/content/doc/packages/maven.en-us.md @@ -8,7 +8,7 @@ menu: sidebar: parent: "packages" name: "Maven" - weight: 50 + weight: 60 identifier: "maven" --- diff --git a/docs/content/doc/packages/npm.en-us.md b/docs/content/doc/packages/npm.en-us.md index 28b7cb8827029..9ab4ac900cfb4 100644 --- a/docs/content/doc/packages/npm.en-us.md +++ b/docs/content/doc/packages/npm.en-us.md @@ -8,7 +8,7 @@ menu: sidebar: parent: "packages" name: "npm" - weight: 60 + weight: 70 identifier: "npm" --- diff --git a/docs/content/doc/packages/nuget.en-us.md b/docs/content/doc/packages/nuget.en-us.md index 5565bf5b895d6..0b92d85a3d4e9 100644 --- a/docs/content/doc/packages/nuget.en-us.md +++ b/docs/content/doc/packages/nuget.en-us.md @@ -8,7 +8,7 @@ menu: sidebar: parent: "packages" name: "NuGet" - weight: 70 + weight: 80 identifier: "nuget" --- diff --git a/docs/content/doc/packages/overview.en-us.md b/docs/content/doc/packages/overview.en-us.md index 1e4209930f551..10f2184bc9c25 100644 --- a/docs/content/doc/packages/overview.en-us.md +++ b/docs/content/doc/packages/overview.en-us.md @@ -30,6 +30,7 @@ The following package managers are currently supported: | [Conan]({{< relref "doc/packages/conan.en-us.md" >}}) | C++ | `conan` | | [Container]({{< relref "doc/packages/container.en-us.md" >}}) | - | any OCI compliant client | | [Generic]({{< relref "doc/packages/generic.en-us.md" >}}) | - | any HTTP client | +| [Helm]({{< relref "doc/packages/helm.en-us.md" >}}) | - | any HTTP client, `cm-push` | | [Maven]({{< relref "doc/packages/maven.en-us.md" >}}) | Java | `mvn`, `gradle` | | [npm]({{< relref "doc/packages/npm.en-us.md" >}}) | JavaScript | `npm`, `yarn` | | [NuGet]({{< relref "doc/packages/nuget.en-us.md" >}}) | .NET | `nuget` | diff --git a/docs/content/doc/packages/pypi.en-us.md b/docs/content/doc/packages/pypi.en-us.md index 1d7a8f22e86de..d9f4872dca4c8 100644 --- a/docs/content/doc/packages/pypi.en-us.md +++ b/docs/content/doc/packages/pypi.en-us.md @@ -8,7 +8,7 @@ menu: sidebar: parent: "packages" name: "PyPI" - weight: 80 + weight: 90 identifier: "pypi" --- diff --git a/docs/content/doc/packages/rubygems.en-us.md b/docs/content/doc/packages/rubygems.en-us.md index 603e925e32afd..9d9ce09b1c6da 100644 --- a/docs/content/doc/packages/rubygems.en-us.md +++ b/docs/content/doc/packages/rubygems.en-us.md @@ -8,7 +8,7 @@ menu: sidebar: parent: "packages" name: "RubyGems" - weight: 90 + weight: 100 identifier: "rubygems" --- diff --git a/integrations/api_packages_helm_test.go b/integrations/api_packages_helm_test.go new file mode 100644 index 0000000000000..fcf5d2f7622c7 --- /dev/null +++ b/integrations/api_packages_helm_test.go @@ -0,0 +1,166 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "fmt" + "net/http" + "testing" + "time" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + helm_module "code.gitea.io/gitea/modules/packages/helm" + "code.gitea.io/gitea/modules/setting" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v2" +) + +func TestPackageHelm(t *testing.T) { + defer prepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + + packageName := "test-chart" + packageVersion := "1.0.3" + packageAuthor := "KN4CK3R" + packageDescription := "Gitea Test Package" + + filename := fmt.Sprintf("%s-%s.tgz", packageName, packageVersion) + + chartContent := `apiVersion: v2 +description: ` + packageDescription + ` +name: ` + packageName + ` +type: application +version: ` + packageVersion + ` +maintainers: +- name: ` + packageAuthor + ` +dependencies: +- name: dep1 + repository: https://example.com/ + version: 1.0.0` + + var buf bytes.Buffer + zw := gzip.NewWriter(&buf) + archive := tar.NewWriter(zw) + archive.WriteHeader(&tar.Header{ + Name: fmt.Sprintf("%s/Chart.yaml", packageName), + Mode: 0o600, + Size: int64(len(chartContent)), + }) + archive.Write([]byte(chartContent)) + archive.Close() + zw.Close() + content := buf.Bytes() + + url := fmt.Sprintf("/api/packages/%s/helm", user.Name) + + t.Run("Upload", func(t *testing.T) { + defer PrintCurrentTest(t)() + + uploadURL := url + "/api/charts" + + req := NewRequestWithBody(t, "POST", uploadURL, bytes.NewReader(content)) + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusCreated) + + pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeHelm) + assert.NoError(t, err) + assert.Len(t, pvs, 1) + + pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) + assert.NoError(t, err) + assert.NotNil(t, pd.SemVer) + assert.IsType(t, &helm_module.Metadata{}, pd.Metadata) + assert.Equal(t, packageName, pd.Package.Name) + assert.Equal(t, packageVersion, pd.Version.Version) + + pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) + assert.NoError(t, err) + assert.Len(t, pfs, 1) + assert.Equal(t, filename, pfs[0].Name) + assert.True(t, pfs[0].IsLead) + + pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID) + assert.NoError(t, err) + assert.Equal(t, int64(len(content)), pb.Size) + + req = NewRequestWithBody(t, "POST", uploadURL, bytes.NewReader(content)) + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusCreated) + }) + + t.Run("Download", func(t *testing.T) { + defer PrintCurrentTest(t)() + + checkDownloadCount := func(count int64) { + pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeHelm) + assert.NoError(t, err) + assert.Len(t, pvs, 1) + assert.Equal(t, count, pvs[0].DownloadCount) + } + + checkDownloadCount(0) + + req := NewRequest(t, "GET", fmt.Sprintf("%s/%s", url, filename)) + req = AddBasicAuthHeader(req, user.Name) + resp := MakeRequest(t, req, http.StatusOK) + + assert.Equal(t, content, resp.Body.Bytes()) + + checkDownloadCount(1) + }) + + t.Run("Index", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("%s/index.yaml", url)) + req = AddBasicAuthHeader(req, user.Name) + resp := MakeRequest(t, req, http.StatusOK) + + type ChartVersion struct { + helm_module.Metadata `yaml:",inline"` + URLs []string `yaml:"urls"` + Created time.Time `yaml:"created,omitempty"` + Removed bool `yaml:"removed,omitempty"` + Digest string `yaml:"digest,omitempty"` + } + + type ServerInfo struct { + ContextPath string `yaml:"contextPath,omitempty"` + } + + type Index struct { + APIVersion string `yaml:"apiVersion"` + Entries map[string][]*ChartVersion `yaml:"entries"` + Generated time.Time `yaml:"generated,omitempty"` + ServerInfo *ServerInfo `yaml:"serverInfo,omitempty"` + } + + var result Index + assert.NoError(t, yaml.NewDecoder(resp.Body).Decode(&result)) + assert.NotEmpty(t, result.Entries) + assert.Contains(t, result.Entries, packageName) + + cvs := result.Entries[packageName] + assert.Len(t, cvs, 1) + + cv := cvs[0] + assert.Equal(t, packageName, cv.Name) + assert.Equal(t, packageVersion, cv.Version) + assert.Equal(t, packageDescription, cv.Description) + assert.Len(t, cv.Maintainers, 1) + assert.Equal(t, packageAuthor, cv.Maintainers[0].Name) + assert.Len(t, cv.Dependencies, 1) + assert.ElementsMatch(t, []string{fmt.Sprintf("%s%s/%s", setting.AppURL, url[1:], filename)}, cv.URLs) + + assert.Equal(t, url, result.ServerInfo.ContextPath) + }) +} diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index 3249260f80a6f..fbdc40f37fbb2 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/packages/composer" "code.gitea.io/gitea/modules/packages/conan" "code.gitea.io/gitea/modules/packages/container" + "code.gitea.io/gitea/modules/packages/helm" "code.gitea.io/gitea/modules/packages/maven" "code.gitea.io/gitea/modules/packages/npm" "code.gitea.io/gitea/modules/packages/nuget" @@ -129,6 +130,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc metadata = &container.Metadata{} case TypeGeneric: // generic packages have no metadata + case TypeHelm: + metadata = &helm.Metadata{} case TypeNuGet: metadata = &nuget.Metadata{} case TypeNpm: diff --git a/models/packages/package.go b/models/packages/package.go index 373bd86d9f7db..bdb535492bb40 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -35,9 +35,10 @@ const ( TypeConan Type = "conan" TypeContainer Type = "container" TypeGeneric Type = "generic" - TypeNuGet Type = "nuget" - TypeNpm Type = "npm" + TypeHelm Type = "helm" TypeMaven Type = "maven" + TypeNpm Type = "npm" + TypeNuGet Type = "nuget" TypePyPI Type = "pypi" TypeRubyGems Type = "rubygems" ) @@ -53,12 +54,14 @@ func (pt Type) Name() string { return "Container" case TypeGeneric: return "Generic" - case TypeNuGet: - return "NuGet" - case TypeNpm: - return "npm" + case TypeHelm: + return "Helm" case TypeMaven: return "Maven" + case TypeNpm: + return "npm" + case TypeNuGet: + return "NuGet" case TypePyPI: return "PyPI" case TypeRubyGems: @@ -78,12 +81,14 @@ func (pt Type) SVGName() string { return "octicon-container" case TypeGeneric: return "octicon-package" - case TypeNuGet: - return "gitea-nuget" - case TypeNpm: - return "gitea-npm" + case TypeHelm: + return "gitea-helm" case TypeMaven: return "gitea-maven" + case TypeNpm: + return "gitea-npm" + case TypeNuGet: + return "gitea-nuget" case TypePyPI: return "gitea-python" case TypeRubyGems: diff --git a/modules/packages/helm/metadata.go b/modules/packages/helm/metadata.go new file mode 100644 index 0000000000000..9517448ca68bb --- /dev/null +++ b/modules/packages/helm/metadata.go @@ -0,0 +1,131 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package helm + +import ( + "archive/tar" + "compress/gzip" + "errors" + "io" + "strings" + + "code.gitea.io/gitea/modules/validation" + + "github.com/hashicorp/go-version" + "gopkg.in/yaml.v2" +) + +var ( + // ErrMissingChartFile indicates a missing Chart.yaml file + ErrMissingChartFile = errors.New("Chart.yaml file is missing") + // ErrInvalidName indicates an invalid package name + ErrInvalidName = errors.New("package name is invalid") + // ErrInvalidVersion indicates an invalid package version + ErrInvalidVersion = errors.New("package version is invalid") + // ErrInvalidChart indicates an invalid chart + ErrInvalidChart = errors.New("chart is invalid") +) + +// Metadata for a Chart file. This models the structure of a Chart.yaml file. +type Metadata struct { + APIVersion string `json:"api_version" yaml:"apiVersion"` + Type string `json:"type,omitempty" yaml:"type,omitempty"` + Name string `json:"name" yaml:"name"` + Version string `json:"version" yaml:"version"` + AppVersion string `json:"app_version,omitempty" yaml:"appVersion,omitempty"` + Home string `json:"home,omitempty" yaml:"home,omitempty"` + Sources []string `json:"sources,omitempty" yaml:"sources,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + Keywords []string `json:"keywords,omitempty" yaml:"keywords,omitempty"` + Maintainers []*Maintainer `json:"maintainers,omitempty" yaml:"maintainers,omitempty"` + Icon string `json:"icon,omitempty" yaml:"icon,omitempty"` + Condition string `json:"condition,omitempty" yaml:"condition,omitempty"` + Tags string `json:"tags,omitempty" yaml:"tags,omitempty"` + Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"` + KubeVersion string `json:"kube_version,omitempty" yaml:"kubeVersion,omitempty"` + Dependencies []*Dependency `json:"dependencies,omitempty" yaml:"dependencies,omitempty"` +} + +type Maintainer struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Email string `json:"email,omitempty" yaml:"email,omitempty"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` +} + +type Dependency struct { + Name string `json:"name" yaml:"name"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + Repository string `json:"repository" yaml:"repository"` + Condition string `json:"condition,omitempty" yaml:"condition,omitempty"` + Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` + Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` + ImportValues []interface{} `json:"import_values,omitempty" yaml:"import-values,omitempty"` + Alias string `json:"alias,omitempty" yaml:"alias,omitempty"` +} + +// ParseChartArchive parses the metadata of a Helm archive +func ParseChartArchive(r io.Reader) (*Metadata, error) { + gzr, err := gzip.NewReader(r) + if err != nil { + return nil, err + } + defer gzr.Close() + + tr := tar.NewReader(gzr) + for { + hd, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + + if hd.Typeflag != tar.TypeReg { + continue + } + + if hd.FileInfo().Name() == "Chart.yaml" { + if strings.Count(hd.Name, "/") != 1 { + continue + } + + return ParseChartFile(tr) + } + } + + return nil, ErrMissingChartFile +} + +// ParseChartFile parses a Chart.yaml file to retrieve the metadata of a Helm chart +func ParseChartFile(r io.Reader) (*Metadata, error) { + var metadata *Metadata + if err := yaml.NewDecoder(r).Decode(&metadata); err != nil { + return nil, err + } + + if metadata.APIVersion == "" { + return nil, ErrInvalidChart + } + + if metadata.Type != "" && metadata.Type != "application" && metadata.Type != "library" { + return nil, ErrInvalidChart + } + + if metadata.Name == "" { + return nil, ErrInvalidName + } + + if _, err := version.NewSemver(metadata.Version); err != nil { + return nil, ErrInvalidVersion + } + + if !validation.IsValidURL(metadata.Home) { + metadata.Home = "" + } + + return metadata, nil +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5662ed2c40200..21bf0c49ea198 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3051,6 +3051,9 @@ container.labels.key = Key container.labels.value = Value generic.download = Download package from the command line: generic.documentation = For more information on the generic registry, see the documentation. +helm.registry = Setup this registry from the command line: +helm.install = To install the package, run the following command: +helm.documentation = For more information on the Helm registry, see the documentation. maven.registry = Setup this registry in your project pom.xml file: maven.install = To use the package include the following in the dependencies block in the pom.xml file: maven.install2 = Run via command line: diff --git a/public/img/svg/gitea-helm.svg b/public/img/svg/gitea-helm.svg new file mode 100644 index 0000000000000..5ab50dd29eb61 --- /dev/null +++ b/public/img/svg/gitea-helm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index f0251b95eb1f5..b5fdc739d7c10 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/routers/api/packages/conan" "code.gitea.io/gitea/routers/api/packages/container" "code.gitea.io/gitea/routers/api/packages/generic" + "code.gitea.io/gitea/routers/api/packages/helm" "code.gitea.io/gitea/routers/api/packages/maven" "code.gitea.io/gitea/routers/api/packages/npm" "code.gitea.io/gitea/routers/api/packages/nuget" @@ -162,6 +163,11 @@ func Routes() *web.Route { }, reqPackageAccess(perm.AccessModeWrite)) }) }) + r.Group("/helm", func() { + r.Get("/index.yaml", helm.Index) + r.Get("/{filename}", helm.DownloadPackageFile) + r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage) + }) r.Group("/maven", func() { r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile) r.Get("/*", maven.DownloadPackageFile) diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go new file mode 100644 index 0000000000000..ae0643a35a90f --- /dev/null +++ b/routers/api/packages/helm/helm.go @@ -0,0 +1,205 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package helm + +import ( + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + packages_model "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/log" + packages_module "code.gitea.io/gitea/modules/packages" + helm_module "code.gitea.io/gitea/modules/packages/helm" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/routers/api/packages/helper" + packages_service "code.gitea.io/gitea/services/packages" + + "gopkg.in/yaml.v2" +) + +func apiError(ctx *context.Context, status int, obj interface{}) { + helper.LogAndProcessError(ctx, status, obj, func(message string) { + type Error struct { + Error string `json:"error"` + } + ctx.JSON(status, Error{ + Error: message, + }) + }) +} + +// Index generates the Helm charts index +func Index(ctx *context.Context) { + pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ + OwnerID: ctx.Package.Owner.ID, + Type: packages_model.TypeHelm, + }) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + baseURL := setting.AppURL + "api/packages/" + url.PathEscape(ctx.Package.Owner.Name) + "/helm" + + type ChartVersion struct { + helm_module.Metadata `yaml:",inline"` + URLs []string `yaml:"urls"` + Created time.Time `yaml:"created,omitempty"` + Removed bool `yaml:"removed,omitempty"` + Digest string `yaml:"digest,omitempty"` + } + + type ServerInfo struct { + ContextPath string `yaml:"contextPath,omitempty"` + } + + type Index struct { + APIVersion string `yaml:"apiVersion"` + Entries map[string][]*ChartVersion `yaml:"entries"` + Generated time.Time `yaml:"generated,omitempty"` + ServerInfo *ServerInfo `yaml:"serverInfo,omitempty"` + } + + entries := make(map[string][]*ChartVersion) + for _, pv := range pvs { + metadata := &helm_module.Metadata{} + if err := json.Unmarshal([]byte(pv.MetadataJSON), &metadata); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + entries[metadata.Name] = append(entries[metadata.Name], &ChartVersion{ + Metadata: *metadata, + Created: pv.CreatedUnix.AsTime(), + URLs: []string{fmt.Sprintf("%s/%s", baseURL, url.PathEscape(createFilename(metadata)))}, + }) + } + + ctx.Resp.WriteHeader(http.StatusOK) + if err := yaml.NewEncoder(ctx.Resp).Encode(&Index{ + APIVersion: "v1", + Entries: entries, + Generated: time.Now(), + ServerInfo: &ServerInfo{ + ContextPath: setting.AppSubURL + "/api/packages/" + url.PathEscape(ctx.Package.Owner.Name) + "/helm", + }, + }); err != nil { + log.Error("YAML encode failed: %v", err) + } +} + +// DownloadPackageFile serves the content of a package +func DownloadPackageFile(ctx *context.Context) { + filename := ctx.Params("filename") + + pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ + OwnerID: ctx.Package.Owner.ID, + Type: packages_model.TypeHelm, + Name: packages_model.SearchValue{ + ExactMatch: true, + Value: ctx.Params("package"), + }, + HasFileWithName: filename, + }) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + if len(pvs) != 1 { + apiError(ctx, http.StatusNotFound, nil) + return + } + + s, pf, err := packages_service.GetFileStreamByPackageVersion( + ctx, + pvs[0], + &packages_service.PackageFileInfo{ + Filename: filename, + }, + ) + if err != nil { + if err == packages_model.ErrPackageFileNotExist { + apiError(ctx, http.StatusNotFound, err) + return + } + apiError(ctx, http.StatusInternalServerError, err) + return + } + defer s.Close() + + ctx.ServeStream(s, pf.Name) +} + +// UploadPackage creates a new package +func UploadPackage(ctx *context.Context) { + upload, needToClose, err := ctx.UploadStream() + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + if needToClose { + defer upload.Close() + } + + buf, err := packages_module.CreateHashedBufferFromReader(upload, 32*1024*1024) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + defer buf.Close() + + metadata, err := helm_module.ParseChartArchive(buf) + if err != nil { + apiError(ctx, http.StatusBadRequest, err) + return + } + + if _, err := buf.Seek(0, io.SeekStart); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + _, _, err = packages_service.CreatePackageOrAddFileToExisting( + &packages_service.PackageCreationInfo{ + PackageInfo: packages_service.PackageInfo{ + Owner: ctx.Package.Owner, + PackageType: packages_model.TypeHelm, + Name: metadata.Name, + Version: metadata.Version, + }, + SemverCompatible: true, + Creator: ctx.Doer, + Metadata: metadata, + }, + &packages_service.PackageFileCreationInfo{ + PackageFileInfo: packages_service.PackageFileInfo{ + Filename: createFilename(metadata), + }, + Data: buf, + IsLead: true, + OverwriteExisting: true, + }, + ) + if err != nil { + if err == packages_model.ErrDuplicatePackageVersion { + apiError(ctx, http.StatusConflict, err) + return + } + apiError(ctx, http.StatusInternalServerError, err) + return + } + + ctx.Status(http.StatusCreated) +} + +func createFilename(metadata *helm_module.Metadata) string { + return strings.ToLower(fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version)) +} diff --git a/routers/api/v1/packages/package.go b/routers/api/v1/packages/package.go index b445e8e2f830e..f3aa19c3192e7 100644 --- a/routers/api/v1/packages/package.go +++ b/routers/api/v1/packages/package.go @@ -40,7 +40,7 @@ func ListPackages(ctx *context.APIContext) { // in: query // description: package type filter // type: string - // enum: [composer, conan, generic, maven, npm, nuget, pypi, rubygems] + // enum: [composer, conan, container, generic, helm, maven, npm, nuget, pypi, rubygems] // - name: q // in: query // description: name filter diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index 114a108feb9f4..373a97407b5e2 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -17,6 +17,7 @@ + diff --git a/templates/package/content/helm.tmpl b/templates/package/content/helm.tmpl new file mode 100644 index 0000000000000..a85f7c4850f14 --- /dev/null +++ b/templates/package/content/helm.tmpl @@ -0,0 +1,57 @@ +{{if eq .PackageDescriptor.Package.Type "helm"}} +

{{.i18n.Tr "packages.installation"}}

+
+
+
+ +
helm repo add gitea {{AppUrl}}api/packages/{{.PackageDescriptor.Owner.Name}}/helm
+helm repo update
+
+
+ +
helm install {{.PackageDescriptor.Package.Name}} gitea/{{.PackageDescriptor.Package.Name}}
+
+
+ +
+
+
+ + {{if .PackageDescriptor.Metadata.Description}} +

{{.i18n.Tr "packages.about"}}

+
+ {{.PackageDescriptor.Metadata.Description}} +
+ {{end}} + + {{if .PackageDescriptor.Metadata.Dependencies}} +

{{.i18n.Tr "packages.dependencies"}}

+
+ + + + + + + + + {{range .PackageDescriptor.Metadata.Dependencies}} + + + + + {{end}} + +
{{.i18n.Tr "packages.dependency.id"}}{{.i18n.Tr "packages.dependency.version"}}
{{.Name}}{{.Version}}
+
+ {{end}} + + {{if .PackageDescriptor.Metadata.Keywords}} +

{{.i18n.Tr "packages.keywords"}}

+
+ {{range .PackageDescriptor.Metadata.Keywords}} + {{.}} + {{end}} +
+ {{end}} +{{end}} diff --git a/templates/package/content/npm.tmpl b/templates/package/content/npm.tmpl index 16347d1b6edfc..bc714e5c97745 100644 --- a/templates/package/content/npm.tmpl +++ b/templates/package/content/npm.tmpl @@ -45,7 +45,7 @@ {{end}} - {{if or .PackageDescriptor.Metadata.Keywords}} + {{if .PackageDescriptor.Metadata.Keywords}}

{{.i18n.Tr "packages.keywords"}}

{{range .PackageDescriptor.Metadata.Keywords}} diff --git a/templates/package/metadata/helm.tmpl b/templates/package/metadata/helm.tmpl new file mode 100644 index 0000000000000..7c97c6358e9a3 --- /dev/null +++ b/templates/package/metadata/helm.tmpl @@ -0,0 +1,4 @@ +{{if eq .PackageDescriptor.Package.Type "helm"}} + {{range .PackageDescriptor.Metadata.Maintainers}}
{{svg "octicon-person" 16 "mr-3"}} {{.Name}}
{{end}} + {{if .PackageDescriptor.Metadata.Home}}
{{svg "octicon-link-external" 16 "mr-3"}} {{.i18n.Tr "packages.details.project_site"}}
{{end}} +{{end}} diff --git a/templates/package/shared/list.tmpl b/templates/package/shared/list.tmpl index 9216e6b9de08d..0b0f71283b155 100644 --- a/templates/package/shared/list.tmpl +++ b/templates/package/shared/list.tmpl @@ -10,6 +10,7 @@ + diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl index 1b1c5d50c6553..bb96da3410669 100644 --- a/templates/package/view.tmpl +++ b/templates/package/view.tmpl @@ -23,9 +23,10 @@ {{template "package/content/conan" .}} {{template "package/content/container" .}} {{template "package/content/generic" .}} - {{template "package/content/nuget" .}} - {{template "package/content/npm" .}} + {{template "package/content/helm" .}} {{template "package/content/maven" .}} + {{template "package/content/npm" .}} + {{template "package/content/nuget" .}} {{template "package/content/pypi" .}} {{template "package/content/rubygems" .}}
@@ -43,9 +44,10 @@ {{template "package/metadata/conan" .}} {{template "package/metadata/container" .}} {{template "package/metadata/generic" .}} - {{template "package/metadata/nuget" .}} - {{template "package/metadata/npm" .}} + {{template "package/metadata/helm" .}} {{template "package/metadata/maven" .}} + {{template "package/metadata/npm" .}} + {{template "package/metadata/nuget" .}} {{template "package/metadata/pypi" .}} {{template "package/metadata/rubygems" .}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4bf3874bae284..e35ba989096ad 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1904,7 +1904,9 @@ "enum": [ "composer", "conan", + "container", "generic", + "helm", "maven", "npm", "nuget", diff --git a/web_src/svg/gitea-helm.svg b/web_src/svg/gitea-helm.svg new file mode 100644 index 0000000000000..120996559222c --- /dev/null +++ b/web_src/svg/gitea-helm.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From a56fcdfa8f19efcc53c2163d60c9c1e1bafbc019 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 20 Apr 2022 07:30:09 +0800 Subject: [PATCH 0010/1287] Fix nil error when some pages are rendered outside request context (#19427) --- templates/base/footer_content.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl index 52e7c7e77e965..f5ec6b0bf3419 100644 --- a/templates/base/footer_content.tmpl +++ b/templates/base/footer_content.tmpl @@ -10,7 +10,7 @@ {{AppVer}} {{end}} {{end}} - {{if ShowFooterTemplateLoadTime}} + {{if and .TemplateLoadTimes ShowFooterTemplateLoadTime}} {{.i18n.Tr "page"}}: {{LoadTimes .PageStartTime}} {{.i18n.Tr "template"}} {{if .TemplateName}} {{.TemplateName}}{{end}}: {{call .TemplateLoadTimes}} From 409ff55a293487868cfe034151d84de4cf4b4235 Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 20 Apr 2022 09:20:53 +0100 Subject: [PATCH 0011/1287] When updating mirror repo intervals by API reschedule next update too (#19429) When a mirror repo interval is updated by the UI it is rescheduled with that interval however the API does not do this. The API also lacks the enable_prune option. This PR adds this functionality in to the API Edit Repo endpoint. Signed-off-by: Andrew Thornton --- modules/structs/repo.go | 2 + routers/api/v1/repo/repo.go | 74 ++++++++++++++++++++++++---------- routers/web/repo/setting.go | 7 +--- templates/swagger/v1_json.tmpl | 5 +++ 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index c218796620eb3..ef247ebc9ce3a 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -187,6 +187,8 @@ type EditRepoOption struct { Archived *bool `json:"archived,omitempty"` // set to a string like `8h30m0s` to set the mirror interval time MirrorInterval *string `json:"mirror_interval,omitempty"` + // enable prune - remove obsolete remote-tracking references + EnablePrune *bool `json:"enable_prune,omitempty"` } // GenerateRepoOption options when creating repository using a template diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index f645502590e73..7df5864455d89 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -614,7 +614,7 @@ func Edit(ctx *context.APIContext) { } if opts.MirrorInterval != nil { - if err := updateMirrorInterval(ctx, opts); err != nil { + if err := updateMirror(ctx, opts); err != nil { return } } @@ -943,37 +943,67 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e return nil } -// updateMirrorInterval updates the repo's mirror Interval -func updateMirrorInterval(ctx *context.APIContext, opts api.EditRepoOption) error { +// updateMirror updates a repo's mirror Interval and EnablePrune +func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error { repo := ctx.Repo.Repository + // only update mirror if interval or enable prune are provided + if opts.MirrorInterval == nil && opts.EnablePrune == nil { + return nil + } + + // these values only make sense if the repo is a mirror + if !repo.IsMirror { + err := fmt.Errorf("repo is not a mirror, can not change mirror interval") + ctx.Error(http.StatusUnprocessableEntity, err.Error(), err) + return err + } + + // get the mirror from the repo + mirror, err := repo_model.GetMirrorByRepoID(repo.ID) + if err != nil { + log.Error("Failed to get mirror: %s", err) + ctx.Error(http.StatusInternalServerError, "MirrorInterval", err) + return err + } + + // update MirrorInterval if opts.MirrorInterval != nil { - if !repo.IsMirror { - err := fmt.Errorf("repo is not a mirror, can not change mirror interval") - ctx.Error(http.StatusUnprocessableEntity, err.Error(), err) - return err - } - mirror, err := repo_model.GetMirrorByRepoID(repo.ID) + + // MirrorInterval should be a duration + interval, err := time.ParseDuration(*opts.MirrorInterval) if err != nil { - log.Error("Failed to get mirror: %s", err) - ctx.Error(http.StatusInternalServerError, "MirrorInterval", err) + log.Error("Wrong format for MirrorInternal Sent: %s", err) + ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err) return err } - if interval, err := time.ParseDuration(*opts.MirrorInterval); err == nil { - mirror.Interval = interval - mirror.Repo = repo - if err := repo_model.UpdateMirror(mirror); err != nil { - log.Error("Failed to Set Mirror Interval: %s", err) - ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err) - return err - } - log.Trace("Repository %s/%s Mirror Interval was Updated to %s", ctx.Repo.Owner.Name, repo.Name, interval) - } else { - log.Error("Wrong format for MirrorInternal Sent: %s", err) + + // Ensure the provided duration is not too short + if interval != 0 && interval < setting.Mirror.MinInterval { + err := fmt.Errorf("invalid mirror interval: %s is below minimum interval: %s", interval, setting.Mirror.MinInterval) ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err) return err } + + mirror.Interval = interval + mirror.Repo = repo + mirror.ScheduleNextUpdate() + log.Trace("Repository %s Mirror[%d] Set Interval: %s NextUpdateUnix: %s", repo.FullName(), mirror.ID, interval, mirror.NextUpdateUnix) } + + // update EnablePrune + if opts.EnablePrune != nil { + mirror.EnablePrune = *opts.EnablePrune + log.Trace("Repository %s Mirror[%d] Set EnablePrune: %t", repo.FullName(), mirror.ID, mirror.EnablePrune) + } + + // finally update the mirror in the DB + if err := repo_model.UpdateMirror(mirror); err != nil { + log.Error("Failed to Set Mirror Interval: %s", err) + ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err) + return err + } + return nil } diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index ef99eee15a258..09cb00177282e 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -32,7 +32,6 @@ import ( "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" @@ -195,11 +194,7 @@ func SettingsPost(ctx *context.Context) { } else { ctx.Repo.Mirror.EnablePrune = form.EnablePrune ctx.Repo.Mirror.Interval = interval - if interval != 0 { - ctx.Repo.Mirror.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(interval) - } else { - ctx.Repo.Mirror.NextUpdateUnix = 0 - } + ctx.Repo.Mirror.ScheduleNextUpdate() if err := repo_model.UpdateMirror(ctx.Repo.Mirror); err != nil { ctx.Data["Err_Interval"] = true ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e35ba989096ad..de74fd8fa399e 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -15084,6 +15084,11 @@ "type": "string", "x-go-name": "Description" }, + "enable_prune": { + "description": "enable prune - remove obsolete remote-tracking references", + "type": "boolean", + "x-go-name": "EnablePrune" + }, "external_tracker": { "$ref": "#/definitions/ExternalTracker" }, From ae6a52440ac47b8d0eed5875de9d801bc68c1a4a Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Wed, 20 Apr 2022 12:43:26 +0200 Subject: [PATCH 0012/1287] Fix panic in team repos API (#19431) * Fix panic in team repos API * Fix pagination * fmt --- models/organization/team_repo.go | 2 +- routers/api/v1/org/team.go | 2 +- routers/web/misc/markdown.go | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/models/organization/team_repo.go b/models/organization/team_repo.go index 03ca4678a5b71..657e83aaa56fa 100644 --- a/models/organization/team_repo.go +++ b/models/organization/team_repo.go @@ -48,7 +48,7 @@ func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) ([]*r ) } if opts.PageSize > 0 { - sess.Limit(opts.PageSize, opts.Page*opts.PageSize) + sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) } var repos []*repo_model.Repository return repos, sess.OrderBy("repository.name"). diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index d0f1fbef749f3..322196b8197ff 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -545,7 +545,7 @@ func GetTeamRepos(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err) return } - repos := make([]*api.Repository, len(team.Repos)) + repos := make([]*api.Repository, len(teamRepos)) for i, repo := range teamRepos { access, err := models.AccessLevel(ctx.Doer, repo) if err != nil { diff --git a/routers/web/misc/markdown.go b/routers/web/misc/markdown.go index b37aaf10ffecb..0567cbb30c267 100644 --- a/routers/web/misc/markdown.go +++ b/routers/web/misc/markdown.go @@ -16,6 +16,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + "mvdan.cc/xurls/v2" ) From f2229e0566478f8a2d8b968777f8ff356f1d4900 Mon Sep 17 00:00:00 2001 From: Campbell He Date: Wed, 20 Apr 2022 22:08:23 +0800 Subject: [PATCH 0013/1287] doc: add brief intro on using traefik as reverse-proxy (#19432) --- docs/content/doc/usage/reverse-proxies.en-us.md | 15 +++++++++++++++ docs/content/doc/usage/reverse-proxies.zh-cn.md | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/docs/content/doc/usage/reverse-proxies.en-us.md b/docs/content/doc/usage/reverse-proxies.en-us.md index 90b2765daa321..008704cdcf747 100644 --- a/docs/content/doc/usage/reverse-proxies.en-us.md +++ b/docs/content/doc/usage/reverse-proxies.en-us.md @@ -348,3 +348,18 @@ The added http-request will automatically add a trailing slash if needed and int Then you **MUST** set something like `[server] ROOT_URL = http://example.com/gitea/` correctly in your configuration. +## Traefik + +If you want traefik to serve your Gitea instance, you can add the following label section to your `docker-compose.yaml` (Assuming the provider is docker). + +```yaml +gitea: + image: gitea/gitea + ... + labels: + - "traefik.enable=true" + - "traefik.http.routers.gitea.rule=Host(`example.com`)" + - "traefik.http.services.gitea-websecure.loadbalancer.server.port=3000" +``` + +This config assumes that you are handling HTTPS on the traefik side and using HTTP between Gitea and traefik. \ No newline at end of file diff --git a/docs/content/doc/usage/reverse-proxies.zh-cn.md b/docs/content/doc/usage/reverse-proxies.zh-cn.md index 741f739dbd7cf..88db0c3790306 100644 --- a/docs/content/doc/usage/reverse-proxies.zh-cn.md +++ b/docs/content/doc/usage/reverse-proxies.zh-cn.md @@ -106,3 +106,19 @@ git.example.com { ``` 然后您**必须**在 Gitea 的配置文件中正确的添加类似 `[server] ROOT_URL = http://git.example.com/git/` 的配置项。 + +## 使用 Traefik 作为反向代理服务 + +如果您想使用 traefik 作为 Gitea 的反向代理服务,您可以在 `docker-compose.yaml` 中添加 label 部分(假设使用 docker 作为 traefik 的 provider): + +```yaml +gitea: + image: gitea/gitea + ... + labels: + - "traefik.enable=true" + - "traefik.http.routers.gitea.rule=Host(`example.com`)" + - "traefik.http.services.gitea-websecure.loadbalancer.server.port=3000" +``` + +这份配置假设您使用 traefik 来处理 HTTPS 服务,并在其和 Gitea 之间使用 HTTP 进行通信。 \ No newline at end of file From 5e68fe7d37561aa9df470e4980cff833a7ee4f5f Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 20 Apr 2022 14:43:15 +0000 Subject: [PATCH 0014/1287] Don't allow merging PR's which are being conflict checked (#19357) * Don't allow merging PR's which are being conflict checked - When a PR is still being conflict checked, don't allow the PR to be merged(the merge button could already be visible before e.g. a new commit was pushed to the PR). - Relevant(should prevent such issue from happening) #19352 Co-authored-by: delvh --- services/pull/check.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/pull/check.go b/services/pull/check.go index 253417072ce06..29dc88e0f0c1d 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -36,6 +36,7 @@ var ( ErrUserNotAllowedToMerge = errors.New("user not allowed to merge") ErrHasMerged = errors.New("has already been merged") ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged") + ErrIsChecking = errors.New("cannot merge while conflict checking is in progress") ErrNotMergableState = errors.New("not in mergeable state") ErrDependenciesLeft = errors.New("is blocked by an open dependency") ) @@ -88,6 +89,10 @@ func CheckPullMergable(ctx context.Context, doer *user_model.User, perm *models. return ErrNotMergableState } + if pr.IsChecking() { + return ErrIsChecking + } + if err := CheckPRReadyToMerge(ctx, pr, false); err != nil { if models.IsErrDisallowedToMerge(err) { if force { From a7f0ce620774be861f11b57d426ab59e27e4171a Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 20 Apr 2022 16:52:16 +0100 Subject: [PATCH 0015/1287] Add uploadpack.allowAnySHA1InWant to allow --filter=blob:none with older git clients (#19430) Older git clients need uploadpack.allowAnySHA1InWant if partial cloning is allowed. Fix #19118 Signed-off-by: Andrew Thornton --- modules/git/git.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/git/git.go b/modules/git/git.go index b97bb149001b3..259759ca81c7c 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -148,7 +148,7 @@ func Init(ctx context.Context) error { // By default partial clones are disabled, enable them from git v2.22 if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil { - globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true") + globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true") } // Save current git version on init to gitVersion otherwise it would require an RWMutex From 1e319ba41a3d5362d253fca540b13b2ee92124c8 Mon Sep 17 00:00:00 2001 From: zeripath Date: Wed, 20 Apr 2022 19:53:34 +0100 Subject: [PATCH 0016/1287] When dumping trim the standard suffices instead of a random suffix (#19440) * When dumping trim the standard suffices instead of a random suffix Instead of using the `path.Ext()` to trim the last "extension" suffix, just iterate through the supported suffices and trim those. Fix #19424 Signed-off-by: Andrew Thornton * fix enum with to have correct supported types only Co-authored-by: 6543 <6543@obermui.de> --- cmd/dump.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/dump.go b/cmd/dump.go index 418042559874c..f72ef05e948f9 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -86,7 +86,7 @@ func (o outputType) String() string { } var outputTypeEnum = &outputType{ - Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"}, + Enum: []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"}, Default: "zip", } @@ -160,7 +160,12 @@ func runDump(ctx *cli.Context) error { fatal("Deleting default logger failed. Can not write to stdout: %v", err) } } else { - fileName = strings.TrimSuffix(fileName, path.Ext(fileName)) + for _, suffix := range outputTypeEnum.Enum { + if strings.HasSuffix(fileName, "."+suffix) { + fileName = strings.TrimSuffix(fileName, "."+suffix) + break + } + } fileName += "." + outType } setting.LoadFromExisting() From 23d37673bd4e498d66140c44a2e31ab4725c6ae2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 20 Apr 2022 21:39:30 +0000 Subject: [PATCH 0017/1287] Don't panic on `ErrEmailInvalid` (#19441) - Don't panic on `ErrEmailInvalid`, this was caused due that we were trying to force `ErrEmailCharIsNotSupported` interface, which panics. - Resolves #19397 --- integrations/api_user_email_test.go | 6 ++++++ routers/api/v1/user/email.go | 13 ++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/integrations/api_user_email_test.go b/integrations/api_user_email_test.go index 9d2b7485d8526..08d236df30e52 100644 --- a/integrations/api_user_email_test.go +++ b/integrations/api_user_email_test.go @@ -69,6 +69,12 @@ func TestAPIAddEmail(t *testing.T) { Primary: false, }, }, emails) + + opts = api.CreateEmailOption{ + Emails: []string{"notAEmail"}, + } + req = NewRequestWithJSON(t, "POST", "/api/v1/user/emails?token="+token, &opts) + session.MakeRequest(t, req, http.StatusUnprocessableEntity) } func TestAPIDeleteEmail(t *testing.T) { diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go index 9060741c5932c..170ffb7736a49 100644 --- a/routers/api/v1/user/email.go +++ b/routers/api/v1/user/email.go @@ -80,9 +80,16 @@ func AddEmail(ctx *context.APIContext) { if err := user_model.AddEmailAddresses(emails); err != nil { if user_model.IsErrEmailAlreadyUsed(err) { ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email) - } else if user_model.IsErrEmailCharIsNotSupported(err) || - user_model.IsErrEmailInvalid(err) { - errMsg := fmt.Sprintf("Email address %s invalid", err.(user_model.ErrEmailInvalid).Email) + } else if user_model.IsErrEmailCharIsNotSupported(err) || user_model.IsErrEmailInvalid(err) { + email := "" + if typedError, ok := err.(user_model.ErrEmailInvalid); ok { + email = typedError.Email + } + if typedError, ok := err.(user_model.ErrEmailCharIsNotSupported); ok { + email = typedError.Email + } + + errMsg := fmt.Sprintf("Email address %q invalid", email) ctx.Error(http.StatusUnprocessableEntity, "", errMsg) } else { ctx.Error(http.StatusInternalServerError, "AddEmailAddresses", err) From f7a8e5c8f20e4de1a19af093c550331434cffe41 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Thu, 21 Apr 2022 00:10:26 +0000 Subject: [PATCH 0018/1287] [skip ci] Updated translations via Crowdin --- options/locale/locale_pt-BR.ini | 2 +- options/locale/locale_pt-PT.ini | 3 +++ options/locale/locale_zh-CN.ini | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 942b7ab96ec2c..af6a293460737 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -596,7 +596,7 @@ theme_desc=Este será o seu tema padrão em todo o site. primary=Principal activated=Ativado requires_activation=Requer ativação -primary_email=Tornar privado +primary_email=Tornar Primário activate_email=Enviar Ativação activations_pending=Ativações pendentes delete_email=Remover diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index ff6dc76c40d73..05f102d9e35a1 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -3042,6 +3042,9 @@ container.labels.key=Chave container.labels.value=Valor generic.download=Descarregar pacote usando a linha de comandos: generic.documentation=Para obter mais informações sobre o registo genérico, consulte a documentação. +helm.registry=Configurar este registo usando a linha de comandos: +helm.install=Para instalar o pacote, execute o seguinte comando: +helm.documentation=Para obter mais informações sobre o registo do Helm, consulte a documentação. maven.registry=Configure este registo no seu ficheiro pom.xml do projecto: maven.install=Para usar este pacote, inclua no bloco dependencies do ficheiro pom.xml o seguinte: maven.install2=Executar usando a linha de comandos: diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 64f806e00ed01..0c395fec9ac83 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -3051,6 +3051,9 @@ container.labels.key=键 container.labels.value=值 generic.download=从命令行下载软件包: generic.documentation=关于通用注册中心的更多信息,请参阅 文档。 +helm.registry=从命令行设置此注册中心: +helm.install=要安装包,请运行以下命令: +helm.documentation=关于 Helm 注册中心的更多信息,请参阅 文档。 maven.registry=在您项目的 pom.xml 文件中设置此注册中心: maven.install=要使用这个软件包,在 pom.xml 文件中的 依赖项 块中包含以下内容: maven.install2=通过命令行运行: From f4c1aa75be1974a4243b04346140922fb66b9b9f Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 21 Apr 2022 01:08:30 +0000 Subject: [PATCH 0019/1287] Fix DELETE request for non-existent public key (#19443) - Add a return for the first "block" of errors, which fixes the double error messages. - Add a return for `externallyManaged`. - Resolves #19398 --- routers/api/v1/user/key.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index cc7ee739ce3a8..71a2c910a6a31 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -262,16 +262,21 @@ func DeletePublicKey(ctx *context.APIContext) { id := ctx.ParamsInt64(":id") externallyManaged, err := asymkey_model.PublicKeyIsExternallyManaged(id) if err != nil { - ctx.Error(http.StatusInternalServerError, "PublicKeyIsExternallyManaged", err) + if asymkey_model.IsErrKeyNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "PublicKeyIsExternallyManaged", err) + } + return } + if externallyManaged { ctx.Error(http.StatusForbidden, "", "SSH Key is externally managed for this user") + return } if err := asymkey_service.DeletePublicKey(ctx.Doer, id); err != nil { - if asymkey_model.IsErrKeyNotExist(err) { - ctx.NotFound() - } else if asymkey_model.IsErrKeyAccessDenied(err) { + if asymkey_model.IsErrKeyAccessDenied(err) { ctx.Error(http.StatusForbidden, "", "You do not have access to this key") } else { ctx.Error(http.StatusInternalServerError, "DeletePublicKey", err) From 725731cb6e45a02e026c48f2f46b7d32d6f32409 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 21 Apr 2022 03:17:23 +0200 Subject: [PATCH 0020/1287] Add Changelog v1.16.6 (#19339) (#19450) * Add Changelog v1.16.6 (#19339) Co-authored-by: delvh Co-authored-by: techknowlogick Co-authored-by: Lunny Xiao Co-authored-by: zeripath * bump version Co-authored-by: delvh Co-authored-by: techknowlogick Co-authored-by: Lunny Xiao Co-authored-by: zeripath --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ docs/config.yaml | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfbbc99b10268..c5ffc6d610a34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,41 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.16.6](https://github.com/go-gitea/gitea/releases/tag/v1.16.6) - 2022-04-20 + +* ENHANCEMENTS + * Only request write when necessary (#18657) (#19422) + * Disable service worker by default (#18914) (#19342) +* BUGFIXES + * When dumping trim the standard suffices instead of a random suffix (#19440) (#19447) + * Fix DELETE request for non-existent public key (#19443) (#19444) + * Don't panic on ErrEmailInvalid (#19441) (#19442) + * Add uploadpack.allowAnySHA1InWant to allow --filter=blob:none with older git clients (#19430) (#19438) + * Warn on SSH connection for incorrect configuration (#19317) (#19437) + * Search Issues via API, dont show 500 if filter result in empty list (#19244) (#19436) + * When updating mirror repo intervals by API reschedule next update too (#19429) (#19433) + * Fix nil error when some pages are rendered outside request context (#19427) (#19428) + * Fix double blob-hunk on diff page (#19404) (#19405) + * Don't allow merging PR's which are being conflict checked (#19357) (#19358) + * Fix middleware function's placements (#19377) (#19378) + * Fix invalid CSRF token bug, make sure CSRF tokens can be up-to-date (#19338) + * Restore user autoregistration with email addresses (#19261) (#19312) + * Move checks for pulls before merge into own function (#19271) (#19277) + * Granular webhook events in editHook (#19251) (#19257) + * Only send webhook events to active system webhooks and only deliver to active hooks (#19234) (#19248) + * Use full output of git show-ref --tags to get tags for PushUpdateAddTag (#19235) (#19236) + * Touch mirrors on even on fail to update (#19217) (#19233) + * Hide sensitive content on admin panel progress monitor (#19218 & #19226) (#19231) + * Fix clone url JS error for the empty repo page (#19209) + * Bump goldmark to v1.4.11 (#19201) (#19203) +* TESTING + * Prevent intermittent failures in RepoIndexerTest (#19225 #19229) (#19228) +* BUILD + * Revert the minimal golang version requirement from 1.17 to 1.16 and add a warning in Makefile (#19319) +* MISC + * Performance improvement for add team user when org has more than 1000 repositories (#19227) (#19289) + * Check go and nodejs version by go.mod and package.json (#19197) (#19254) + ## [1.16.5](https://github.com/go-gitea/gitea/releases/tag/v1.16.5) - 2022-03-23 * BREAKING diff --git a/docs/config.yaml b/docs/config.yaml index e2180daa272f6..d7938085408e9 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -18,7 +18,7 @@ params: description: Git with a cup of tea author: The Gitea Authors website: https://docs.gitea.io - version: 1.16.5 + version: 1.16.6 minGoVersion: 1.17 goVersion: 1.18 minNodeVersion: 12.17 From 225044e6563e4ed2b41d1aed8b3967755c064fbb Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Wed, 20 Apr 2022 21:17:39 -0400 Subject: [PATCH 0021/1287] node12 is EOL (#19451) * node12 is EOL * fix lockfile --- docs/config.yaml | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index d7938085408e9..93961f32c0bd7 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -21,7 +21,7 @@ params: version: 1.16.6 minGoVersion: 1.17 goVersion: 1.18 - minNodeVersion: 12.17 + minNodeVersion: 14 outputs: home: diff --git a/package-lock.json b/package-lock.json index 30412800bfd29..8b63409e2868c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,7 @@ "updates": "13.0.4" }, "engines": { - "node": ">= 12.17.0" + "node": ">= 14" } }, "node_modules/@ampproject/remapping": { diff --git a/package.json b/package.json index 3848b7ec8f849..db3d877dc2902 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "engines": { - "node": ">= 12.17.0" + "node": ">= 14" }, "dependencies": { "@claviska/jquery-minicolors": "2.3.6", From c764355676eb6d67674d095f92576a85688fe6cb Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 21 Apr 2022 17:17:57 +0200 Subject: [PATCH 0022/1287] RepoAssignment ensure to close before overwrite (#19449) * check if GitRepo already open and close if * only run RepoAssignment once * refactor context helper for api to open GitRepo --- modules/context/api.go | 81 +++++++++++------------ modules/context/repo.go | 25 +++++-- routers/api/v1/api.go | 36 +++++----- routers/api/v1/repo/blob.go | 3 +- routers/api/v1/repo/commits.go | 1 + routers/api/v1/repo/file.go | 24 ++----- routers/api/v1/repo/hook.go | 5 ++ routers/api/v1/repo/notes.go | 12 ++-- services/repository/files/content.go | 9 +-- services/repository/files/content_test.go | 9 ++- templates/swagger/v1_json.tmpl | 12 ++++ 11 files changed, 121 insertions(+), 96 deletions(-) diff --git a/modules/context/api.go b/modules/context/api.go index e5c2eeda0a334..41d559f5b1c5f 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -285,36 +285,6 @@ func APIContexter() func(http.Handler) http.Handler { } } -// ReferencesGitRepo injects the GitRepo into the Context -func ReferencesGitRepo(allowEmpty bool) func(ctx *APIContext) (cancel context.CancelFunc) { - return func(ctx *APIContext) (cancel context.CancelFunc) { - // Empty repository does not have reference information. - if !allowEmpty && ctx.Repo.Repository.IsEmpty { - return - } - - // For API calls. - if ctx.Repo.GitRepo == nil { - repoPath := repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) - gitRepo, err := git.OpenRepository(ctx, repoPath) - if err != nil { - ctx.Error(http.StatusInternalServerError, "RepoRef Invalid repo "+repoPath, err) - return - } - ctx.Repo.GitRepo = gitRepo - // We opened it, we should close it - return func() { - // If it's been set to nil then assume someone else has closed it. - if ctx.Repo.GitRepo != nil { - ctx.Repo.GitRepo.Close() - } - } - } - - return - } -} - // NotFound handles 404s for APIContext // String will replace message, errors will be added to a slice func (ctx *APIContext) NotFound(objs ...interface{}) { @@ -340,33 +310,62 @@ func (ctx *APIContext) NotFound(objs ...interface{}) { }) } -// RepoRefForAPI handles repository reference names when the ref name is not explicitly given -func RepoRefForAPI(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - ctx := GetAPIContext(req) +// ReferencesGitRepo injects the GitRepo into the Context +// you can optional skip the IsEmpty check +func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context.CancelFunc) { + return func(ctx *APIContext) (cancel context.CancelFunc) { // Empty repository does not have reference information. - if ctx.Repo.Repository.IsEmpty { + if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) { return } - var err error - + // For API calls. if ctx.Repo.GitRepo == nil { repoPath := repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) - ctx.Repo.GitRepo, err = git.OpenRepository(ctx, repoPath) + gitRepo, err := git.OpenRepository(ctx, repoPath) if err != nil { - ctx.InternalServerError(err) + ctx.Error(http.StatusInternalServerError, "RepoRef Invalid repo "+repoPath, err) return } + ctx.Repo.GitRepo = gitRepo // We opened it, we should close it - defer func() { + return func() { // If it's been set to nil then assume someone else has closed it. if ctx.Repo.GitRepo != nil { ctx.Repo.GitRepo.Close() } - }() + } + } + + return + } +} + +// RepoRefForAPI handles repository reference names when the ref name is not explicitly given +func RepoRefForAPI(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := GetAPIContext(req) + + if ctx.Repo.GitRepo == nil { + ctx.InternalServerError(fmt.Errorf("no open git repo")) + return + } + + if ref := ctx.FormTrim("ref"); len(ref) > 0 { + commit, err := ctx.Repo.GitRepo.GetCommit(ref) + if err != nil { + if git.IsErrNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "GetBlobByPath", err) + } + return + } + ctx.Repo.Commit = commit + return } + var err error refName := getRefName(ctx.Context, RepoRefAny) if ctx.Repo.GitRepo.IsBranchExist(refName) { diff --git a/modules/context/repo.go b/modules/context/repo.go index a7c9a982c42b2..4687434455a39 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -221,13 +221,21 @@ func (r *Repository) FileExists(path, branch string) (bool, error) { // GetEditorconfig returns the .editorconfig definition if found in the // HEAD of the default repo branch. -func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) { +func (r *Repository) GetEditorconfig(optCommit ...*git.Commit) (*editorconfig.Editorconfig, error) { if r.GitRepo == nil { return nil, nil } - commit, err := r.GitRepo.GetBranchCommit(r.Repository.DefaultBranch) - if err != nil { - return nil, err + var ( + err error + commit *git.Commit + ) + if len(optCommit) != 0 { + commit = optCommit[0] + } else { + commit, err = r.GitRepo.GetBranchCommit(r.Repository.DefaultBranch) + if err != nil { + return nil, err + } } treeEntry, err := commit.GetTreeEntryByPath(".editorconfig") if err != nil { @@ -407,6 +415,12 @@ func RepoIDAssignment() func(ctx *Context) { // RepoAssignment returns a middleware to handle repository assignment func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { + if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce { + log.Trace("RepoAssignment was exec already, skipping second call ...") + return + } + ctx.Data["repoAssignmentExecuted"] = true + var ( owner *user_model.User err error @@ -602,6 +616,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err) return } + if ctx.Repo.GitRepo != nil { + ctx.Repo.GitRepo.Close() + } ctx.Repo.GitRepo = gitRepo // We opened it, we should close it diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index a430eb453aa9b..aec2a6d7b2c42 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -796,7 +796,7 @@ func Routes() *web.Route { m.Combo("").Get(repo.GetHook). Patch(bind(api.EditHookOption{}), repo.EditHook). Delete(repo.DeleteHook) - m.Post("/tests", context.RepoRefForAPI, repo.TestHook) + m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) }) }, reqToken(), reqAdmin(), reqWebhooksEnabled()) m.Group("/collaborators", func() { @@ -813,16 +813,16 @@ func Routes() *web.Route { Put(reqAdmin(), repo.AddTeam). Delete(reqAdmin(), repo.DeleteTeam) }, reqToken()) - m.Get("/raw/*", context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) + m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) m.Group("/branches", func() { - m.Get("", context.ReferencesGitRepo(false), repo.ListBranches) - m.Get("/*", context.ReferencesGitRepo(false), repo.GetBranch) - m.Delete("/*", reqRepoWriter(unit.TypeCode), context.ReferencesGitRepo(false), repo.DeleteBranch) - m.Post("", reqRepoWriter(unit.TypeCode), context.ReferencesGitRepo(false), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) - }, reqRepoReader(unit.TypeCode)) + m.Get("", repo.ListBranches) + m.Get("/*", repo.GetBranch) + m.Delete("/*", reqRepoWriter(unit.TypeCode), repo.DeleteBranch) + m.Post("", reqRepoWriter(unit.TypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch) + }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) m.Post("", bind(api.CreateBranchProtectionOption{}), repo.CreateBranchProtection) @@ -941,10 +941,10 @@ func Routes() *web.Route { }) m.Group("/releases", func() { m.Combo("").Get(repo.ListReleases). - Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(false), bind(api.CreateReleaseOption{}), repo.CreateRelease) + Post(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) m.Group("/{id}", func() { m.Combo("").Get(repo.GetRelease). - Patch(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(false), bind(api.EditReleaseOption{}), repo.EditRelease). + Patch(reqToken(), reqRepoWriter(unit.TypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteRelease) m.Group("/assets", func() { m.Combo("").Get(repo.ListReleaseAttachments). @@ -961,7 +961,7 @@ func Routes() *web.Route { }) }, reqRepoReader(unit.TypeReleases)) m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync) - m.Get("/editorconfig/{filename}", context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) + m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { m.Combo("").Get(repo.ListPullRequests). Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) @@ -992,13 +992,13 @@ func Routes() *web.Route { Delete(reqToken(), bind(api.PullReviewRequestOptions{}), repo.DeleteReviewRequests). Post(reqToken(), bind(api.PullReviewRequestOptions{}), repo.CreateReviewRequests) }) - }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(false)) + }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) m.Group("/statuses", func() { m.Combo("/{sha}").Get(repo.GetCommitStatuses). Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) }, reqRepoReader(unit.TypeCode)) m.Group("/commits", func() { - m.Get("", context.ReferencesGitRepo(false), repo.GetAllCommits) + m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) m.Group("/{ref}", func() { m.Get("/status", repo.GetCombinedCommitStatusByRef) m.Get("/statuses", repo.GetCommitStatusesByRef) @@ -1006,16 +1006,16 @@ func Routes() *web.Route { }, reqRepoReader(unit.TypeCode)) m.Group("/git", func() { m.Group("/commits", func() { - m.Get("/{sha}", context.ReferencesGitRepo(false), repo.GetSingleCommit) + m.Get("/{sha}", repo.GetSingleCommit) m.Get("/{sha}.{diffType:diff|patch}", repo.DownloadCommitDiffOrPatch) }) m.Get("/refs", repo.GetGitAllRefs) m.Get("/refs/*", repo.GetGitRefs) - m.Get("/trees/{sha}", context.RepoRefForAPI, repo.GetTree) - m.Get("/blobs/{sha}", context.RepoRefForAPI, repo.GetBlob) - m.Get("/tags/{sha}", context.RepoRefForAPI, repo.GetAnnotatedTag) + m.Get("/trees/{sha}", repo.GetTree) + m.Get("/blobs/{sha}", repo.GetBlob) + m.Get("/tags/{sha}", repo.GetAnnotatedTag) m.Get("/notes/{sha}", repo.GetNote) - }, reqRepoReader(unit.TypeCode)) + }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), repo.ApplyDiffPatch) m.Group("/contents", func() { m.Get("", repo.GetContentsList) @@ -1035,7 +1035,7 @@ func Routes() *web.Route { Delete(reqToken(), repo.DeleteTopic) }, reqAdmin()) }, reqAnyRepoReader()) - m.Get("/issue_templates", context.ReferencesGitRepo(false), repo.GetIssueTemplates) + m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages) }, repoAssignment()) }) diff --git a/routers/api/v1/repo/blob.go b/routers/api/v1/repo/blob.go index 19d893a68b92e..035f2dc1e1de9 100644 --- a/routers/api/v1/repo/blob.go +++ b/routers/api/v1/repo/blob.go @@ -45,7 +45,8 @@ func GetBlob(ctx *context.APIContext) { ctx.Error(http.StatusBadRequest, "", "sha not provided") return } - if blob, err := files_service.GetBlobBySHA(ctx, ctx.Repo.Repository, sha); err != nil { + + if blob, err := files_service.GetBlobBySHA(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, sha); err != nil { ctx.Error(http.StatusBadRequest, "", err) } else { ctx.JSON(http.StatusOK, blob) diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index b6c47e0685186..c79c34ec42961 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -269,6 +269,7 @@ func DownloadCommitDiffOrPatch(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" repoPath := repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) + // TODO: use gitRepo from context if err := git.GetRawDiff( ctx, repoPath, diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index a4811b370a911..ed51f6f4df2a2 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -62,22 +62,7 @@ func GetRawFile(ctx *context.APIContext) { return } - commit := ctx.Repo.Commit - - if ref := ctx.FormTrim("ref"); len(ref) > 0 { - var err error - commit, err = ctx.Repo.GitRepo.GetCommit(ref) - if err != nil { - if git.IsErrNotExist(err) { - ctx.NotFound() - } else { - ctx.Error(http.StatusInternalServerError, "GetBlobByPath", err) - } - return - } - } - - blob, err := commit.GetBlobByPath(ctx.Repo.TreePath) + blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreePath) if err != nil { if git.IsErrNotExist(err) { ctx.NotFound() @@ -157,13 +142,18 @@ func GetEditorconfig(ctx *context.APIContext) { // description: filepath of file to get // type: string // required: true + // - name: ref + // in: query + // description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)" + // type: string + // required: false // responses: // 200: // description: success // "404": // "$ref": "#/responses/notFound" - ec, err := ctx.Repo.GetEditorconfig() + ec, err := ctx.Repo.GetEditorconfig(ctx.Repo.Commit) if err != nil { if git.IsErrNotExist(err) { ctx.NotFound(err) diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index c79a1d6b13522..7ec6cd88aba63 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -138,6 +138,11 @@ func TestHook(ctx *context.APIContext) { // type: integer // format: int64 // required: true + // - name: ref + // in: query + // description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)" + // type: string + // required: false // responses: // "204": // "$ref": "#/responses/empty" diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go index f85883566f012..bd8e27e40bf40 100644 --- a/routers/api/v1/repo/notes.go +++ b/routers/api/v1/repo/notes.go @@ -55,15 +55,13 @@ func GetNote(ctx *context.APIContext) { } func getNote(ctx *context.APIContext, identifier string) { - gitRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath()) - if err != nil { - ctx.Error(http.StatusInternalServerError, "OpenRepository", err) + if ctx.Repo.GitRepo == nil { + ctx.InternalServerError(fmt.Errorf("no open git repo")) return } - defer gitRepo.Close() + var note git.Note - err = git.GetNote(ctx, gitRepo, identifier, ¬e) - if err != nil { + if err := git.GetNote(ctx, ctx.Repo.GitRepo, identifier, ¬e); err != nil { if git.IsErrNotExist(err) { ctx.NotFound(identifier) return @@ -72,7 +70,7 @@ func getNote(ctx *context.APIContext, identifier string) { return } - cmt, err := convert.ToCommit(ctx.Repo.Repository, gitRepo, note.Commit, nil) + cmt, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, note.Commit, nil) if err != nil { ctx.Error(http.StatusInternalServerError, "ToCommit", err) return diff --git a/services/repository/files/content.go b/services/repository/files/content.go index 9037a84349e5a..2237671a60cbb 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -164,7 +164,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref // Now populate the rest of the ContentsResponse based on entry type if entry.IsRegular() || entry.IsExecutable() { contentsResponse.Type = string(ContentTypeRegular) - if blobResponse, err := GetBlobBySHA(ctx, repo, entry.ID.String()); err != nil { + if blobResponse, err := GetBlobBySHA(ctx, repo, gitRepo, entry.ID.String()); err != nil { return nil, err } else if !forList { // We don't show the content if we are getting a list of FileContentResponses @@ -220,12 +220,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref } // GetBlobBySHA get the GitBlobResponse of a repository using a sha hash. -func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, sha string) (*api.GitBlobResponse, error) { - gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath()) - if err != nil { - return nil, err - } - defer closer.Close() +func GetBlobBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, sha string) (*api.GitBlobResponse, error) { gitBlob, err := gitRepo.GetBlob(sha) if err != nil { return nil, err diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index 8a3e589bdf9b1..342ebae329168 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -8,7 +8,9 @@ import ( "path/filepath" "testing" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/git" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" @@ -234,7 +236,12 @@ func TestGetBlobBySHA(t *testing.T) { ctx.SetParams(":id", "1") ctx.SetParams(":sha", sha) - gbr, err := GetBlobBySHA(ctx, ctx.Repo.Repository, ctx.Params(":sha")) + gitRepo, err := git.OpenRepository(ctx, repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)) + if err != nil { + t.Fail() + } + + gbr, err := GetBlobBySHA(ctx, ctx.Repo.Repository, gitRepo, ctx.Params(":sha")) expectedGBR := &api.GitBlobResponse{ Content: "dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK", Encoding: "base64", diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index de74fd8fa399e..0374a53a6555c 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -3668,6 +3668,12 @@ "name": "filepath", "in": "path", "required": true + }, + { + "type": "string", + "description": "The name of the commit/branch/tag. Default the repository’s default branch (usually master)", + "name": "ref", + "in": "query" } ], "responses": { @@ -4559,6 +4565,12 @@ "name": "id", "in": "path", "required": true + }, + { + "type": "string", + "description": "The name of the commit/branch/tag. Default the repository’s default branch (usually master)", + "name": "ref", + "in": "query" } ], "responses": { From 3ec1b6c2238c9eb46709091567eb2564aec86d99 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 21 Apr 2022 16:05:53 +0000 Subject: [PATCH 0023/1287] Fix logging of Transfer API (#19456) - Use the correct fullname's in tracing calls. - Return correct function name in error. Co-authored-by: 6543 <6543@obermui.de> --- routers/api/v1/repo/transfer.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 7578fbd1873ce..241c578e608a6 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -104,14 +104,16 @@ func Transfer(ctx *context.APIContext) { ctx.Repo.GitRepo = nil } + oldFullname := ctx.Repo.Repository.FullName() + if err := repo_service.StartRepositoryTransfer(ctx.Doer, newOwner, ctx.Repo.Repository, teams); err != nil { if models.IsErrRepoTransferInProgress(err) { - ctx.Error(http.StatusConflict, "CreatePendingRepositoryTransfer", err) + ctx.Error(http.StatusConflict, "StartRepositoryTransfer", err) return } if repo_model.IsErrRepoAlreadyExist(err) { - ctx.Error(http.StatusUnprocessableEntity, "CreatePendingRepositoryTransfer", err) + ctx.Error(http.StatusUnprocessableEntity, "StartRepositoryTransfer", err) return } @@ -120,12 +122,12 @@ func Transfer(ctx *context.APIContext) { } if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer { - log.Trace("Repository transfer initiated: %s -> %s", ctx.Repo.Repository.FullName(), newOwner.Name) + log.Trace("Repository transfer initiated: %s -> %s", oldFullname, ctx.Repo.Repository.FullName()) ctx.JSON(http.StatusCreated, convert.ToRepo(ctx.Repo.Repository, perm.AccessModeAdmin)) return } - log.Trace("Repository transferred: %s -> %s", ctx.Repo.Repository.FullName(), newOwner.Name) + log.Trace("Repository transferred: %s -> %s", oldFullname, ctx.Repo.Repository.FullName()) ctx.JSON(http.StatusAccepted, convert.ToRepo(ctx.Repo.Repository, perm.AccessModeAdmin)) } From ebe569a268bbe71bf2bc30cd2829227700688b57 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 21 Apr 2022 21:55:45 +0000 Subject: [PATCH 0024/1287] Set correct PR status on 3way on conflict checking (#19457) * Set correct PR status on 3way on conflict checking - When 3-way merge is enabled for conflict checking, it has a new interesting behavior that it doesn't return any error when it found a conflict, so we change the condition to not check for the error, but instead check if conflictedfiles is populated, this fixes a issue whereby PR status wasn't correctly on conflicted PR's. - Refactor the mergeable property(which was incorrectly set and lead me this bug) to be more maintainable. - Add a dedicated test for conflicting checking, so it should prevent future issues with this. * Fix linter --- integrations/pull_merge_test.go | 73 +++++++++++++++++++++++++++++++++ models/pull.go | 11 +++++ modules/convert/pull.go | 5 +-- services/pull/patch.go | 6 ++- 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/integrations/pull_merge_test.go b/integrations/pull_merge_test.go index d7cb042e9c127..f8e8c79a8864e 100644 --- a/integrations/pull_merge_test.go +++ b/integrations/pull_merge_test.go @@ -26,6 +26,8 @@ import ( "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation/i18n" "code.gitea.io/gitea/services/pull" + repo_service "code.gitea.io/gitea/services/repository" + files_service "code.gitea.io/gitea/services/repository/files" "github.com/stretchr/testify/assert" ) @@ -346,3 +348,74 @@ func TestCantMergeUnrelated(t *testing.T) { gitRepo.Close() }) } + +func TestConflictChecking(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + + // Create new clean repo to test conflict checking. + baseRepo, err := repo_service.CreateRepository(user, user, models.CreateRepoOptions{ + Name: "conflict-checking", + Description: "Tempo repo", + AutoInit: true, + Readme: "Default", + DefaultBranch: "main", + }) + assert.NoError(t, err) + assert.NotEmpty(t, baseRepo) + + // create a commit on new branch. + _, err = files_service.CreateOrUpdateRepoFile(git.DefaultContext, baseRepo, user, &files_service.UpdateRepoFileOptions{ + TreePath: "important_file", + Message: "Add a important file", + Content: "Just a non-important file", + IsNewFile: true, + OldBranch: "main", + NewBranch: "important-secrets", + }) + assert.NoError(t, err) + + // create a commit on main branch. + _, err = files_service.CreateOrUpdateRepoFile(git.DefaultContext, baseRepo, user, &files_service.UpdateRepoFileOptions{ + TreePath: "important_file", + Message: "Add a important file", + Content: "Not the same content :P", + IsNewFile: true, + OldBranch: "main", + NewBranch: "main", + }) + assert.NoError(t, err) + + // create Pull to merge the important-secrets branch into main branch. + pullIssue := &models.Issue{ + RepoID: baseRepo.ID, + Title: "PR with conflict!", + PosterID: user.ID, + Poster: user, + IsPull: true, + } + + pullRequest := &models.PullRequest{ + HeadRepoID: baseRepo.ID, + BaseRepoID: baseRepo.ID, + HeadBranch: "important-secrets", + BaseBranch: "main", + HeadRepo: baseRepo, + BaseRepo: baseRepo, + Type: models.PullRequestGitea, + } + err = pull.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil) + assert.NoError(t, err) + + issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "PR with conflict!"}).(*models.Issue) + conflictingPR, err := models.GetPullRequestByIssueID(issue.ID) + assert.NoError(t, err) + + // Ensure conflictedFiles is populated. + assert.Equal(t, 1, len(conflictingPR.ConflictedFiles)) + // Check if status is correct. + assert.Equal(t, models.PullRequestStatusConflict, conflictingPR.Status) + // Ensure that mergeable returns false + assert.False(t, conflictingPR.Mergeable()) + }) +} diff --git a/models/pull.go b/models/pull.go index 439005deb4264..ac44ebf0bea24 100644 --- a/models/pull.go +++ b/models/pull.go @@ -701,3 +701,14 @@ func (pr *PullRequest) GetHeadBranchHTMLURL() string { } return pr.HeadRepo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(pr.HeadBranch) } + +// Mergeable returns if the pullrequest is mergeable. +func (pr *PullRequest) Mergeable() bool { + // If a pull request isn't mergable if it's: + // - Being conflict checked. + // - Has a conflict. + // - Received a error while being conflict checked. + // - Is a work-in-progress pull request. + return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict && + pr.Status != PullRequestStatusError && !pr.IsWorkInProgress() +} diff --git a/modules/convert/pull.go b/modules/convert/pull.go index 9c53afe8f38d0..3b39e3d2c1269 100644 --- a/modules/convert/pull.go +++ b/modules/convert/pull.go @@ -68,6 +68,7 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo PatchURL: pr.Issue.PatchURL(), HasMerged: pr.HasMerged, MergeBase: pr.MergeBase, + Mergeable: pr.Mergeable(), Deadline: apiIssue.Deadline, Created: pr.Issue.CreatedUnix.AsTimePtr(), Updated: pr.Issue.UpdatedUnix.AsTimePtr(), @@ -191,10 +192,6 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo } } - if pr.Status != models.PullRequestStatusChecking { - mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress() - apiPullRequest.Mergeable = mergeable - } if pr.HasMerged { apiPullRequest.Merged = pr.MergedUnix.AsTimePtr() apiPullRequest.MergedCommitID = &pr.MergedCommitID diff --git a/services/pull/patch.go b/services/pull/patch.go index f86141aa7aecf..f118ef33d022a 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -444,14 +444,16 @@ func checkConflicts(ctx context.Context, pr *models.PullRequest, gitRepo *git.Re }, }) - // 9. If there is a conflict the `git apply` command will return a non-zero error code - so there will be a positive error. - if err != nil { + // 9. Check if the found conflictedfiles is non-zero, "err" could be non-nil, so we should ignore it if we found conflicts. + // Note: `"err" could be non-nil` is due that if enable 3-way merge, it doesn't return any error on found conflicts. + if len(pr.ConflictedFiles) > 0 { if conflict { pr.Status = models.PullRequestStatusConflict log.Trace("Found %d files conflicted: %v", len(pr.ConflictedFiles), pr.ConflictedFiles) return true, nil } + } else if err != nil { return false, fmt.Errorf("git apply --check: %v", err) } return false, nil From 0dfc2e55ea258d2b1a3cd86e2b6f27a481e495ff Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Fri, 22 Apr 2022 00:10:36 +0000 Subject: [PATCH 0025/1287] [skip ci] Updated translations via Crowdin --- options/locale/locale_pt-BR.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index af6a293460737..99e526269cb71 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1944,6 +1944,7 @@ settings.event_pull_request_review_desc=Pull request aprovado, rejeitado ou revi settings.event_pull_request_sync=Pull Request Sincronizado settings.event_pull_request_sync_desc=Pull request sincronizado. settings.event_package=Pacote +settings.event_package_desc=Pacote criado ou excluído em um repositório. settings.branch_filter=Filtro de branch settings.branch_filter_desc=Lista dos branches a serem considerados nos eventos push, criação de branch e exclusão de branch, especificados como padrão glob. Se estiver vazio ou for *, eventos para todos os branches serão relatados. Veja github.com/gobwas/glob documentação da sintaxe. Exemplos: master, {master,release*}. settings.active=Ativo From 0dcc74a8a7eebe816f855ad0375714c52f8b785a Mon Sep 17 00:00:00 2001 From: zeripath Date: Fri, 22 Apr 2022 16:20:04 +0100 Subject: [PATCH 0026/1287] Prevent dangling cat-file calls (goroutine alternative) (#19454) If an `os/exec.Command` is passed non `*os.File` as an input/output, go will create `os.Pipe`s and wait for their closure in `cmd.Wait()`. If the code following this is responsible for closing `io.Pipe`s or other handlers then on process death from context cancellation the `Wait` can hang. There are two possible solutions: 1. use `os.Pipe` as the input/output as `cmd.Wait` does not wait for these. 2. create a goroutine waiting on the context cancellation that will close the inputs. This PR provides the second option - which is a simpler change that can be more easily backported. Closes #19448 Signed-off-by: Andrew Thornton --- modules/git/batch_reader.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go index 5a0a82b13a9c8..902fa897185f5 100644 --- a/modules/git/batch_reader.go +++ b/modules/git/batch_reader.go @@ -57,6 +57,12 @@ func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError, <-closed } + // Ensure cancel is called as soon as the provided context is cancelled + go func() { + <-ctx.Done() + cancel() + }() + _, filename, line, _ := runtime.Caller(2) filename = strings.TrimPrefix(filename, callerPrefix) @@ -101,6 +107,12 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi <-closed } + // Ensure cancel is called as soon as the provided context is cancelled + go func() { + <-ctx.Done() + cancel() + }() + _, filename, line, _ := runtime.Caller(2) filename = strings.TrimPrefix(filename, callerPrefix) From 240b3aa218ceeea579e622435800a49fa1820910 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 22 Apr 2022 17:19:55 +0000 Subject: [PATCH 0027/1287] Mark TemplateLoading error as "UnprocessableEntity" (#19445) - Don't return Internal Server error if the user provide incorrect label template, instead return UnprocessableEntity. - Resolves #19399 --- routers/api/v1/repo/repo.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 7df5864455d89..16942dd3bae88 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -248,7 +249,8 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre if repo_model.IsErrRepoAlreadyExist(err) { ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") } else if db.IsErrNameReserved(err) || - db.IsErrNamePatternNotAllowed(err) { + db.IsErrNamePatternNotAllowed(err) || + repo_module.IsErrIssueLabelTemplateLoad(err) { ctx.Error(http.StatusUnprocessableEntity, "", err) } else { ctx.Error(http.StatusInternalServerError, "CreateRepository", err) From 9550e5a23e9f8581cc13f54b336307ee02d430cc Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sat, 23 Apr 2022 00:10:15 +0000 Subject: [PATCH 0028/1287] [skip ci] Updated translations via Crowdin --- options/locale/locale_pt-BR.ini | 67 ++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 99e526269cb71..6cadc3f68857d 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1,4 +1,4 @@ -home=Página inicial +home=Inicio dashboard=Painel explore=Explorar help=Ajuda @@ -226,8 +226,8 @@ default_keep_email_private=Ocultar endereços de e-mail por padrão default_keep_email_private_popup=Ocultar endereços de e-mail de novas contas de usuário por padrão. default_allow_create_organization=Permitir a criação de organizações por padrão default_allow_create_organization_popup=Permitir que novas contas de usuários criem organizações por padrão. -default_enable_timetracking=Habilitar o contador de tempo por padrão -default_enable_timetracking_popup=Habilitar o contador de tempo para novos repositórios por padrão. +default_enable_timetracking=Habilitar o Cronômetro por Padrão +default_enable_timetracking_popup=Habilitar o cronômetro para novos repositórios por padrão. no_reply_address=Domínio de e-mail oculto no_reply_address_helper=Nome de domínio para usuários com um endereço de e-mail oculto. Por exemplo, o nome de usuário 'joe' será registrado no Git como 'joe@noreply.example.org' se o domínio de e-mail oculto estiver definido como 'noreply.example.org'. password_algorithm=Algoritmo Hash de Senha @@ -451,6 +451,7 @@ lang_select_error=Selecione um idioma da lista. username_been_taken=O nome de usuário já está sendo usado. username_change_not_local_user=Usuários não-locais não são autorizados a alterar nome de usuário. repo_name_been_taken=O nome de repositório já está sendo usado. +repository_force_private=Forçar Privado está ativado: repositórios privados não podem ser tornados públicos. repository_files_already_exist=Arquivos já existem neste repositório. Contate o administrador. repository_files_already_exist.adopt=Arquivos já existem neste repositório e só podem ser adotados. repository_files_already_exist.delete=Arquivos já existem neste repositório. Você deve deletá-los. @@ -555,8 +556,10 @@ hidden_comment_types=Tipos de comentários ocultos comment_type_group_reference=Referência comment_type_group_label=Rótulo comment_type_group_milestone=Marco +comment_type_group_assignee=Atribuído comment_type_group_title=Título comment_type_group_branch=Branch +comment_type_group_time_tracking=Contador de tempo comment_type_group_deadline=Prazo final comment_type_group_dependency=Dependência comment_type_group_lock=Status de Bloqueio @@ -764,6 +767,7 @@ twofa_failed_get_secret=Falha ao obter o segredo. webauthn_desc=Chaves de segurança são dispositivos de hardware que contém chaves de criptografia. Elas podem ser usadas para autenticação de dois fatores. A chave de segurança deve suportar o padrão WebAuthnn Authenticator. webauthn_register_key=Adicionar chave de segurança +webauthn_nickname=Apelido webauthn_delete_key=Remover chave de segurança webauthn_delete_key_desc=Se você remover uma chave de segurança, não poderá mais entrar com ela. Continuar? @@ -1375,13 +1379,14 @@ issues.delete.title=Apagar esta issue? issues.delete.text=Você realmente deseja excluir esta issue? (Isto irá remover permanentemente todo o conteúdo. Considere fechá-la em vez disso, se você pretende mantê-la arquivado) issues.tracker=Contador de tempo issues.start_tracking_short=Iniciar Cronômetro -issues.start_tracking=Iniciar contador de tempo +issues.start_tracking=Iniciar Cronômetro issues.start_tracking_history=`começou a trabalhar %s` issues.tracker_auto_close=Contador de tempo será parado automaticamente quando esta issue for fechada -issues.stop_tracking=Parar cronômetro +issues.tracking_already_started=`Você já iniciou o cronômetro em outra issue!` +issues.stop_tracking=Parar Cronômetro issues.stop_tracking_history=`parou de trabalhar %s` issues.cancel_tracking=Descartar -issues.cancel_tracking_history=`cancelou contador de tempo %s` +issues.cancel_tracking_history=`cancelou o cronômetro %s` issues.add_time=Adicionar tempo manualmente issues.del_time=Apagar este registro de tempo issues.add_time_short=Adicionar tempo @@ -1774,7 +1779,7 @@ settings.tracker_issue_style=Formato de número do issue tracker externo settings.tracker_issue_style.numeric=Numérico settings.tracker_issue_style.alphanumeric=Alfanumérico settings.tracker_url_format_desc=Use os espaços reservados {user}, {repo} e {index} para o nome de usuário, nome do repositório e o índice de problemas. -settings.enable_timetracker=Habilitar contador de tempo +settings.enable_timetracker=Habilitar Cronômetro settings.allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo settings.pulls_desc=Habilitar pull requests no repositório settings.pulls.ignore_whitespace=Ignorar espaço em branco em conflitos @@ -2026,6 +2031,7 @@ settings.dismiss_stale_approvals_desc=Quando novos commits que mudam o conteúdo settings.require_signed_commits=Exibir commits assinados settings.require_signed_commits_desc=Rejeitar pushes para este branch se não estiverem assinados ou não forem validáveis. settings.protect_protected_file_patterns=Padrões de arquivos protegidos (separados usando ponto e vírgula '\;'): +settings.protect_protected_file_patterns_desc=Arquivos protegidos que não têm permissão para serem alterados diretamente, mesmo se o usuário tiver permissão para adicionar, editar ou apagar arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula ('\;'). Veja github.com/gobwas/glob documentação para sintaxe de padrões. Exemplos: .drone.yml, /docs/**/*.txt. settings.protect_unprotected_file_patterns=Padrões de arquivos desprotegidos (separados usando ponto e vírgula '\;'): settings.protect_unprotected_file_patterns_desc=Arquivos não protegidos que podem ser alterados diretamente se o usuário tiver acesso de gravação, ignorando as restrições de push. Vários padrões podem ser separados usando ponto e vírgula ('\;'). Veja github.com/gobwas/glob documentação para sintaxe de padrões. Exemplos: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Habilitar proteção @@ -2415,7 +2421,9 @@ dashboard.check_repo_stats=Verificar estatísticas de todos os repositórios dashboard.archive_cleanup=Apagar arquivos antigos de repositório dashboard.deleted_branches_cleanup=Realizar limpeza de branches apagados dashboard.git_gc_repos=Coleta de lixo em todos os repositórios +dashboard.resync_all_sshkeys=Atualizar o arquivo '.ssh/authorized_keys' com as chaves SSH do Gitea. dashboard.resync_all_sshkeys.desc=(Não necessário para o servidor SSH embutido.) +dashboard.resync_all_sshprincipals=Atualizar o arquivo '.ssh/authorized_principals' com os diretores do Gitea SSH. dashboard.resync_all_sshprincipals.desc=(Não necessário para o servidor SSH embutido.) dashboard.resync_all_hooks=Ressincronizar hooks pre-receive, update e post-receive de todos os repositórios. dashboard.reinit_missing_repos=Reinicializar todos os repositórios Git perdidos cujos registros existem @@ -2484,12 +2492,14 @@ users.prohibit_login=Desabilitar acesso users.is_admin=É administrador users.is_restricted=Está restrito users.allow_git_hook=Pode criar hooks Git +users.allow_git_hook_tooltip=Hooks Git são executados como o usuário do SO que executa Gitea e terá o mesmo nível de acesso ao servidor. Como resultado, os usuários com esse privilégio especial de Hook do Git podem acessar e modificar todos os repositórios do Gitea, bem como o banco de dados usado pelo Gitea. Por conseguinte, podem também obter privilégios de administrador do Gitea. users.allow_import_local=Pode importar repositórios locais users.allow_create_organization=Pode criar organizações users.update_profile=Atualizar conta de usuário users.delete_account=Excluir conta de usuário users.still_own_repo=Este usuário ainda possui um ou mais repositórios. Exclua ou transfira esses repositórios primeiro. users.still_has_org=Este usuário é membro de uma organização. Remova o usuário de qualquer organização primeiro. +users.still_own_packages=Este usuário ainda possui um ou mais pacotes. Exclua esses pacotes primeiro. users.deletion_success=A conta de usuário foi excluída. users.reset_2fa=Reinicializar 2FA users.list_status_filter.menu_text=Filtro @@ -2526,6 +2536,7 @@ orgs.new_orga=Nova organização repos.repo_manage_panel=Gerenciamento do repositório repos.unadopted=Repositórios Não Adotados +repos.unadopted.no_more=Não foram encontrados mais repositórios não adotados repos.owner=Proprietário repos.name=Nome repos.private=Privado @@ -2544,6 +2555,7 @@ packages.version=Versão packages.type=Tipo packages.repository=Repositório packages.size=Tamanho +packages.published=Publicado defaulthooks=Webhooks Padrões defaulthooks.desc=Webhooks automaticamente fazem requisições HTTP POST para um servidor quando acionados por determinados eventos do Gitea. Webhooks definidos aqui são os padrões e serão copiados para todos os novos repositórios. Leia mais no guia de webhooks. @@ -2714,8 +2726,8 @@ config.active_code_lives=Ativar Code Lives config.reset_password_code_lives=Tempo de expiração do código de recuperação de conta config.default_keep_email_private=Ocultar endereços de e-mail por padrão config.default_allow_create_organization=Permitir a criação de organizações por padrão -config.enable_timetracking=Habilitar contador de tempo -config.default_enable_timetracking=Habilitar o contador de tempo por padrão +config.enable_timetracking=Habilitar Cronômetro +config.default_enable_timetracking=Habilitar o Cronômetro por Padrão config.default_allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo config.no_reply_address=Ocultar domínio de e-mail config.default_visibility_organization=Visibilidade padrão para novas organizações @@ -2971,9 +2983,12 @@ error.unit_not_allowed=Você não tem permissão para acessar esta seção do re title=Pacotes desc=Gerenciar pacotes do repositório. empty=Não há pacotes ainda. +empty.documentation=Para obter mais informações sobre o registro de pacote, consulte a documentação. filter.type=Tipo filter.type.all=Todos filter.no_result=Seu filtro não produziu resultados. +published_by=Publicado %[1]s por %[3]s +published_by_in=Publicado %[1]s por %[3]s em %[5]s installation=Instalação about=Sobre este pacote requirements=Requisitos @@ -2984,26 +2999,60 @@ details.author=Autor details.project_site=Site do Projeto details.license=Licença versions=Versões +versions.on=em versions.view_all=Ver todas +dependency.id=ID dependency.version=Versão +composer.registry=Configure este registro em seu arquivo ~/.composer/config.json: +composer.install=Para instalar o pacote usando o Composer, execute o seguinte comando: +composer.documentation=Para obter mais informações sobre o registro do Composer, consulte a documentação. composer.dependencies=Dependências composer.dependencies.development=Dependências de Desenvolvimento conan.details.repository=Repositório +conan.registry=Configure este registro pela linha de comando: +conan.install=Para instalar o pacote usando o Conan, execute o seguinte comando: +conan.documentation=Para obter mais informações sobre o registro Conan, consulte a documentação. container.details.type=Tipo de Imagem container.details.platform=Plataforma container.details.repository_site=Site do Repositório container.details.documentation_site=Site da Documentação +container.pull=Puxe a imagem pela linha de comando: +container.documentation=Para obter mais informações sobre o registro de Container, consulte a documentação. container.multi_arch=S.O. / Arquitetura container.labels=Rótulos container.labels.key=Chave container.labels.value=Valor generic.download=Baixar pacote pela linha de comando: +generic.documentation=Para obter mais informações sobre o registro genérico, consulte a documentação. +helm.registry=Configurar este registro pela linha de comando: +helm.install=Para instalar o pacote, execute o seguinte comando: +helm.documentation=Para obter mais informações sobre o registro Helm, consulte a documentação. +maven.registry=Configure este registro no arquivo pom.xml do seu projeto: +maven.install=Para usar o pacote inclua o seguinte no bloco de dependencies no arquivo pom.xml: maven.install2=Executar via linha de comando: +maven.download=Para baixar a dependência, execute via linha de comando: +maven.documentation=Para obter mais informações sobre o registro Maven, consulte a documentação. +nuget.registry=Configurar este registro pela linha de comando: +nuget.install=Para instalar o pacote usando NuGet, execute o seguinte comando: +nuget.documentation=Para obter mais informações sobre o registro Nuget, consulte a documentação. +npm.registry=Configure este registro no arquivo .npmrc do seu projeto: +npm.install=Para instalar o pacote usando o npm, execute o seguinte comando: +npm.install2=ou adicione-o ao arquivo package.json: +npm.documentation=Para obter mais informações sobre o registro npm, consulte a documentação. npm.dependencies=Dependências npm.dependencies.development=Dependências de Desenvolvimento npm.dependencies.optional=Dependências Opcionais npm.details.tag=Tag pypi.requires=Requer Python +pypi.install=Para instalar o pacote usando pip, execute o seguinte comando: +pypi.documentation=Para obter mais informações sobre o registro PyPI, consulte a documentação. +rubygems.install=Para instalar o pacote usando gem, execute o seguinte comando: +rubygems.install2=ou adicione-o ao Gemfile: +rubygems.dependencies.runtime=Dependências de Execução +rubygems.dependencies.development=Dependências de Desenvolvimento +rubygems.required.ruby=Requer o Ruby versão +rubygems.required.rubygems=Requer o RubyGem versão +rubygems.documentation=Para obter mais informações sobre o registro do RubyGems, consulte a documentação. settings.link=Vincular este pacote a um repositório settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório. settings.link.select=Selecionar Repositório From 1f0541780538d03108fc741399480851759275bd Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 23 Apr 2022 16:56:33 +0000 Subject: [PATCH 0029/1287] Use horizontal tabs for repo header on mobile (#19468) * Use horizontal tabs for repo header on mobile - The current behavior of the repo header on mobile is to display them vertically column-by-column. I've only experience annoyance due to this while trying to visit gitea instanced on mobile. This commit changes this behavior to use horizontal tabs, it uses less tabs and doesn't bloat 60% of your mobile screen with the repo headers. - A small fix added in this commit is to give some space around the repo buttons, current behavior is that they are too "close" to the repo title. * Fix lint --- templates/repo/header.tmpl | 2 +- web_src/less/_repository.less | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 83ad79e12d59b..198b05b4ba561 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -144,7 +144,7 @@ {{end}} -
+
{{if not (or .Repository.IsBeingCreated .Repository.IsBroken)}}