aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile21
-rw-r--r--cmd/admin_auth_smtp.go4
-rw-r--r--cmd/admin_auth_smtp_test.go58
-rw-r--r--cmd/cmd.go10
-rw-r--r--cmd/cmd_test.go38
-rw-r--r--cmd/main.go2
-rw-r--r--cmd/manager_logging.go3
-rw-r--r--cmd/web.go4
-rw-r--r--models/migrations/v1_22/v294_test.go3
-rw-r--r--models/packages/package_property.go4
-rw-r--r--modules/actions/workflows.go16
-rw-r--r--modules/packages/content_store.go3
-rw-r--r--modules/public/public.go1
-rw-r--r--modules/setting/markup.go2
-rw-r--r--options/locale/locale_uk-UA.ini25
-rw-r--r--routers/api/packages/alpine/alpine.go4
-rw-r--r--routers/api/packages/arch/arch.go2
-rw-r--r--routers/api/packages/cargo/cargo.go2
-rw-r--r--routers/api/packages/chef/chef.go2
-rw-r--r--routers/api/packages/composer/composer.go2
-rw-r--r--routers/api/packages/conan/conan.go2
-rw-r--r--routers/api/packages/conda/conda.go2
-rw-r--r--routers/api/packages/container/container.go2
-rw-r--r--routers/api/packages/container/manifest.go42
-rw-r--r--routers/api/packages/cran/cran.go2
-rw-r--r--routers/api/packages/debian/debian.go6
-rw-r--r--routers/api/packages/generic/generic.go2
-rw-r--r--routers/api/packages/goproxy/goproxy.go2
-rw-r--r--routers/api/packages/helm/helm.go2
-rw-r--r--routers/api/packages/maven/maven.go2
-rw-r--r--routers/api/packages/npm/npm.go4
-rw-r--r--routers/api/packages/nuget/nuget.go4
-rw-r--r--routers/api/packages/pub/pub.go2
-rw-r--r--routers/api/packages/pypi/pypi.go2
-rw-r--r--routers/api/packages/rpm/rpm.go4
-rw-r--r--routers/api/packages/rubygems/rubygems.go2
-rw-r--r--routers/api/packages/swift/swift.go2
-rw-r--r--routers/api/packages/vagrant/vagrant.go2
-rw-r--r--routers/install/install.go6
-rw-r--r--routers/private/hook_verification_test.go4
-rw-r--r--routers/web/explore/code.go6
-rw-r--r--routers/web/user/code.go6
-rw-r--r--routers/web/user/package.go55
-rw-r--r--routers/web/web.go1
-rw-r--r--services/packages/arch/vercmp.go2
-rw-r--r--services/packages/container/common.go31
-rw-r--r--services/packages/packages.go30
-rw-r--r--templates/package/content/container.tmpl32
-rw-r--r--templates/package/content/pypi.tmpl2
-rw-r--r--templates/package/shared/view.tmpl13
-rw-r--r--tests/integration/api_issue_test.go8
-rw-r--r--tests/integration/api_packages_container_test.go3
-rw-r--r--tests/integration/issue_test.go8
-rwxr-xr-xtools/lint-go-gopls.sh2
-rw-r--r--web_src/css/index.css1
-rw-r--r--web_src/css/repo.css4
-rw-r--r--web_src/css/repo/packages.css25
57 files changed, 326 insertions, 205 deletions
diff --git a/Makefile b/Makefile
index bb70b91bb9..c868ef4463 100644
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,8 @@ XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
-GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1
+GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.19.0
+GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.0
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest
@@ -230,7 +231,7 @@ clean: ## delete backend and integration files
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
.PHONY: fmt
-fmt: ## format the Go code
+fmt: ## format the Go and template code
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
@@ -249,6 +250,19 @@ fmt-check: fmt
exit 1; \
fi
+.PHONY: fix
+fix: ## apply automated fixes to Go code
+ $(GO) run $(GOPLS_MODERNIZE_PACKAGE) -fix ./...
+
+.PHONY: fix-check
+fix-check: fix
+ @diff=$$(git diff --color=always $(GO_SOURCES)); \
+ if [ -n "$$diff" ]; then \
+ echo "Please run 'make fix' and commit the result:"; \
+ printf "%s" "$${diff}"; \
+ exit 1; \
+ fi
+
.PHONY: $(TAGS_EVIDENCE)
$(TAGS_EVIDENCE):
@mkdir -p $(MAKE_EVIDENCE_DIR)
@@ -288,7 +302,7 @@ checks: checks-frontend checks-backend ## run various consistency checks
checks-frontend: lockfile-check svg-check ## check frontend files
.PHONY: checks-backend
-checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check ## check backend files
+checks-backend: tidy-check swagger-check fmt-check fix-check swagger-validate security-check ## check backend files
.PHONY: lint
lint: lint-frontend lint-backend lint-spell ## lint everything
@@ -809,6 +823,7 @@ deps-tools: ## install tool dependencies
$(GO) install $(GOVULNCHECK_PACKAGE) & \
$(GO) install $(ACTIONLINT_PACKAGE) & \
$(GO) install $(GOPLS_PACKAGE) & \
+ $(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
wait
node_modules: package-lock.json
diff --git a/cmd/admin_auth_smtp.go b/cmd/admin_auth_smtp.go
index e9daf71809..93e0587fc3 100644
--- a/cmd/admin_auth_smtp.go
+++ b/cmd/admin_auth_smtp.go
@@ -39,12 +39,10 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{
Name: "force-smtps",
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
- Value: true,
},
&cli.BoolFlag{
Name: "skip-verify",
Usage: "Skip TLS verify.",
- Value: true,
},
&cli.StringFlag{
Name: "helo-hostname",
@@ -54,7 +52,6 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{
Name: "disable-helo",
Usage: "Disable SMTP helo.",
- Value: true,
},
&cli.StringFlag{
Name: "allowed-domains",
@@ -64,7 +61,6 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.",
- Value: true,
},
&cli.BoolFlag{
Name: "active",
diff --git a/cmd/admin_auth_smtp_test.go b/cmd/admin_auth_smtp_test.go
index 9778ff87d2..e54e01830c 100644
--- a/cmd/admin_auth_smtp_test.go
+++ b/cmd/admin_auth_smtp_test.go
@@ -60,10 +60,8 @@ func TestAddSMTP(t *testing.T) {
Auth: "PLAIN",
Host: "localhost",
Port: 25,
- // ForceSMTPS: true,
- // SkipVerify: true,
},
- TwoFactorPolicy: "skip",
+ TwoFactorPolicy: "",
},
},
{
@@ -73,12 +71,12 @@ func TestAddSMTP(t *testing.T) {
"--host", "localhost",
"--port", "25",
"--auth-type", "LOGIN",
- "--force-smtps=false",
- "--skip-verify=false",
+ "--force-smtps",
+ "--skip-verify",
"--helo-hostname", "example.com",
- "--disable-helo=false",
+ "--disable-helo=true",
"--allowed-domains", "example.com,example.org",
- "--skip-local-2fa=false",
+ "--skip-local-2fa",
"--active=false",
},
source: &auth_model.Source{
@@ -89,13 +87,13 @@ func TestAddSMTP(t *testing.T) {
Auth: "LOGIN",
Host: "localhost",
Port: 25,
- ForceSMTPS: false,
- SkipVerify: false,
+ ForceSMTPS: true,
+ SkipVerify: true,
HeloHostname: "example.com",
- DisableHelo: false,
+ DisableHelo: true,
AllowedDomains: "example.com,example.org",
},
- TwoFactorPolicy: "",
+ TwoFactorPolicy: "skip",
},
},
}
@@ -157,13 +155,10 @@ func TestUpdateSMTP(t *testing.T) {
Name: "old name",
IsActive: true,
Cfg: &smtp.Source{
- Auth: "PLAIN",
- Host: "old host",
- Port: 26,
- ForceSMTPS: true,
- SkipVerify: true,
+ Auth: "PLAIN",
+ Host: "old host",
+ Port: 26,
},
- TwoFactorPolicy: "",
},
args: []string{
"--id", "1",
@@ -177,13 +172,10 @@ func TestUpdateSMTP(t *testing.T) {
Name: "test",
IsActive: true,
Cfg: &smtp.Source{
- Auth: "PLAIN",
- Host: "localhost",
- Port: 25,
- ForceSMTPS: true,
- SkipVerify: true,
+ Auth: "PLAIN",
+ Host: "localhost",
+ Port: 25,
},
- TwoFactorPolicy: "skip",
},
},
{
@@ -197,10 +189,7 @@ func TestUpdateSMTP(t *testing.T) {
Auth: "PLAIN",
Host: "old host",
Port: 26,
- ForceSMTPS: true,
- SkipVerify: true,
HeloHostname: "old.example.com",
- DisableHelo: false,
AllowedDomains: "old.example.com",
},
TwoFactorPolicy: "",
@@ -211,12 +200,12 @@ func TestUpdateSMTP(t *testing.T) {
"--host", "localhost",
"--port", "25",
"--auth-type", "LOGIN",
- "--force-smtps=false",
- "--skip-verify=false",
+ "--force-smtps",
+ "--skip-verify",
"--helo-hostname", "example.com",
- "--disable-helo=true",
+ "--disable-helo",
"--allowed-domains", "example.com,example.org",
- "--skip-local-2fa=true",
+ "--skip-local-2fa",
"--active=false",
},
authSource: &auth_model.Source{
@@ -228,8 +217,8 @@ func TestUpdateSMTP(t *testing.T) {
Auth: "LOGIN",
Host: "localhost",
Port: 25,
- ForceSMTPS: false,
- SkipVerify: false,
+ ForceSMTPS: true,
+ SkipVerify: true,
HeloHostname: "example.com",
DisableHelo: true,
AllowedDomains: "example.com,example.org",
@@ -252,11 +241,8 @@ func TestUpdateSMTP(t *testing.T) {
Name: "test",
IsActive: true,
Cfg: &smtp.Source{
- Auth: "PLAIN",
- SkipVerify: true,
- ForceSMTPS: true,
+ Auth: "PLAIN",
},
- TwoFactorPolicy: "skip",
}, nil
},
diff --git a/cmd/cmd.go b/cmd/cmd.go
index 7a4d5d0d89..5b96bcbf9a 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -132,3 +132,13 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cl
return ctx, nil
}
}
+
+func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) {
+ // Dirty patch for urfave/cli's strange design.
+ // "./gitea bad-cmd" should not start the web server.
+ rootArgs := cmd.Root().Args().Slice()
+ if len(rootArgs) != 0 && rootArgs[0] != cmd.Name {
+ return rootArgs[0], false
+ }
+ return "", true
+}
diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go
new file mode 100644
index 0000000000..a36d05c76e
--- /dev/null
+++ b/cmd/cmd_test.go
@@ -0,0 +1,38 @@
+// Copyright 2025 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package cmd
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli/v3"
+)
+
+func TestDefaultCommand(t *testing.T) {
+ test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) {
+ called := false
+ cmd := &cli.Command{
+ DefaultCommand: "test",
+ Commands: []*cli.Command{
+ {
+ Name: "test",
+ Action: func(ctx context.Context, command *cli.Command) error {
+ retName, retValid := isValidDefaultSubCommand(command)
+ assert.Equal(t, expectedRetName, retName)
+ assert.Equal(t, expectedRetValid, retValid)
+ called = true
+ return nil
+ },
+ },
+ },
+ }
+ assert.NoError(t, cmd.Run(t.Context(), args))
+ assert.True(t, called)
+ }
+ test(t, []string{"./gitea"}, "", true)
+ test(t, []string{"./gitea", "test"}, "", true)
+ test(t, []string{"./gitea", "other"}, "other", false)
+}
diff --git a/cmd/main.go b/cmd/main.go
index 128b8776b4..3b8a8a9311 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -152,6 +152,8 @@ func NewMainApp(appVer AppVersion) *cli.Command {
CmdDocs,
}
+ // TODO: we should eventually drop the default command,
+ // but not sure whether it would break Windows users who used to double-click the EXE to run.
app.DefaultCommand = CmdWeb.Name
app.Flags = append(app.Flags, cli.VersionFlag)
diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go
index c83073e9c6..ac29e7d3e5 100644
--- a/cmd/manager_logging.go
+++ b/cmd/manager_logging.go
@@ -119,7 +119,6 @@ var (
Name: "rotate",
Aliases: []string{"r"},
Usage: "Rotate logs",
- Value: true,
},
&cli.Int64Flag{
Name: "max-size",
@@ -130,7 +129,6 @@ var (
Name: "daily",
Aliases: []string{"d"},
Usage: "Rotate logs daily",
- Value: true,
},
&cli.IntFlag{
Name: "max-days",
@@ -141,7 +139,6 @@ var (
Name: "compress",
Aliases: []string{"z"},
Usage: "Compress rotated logs",
- Value: true,
},
&cli.IntFlag{
Name: "compression-level",
diff --git a/cmd/web.go b/cmd/web.go
index 39e336fe54..61ee3cbc20 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -251,6 +251,10 @@ func runWeb(_ context.Context, cmd *cli.Command) error {
}
}()
+ if subCmdName, valid := isValidDefaultSubCommand(cmd); !valid {
+ return fmt.Errorf("unknown command: %s", subCmdName)
+ }
+
managerCtx, cancel := context.WithCancel(context.Background())
graceful.InitManager(managerCtx)
defer cancel()
diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go
index a1d702cb77..c3de332650 100644
--- a/models/migrations/v1_22/v294_test.go
+++ b/models/migrations/v1_22/v294_test.go
@@ -4,7 +4,6 @@
package v1_22 //nolint
import (
- "slices"
"testing"
"code.gitea.io/gitea/models/migrations/base"
@@ -44,7 +43,7 @@ func Test_AddUniqueIndexForProjectIssue(t *testing.T) {
for _, index := range tables[0].Indexes {
if index.Type == schemas.UniqueType {
found = true
- slices.Equal(index.Cols, []string{"project_id", "issue_id"})
+ assert.ElementsMatch(t, index.Cols, []string{"project_id", "issue_id"})
break
}
}
diff --git a/models/packages/package_property.go b/models/packages/package_property.go
index 10670951ad..7ddbfd97e9 100644
--- a/models/packages/package_property.go
+++ b/models/packages/package_property.go
@@ -92,8 +92,8 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
return err
}
-// DeletePropertyByName deletes properties by name
-func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
+// DeletePropertiesByName deletes properties by name
+func DeletePropertiesByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
return err
}
diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go
index 5cdd45b046..e7677edc59 100644
--- a/modules/actions/workflows.go
+++ b/modules/actions/workflows.go
@@ -565,18 +565,12 @@ func matchPullRequestReviewEvent(prPayload *api.PullRequestPayload, evt *jobpars
actions = append(actions, "submitted", "edited")
}
- matched := false
for _, val := range vals {
if slices.ContainsFunc(actions, glob.MustCompile(val, '/').Match) {
- matched = true
- }
- if matched {
+ matchTimes++
break
}
}
- if matched {
- matchTimes++
- }
default:
log.Warn("pull request review event unsupported condition %q", cond)
}
@@ -611,18 +605,12 @@ func matchPullRequestReviewCommentEvent(prPayload *api.PullRequestPayload, evt *
actions = append(actions, "created", "edited")
}
- matched := false
for _, val := range vals {
if slices.ContainsFunc(actions, glob.MustCompile(val, '/').Match) {
- matched = true
- }
- if matched {
+ matchTimes++
break
}
}
- if matched {
- matchTimes++
- }
default:
log.Warn("pull request review comment event unsupported condition %q", cond)
}
diff --git a/modules/packages/content_store.go b/modules/packages/content_store.go
index 37612556d7..dadb7eaefc 100644
--- a/modules/packages/content_store.go
+++ b/modules/packages/content_store.go
@@ -28,8 +28,7 @@ func NewContentStore() *ContentStore {
return contentStore
}
-// Get gets a package blob
-func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
+func (s *ContentStore) OpenBlob(key BlobHash256Key) (storage.Object, error) {
return s.store.Open(KeyToRelativePath(key))
}
diff --git a/modules/public/public.go b/modules/public/public.go
index 9bc04f3f7e..a7eace1538 100644
--- a/modules/public/public.go
+++ b/modules/public/public.go
@@ -110,5 +110,4 @@ func servePublicAsset(w http.ResponseWriter, req *http.Request, fi os.FileInfo,
}
}
http.ServeContent(w, req, fi.Name(), modtime, content)
- return
}
diff --git a/modules/setting/markup.go b/modules/setting/markup.go
index afaaaa2831..057b0650c3 100644
--- a/modules/setting/markup.go
+++ b/modules/setting/markup.go
@@ -149,7 +149,7 @@ func loadMarkupFrom(rootCfg ConfigProvider) {
func newMarkupSanitizer(name string, sec ConfigSection) {
rule, ok := createMarkupSanitizerRule(name, sec)
if ok {
- if after, ok0 := strings.CutPrefix(name, "sanitizer."); ok0 {
+ if after, found := strings.CutPrefix(name, "sanitizer."); found {
names := strings.SplitN(after, ".", 2)
name = names[0]
}
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index a740ac1ff5..fab4a06ecb 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -1334,6 +1334,7 @@ editor.new_branch_name_desc=Назва нової гілки…
editor.cancel=Відмінити
editor.filename_cannot_be_empty=Назва файлу не може бути порожньою.
editor.filename_is_invalid=Назва файлу недійсна: "%s".
+editor.commit_email=Електронна пошта коміту
editor.invalid_commit_email=Адреса електронної пошти для коміту недійсна.
editor.file_is_a_symlink=`"%s" - це символічне посилання. Символічні посилання не можна редагувати у веб-редакторі`
editor.filename_is_a_directory=Назва файлу '%s' вже використовується як назва каталогу у цьому сховищі.
@@ -1369,6 +1370,7 @@ commits.signed_by_untrusted_user=Підписаний недовіреним к
commits.signed_by_untrusted_user_unmatched=Підписано недовіреним користувачем, який не відповідає комітеру
commits.gpg_key_id=Ідентифікатор GPG ключа
commits.ssh_key_fingerprint=Відбиток ключа SSH
+commits.view_path=Переглянути в історії
commits.view_file_diff=Переглянути зміни до цього файлу в цьому коміті
commit.revert=Повернути до попереднього стану
@@ -1497,6 +1499,7 @@ issues.filter_project=Проєкт
issues.filter_project_all=Всі проєкти
issues.filter_project_none=Проєкт відсутній
issues.filter_assignee=Виконавець
+issues.filter_assignee_any_assignee=Призначено будь-кому
issues.filter_poster=Автор
issues.filter_user_placeholder=Пошук користувачів
issues.filter_user_no_select=Усі користувачі
@@ -1527,6 +1530,8 @@ issues.action_milestone=Етап
issues.action_milestone_no_select=Етап відсутній
issues.action_assignee=Виконавець
issues.action_assignee_no_select=Немає виконавця
+issues.action_check=Встановити/зняти позначку
+issues.action_check_all=Встановити/зняти позначку з усіх елементів
issues.opened_by=%[1]s відкрито <a href="%[2]s">%[3]s</a>
issues.opened_by_fake=%[1]s відкрито користувачем %[2]s
issues.previous=Попередній
@@ -1534,6 +1539,7 @@ issues.next=Далі
issues.open_title=Відкрито
issues.closed_title=Закрито
issues.draft_title=Чернетка
+issues.num_comments_1=%d коментар
issues.num_comments=%d коментарів
issues.commented_at=`прокоментував(ла) <a href="#%s">%s</a>`
issues.delete_comment_confirm=Ви впевнені, що хочете видалити цей коментар?
@@ -1542,6 +1548,7 @@ issues.context.quote_reply=Цитувати відповідь
issues.context.reference_issue=Посилання в новій задачі
issues.context.edit=Редагувати
issues.context.delete=Видалити
+issues.close=Закрити задачу
issues.comment_manually_pull_merged_at=вручну об'єднав(-ла) коміти %[1]s в %[2]s %[3]s
issues.close_comment_issue=Закрити з коментарем
issues.reopen_issue=Відкрити знову
@@ -1568,6 +1575,7 @@ issues.role.collaborator=Співавтор
issues.role.collaborator_helper=Цей користувач був запрошений до співпраці у сховищі.
issues.role.first_time_contributor=Учасник, який вперше долучився
issues.role.first_time_contributor_helper=Це перший внесок цього користувача в сховищі.
+issues.role.contributor=Співавтор
issues.role.contributor_helper=Цей користувач раніше вже вносив зміни до сховища.
issues.re_request_review=Повторно попросити рецензію
issues.is_stale=З часу останньої перевірки в цей PR було внесено деякі зміни
@@ -2190,6 +2198,7 @@ settings.webhook.response=Відповідь
settings.webhook.headers=Заголовки
settings.webhook.payload=Зміст
settings.webhook.body=Тіло
+settings.webhook.replay.description=Повторити цей веб-хук.
settings.githook_edit_desc=Якщо хук неактивний, буде показано зразок вмісту. Якщо залишити вміст порожнім, хук буде вимкнено.
settings.githook_name=Назва хуку
settings.githook_content=Зміст хука
@@ -2246,6 +2255,7 @@ settings.event_pull_request_sync=Запит на злиття синхроніз
settings.event_pull_request_sync_desc=Запит до злиття синхронізовано.
settings.event_package=Пакет
settings.branch_filter=Фільтр гілок
+settings.authorization_header=Заголовок авторизації
settings.active=Активний
settings.active_helper=Інформацію про викликані події буде надіслано за цією веб-хук URL-адресою.
settings.add_hook_success=Веб-хук було додано.
@@ -2294,6 +2304,7 @@ settings.protect_disable_push=Заборонити Push
settings.protect_disable_push_desc=Для цієї гілки буде заборонено виконання push.
settings.protect_enable_push=Дозволити Push
settings.protect_enable_push_desc=Будь-хто із правом запису зможе виконувати push для цієї гілки (за виключенням force push).
+settings.protect_enable_merge=Увімкнути об’єднання
settings.protect_check_status_contexts=Увімкнути перевірку стану
settings.protect_status_check_patterns=Шаблони перевірки стану:
settings.protect_status_check_patterns_desc=Введіть шаблони, щоб вказати, які перевірки стану повинні пройти гілки, перш ніж їх буде об'єднано у гілку, що відповідає цьому правилу. Кожен рядок визначає шаблон. Шаблони не можуть бути порожніми.
@@ -2327,6 +2338,8 @@ settings.block_outdated_branch_desc=Об'єднання буде неможли
settings.block_admin_merge_override=Адміністратори повинні дотримуватися правил захисту гілки
settings.block_admin_merge_override_desc=Адміністратори повинні дотримуватися правил захисту гілки і не можуть їх обійти.
settings.default_branch_desc=Обрати типову гілку сховища для запитів на злиття і комітів:
+settings.merge_style_desc=Стилі об'єднання
+settings.default_merge_style_desc=Типовий стиль об'єднання
settings.choose_branch=Оберіть гілку…
settings.no_protected_branch=Немає захищених гілок.
settings.edit_protected_branch=Редагувати
@@ -2348,12 +2361,14 @@ settings.chat_id=ID чату
settings.matrix.homeserver_url=URL домашньої сторінки
settings.matrix.room_id=ID кімнати
settings.matrix.message_type=Тип повідомлення
+settings.visibility.public.button=Зробити публічним
settings.archive.header=Відправити репозиторій в архів
settings.archive.success=Сховище успішно заархівовано.
settings.archive.error=Сталася помилка при спробі архівувати репозиторій. Докладнішу інформацію дивіться у журналі.
settings.archive.error_ismirror=Неможливо архівувати дзеркальне сховище.
settings.archive.branchsettings_unavailable=Параметри гілки не доступні, якщо репозиторій архівний.
settings.archive.tagsettings_unavailable=Параметри міток недоступні, якщо репозиторій архівний.
+settings.unarchive.header=Розархівувати це сховище
settings.unarchive.success=Сховище успішно розархівовано.
settings.update_avatar_success=Аватар репозиторію оновлений.
settings.lfs=LFS
@@ -2437,6 +2452,7 @@ diff.protected=Захищений
diff.image.side_by_side=Поруч
diff.image.swipe=Провести пальцем
diff.image.overlay=Накласти
+diff.has_escaped=Цей рядок містить приховані символи Юнікоду
diff.show_file_tree=Показати дерево файлів
diff.hide_file_tree=Сховати дерево файлів
diff.submodule_added=Підмодуль %[1]s додано в %[2]s
@@ -2749,6 +2765,7 @@ dashboard.resync_all_hooks=Заново синхронізувати хуки п
dashboard.reinit_missing_repos=Заново ініціалізувати всі відсутні сховища Git'а, для яких існують записи
dashboard.sync_external_users=Синхронізувати дані зовнішніх користувачів
dashboard.cleanup_hook_task_table=Очистити таблицю hook_task
+dashboard.cleanup_actions=Очищення ресурсів прострочених дій
dashboard.server_uptime=Час роботи сервера
dashboard.current_goroutine=Поточна кількість Goroutines
dashboard.current_memory_usage=Поточне використання пам'яті
@@ -2778,7 +2795,11 @@ dashboard.total_gc_time=Загальна пауза збирача сміття
dashboard.total_gc_pause=Загальна пауза збирача сміття (GC)
dashboard.last_gc_pause=Остання пауза збирача сміття (GC)
dashboard.gc_times=Кількість запусків збирача сміття (GC)
+dashboard.delete_old_actions=Видалити всі старі дії з бази даних
dashboard.update_checker=Перевірка оновлень
+dashboard.gc_lfs=Збір сміття мета-об'єктів LFS
+dashboard.cancel_abandoned_jobs=Скасувати покинуті завдання
+dashboard.start_schedule_tasks=Запуск запланованих завдань
dashboard.sync_branch.started=Розпочато синхронізацію гілок
dashboard.rebuild_issue_indexer=Перебудувати індексатор задач
dashboard.sync_repo_licenses=Синхронізувати ліцензії сховища
@@ -2851,6 +2872,7 @@ emails.not_updated=Не вдалось оновити адресу електр
emails.duplicate_active=Ця адреса електронної пошти вже активна для іншого користувача.
emails.change_email_header=Редагувати властивості електронної пошти
emails.change_email_text=Ви впевнені, що хочете оновити адресу електронної пошти?
+emails.delete=Видалити адресу електронної пошти
emails.delete_desc=Ви впевнені, що хочете видалити адресу електронної пошти?
emails.deletion_success=Адресу електронної пошти видалено.
emails.delete_primary_email_error=Ви не можете видалити основну адресу електронної пошти.
@@ -2871,6 +2893,7 @@ repos.issues=Задачі
repos.size=Розмір
repos.lfs_size=Розмір LFS
+packages.package_manage_panel=Керування пакетами
packages.total_size=Загальний розмір: %s
packages.unreferenced_size=Розмір без посилань: %s
packages.cleanup=Очистити прострочені дані
@@ -3393,6 +3416,7 @@ settings.link=Прив'язати пакет до сховища
settings.link.description=Якщо ви зв'яжете пакет зі сховищем, його буде вказано у списку пакетів сховища.
settings.link.select=Обрати сховище
settings.link.button=Оновити посилання на сховище
+settings.link.error=Не вдалося оновити посилання на сховище.
settings.delete=Видалити пакет
settings.delete.description=Видалення пакета є остаточним і не може бути скасоване.
settings.delete.notice=Ви збираєтесь видалити %s (%s). Цю операцію неможливо скасувати, ви впевнені?
@@ -3516,6 +3540,7 @@ logs.always_expand_running=Завжди розгортати поточні жу
[projects]
deleted.display_name=Видалений проєкт
type-1.display_name=Індивідуальний проєкт
+type-2.display_name=Проєкт сховища
type-3.display_name=Проєкт організації
enter_fullscreen=Повноекранний режим
exit_fullscreen=Вийти з повноекранного режиму
diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go
index f35cff3df2..ba4a4f23ce 100644
--- a/routers/api/packages/alpine/alpine.go
+++ b/routers/api/packages/alpine/alpine.go
@@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
@@ -216,7 +216,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/arch/arch.go b/routers/api/packages/arch/arch.go
index f5dc6c1d01..bf9cc3f1b8 100644
--- a/routers/api/packages/arch/arch.go
+++ b/routers/api/packages/arch/arch.go
@@ -239,7 +239,7 @@ func GetPackageOrRepositoryFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go
index 57cb83404f..cfcf79244f 100644
--- a/routers/api/packages/cargo/cargo.go
+++ b/routers/api/packages/cargo/cargo.go
@@ -165,7 +165,7 @@ func ListOwners(ctx *context.Context) {
// DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go
index a0c8c5696c..1f11afe548 100644
--- a/routers/api/packages/chef/chef.go
+++ b/routers/api/packages/chef/chef.go
@@ -343,7 +343,7 @@ func DownloadPackage(ctx *context.Context) {
pf := pd.Files[0].File
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go
index 3713805579..9daf0ffeff 100644
--- a/routers/api/packages/composer/composer.go
+++ b/routers/api/packages/composer/composer.go
@@ -160,7 +160,7 @@ func PackageMetadata(ctx *context.Context) {
// DownloadPackageFile serves the content of a package
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go
index 8019eee9f7..fe70e02cd6 100644
--- a/routers/api/packages/conan/conan.go
+++ b/routers/api/packages/conan/conan.go
@@ -480,7 +480,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go
index 7a46681235..fe7542dd18 100644
--- a/routers/api/packages/conda/conda.go
+++ b/routers/api/packages/conda/conda.go
@@ -293,7 +293,7 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pfs[0]
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 477c3bc71a..972506103a 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -705,7 +705,7 @@ func DeleteManifest(ctx *context.Context) {
func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) {
serveDirectReqParams := make(url.Values)
serveDirectReqParams.Set("response-content-type", pfd.Properties.GetByName(container_module.PropertyMediaType))
- s, u, _, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob, serveDirectReqParams)
+ s, u, _, err := packages_service.OpenBlobForDownload(ctx, pfd.File, pfd.Blob, serveDirectReqParams)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go
index 0cbd46e943..a640bcda25 100644
--- a/routers/api/packages/container/manifest.go
+++ b/routers/api/packages/container/manifest.go
@@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/util"
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
+ container_service "code.gitea.io/gitea/services/packages/container"
"github.com/opencontainers/go-digest"
oci "github.com/opencontainers/image-spec/specs-go/v1"
@@ -84,12 +85,11 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
manifestDigest := ""
err := func() error {
- var manifest oci.Manifest
- if err := json.NewDecoder(buf).Decode(&manifest); err != nil {
+ manifest, configDescriptor, metadata, err := container_service.ParseManifestMetadata(ctx, buf, mci.Owner.ID, mci.Image)
+ if err != nil {
return err
}
-
- if _, err := buf.Seek(0, io.SeekStart); err != nil {
+ if _, err = buf.Seek(0, io.SeekStart); err != nil {
return err
}
@@ -99,28 +99,7 @@ func processOciImageManifest(ctx context.Context, mci *manifestCreationInfo, buf
}
defer committer.Close()
- configDescriptor, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
- OwnerID: mci.Owner.ID,
- Image: mci.Image,
- Digest: string(manifest.Config.Digest),
- })
- if err != nil {
- return err
- }
-
- configReader, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(configDescriptor.Blob.HashSHA256))
- if err != nil {
- return err
- }
- defer configReader.Close()
-
- metadata, err := container_module.ParseImageConfig(manifest.Config.MediaType, configReader)
- if err != nil {
- return err
- }
-
blobReferences := make([]*blobReference, 0, 1+len(manifest.Layers))
-
blobReferences = append(blobReferences, &blobReference{
Digest: manifest.Config.Digest,
MediaType: manifest.Config.MediaType,
@@ -388,19 +367,16 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
return nil, err
}
} else {
- props, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged)
- if err != nil {
+ if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestTagged); err != nil {
return nil, err
}
- for _, prop := range props {
- if err = packages_model.DeletePropertyByID(ctx, prop.ID); err != nil {
- return nil, err
- }
- }
}
+ if err = packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference); err != nil {
+ return nil, err
+ }
for _, manifest := range metadata.Manifests {
- if err = packages_model.InsertOrUpdateProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
+ if _, err = packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
return nil, err
}
}
diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go
index 8a20072cb6..732acd215f 100644
--- a/routers/api/packages/cran/cran.go
+++ b/routers/api/packages/cran/cran.go
@@ -250,7 +250,7 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) {
return
}
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go
index fec34c91a6..346f71fa5d 100644
--- a/routers/api/packages/debian/debian.go
+++ b/routers/api/packages/debian/debian.go
@@ -59,7 +59,7 @@ func GetRepositoryFile(ctx *context.Context) {
key += "|" + component + "|" + architecture
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
@@ -106,7 +106,7 @@ func GetRepositoryFileByHash(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
@@ -210,7 +210,7 @@ func DownloadPackageFile(ctx *context.Context) {
name := ctx.PathParam("name")
version := ctx.PathParam("version")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go
index 0b5daa7334..db7aeace50 100644
--- a/routers/api/packages/generic/generic.go
+++ b/routers/api/packages/generic/generic.go
@@ -31,7 +31,7 @@ func apiError(ctx *context.Context, status int, obj any) {
// DownloadPackageFile serves the specific generic package.
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go
index bde29df739..89ec86bce9 100644
--- a/routers/api/packages/goproxy/goproxy.go
+++ b/routers/api/packages/goproxy/goproxy.go
@@ -106,7 +106,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, util.ErrNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go
index fb12daaa46..39c34f4da4 100644
--- a/routers/api/packages/helm/helm.go
+++ b/routers/api/packages/helm/helm.go
@@ -122,7 +122,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pvs[0],
&packages_service.PackageFileInfo{
diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go
index 9089c2eccf..40a8ff8242 100644
--- a/routers/api/packages/maven/maven.go
+++ b/routers/api/packages/maven/maven.go
@@ -223,7 +223,7 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool
return
}
- s, u, _, err := packages_service.GetPackageBlobStream(ctx, pf, pb, nil)
+ s, u, _, err := packages_service.OpenBlobForDownload(ctx, pf, pb, nil)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go
index 6ec46bcb36..1f09816d32 100644
--- a/routers/api/packages/npm/npm.go
+++ b/routers/api/packages/npm/npm.go
@@ -85,7 +85,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.PathParam("version")
filename := ctx.PathParam("filename")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -132,7 +132,7 @@ func DownloadPackageFileByName(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pvs[0],
&packages_service.PackageFileInfo{
diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go
index fa5067a278..0a3254979d 100644
--- a/routers/api/packages/nuget/nuget.go
+++ b/routers/api/packages/nuget/nuget.go
@@ -405,7 +405,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.PathParam("version")
filename := ctx.PathParam("filename")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -669,7 +669,7 @@ func DownloadSymbolFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, u, pf, err := packages_service.OpenFileForDownload(ctx, pfs[0])
if err != nil {
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
apiError(ctx, http.StatusNotFound, err)
diff --git a/routers/api/packages/pub/pub.go b/routers/api/packages/pub/pub.go
index e7b07aefd0..4bd36e94b6 100644
--- a/routers/api/packages/pub/pub.go
+++ b/routers/api/packages/pub/pub.go
@@ -274,7 +274,7 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pd.Files[0].File
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go
index 199f4e7478..9b5ae6c89d 100644
--- a/routers/api/packages/pypi/pypi.go
+++ b/routers/api/packages/pypi/pypi.go
@@ -82,7 +82,7 @@ func DownloadPackageFile(ctx *context.Context) {
packageVersion := ctx.PathParam("version")
filename := ctx.PathParam("filename")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go
index a00a61c079..938c35341d 100644
--- a/routers/api/packages/rpm/rpm.go
+++ b/routers/api/packages/rpm/rpm.go
@@ -96,7 +96,7 @@ func GetRepositoryFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pv,
&packages_service.PackageFileInfo{
@@ -220,7 +220,7 @@ func DownloadPackageFile(ctx *context.Context) {
name := ctx.PathParam("name")
version := ctx.PathParam("version")
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go
index cb880c8bdb..774d5520fd 100644
--- a/routers/api/packages/rubygems/rubygems.go
+++ b/routers/api/packages/rubygems/rubygems.go
@@ -178,7 +178,7 @@ func DownloadPackageFile(ctx *context.Context) {
return
}
- s, u, pf, err := packages_service.GetFileStreamByPackageVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageVersion(
ctx,
pvs[0],
&packages_service.PackageFileInfo{
diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go
index 47439c4c3b..bf542f33a7 100644
--- a/routers/api/packages/swift/swift.go
+++ b/routers/api/packages/swift/swift.go
@@ -429,7 +429,7 @@ func DownloadPackageFile(ctx *context.Context) {
pf := pd.Files[0].File
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/routers/api/packages/vagrant/vagrant.go b/routers/api/packages/vagrant/vagrant.go
index 3afaa5de1f..9eb67e5397 100644
--- a/routers/api/packages/vagrant/vagrant.go
+++ b/routers/api/packages/vagrant/vagrant.go
@@ -218,7 +218,7 @@ func UploadPackageFile(ctx *context.Context) {
}
func DownloadPackageFile(ctx *context.Context) {
- s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ s, u, pf, err := packages_service.OpenFileForDownloadByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
diff --git a/routers/install/install.go b/routers/install/install.go
index 4b3eba78b3..b9bc41dfcf 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -99,11 +99,7 @@ func Install(ctx *context.Context) {
form.SSLMode = setting.Database.SSLMode
curDBType := setting.Database.Type.String()
- var isCurDBTypeSupported bool
- if slices.Contains(setting.SupportedDatabaseTypes, curDBType) {
- isCurDBTypeSupported = true
- }
- if !isCurDBTypeSupported {
+ if !slices.Contains(setting.SupportedDatabaseTypes, curDBType) {
curDBType = "mysql"
}
ctx.Data["CurDbType"] = curDBType
diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go
index f6c2e1087f..8653e34daa 100644
--- a/routers/private/hook_verification_test.go
+++ b/routers/private/hook_verification_test.go
@@ -18,7 +18,9 @@ func TestVerifyCommits(t *testing.T) {
unittest.PrepareTestEnv(t)
gitRepo, err := git.OpenRepository(t.Context(), testReposDir+"repo1_hook_verification")
- defer gitRepo.Close()
+ if err != nil {
+ defer gitRepo.Close()
+ }
assert.NoError(t, err)
objectFormat, err := gitRepo.GetObjectFormat()
diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go
index 8bde983e30..3bb50ef397 100644
--- a/routers/web/explore/code.go
+++ b/routers/web/explore/code.go
@@ -94,11 +94,7 @@ func Code(ctx *context.Context) {
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
- var find bool
- if slices.Contains(loadRepoIDs, result.RepoID) {
- find = true
- }
- if !find {
+ if !slices.Contains(loadRepoIDs, result.RepoID) {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
diff --git a/routers/web/user/code.go b/routers/web/user/code.go
index 20ab1405dd..11579c40a6 100644
--- a/routers/web/user/code.go
+++ b/routers/web/user/code.go
@@ -87,11 +87,7 @@ func CodeSearch(ctx *context.Context) {
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
- var find bool
- if slices.Contains(loadRepoIDs, result.RepoID) {
- find = true
- }
- if !find {
+ if !slices.Contains(loadRepoIDs, result.RepoID) {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
diff --git a/routers/web/user/package.go b/routers/web/user/package.go
index 8c85fc22c7..fd33a81901 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -4,6 +4,8 @@
package user
import (
+ gocontext "context"
+ "errors"
"net/http"
"net/url"
@@ -20,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/optional"
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
arch_module "code.gitea.io/gitea/modules/packages/arch"
+ container_module "code.gitea.io/gitea/modules/packages/container"
debian_module "code.gitea.io/gitea/modules/packages/debian"
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
"code.gitea.io/gitea/modules/setting"
@@ -31,6 +34,7 @@ import (
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
packages_service "code.gitea.io/gitea/services/packages"
+ container_service "code.gitea.io/gitea/services/packages/container"
)
const (
@@ -162,6 +166,24 @@ func RedirectToLastVersion(ctx *context.Context) {
ctx.Redirect(pd.VersionWebLink())
}
+func viewPackageContainerImage(ctx gocontext.Context, pd *packages_model.PackageDescriptor, digest string) (*container_module.Metadata, error) {
+ manifestBlob, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
+ OwnerID: pd.Owner.ID,
+ Image: pd.Package.LowerName,
+ Digest: digest,
+ })
+ if err != nil {
+ return nil, err
+ }
+ manifestReader, err := packages_service.OpenBlobStream(manifestBlob.Blob)
+ if err != nil {
+ return nil, err
+ }
+ defer manifestReader.Close()
+ _, _, metadata, err := container_service.ParseManifestMetadata(ctx, manifestReader, pd.Owner.ID, pd.Package.LowerName)
+ return metadata, err
+}
+
// ViewPackageVersion displays a single package version
func ViewPackageVersion(ctx *context.Context) {
if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
@@ -169,6 +191,7 @@ func ViewPackageVersion(ctx *context.Context) {
return
}
+ versionSub := ctx.PathParam("version_sub")
pd := ctx.Package.Descriptor
ctx.Data["Title"] = pd.Package.Name
ctx.Data["IsPackagesPage"] = true
@@ -180,6 +203,9 @@ func ViewPackageVersion(ctx *context.Context) {
}
ctx.Data["PackageRegistryHost"] = registryHostURL.Host
+ var pvs []*packages_model.PackageVersion
+ pvsTotal := int64(0)
+
switch pd.Package.Type {
case packages_model.TypeAlpine:
branches := make(container.Set[string])
@@ -257,21 +283,26 @@ func ViewPackageVersion(ctx *context.Context) {
ctx.Data["Groups"] = util.Sorted(groups.Values())
ctx.Data["Architectures"] = util.Sorted(architectures.Values())
- }
-
- var (
- total int64
- pvs []*packages_model.PackageVersion
- )
- switch pd.Package.Type {
case packages_model.TypeContainer:
- pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
+ imageMetadata := pd.Metadata
+ if versionSub != "" {
+ imageMetadata, err = viewPackageContainerImage(ctx, pd, versionSub)
+ if errors.Is(err, util.ErrNotExist) {
+ ctx.NotFound(nil)
+ return
+ } else if err != nil {
+ ctx.ServerError("viewPackageContainerImage", err)
+ return
+ }
+ }
+ ctx.Data["ContainerImageMetadata"] = imageMetadata
+ pvs, pvsTotal, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
Paginator: db.NewAbsoluteListOptions(0, 5),
PackageID: pd.Package.ID,
IsTagged: true,
})
default:
- pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
+ pvs, pvsTotal, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
Paginator: db.NewAbsoluteListOptions(0, 5),
PackageID: pd.Package.ID,
IsInternal: optional.Some(false),
@@ -283,7 +314,7 @@ func ViewPackageVersion(ctx *context.Context) {
}
ctx.Data["LatestVersions"] = pvs
- ctx.Data["TotalVersionCount"] = total
+ ctx.Data["TotalVersionCount"] = pvsTotal
ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin()
@@ -482,9 +513,9 @@ func DownloadPackageFile(ctx *context.Context) {
return
}
- s, u, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ s, u, _, err := packages_service.OpenFileForDownload(ctx, pf)
if err != nil {
- ctx.ServerError("GetPackageFileStream", err)
+ ctx.ServerError("OpenFileForDownload", err)
return
}
diff --git a/routers/web/web.go b/routers/web/web.go
index 5eba29c601..a54f96ec68 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1012,6 +1012,7 @@ func registerWebRoutes(m *web.Router) {
m.Get("/versions", user.ListPackageVersions)
m.Group("/{version}", func() {
m.Get("", user.ViewPackageVersion)
+ m.Get("/{version_sub}", user.ViewPackageVersion)
m.Get("/files/{fileid}", user.DownloadPackageFile)
m.Group("/settings", func() {
m.Get("", user.PackageSettings)
diff --git a/services/packages/arch/vercmp.go b/services/packages/arch/vercmp.go
index 6dcd0df419..d44aa530f0 100644
--- a/services/packages/arch/vercmp.go
+++ b/services/packages/arch/vercmp.go
@@ -35,7 +35,7 @@ func parseEVR(evr string) (epoch, version, release string) {
func compareSegments(a, b []string) int {
lenA, lenB := len(a), len(b)
l := min(lenA, lenB)
- for i := 0; i < l; i++ {
+ for i := range l {
if r := compare(a[i], b[i]); r != 0 {
return r
}
diff --git a/services/packages/container/common.go b/services/packages/container/common.go
index 5a14ed5b7a..71e8b86fcd 100644
--- a/services/packages/container/common.go
+++ b/services/packages/container/common.go
@@ -5,11 +5,17 @@ package container
import (
"context"
+ "io"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
+ container_service "code.gitea.io/gitea/models/packages/container"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
+
+ "github.com/opencontainers/image-spec/specs-go/v1"
)
// UpdateRepositoryNames updates the repository name property for all packages of the specific owner
@@ -22,7 +28,7 @@ func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwner
newOwnerName = strings.ToLower(newOwnerName)
for _, p := range ps {
- if err := packages_model.DeletePropertyByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
+ if err := packages_model.DeletePropertiesByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
return err
}
@@ -33,3 +39,26 @@ func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwner
return nil
}
+
+func ParseManifestMetadata(ctx context.Context, rd io.Reader, ownerID int64, imageName string) (*v1.Manifest, *packages_model.PackageFileDescriptor, *container_module.Metadata, error) {
+ var manifest v1.Manifest
+ if err := json.NewDecoder(rd).Decode(&manifest); err != nil {
+ return nil, nil, nil, err
+ }
+ configDescriptor, err := container_service.GetContainerBlob(ctx, &container_service.BlobSearchOptions{
+ OwnerID: ownerID,
+ Image: imageName,
+ Digest: string(manifest.Config.Digest),
+ })
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ configReader, err := packages.NewContentStore().OpenBlob(packages.BlobHash256Key(configDescriptor.Blob.HashSHA256))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ defer configReader.Close()
+ metadata, err := container_module.ParseImageConfig(manifest.Config.MediaType, configReader)
+ return &manifest, configDescriptor, metadata, err
+}
diff --git a/services/packages/packages.go b/services/packages/packages.go
index bd1d460fd3..517334cbc7 100644
--- a/services/packages/packages.go
+++ b/services/packages/packages.go
@@ -563,8 +563,8 @@ func DeletePackageFile(ctx context.Context, pf *packages_model.PackageFile) erro
return packages_model.DeleteFileByID(ctx, pf.ID)
}
-// GetFileStreamByPackageNameAndVersion returns the content of the specific package file
-func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+// OpenFileForDownloadByPackageNameAndVersion returns the content of the specific package file and increases the download counter.
+func OpenFileForDownloadByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey)
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
@@ -576,32 +576,38 @@ func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo,
return nil, nil, nil, err
}
- return GetFileStreamByPackageVersion(ctx, pv, pfi)
+ return OpenFileForDownloadByPackageVersion(ctx, pv, pfi)
}
-// GetFileStreamByPackageVersion returns the content of the specific package file
-func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+// OpenFileForDownloadByPackageVersion returns the content of the specific package file and increases the download counter.
+func OpenFileForDownloadByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey)
if err != nil {
return nil, nil, nil, err
}
- return GetPackageFileStream(ctx, pf)
+ return OpenFileForDownload(ctx, pf)
}
-// GetPackageFileStream returns the content of the specific package file
-func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+// OpenFileForDownload returns the content of the specific package file and increases the download counter.
+func OpenFileForDownload(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
pb, err := packages_model.GetBlobByID(ctx, pf.BlobID)
if err != nil {
return nil, nil, nil, err
}
- return GetPackageBlobStream(ctx, pf, pb, nil)
+ return OpenBlobForDownload(ctx, pf, pb, nil)
}
-// GetPackageBlobStream returns the content of the specific package blob
+func OpenBlobStream(pb *packages_model.PackageBlob) (io.ReadSeekCloser, error) {
+ cs := packages_module.NewContentStore()
+ key := packages_module.BlobHash256Key(pb.HashSHA256)
+ return cs.OpenBlob(key)
+}
+
+// OpenBlobForDownload returns the content of the specific package blob and increases the download counter.
// If the storage supports direct serving and it's enabled, only the direct serving url is returned.
-func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob, serveDirectReqParams url.Values) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
+func OpenBlobForDownload(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob, serveDirectReqParams url.Values) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) {
key := packages_module.BlobHash256Key(pb.HashSHA256)
cs := packages_module.NewContentStore()
@@ -617,7 +623,7 @@ func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, p
}
}
if u == nil {
- s, err = cs.Get(key)
+ s, err = cs.OpenBlob(key)
}
if err == nil {
diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl
index b4e12cf26b..1a1335aaa6 100644
--- a/templates/package/content/container.tmpl
+++ b/templates/package/content/container.tmpl
@@ -49,7 +49,11 @@
{{/* "unknown/unknown" is attestation-manifest, so we should skip it */}}
{{if ne .Platform "unknown/unknown"}}
<tr>
- <td><a class="tw-font-mono" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .Digest}}">{{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}}</a></td>
+ <td>
+ <a class="tw-font-mono" href="{{$.PackageDescriptor.PackageWebLink}}/{{$.PackageDescriptor.Version.LowerVersion}}/{{PathEscape .Digest}}">
+ {{StringUtils.TrimPrefix .Digest "sha256:" | ShortSha}}
+ </a>
+ </td>
<td>{{.Platform}}</td>
<td>{{FileSize .Size}}</td>
</tr>
@@ -65,12 +69,24 @@
{{.PackageDescriptor.Metadata.Description}}
</div>
{{end}}
- {{if .PackageDescriptor.Metadata.ImageLayers}}
- <h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.layers"}}</h4>
+
+ {{/* a container manifest may contain sub manifests, so here we try to display some information of the sub manifest,
+ not perfect, just better than before */}}
+ {{$imageMetadata := .ContainerImageMetadata}}
+ {{if $imageMetadata.ImageLayers}}
+ <h4 class="ui top attached header flex-text-block">
+ {{ctx.Locale.Tr "packages.container.layers"}}
+ {{/* only show the platform if the image metadata is not the package's, which means that it is a sub manifest */}}
+ {{if ne .ContainerImageMetadata .PackageDescriptor.Metadata}}
+ <span class="tw-text-sm flex-text-inline" title="{{ctx.Locale.Tr "packages.container.details.platform"}}">
+ ({{svg "octicon-cpu" 12}} {{.ContainerImageMetadata.Platform}})
+ </span>
+ {{end}}
+ </h4>
<div class="ui attached segment tw-break-anywhere">
- <table class="ui very basic compact table">
+ <table class="ui very basic compact table tw-font-mono">
<tbody>
- {{range .PackageDescriptor.Metadata.ImageLayers}}
+ {{range $imageMetadata.ImageLayers}}
<tr>
<td>{{.}}</td>
</tr>
@@ -79,10 +95,10 @@
</table>
</div>
{{end}}
- {{if .PackageDescriptor.Metadata.Labels}}
+ {{if $imageMetadata.Labels}}
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.container.labels"}}</h4>
<div class="ui attached segment">
- <table class="ui very basic compact table">
+ <table class="ui very basic compact table tw-font-mono">
<thead>
<tr>
<th>{{ctx.Locale.Tr "packages.container.labels.key"}}</th>
@@ -90,7 +106,7 @@
</tr>
</thead>
<tbody>
- {{range $key, $value := .PackageDescriptor.Metadata.Labels}}
+ {{range $key, $value := $imageMetadata.Labels}}
<tr>
<td class="tw-align-top">{{$key}}</td>
<td class="tw-break-anywhere">{{$value}}</td>
diff --git a/templates/package/content/pypi.tmpl b/templates/package/content/pypi.tmpl
index 2a22a6ed71..2625c160fe 100644
--- a/templates/package/content/pypi.tmpl
+++ b/templates/package/content/pypi.tmpl
@@ -4,7 +4,7 @@
<div class="ui form">
<div class="field">
<label>{{svg "octicon-terminal"}} {{ctx.Locale.Tr "packages.pypi.install"}}</label>
- <div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> {{.PackageDescriptor.Package.Name}}</code></pre></div>
+ <div class="markup"><pre class="code-block"><code>pip install --index-url <origin-url data-url="{{AppSubUrl}}/api/packages/{{.PackageDescriptor.Owner.Name}}/pypi/simple/"></origin-url> --extra-index-url https://pypi.org/ {{.PackageDescriptor.Package.Name}}</code></pre></div>
</div>
<div class="field">
<label>{{ctx.Locale.Tr "packages.registry.documentation" "PyPI" "https://docs.gitea.com/usage/packages/pypi/"}}</label>
diff --git a/templates/package/shared/view.tmpl b/templates/package/shared/view.tmpl
index 713e1bbfc5..52673accf9 100644
--- a/templates/package/shared/view.tmpl
+++ b/templates/package/shared/view.tmpl
@@ -1,4 +1,5 @@
<div class="issue-title-header">
+ {{$packageVersionLink := print $.PackageDescriptor.PackageWebLink "/" (PathEscape .PackageDescriptor.Version.LowerVersion)}}
<h1>{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</h1>
<div>
{{$timeStr := DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}
@@ -9,8 +10,8 @@
{{end}}
</div>
</div>
-<div class="issue-content">
- <div class="issue-content-left">
+<div class="packages-content">
+ <div class="packages-content-left">
{{template "package/content/alpine" .}}
{{template "package/content/arch" .}}
{{template "package/content/cargo" .}}
@@ -34,7 +35,7 @@
{{template "package/content/swift" .}}
{{template "package/content/vagrant" .}}
</div>
- <div class="issue-content-right ui segment">
+ <div class="ui segment packages-content-right">
<strong>{{ctx.Locale.Tr "packages.details"}}</strong>
<div class="ui relaxed list flex-items-block">
<div class="item">{{svg .PackageDescriptor.Package.Type.SVGName}} {{.PackageDescriptor.Package.Type.Name}}</div>
@@ -74,8 +75,8 @@
<div class="ui relaxed list">
{{range .PackageDescriptor.Files}}
<div class="item">
- <a href="{{$.Link}}/files/{{.File.ID}}">{{.File.Name}}</a>
- <span class="text small file-size">{{FileSize .Blob.Size}}</span>
+ <a href="{{$packageVersionLink}}/files/{{.File.ID}}">{{.File.Name}}</a>
+ <span class="text small tw-whitespace-nowrap">{{FileSize .Blob.Size}}</span>
</div>
{{end}}
</div>
@@ -98,7 +99,7 @@
<div class="item">{{svg "octicon-issue-opened"}} <a href="{{.PackageDescriptor.Repository.Link}}/issues">{{ctx.Locale.Tr "repo.issues"}}</a></div>
{{end}}
{{if .CanWritePackages}}
- <div class="item">{{svg "octicon-tools"}} <a href="{{.Link}}/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
+ <div class="item">{{svg "octicon-tools"}} <a href="{{$packageVersionLink}}/settings">{{ctx.Locale.Tr "repo.settings"}}</a></div>
{{end}}
</div>
{{end}}
diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go
index ce9c33c049..370c90a100 100644
--- a/tests/integration/api_issue_test.go
+++ b/tests/integration/api_issue_test.go
@@ -267,9 +267,7 @@ func TestAPISearchIssues(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// as this API was used in the frontend, it uses UI page size
- expectedIssueCount := min(
- // from the fixtures
- 20, setting.UI.IssuePagingNum)
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
link, _ := url.Parse("/api/v1/repos/issues/search")
token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)
@@ -370,9 +368,7 @@ func TestAPISearchIssuesWithLabels(t *testing.T) {
defer tests.PrepareTestEnv(t)()
// as this API was used in the frontend, it uses UI page size
- expectedIssueCount := min(
- // from the fixtures
- 20, setting.UI.IssuePagingNum)
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
link, _ := url.Parse("/api/v1/repos/issues/search")
token := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue)
diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go
index 90bf1c3d21..8ae33dc35c 100644
--- a/tests/integration/api_packages_container_test.go
+++ b/tests/integration/api_packages_container_test.go
@@ -562,8 +562,7 @@ func TestPackageContainer(t *testing.T) {
assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository))
assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
- // only the last manifest digest is associated with the version (OCI builders will push the index manifest digest as the final step)
- assert.ElementsMatch(t, []string{untaggedManifestDigest}, getAllByName(pd.VersionProperties, container_module.PropertyManifestReference))
+ assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.VersionProperties, container_module.PropertyManifestReference))
assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
metadata := pd.Metadata.(*container_module.Metadata)
diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go
index 7b803cd54d..aff8aeda83 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -488,9 +488,7 @@ func TestSearchIssues(t *testing.T) {
session := loginUser(t, "user2")
- expectedIssueCount := min(
- // from the fixtures
- 20, setting.UI.IssuePagingNum)
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
link, _ := url.Parse("/issues/search")
req := NewRequest(t, "GET", link.String())
@@ -581,9 +579,7 @@ func TestSearchIssues(t *testing.T) {
func TestSearchIssuesWithLabels(t *testing.T) {
defer tests.PrepareTestEnv(t)()
- expectedIssueCount := min(
- // from the fixtures
- 20, setting.UI.IssuePagingNum)
+ expectedIssueCount := min(20, setting.UI.IssuePagingNum) // 20 is from the fixtures
session := loginUser(t, "user1")
link, _ := url.Parse("/issues/search")
diff --git a/tools/lint-go-gopls.sh b/tools/lint-go-gopls.sh
index a222ea14d7..2cd26ca6fe 100755
--- a/tools/lint-go-gopls.sh
+++ b/tools/lint-go-gopls.sh
@@ -11,7 +11,7 @@ IGNORE_PATTERNS=(
# current absolute path, indicating a error was found. This is necessary
# because the tool does not set non-zero exit code when errors are found.
# ref: https://github.com/golang/go/issues/67078
-ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}"));
+ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check -severity=warning "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}"));
NUM_ERRORS=$(echo -n "$ERROR_LINES" | wc -l)
if [ "$NUM_ERRORS" -eq "0" ]; then
diff --git a/web_src/css/index.css b/web_src/css/index.css
index de15c5c69c..291cd04b2b 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -70,6 +70,7 @@
@import "./repo/reactions.css";
@import "./repo/clone.css";
@import "./repo/commit-sign.css";
+@import "./repo/packages.css";
@import "./editor/fileeditor.css";
@import "./editor/combomarkdowneditor.css";
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index f7ede5bf92..cbc890e356 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -1227,10 +1227,6 @@ td .commit-summary {
font-weight: var(--font-weight-normal);
}
-.repository.packages .file-size {
- white-space: nowrap;
-}
-
.repository .activity-header {
display: flex;
justify-content: space-between;
diff --git a/web_src/css/repo/packages.css b/web_src/css/repo/packages.css
new file mode 100644
index 0000000000..75675f5243
--- /dev/null
+++ b/web_src/css/repo/packages.css
@@ -0,0 +1,25 @@
+.packages-content {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+}
+
+.packages-content-left {
+ margin: 0 !important;
+ width: calc(100% - 250px - 16px);
+}
+
+.packages-content-right {
+ margin: 0 !important;
+ width: 250px;
+}
+
+@media (max-width: 767.98px) {
+ .packages-content {
+ flex-direction: column;
+ }
+ .packages-content-left,
+ .packages-content-right {
+ width: 100%;
+ }
+}