diff --git a/cmd/actions.go b/cmd/actions.go index 275fd7904e..f582c16c81 100644 --- a/cmd/actions.go +++ b/cmd/actions.go @@ -50,6 +50,6 @@ func runGenerateActionsRunnerToken(c *cli.Context) error { if extra.HasError() { return handleCliResponseExtra(extra) } - _, _ = fmt.Printf("%s\n", respText) + _, _ = fmt.Printf("%s\n", respText.Text) return nil } diff --git a/cmd/doctor.go b/cmd/doctor.go index f891b12608..e433f4adc5 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -15,9 +15,9 @@ import ( "code.gitea.io/gitea/models/migrations" migrate_base "code.gitea.io/gitea/models/migrations/base" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/doctor" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/doctor" "github.com/urfave/cli/v2" "xorm.io/xorm" diff --git a/cmd/doctor_test.go b/cmd/doctor_test.go index 75376a567e..3e1ff299c5 100644 --- a/cmd/doctor_test.go +++ b/cmd/doctor_test.go @@ -7,8 +7,8 @@ import ( "context" "testing" - "code.gitea.io/gitea/modules/doctor" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/doctor" "github.com/stretchr/testify/assert" "github.com/urfave/cli/v2" diff --git a/cmd/keys.go b/cmd/keys.go index 9d5278f109..ceeec48486 100644 --- a/cmd/keys.go +++ b/cmd/keys.go @@ -78,6 +78,6 @@ func runKeys(c *cli.Context) error { if extra.Error != nil { return extra.Error } - _, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString)) + _, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text)) return nil } diff --git a/cmd/mailer.go b/cmd/mailer.go index 646330e85a..0c5f2c8c8d 100644 --- a/cmd/mailer.go +++ b/cmd/mailer.go @@ -45,6 +45,6 @@ func runSendMail(c *cli.Context) error { if extra.HasError() { return handleCliResponseExtra(extra) } - _, _ = fmt.Printf("Sent %s email(s) to all users\n", respText) + _, _ = fmt.Printf("Sent %s email(s) to all users\n", respText.Text) return nil } diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go index acc3ba16ba..aa49445a89 100644 --- a/cmd/migrate_storage.go +++ b/cmd/migrate_storage.go @@ -110,6 +110,9 @@ func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error { func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error { return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error { + if user.CustomAvatarRelativePath() == "" { + return nil + } _, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath()) return err }) @@ -117,6 +120,9 @@ func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error { return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error { + if repo.CustomAvatarRelativePath() == "" { + return nil + } _, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath()) return err }) diff --git a/docs/content/administration/environment-variables.en-us.md b/docs/content/administration/environment-variables.en-us.md index f910cf060e..2c6fcbe681 100644 --- a/docs/content/administration/environment-variables.en-us.md +++ b/docs/content/administration/environment-variables.en-us.md @@ -27,14 +27,15 @@ GITEA_CUSTOM=/home/gitea/custom ./gitea web ## From Go language -As Gitea is written in Go, it uses some Go variables, such as: +As Gitea is written in Go, it uses some variables that influence the behaviour of Go's runtime, such as: -- `GOOS` -- `GOARCH` -- [`GOPATH`](https://golang.org/cmd/go/#hdr-GOPATH_environment_variable) +- `GOMEMLIMIT` +- `GOGC` +- `GOMAXPROCS` +- `GODEBUG` For documentation about each of the variables available, refer to the -[official Go documentation](https://golang.org/cmd/go/#hdr-Environment_variables). +[official Go documentation on runtime environment variables](https://pkg.go.dev/runtime#hdr-Environment_Variables). ## Gitea files diff --git a/docs/content/usage/packages/rpm.en-us.md b/docs/content/usage/packages/rpm.en-us.md index 586e48d47f..1f93376b7b 100644 --- a/docs/content/usage/packages/rpm.en-us.md +++ b/docs/content/usage/packages/rpm.en-us.md @@ -24,16 +24,26 @@ The following examples use `dnf`. ## Configuring the package registry -To register the RPM registry add the url to the list of known apt sources: +To register the RPM registry add the url to the list of known sources: ```shell dnf config-manager --add-repo https://gitea.example.com/api/packages/{owner}/rpm/{group}.repo ``` -| Placeholder | Description | -| ----------- |----------------------------------------------------| -| `owner` | The owner of the package. | -| `group` | Everything, e.g. `el7`, `rocky/el9` , `test/fc38`.| +| Placeholder | Description | +| ----------- | ----------- | +| `owner` | The owner of the package. | +| `group` | Optional: Everything, e.g. empty, `el7`, `rocky/el9`, `test/fc38`. | + +Example: + +```shell +# without a group +dnf config-manager --add-repo https://gitea.example.com/api/packages/testuser/rpm.repo + +# with the group 'centos/el7' +dnf config-manager --add-repo https://gitea.example.com/api/packages/testuser/rpm/centos/el7.repo +``` If the registry is private, provide credentials in the url. You can use a password or a [personal access token](development/api-usage.md#authentication): @@ -41,7 +51,7 @@ If the registry is private, provide credentials in the url. You can use a passwo dnf config-manager --add-repo https://{username}:{your_password_or_token}@gitea.example.com/api/packages/{owner}/rpm/{group}.repo ``` -You have to add the credentials to the urls in the `rpm.repo` file in `/etc/yum.repos.d` too. +You have to add the credentials to the urls in the created `.repo` file in `/etc/yum.repos.d` too. ## Publish a package @@ -54,11 +64,17 @@ PUT https://gitea.example.com/api/packages/{owner}/rpm/{group}/upload | Parameter | Description | | --------- | ----------- | | `owner` | The owner of the package. | -| `group` | Everything, e.g. `el7`, `rocky/el9` , `test/fc38`.| +| `group` | Optional: Everything, e.g. empty, `el7`, `rocky/el9`, `test/fc38`. | Example request using HTTP Basic authentication: ```shell +# without a group +curl --user your_username:your_password_or_token \ + --upload-file path/to/file.rpm \ + https://gitea.example.com/api/packages/testuser/rpm/upload + +# with the group 'centos/el7' curl --user your_username:your_password_or_token \ --upload-file path/to/file.rpm \ https://gitea.example.com/api/packages/testuser/rpm/centos/el7/upload @@ -83,17 +99,22 @@ To delete an RPM package perform a HTTP DELETE operation. This will delete the p DELETE https://gitea.example.com/api/packages/{owner}/rpm/{group}/package/{package_name}/{package_version}/{architecture} ``` -| Parameter | Description | -|-------------------|----------------------------| -| `owner` | The owner of the package. | -| `group` | The package group . | -| `package_name` | The package name. | -| `package_version` | The package version. | -| `architecture` | The package architecture. | +| Parameter | Description | +| ----------------- | ----------- | +| `owner` | The owner of the package. | +| `group` | Optional: The package group. | +| `package_name` | The package name. | +| `package_version` | The package version. | +| `architecture` | The package architecture. | Example request using HTTP Basic authentication: ```shell +# without a group +curl --user your_username:your_token_or_password -X DELETE \ + https://gitea.example.com/api/packages/testuser/rpm/package/test-package/1.0.0/x86_64 + +# with the group 'centos/el7' curl --user your_username:your_token_or_password -X DELETE \ https://gitea.example.com/api/packages/testuser/rpm/centos/el7/package/test-package/1.0.0/x86_64 ``` diff --git a/go.mod b/go.mod index e1b37c65f6..7bf0804871 100644 --- a/go.mod +++ b/go.mod @@ -306,6 +306,8 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142 replace github.com/nektos/act => gitea.com/gitea/act v0.2.51 +replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 + exclude github.com/gofrs/uuid v3.2.0+incompatible exclude github.com/gofrs/uuid v4.0.0+incompatible diff --git a/go.sum b/go.sum index ae6c91f902..52aeabfeac 100644 --- a/go.sum +++ b/go.sum @@ -501,8 +501,6 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= -github.com/gorilla/feeds v1.1.2 h1:pxzZ5PD3RJdhFH2FsJJ4x6PqMqbgFk1+Vez4XWBW8Iw= -github.com/gorilla/feeds v1.1.2/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -904,6 +902,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 h1:3seWKGVhGoc66Ht5QlhQsr4xT2caDnFegsnh2NqvENU= +github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= diff --git a/models/actions/run.go b/models/actions/run.go index 1a3701b0b0..fcac58d515 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -46,10 +46,13 @@ type ActionRun struct { TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow Status Status `xorm:"index"` Version int `xorm:"version default 0"` // Status could be updated concomitantly, so an optimistic lock is needed - Started timeutil.TimeStamp - Stopped timeutil.TimeStamp - Created timeutil.TimeStamp `xorm:"created"` - Updated timeutil.TimeStamp `xorm:"updated"` + // Started and Stopped is used for recording last run time, if rerun happened, they will be reset to 0 + Started timeutil.TimeStamp + Stopped timeutil.TimeStamp + // PreviousDuration is used for recording previous duration + PreviousDuration time.Duration + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` } func init() { @@ -118,7 +121,7 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error { } func (run *ActionRun) Duration() time.Duration { - return calculateDuration(run.Started, run.Stopped, run.Status) + return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration } func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) { diff --git a/models/git/commit_status.go b/models/git/commit_status.go index c126d17f20..1118b6cc8c 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -37,7 +37,7 @@ type CommitStatus struct { SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"` TargetURL string `xorm:"TEXT"` Description string `xorm:"TEXT"` - ContextHash string `xorm:"char(40) index"` + ContextHash string `xorm:"VARCHAR(64) index"` Context string `xorm:"TEXT"` Creator *user_model.User `xorm:"-"` CreatorID int64 diff --git a/models/issues/comment.go b/models/issues/comment.go index a1698d4824..8a3bae5b88 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -270,7 +270,7 @@ type Comment struct { UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` // Reference issue in commit message - CommitSHA string `xorm:"VARCHAR(40)"` + CommitSHA string `xorm:"VARCHAR(64)"` Attachments []*repo_model.Attachment `xorm:"-"` Reactions ReactionList `xorm:"-"` diff --git a/models/issues/pull.go b/models/issues/pull.go index 614ee9a87c..4ae6e38ae1 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -171,11 +171,11 @@ type PullRequest struct { HeadBranch string HeadCommitID string `xorm:"-"` BaseBranch string - MergeBase string `xorm:"VARCHAR(40)"` + MergeBase string `xorm:"VARCHAR(64)"` AllowMaintainerEdit bool `xorm:"NOT NULL DEFAULT false"` HasMerged bool `xorm:"INDEX"` - MergedCommitID string `xorm:"VARCHAR(40)"` + MergedCommitID string `xorm:"VARCHAR(64)"` MergerID int64 `xorm:"INDEX"` Merger *user_model.User `xorm:"-"` MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` diff --git a/models/issues/review.go b/models/issues/review.go index 4cbfa4f443..f2022ae0aa 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -116,7 +116,7 @@ type Review struct { Content string `xorm:"TEXT"` // Official is a review made by an assigned approver (counts towards approval) Official bool `xorm:"NOT NULL DEFAULT false"` - CommitID string `xorm:"VARCHAR(40)"` + CommitID string `xorm:"VARCHAR(64)"` Stale bool `xorm:"NOT NULL DEFAULT false"` Dismissed bool `xorm:"NOT NULL DEFAULT false"` diff --git a/models/migrations/fixtures/Test_RepositoryFormat/repository.yml b/models/migrations/fixtures/Test_RepositoryFormat/repository.yml new file mode 100644 index 0000000000..5a3675917c --- /dev/null +++ b/models/migrations/fixtures/Test_RepositoryFormat/repository.yml @@ -0,0 +1,11 @@ +# type Repository struct { +# ID int64 `xorm:"pk autoincr"` +# } +- + id: 1 +- + id: 2 +- + id: 3 +- + id: 10 diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 3b4ac24a2c..beb1f3bb96 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -554,6 +554,10 @@ var migrations = []Migration{ NewMigration("Add combined Index to issue_user.uid and issue_id", v1_22.AddCombinedIndexToIssueUser), // v284 -> v285 NewMigration("Add ignore stale approval column on branch table", v1_22.AddIgnoreStaleApprovalsColumnToProtectedBranchTable), + // v285 -> v286 + NewMigration("Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun), + // v286 -> v287 + NewMigration("Add support for SHA256 git repositories", v1_22.AdjustDBForSha256), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go new file mode 100644 index 0000000000..c0dacd40bc --- /dev/null +++ b/models/migrations/v1_22/v285.go @@ -0,0 +1,18 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "time" + + "xorm.io/xorm" +) + +func AddPreviousDurationToActionRun(x *xorm.Engine) error { + type ActionRun struct { + PreviousDuration time.Duration + } + + return x.Sync(&ActionRun{}) +} diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go new file mode 100644 index 0000000000..ef19f64221 --- /dev/null +++ b/models/migrations/v1_22/v286.go @@ -0,0 +1,104 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT +package v1_22 //nolint + +import ( + "errors" + "fmt" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + + "xorm.io/xorm" +) + +func expandHashReferencesToSha256(x *xorm.Engine) error { + alteredTables := [][2]string{ + {"commit_status", "context_hash"}, + {"comment", "commit_sha"}, + {"pull_request", "merge_base"}, + {"pull_request", "merged_commit_id"}, + {"review", "commit_id"}, + {"review_state", "commit_sha"}, + {"repo_archiver", "commit_id"}, + {"release", "sha1"}, + {"repo_indexer_status", "commit_sha"}, + } + + db := x.NewSession() + defer db.Close() + + if err := db.Begin(); err != nil { + return err + } + + if !setting.Database.Type.IsSQLite3() { + if setting.Database.Type.IsMSSQL() { + // drop indexes that need to be re-created afterwards + droppedIndexes := []string{ + "DROP INDEX commit_status.IDX_commit_status_context_hash", + "DROP INDEX review_state.UQE_review_state_pull_commit_user", + "DROP INDEX repo_archiver.UQE_repo_archiver_s", + } + for _, s := range droppedIndexes { + _, err := db.Exec(s) + if err != nil { + return errors.New(s + " " + err.Error()) + } + } + } + + for _, alts := range alteredTables { + var err error + if setting.Database.Type.IsMySQL() { + _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1])) + } else if setting.Database.Type.IsMSSQL() { + _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` VARCHAR(64)", alts[0], alts[1])) + } else { + _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1])) + } + if err != nil { + return fmt.Errorf("alter column '%s' of table '%s' failed: %w", alts[1], alts[0], err) + } + } + + if setting.Database.Type.IsMSSQL() { + recreateIndexes := []string{ + "CREATE INDEX IDX_commit_status_context_hash ON commit_status(context_hash)", + "CREATE UNIQUE INDEX UQE_review_state_pull_commit_user ON review_state(user_id, pull_id, commit_sha)", + "CREATE UNIQUE INDEX UQE_repo_archiver_s ON repo_archiver(repo_id, type, commit_id)", + } + for _, s := range recreateIndexes { + _, err := db.Exec(s) + if err != nil { + return errors.New(s + " " + err.Error()) + } + } + } + } + log.Debug("Updated database tables to hold SHA256 git hash references") + + return db.Commit() +} + +func addObjectFormatNameToRepository(x *xorm.Engine) error { + type Repository struct { + ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"` + } + + if err := x.Sync(new(Repository)); err != nil { + return err + } + + // Here to catch weird edge-cases where column constraints above are + // not applied by the DB backend + _, err := x.Exec("UPDATE repository set object_format_name = 'sha1' WHERE object_format_name = '' or object_format_name IS NULL") + return err +} + +func AdjustDBForSha256(x *xorm.Engine) error { + if err := expandHashReferencesToSha256(x); err != nil { + return err + } + return addObjectFormatNameToRepository(x) +} diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go new file mode 100644 index 0000000000..e36a18a116 --- /dev/null +++ b/models/migrations/v1_22/v286_test.go @@ -0,0 +1,62 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "testing" + + "code.gitea.io/gitea/models/migrations/base" + + "github.com/stretchr/testify/assert" + "xorm.io/xorm" +) + +func PrepareOldRepository(t *testing.T) (*xorm.Engine, func()) { + type Repository struct { // old struct + ID int64 `xorm:"pk autoincr"` + } + + // Prepare and load the testing database + return base.PrepareTestEnv(t, 0, new(Repository)) +} + +func Test_RepositoryFormat(t *testing.T) { + x, deferable := PrepareOldRepository(t) + defer deferable() + + type Repository struct { + ID int64 `xorm:"pk autoincr"` + ObjectFormatName string `xorg:"not null default('sha1')"` + } + + repo := new(Repository) + + // check we have some records to migrate + count, err := x.Count(new(Repository)) + assert.NoError(t, err) + assert.EqualValues(t, 4, count) + + assert.NoError(t, AdjustDBForSha256(x)) + + repo.ID = 20 + repo.ObjectFormatName = "sha256" + _, err = x.Insert(repo) + assert.NoError(t, err) + + count, err = x.Count(new(Repository)) + assert.NoError(t, err) + assert.EqualValues(t, 5, count) + + repo = new(Repository) + ok, err := x.ID(2).Get(repo) + assert.NoError(t, err) + assert.EqualValues(t, true, ok) + assert.EqualValues(t, "sha1", repo.ObjectFormatName) + + repo = new(Repository) + ok, err = x.ID(20).Get(repo) + assert.NoError(t, err) + assert.EqualValues(t, true, ok) + assert.EqualValues(t, "sha256", repo.ObjectFormatName) +} diff --git a/models/packages/package.go b/models/packages/package.go index 380a076f9d..65a2574150 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -191,18 +191,18 @@ type Package struct { func TryInsertPackage(ctx context.Context, p *Package) (*Package, error) { e := db.GetEngine(ctx) - key := &Package{ - OwnerID: p.OwnerID, - Type: p.Type, - LowerName: p.LowerName, - } + existing := &Package{} - has, err := e.Get(key) + has, err := e.Where(builder.Eq{ + "owner_id": p.OwnerID, + "type": p.Type, + "lower_name": p.LowerName, + }).Get(existing) if err != nil { return nil, err } if has { - return key, ErrDuplicatePackage + return existing, ErrDuplicatePackage } if _, err = e.Insert(p); err != nil { return nil, err diff --git a/models/packages/package_blob.go b/models/packages/package_blob.go index d1f470d520..d9c30b6533 100644 --- a/models/packages/package_blob.go +++ b/models/packages/package_blob.go @@ -41,12 +41,20 @@ type PackageBlob struct { func GetOrInsertBlob(ctx context.Context, pb *PackageBlob) (*PackageBlob, bool, error) { e := db.GetEngine(ctx) - has, err := e.Get(pb) + existing := &PackageBlob{} + + has, err := e.Where(builder.Eq{ + "size": pb.Size, + "hash_md5": pb.HashMD5, + "hash_sha1": pb.HashSHA1, + "hash_sha256": pb.HashSHA256, + "hash_sha512": pb.HashSHA512, + }).Get(existing) if err != nil { return nil, false, err } if has { - return pb, true, nil + return existing, true, nil } if _, err = e.Insert(pb); err != nil { return nil, false, err diff --git a/models/packages/package_file.go b/models/packages/package_file.go index 1c2c9ac072..1bb6b57a34 100644 --- a/models/packages/package_file.go +++ b/models/packages/package_file.go @@ -46,18 +46,18 @@ type PackageFile struct { func TryInsertFile(ctx context.Context, pf *PackageFile) (*PackageFile, error) { e := db.GetEngine(ctx) - key := &PackageFile{ - VersionID: pf.VersionID, - LowerName: pf.LowerName, - CompositeKey: pf.CompositeKey, - } + existing := &PackageFile{} - has, err := e.Get(key) + has, err := e.Where(builder.Eq{ + "version_id": pf.VersionID, + "lower_name": pf.LowerName, + "composite_key": pf.CompositeKey, + }).Get(existing) if err != nil { return nil, err } if has { - return pf, ErrDuplicatePackageFile + return existing, ErrDuplicatePackageFile } if _, err = e.Insert(pf); err != nil { return nil, err @@ -93,13 +93,13 @@ func GetFileForVersionByName(ctx context.Context, versionID int64, name, key str return nil, ErrPackageFileNotExist } - pf := &PackageFile{ - VersionID: versionID, - LowerName: strings.ToLower(name), - CompositeKey: key, - } + pf := &PackageFile{} - has, err := db.GetEngine(ctx).Get(pf) + has, err := db.GetEngine(ctx).Where(builder.Eq{ + "version_id": versionID, + "lower_name": strings.ToLower(name), + "composite_key": key, + }).Get(pf) if err != nil { return nil, err } diff --git a/models/packages/package_version.go b/models/packages/package_version.go index 9999fc4dab..8fc475691b 100644 --- a/models/packages/package_version.go +++ b/models/packages/package_version.go @@ -39,17 +39,17 @@ type PackageVersion struct { func GetOrInsertVersion(ctx context.Context, pv *PackageVersion) (*PackageVersion, error) { e := db.GetEngine(ctx) - key := &PackageVersion{ - PackageID: pv.PackageID, - LowerVersion: pv.LowerVersion, - } + existing := &PackageVersion{} - has, err := e.Get(key) + has, err := e.Where(builder.Eq{ + "package_id": pv.PackageID, + "lower_version": pv.LowerVersion, + }).Get(existing) if err != nil { return nil, err } if has { - return key, ErrDuplicatePackageVersion + return existing, ErrDuplicatePackageVersion } if _, err = e.Insert(pv); err != nil { return nil, err diff --git a/models/packages/rpm/search.go b/models/packages/rpm/search.go new file mode 100644 index 0000000000..e697421b49 --- /dev/null +++ b/models/packages/rpm/search.go @@ -0,0 +1,23 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package rpm + +import ( + "context" + + packages_model "code.gitea.io/gitea/models/packages" + rpm_module "code.gitea.io/gitea/modules/packages/rpm" +) + +// GetGroups gets all available groups +func GetGroups(ctx context.Context, ownerID int64) ([]string, error) { + return packages_model.GetDistinctPropertyValues( + ctx, + packages_model.TypeRpm, + ownerID, + packages_model.PropertyTypeFile, + rpm_module.PropertyGroup, + nil, + ) +} diff --git a/models/pull/review_state.go b/models/pull/review_state.go index 1a2b1e165f..e46a22a49d 100644 --- a/models/pull/review_state.go +++ b/models/pull/review_state.go @@ -39,7 +39,7 @@ type ReviewState struct { ID int64 `xorm:"pk autoincr"` UserID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user)"` PullID int64 `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"` // Which PR was the review on? - CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"` // Which commit was the head commit for the review? + CommitSHA string `xorm:"NOT NULL VARCHAR(64) UNIQUE(pull_commit_user)"` // Which commit was the head commit for the review? UpdatedFiles map[string]ViewedState `xorm:"NOT NULL LONGTEXT JSON"` // Stores for each of the changed files of a PR whether they have been viewed, changed since last viewed, or not viewed UpdatedUnix timeutil.TimeStamp `xorm:"updated"` // Is an accurate indicator of the order of commits as we do not expect it to be possible to make reviews on previous commits } diff --git a/models/repo/archiver.go b/models/repo/archiver.go index d9520c670c..14ffa1d89b 100644 --- a/models/repo/archiver.go +++ b/models/repo/archiver.go @@ -33,7 +33,7 @@ type RepoArchiver struct { //revive:disable-line:exported RepoID int64 `xorm:"index unique(s)"` Type git.ArchiveType `xorm:"unique(s)"` Status ArchiverStatus - CommitID string `xorm:"VARCHAR(40) unique(s)"` + CommitID string `xorm:"VARCHAR(64) unique(s)"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` } diff --git a/models/repo/release.go b/models/repo/release.go index 72a73f8e80..1f37f11b2e 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -75,7 +75,7 @@ type Release struct { Target string TargetBehind string `xorm:"-"` // to handle non-existing or empty target Title string - Sha1 string `xorm:"VARCHAR(40)"` + Sha1 string `xorm:"VARCHAR(64)"` NumCommits int64 NumCommitsBehind int64 `xorm:"-"` Note string `xorm:"TEXT"` diff --git a/models/repo/repo.go b/models/repo/repo.go index 3695e1f78b..4401041cdd 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -180,7 +180,7 @@ type Repository struct { IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` Topics []string `xorm:"TEXT JSON"` - ObjectFormatName string `xorm:"-"` + ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"` TrustModel TrustModelType @@ -276,10 +276,6 @@ func (repo *Repository) AfterLoad() { repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects repo.NumOpenActionRuns = repo.NumActionRuns - repo.NumClosedActionRuns - - // this is a temporary behaviour to support old repos, next step is to store the object format in the database - // and read from database so this line could be removed. To not depend on git module, we use a constant variable here - repo.ObjectFormatName = "sha1" } // LoadAttributes loads attributes of the repository. diff --git a/models/repo/repo_indexer.go b/models/repo/repo_indexer.go index bad1248b40..6e19d8f937 100644 --- a/models/repo/repo_indexer.go +++ b/models/repo/repo_indexer.go @@ -27,7 +27,7 @@ const ( type RepoIndexerStatus struct { //revive:disable-line:exported ID int64 `xorm:"pk autoincr"` RepoID int64 `xorm:"INDEX(s)"` - CommitSha string `xorm:"VARCHAR(40)"` + CommitSha string `xorm:"VARCHAR(64)"` IndexerType RepoIndexerType `xorm:"INDEX(s) NOT NULL DEFAULT 0"` } diff --git a/modules/context/package.go b/modules/context/package.go index 87e817c1cd..c452c657e7 100644 --- a/modules/context/package.go +++ b/modules/context/package.go @@ -93,7 +93,7 @@ func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any)) } func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) { - if setting.Service.RequireSignInView && doer == nil { + if setting.Service.RequireSignInView && (doer == nil || doer.IsGhost()) { return perm.AccessModeNone, nil } diff --git a/modules/git/blame_sha256_test.go b/modules/git/blame_sha256_test.go new file mode 100644 index 0000000000..01de0454a3 --- /dev/null +++ b/modules/git/blame_sha256_test.go @@ -0,0 +1,144 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadingBlameOutputSha256(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + t.Run("Without .git-blame-ignore-revs", func(t *testing.T) { + repo, err := OpenRepository(ctx, "./tests/repos/repo5_pulls_sha256") + assert.NoError(t, err) + defer repo.Close() + + commit, err := repo.GetCommit("0b69b7bb649b5d46e14cabb6468685e5dd721290acc7ffe604d37cde57927345") + assert.NoError(t, err) + + parts := []*BlamePart{ + { + Sha: "1e35a51dc00fd7de730344c07061acfe80e8117e075ac979b6a29a3a045190ca", + Lines: []string{ + "# test_repo", + "Test repository for testing migration from github to gitea", + }, + }, + { + Sha: "0b69b7bb649b5d46e14cabb6468685e5dd721290acc7ffe604d37cde57927345", + Lines: []string{"", "Do not make any changes to this repo it is used for unit testing"}, + PreviousSha: "1e35a51dc00fd7de730344c07061acfe80e8117e075ac979b6a29a3a045190ca", + PreviousPath: "README.md", + }, + } + + for _, bypass := range []bool{false, true} { + blameReader, err := CreateBlameReader(ctx, Sha256ObjectFormat, "./tests/repos/repo5_pulls_sha256", commit, "README.md", bypass) + assert.NoError(t, err) + assert.NotNil(t, blameReader) + defer blameReader.Close() + + assert.False(t, blameReader.UsesIgnoreRevs()) + + for _, part := range parts { + actualPart, err := blameReader.NextPart() + assert.NoError(t, err) + assert.Equal(t, part, actualPart) + } + + // make sure all parts have been read + actualPart, err := blameReader.NextPart() + assert.Nil(t, actualPart) + assert.NoError(t, err) + } + }) + + t.Run("With .git-blame-ignore-revs", func(t *testing.T) { + repo, err := OpenRepository(ctx, "./tests/repos/repo6_blame_sha256") + assert.NoError(t, err) + defer repo.Close() + + full := []*BlamePart{ + { + Sha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc", + Lines: []string{"line", "line"}, + }, + { + Sha: "9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe", + Lines: []string{"changed line"}, + PreviousSha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc", + PreviousPath: "blame.txt", + }, + { + Sha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc", + Lines: []string{"line", "line", ""}, + }, + } + + cases := []struct { + CommitID string + UsesIgnoreRevs bool + Bypass bool + Parts []*BlamePart + }{ + { + CommitID: "e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3", + UsesIgnoreRevs: true, + Bypass: false, + Parts: []*BlamePart{ + { + Sha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc", + Lines: []string{"line", "line", "changed line", "line", "line", ""}, + }, + }, + }, + { + CommitID: "e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3", + UsesIgnoreRevs: false, + Bypass: true, + Parts: full, + }, + { + CommitID: "9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe", + UsesIgnoreRevs: false, + Bypass: false, + Parts: full, + }, + { + CommitID: "9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe", + UsesIgnoreRevs: false, + Bypass: false, + Parts: full, + }, + } + + for _, c := range cases { + commit, err := repo.GetCommit(c.CommitID) + assert.NoError(t, err) + + blameReader, err := CreateBlameReader(ctx, repo.objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass) + assert.NoError(t, err) + assert.NotNil(t, blameReader) + defer blameReader.Close() + + assert.Equal(t, c.UsesIgnoreRevs, blameReader.UsesIgnoreRevs()) + + for _, part := range c.Parts { + actualPart, err := blameReader.NextPart() + assert.NoError(t, err) + assert.Equal(t, part, actualPart) + } + + // make sure all parts have been read + actualPart, err := blameReader.NextPart() + assert.Nil(t, actualPart) + assert.NoError(t, err) + } + }) +} diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go index d74bcffed8..4809d6c7ed 100644 --- a/modules/git/commit_reader.go +++ b/modules/git/commit_reader.go @@ -85,6 +85,8 @@ readLoop: commit.Committer.Decode(data) _, _ = payloadSB.Write(line) case "gpgsig": + fallthrough + case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present. _, _ = signatureSB.Write(data) _ = signatureSB.WriteByte('\n') pgpsig = true diff --git a/modules/git/commit_sha256_test.go b/modules/git/commit_sha256_test.go new file mode 100644 index 0000000000..82112cb409 --- /dev/null +++ b/modules/git/commit_sha256_test.go @@ -0,0 +1,195 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build !gogit + +package git + +import ( + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCommitsCountSha256(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256") + + commitsCount, err := CommitsCount(DefaultContext, + CommitsCountOptions{ + RepoPath: bareRepo1Path, + Revision: []string{"f004f41359117d319dedd0eaab8c5259ee2263da839dcba33637997458627fdc"}, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(3), commitsCount) +} + +func TestCommitsCountWithoutBaseSha256(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256") + + commitsCount, err := CommitsCount(DefaultContext, + CommitsCountOptions{ + RepoPath: bareRepo1Path, + Not: "main", + Revision: []string{"branch1"}, + }) + + assert.NoError(t, err) + assert.Equal(t, int64(2), commitsCount) +} + +func TestGetFullCommitIDSha256(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256") + + id, err := GetFullCommitID(DefaultContext, bareRepo1Path, "f004f4") + assert.NoError(t, err) + assert.Equal(t, "f004f41359117d319dedd0eaab8c5259ee2263da839dcba33637997458627fdc", id) +} + +func TestGetFullCommitIDErrorSha256(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256") + + id, err := GetFullCommitID(DefaultContext, bareRepo1Path, "unknown") + assert.Empty(t, id) + if assert.Error(t, err) { + assert.EqualError(t, err, "object does not exist [id: unknown, rel_path: ]") + } +} + +func TestCommitFromReaderSha256(t *testing.T) { + commitString := `9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 commit 1114 +tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e +parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8 +author Adam Majer 1698676906 +0100 +committer Adam Majer 1698676906 +0100 +gpgsig-sha256 -----BEGIN PGP SIGNATURE----- +` + " " + ` + iQIrBAABCgAtFiEES+fB08xlgTrzSdQvhkUIsBsmec8FAmU/wKoPHGFtYWplckBz + dXNlLmRlAAoJEIZFCLAbJnnP4s4PQIJATa++WPzR6/H4etT7bsOGoMyguEJYyWOd + aTybplzT7QAL7h2to0QszGabtzMJPIA39xSFZNYNN30voK5YyyYibXluPKgjemfK + WNXwF+gkwgZI38gSvKf+vlqI+EYyIFe19wOhiju0m8SIlB5NEPiWHa17q2mqmqqx + 1FWa2JdqLPYjAtSLFXeSZegrY5V1FxdemyMUONkg8YO9OSIMZiE0GsnnOXQ3xcT4 + JTCnmlUxIKw689UiEY80JopUIq+Wl7+qq9507IYYSUCyB6JazL42AKMzVCbD+qBP + oOzh/hafYgk9H9qCQXaLbmvs17zXRpicig1bAzqgAy1FDelvpERyRTydEajSLIG6 + U1cRCkgXCZ0NfsYNPPmBa8b3+rnstypXYTbyMwTln7FfUAaGo6o9JYiPMkzxlmsy + zfp/tcaY8+LlBL9aOJjtv+a0p+HrpCGd6CCa4ARfphTLq8QRSSh8uzlB9N+6HnRI + VAEUo6ecdDxSpyt2naeg9pKus/BRi7P6g4B1hkk/zZstUX/QP4IQuAJbXjkvsC+X + HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR + 8NCJUL2Nwj0ywKIavC99Ipkb8AsFwpVT6U6effs6 + =xybZ + -----END PGP SIGNATURE----- + +signed commit` + + sha := &Sha256Hash{ + 0x94, 0x33, 0xb2, 0xa6, 0x2b, 0x96, 0x4c, 0x17, 0xa4, 0x48, 0x5a, 0xe1, 0x80, 0xf4, 0x5f, 0x59, + 0x5d, 0x3e, 0x69, 0xd3, 0x1b, 0x78, 0x60, 0x87, 0x77, 0x5e, 0x28, 0xc6, 0xb6, 0x39, 0x9d, 0xf0, + } + gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare_sha256")) + assert.NoError(t, err) + assert.NotNil(t, gitRepo) + defer gitRepo.Close() + + commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString)) + assert.NoError(t, err) + if !assert.NotNil(t, commitFromReader) { + return + } + assert.EqualValues(t, sha, commitFromReader.ID) + assert.EqualValues(t, `-----BEGIN PGP SIGNATURE----- + +iQIrBAABCgAtFiEES+fB08xlgTrzSdQvhkUIsBsmec8FAmU/wKoPHGFtYWplckBz +dXNlLmRlAAoJEIZFCLAbJnnP4s4PQIJATa++WPzR6/H4etT7bsOGoMyguEJYyWOd +aTybplzT7QAL7h2to0QszGabtzMJPIA39xSFZNYNN30voK5YyyYibXluPKgjemfK +WNXwF+gkwgZI38gSvKf+vlqI+EYyIFe19wOhiju0m8SIlB5NEPiWHa17q2mqmqqx +1FWa2JdqLPYjAtSLFXeSZegrY5V1FxdemyMUONkg8YO9OSIMZiE0GsnnOXQ3xcT4 +JTCnmlUxIKw689UiEY80JopUIq+Wl7+qq9507IYYSUCyB6JazL42AKMzVCbD+qBP +oOzh/hafYgk9H9qCQXaLbmvs17zXRpicig1bAzqgAy1FDelvpERyRTydEajSLIG6 +U1cRCkgXCZ0NfsYNPPmBa8b3+rnstypXYTbyMwTln7FfUAaGo6o9JYiPMkzxlmsy +zfp/tcaY8+LlBL9aOJjtv+a0p+HrpCGd6CCa4ARfphTLq8QRSSh8uzlB9N+6HnRI +VAEUo6ecdDxSpyt2naeg9pKus/BRi7P6g4B1hkk/zZstUX/QP4IQuAJbXjkvsC+X +HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR +8NCJUL2Nwj0ywKIavC99Ipkb8AsFwpVT6U6effs6 +=xybZ +-----END PGP SIGNATURE----- +`, commitFromReader.Signature.Signature) + assert.EqualValues(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e +parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8 +author Adam Majer 1698676906 +0100 +committer Adam Majer 1698676906 +0100 + +signed commit`, commitFromReader.Signature.Payload) + assert.EqualValues(t, "Adam Majer ", commitFromReader.Author.String()) + + commitFromReader2, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString+"\n\n")) + assert.NoError(t, err) + commitFromReader.CommitMessage += "\n\n" + commitFromReader.Signature.Payload += "\n\n" + assert.EqualValues(t, commitFromReader, commitFromReader2) +} + +func TestHasPreviousCommitSha256(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare_sha256") + + repo, err := openRepositoryWithDefaultContext(bareRepo1Path) + assert.NoError(t, err) + defer repo.Close() + + commit, err := repo.GetCommit("f004f41359117d319dedd0eaab8c5259ee2263da839dcba33637997458627fdc") + assert.NoError(t, err) + + parentSHA := MustIDFromString("b0ec7af4547047f12d5093e37ef8f1b3b5415ed8ee17894d43a34d7d34212e9c") + notParentSHA := MustIDFromString("42e334efd04cd36eea6da0599913333c26116e1a537ca76e5b6e4af4dda00236") + assert.Equal(t, repo.objectFormat, parentSHA.Type()) + assert.Equal(t, repo.objectFormat.Name(), "sha256") + + haz, err := commit.HasPreviousCommit(parentSHA) + assert.NoError(t, err) + assert.True(t, haz) + + hazNot, err := commit.HasPreviousCommit(notParentSHA) + assert.NoError(t, err) + assert.False(t, hazNot) + + selfNot, err := commit.HasPreviousCommit(commit.ID) + assert.NoError(t, err) + assert.False(t, selfNot) +} + +func TestGetCommitFileStatusMergesSha256(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo6_merge_sha256") + + commitFileStatus, err := GetCommitFileStatus(DefaultContext, bareRepo1Path, "d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1") + assert.NoError(t, err) + + expected := CommitFileStatus{ + []string{ + "add_file.txt", + }, + []string{}, + []string{ + "to_modify.txt", + }, + } + + assert.Equal(t, expected.Added, commitFileStatus.Added) + assert.Equal(t, expected.Removed, commitFileStatus.Removed) + assert.Equal(t, expected.Modified, commitFileStatus.Modified) + + expected = CommitFileStatus{ + []string{}, + []string{ + "to_remove.txt", + }, + []string{}, + } + + commitFileStatus, err = GetCommitFileStatus(DefaultContext, bareRepo1Path, "da1ded40dc8e5b7c564171f4bf2fc8370487decfb1cb6a99ef28f3ed73d09172") + assert.NoError(t, err) + + assert.Equal(t, expected.Added, commitFileStatus.Added) + assert.Equal(t, expected.Removed, commitFileStatus.Removed) + assert.Equal(t, expected.Modified, commitFileStatus.Modified) +} diff --git a/modules/git/git.go b/modules/git/git.go index 24eff05afc..89c23ff230 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -185,7 +185,13 @@ func InitFull(ctx context.Context) (err error) { globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") } SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil - SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil + SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil && !isGogit + if SupportHashSha256 { + SupportedObjectFormats = append(SupportedObjectFormats, Sha256ObjectFormat) + } else { + log.Warn("sha256 hash support is disabled - requires Git >= 2.42. Gogit is currently unsupported") + } + if setting.LFS.StartServer { if CheckGitVersionAtLeast("2.1.2") != nil { return errors.New("LFS server support requires Git >= 2.1.2") diff --git a/modules/git/object_format.go b/modules/git/object_format.go index 27771e7459..a056b20e8a 100644 --- a/modules/git/object_format.go +++ b/modules/git/object_format.go @@ -5,6 +5,7 @@ package git import ( "crypto/sha1" + "crypto/sha256" "regexp" "strconv" ) @@ -12,6 +13,9 @@ import ( // sha1Pattern can be used to determine if a string is an valid sha var sha1Pattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`) +// sha256Pattern can be used to determine if a string is an valid sha +var sha256Pattern = regexp.MustCompile(`^[0-9a-f]{4,64}$`) + type ObjectFormat interface { // Name returns the name of the object format Name() string @@ -29,11 +33,12 @@ type ObjectFormat interface { ComputeHash(t ObjectType, content []byte) ObjectID } +/* SHA1 Type */ type Sha1ObjectFormatImpl struct{} var ( - emptyObjectID = &Sha1Hash{} - emptyTree = &Sha1Hash{ + emptySha1ObjectID = &Sha1Hash{} + emptySha1Tree = &Sha1Hash{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04, } @@ -41,11 +46,11 @@ var ( func (Sha1ObjectFormatImpl) Name() string { return "sha1" } func (Sha1ObjectFormatImpl) EmptyObjectID() ObjectID { - return emptyObjectID + return emptySha1ObjectID } func (Sha1ObjectFormatImpl) EmptyTree() ObjectID { - return emptyTree + return emptySha1Tree } func (Sha1ObjectFormatImpl) FullLength() int { return 40 } func (Sha1ObjectFormatImpl) IsValid(input string) bool { @@ -72,11 +77,59 @@ func (h Sha1ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) ObjectID return &sha1 } -var Sha1ObjectFormat ObjectFormat = Sha1ObjectFormatImpl{} +/* SHA256 Type */ +type Sha256ObjectFormatImpl struct{} + +var ( + emptySha256ObjectID = &Sha256Hash{} + emptySha256Tree = &Sha256Hash{ + 0x6e, 0xf1, 0x9b, 0x41, 0x22, 0x5c, 0x53, 0x69, 0xf1, 0xc1, + 0x04, 0xd4, 0x5d, 0x8d, 0x85, 0xef, 0xa9, 0xb0, 0x57, 0xb5, + 0x3b, 0x14, 0xb4, 0xb9, 0xb9, 0x39, 0xdd, 0x74, 0xde, 0xcc, + 0x53, 0x21, + } +) + +func (Sha256ObjectFormatImpl) Name() string { return "sha256" } +func (Sha256ObjectFormatImpl) EmptyObjectID() ObjectID { + return emptySha256ObjectID +} + +func (Sha256ObjectFormatImpl) EmptyTree() ObjectID { + return emptySha256Tree +} +func (Sha256ObjectFormatImpl) FullLength() int { return 64 } +func (Sha256ObjectFormatImpl) IsValid(input string) bool { + return sha256Pattern.MatchString(input) +} + +func (Sha256ObjectFormatImpl) MustID(b []byte) ObjectID { + var id Sha256Hash + copy(id[0:32], b) + return &id +} + +// ComputeHash compute the hash for a given ObjectType and content +func (h Sha256ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) ObjectID { + hasher := sha256.New() + _, _ = hasher.Write(t.Bytes()) + _, _ = hasher.Write([]byte(" ")) + _, _ = hasher.Write([]byte(strconv.FormatInt(int64(len(content)), 10))) + _, _ = hasher.Write([]byte{0}) + + // HashSum generates a SHA256 for the provided hash + var sha256 Sha1Hash + copy(sha256[:], hasher.Sum(nil)) + return &sha256 +} + +var ( + Sha1ObjectFormat ObjectFormat = Sha1ObjectFormatImpl{} + Sha256ObjectFormat ObjectFormat = Sha256ObjectFormatImpl{} +) var SupportedObjectFormats = []ObjectFormat{ Sha1ObjectFormat, - // TODO: add sha256 } func ObjectFormatFromName(name string) ObjectFormat { diff --git a/modules/git/object_id.go b/modules/git/object_id.go index 01c23ed3da..4f8c39ee1d 100644 --- a/modules/git/object_id.go +++ b/modules/git/object_id.go @@ -16,6 +16,7 @@ type ObjectID interface { Type() ObjectFormat } +/* SHA1 */ type Sha1Hash [20]byte func (h *Sha1Hash) String() string { @@ -39,6 +40,21 @@ func MustIDFromString(hexHash string) ObjectID { return id } +/* SHA256 */ +type Sha256Hash [32]byte + +func (h *Sha256Hash) String() string { + return hex.EncodeToString(h[:]) +} + +func (h *Sha256Hash) IsZero() bool { + empty := Sha256Hash{} + return bytes.Equal(empty[:], h[:]) +} +func (h *Sha256Hash) RawValue() []byte { return h[:] } +func (*Sha256Hash) Type() ObjectFormat { return Sha256ObjectFormat } + +/* utility */ func NewIDFromString(hexHash string) (ObjectID, error) { var theObjectFormat ObjectFormat for _, objectFormat := range SupportedObjectFormats { diff --git a/modules/git/object_id_gogit.go b/modules/git/object_id_gogit.go index 0cebb0d50b..db4c4ae0bd 100644 --- a/modules/git/object_id_gogit.go +++ b/modules/git/object_id_gogit.go @@ -13,6 +13,8 @@ func ParseGogitHash(h plumbing.Hash) ObjectID { switch hash.Size { case 20: return Sha1ObjectFormat.MustID(h[:]) + case 32: + return Sha256ObjectFormat.MustID(h[:]) } return nil diff --git a/modules/git/repo.go b/modules/git/repo.go index 7ccce0ba20..c3de2eb0ad 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -97,15 +97,12 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma } cmd := NewCommand(ctx, "init") - if SupportHashSha256 { - if objectFormatName == "" { - objectFormatName = Sha1ObjectFormat.Name() - } - if !IsValidObjectFormat(objectFormatName) { - return fmt.Errorf("invalid object format: %s", objectFormatName) - } - cmd.AddOptionValues("--object-format", objectFormatName) + + if !IsValidObjectFormat(objectFormatName) { + return fmt.Errorf("invalid object format: %s", objectFormatName) } + cmd.AddOptionValues("--object-format", objectFormatName) + if bare { cmd.AddArguments("--bare") } diff --git a/modules/git/repo_archive.go b/modules/git/repo_archive.go index 2b45a50f19..1bf1aa41b9 100644 --- a/modules/git/repo_archive.go +++ b/modules/git/repo_archive.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "io" + "os" "path/filepath" "strings" ) @@ -62,11 +63,15 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t cmd.AddOptionFormat("--format=%s", format.String()) cmd.AddDynamicArguments(commitID) + // Avoid LFS hooks getting installed because of /etc/gitconfig, which can break pull requests. + env := append(os.Environ(), "GIT_CONFIG_NOSYSTEM=1") + var stderr strings.Builder err := cmd.Run(&RunOpts{ Dir: repo.Path, Stdout: target, Stderr: &stderr, + Env: env, }) if err != nil { return ConcatenateError(err, stderr.String()) diff --git a/modules/git/repo_base.go b/modules/git/repo_base.go index 2c6df8b9c4..a9d91d2deb 100644 --- a/modules/git/repo_base.go +++ b/modules/git/repo_base.go @@ -8,6 +8,8 @@ import ( "io" ) +var isGogit bool + // contextKey is a value for use with context.WithValue. type contextKey struct { name string diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go index d0b8e79368..90123ee84b 100644 --- a/modules/git/repo_base_gogit.go +++ b/modules/git/repo_base_gogit.go @@ -21,6 +21,10 @@ import ( "github.com/go-git/go-git/v5/storage/filesystem" ) +func init() { + isGogit = true +} + // Repository represents a Git repository. type Repository struct { Path string diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go index a783366cc1..d5a350a926 100644 --- a/modules/git/repo_base_nogogit.go +++ b/modules/git/repo_base_nogogit.go @@ -15,6 +15,10 @@ import ( "code.gitea.io/gitea/modules/log" ) +func init() { + isGogit = false +} + // Repository represents a Git repository. type Repository struct { Path string diff --git a/modules/git/tests/repos/repo1_bare_sha256/HEAD b/modules/git/tests/repos/repo1_bare_sha256/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/modules/git/tests/repos/repo1_bare_sha256/config b/modules/git/tests/repos/repo1_bare_sha256/config new file mode 100644 index 0000000000..2388a50b2f --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 1 + filemode = true + bare = true +[extensions] + objectformat = sha256 diff --git a/modules/git/tests/repos/repo1_bare_sha256/description b/modules/git/tests/repos/repo1_bare_sha256/description new file mode 100644 index 0000000000..498b267a8c --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/modules/git/tests/repos/repo1_bare_sha256/info/exclude b/modules/git/tests/repos/repo1_bare_sha256/info/exclude new file mode 100644 index 0000000000..a5196d1be8 --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/modules/git/tests/repos/repo1_bare_sha256/info/refs b/modules/git/tests/repos/repo1_bare_sha256/info/refs new file mode 100644 index 0000000000..b4de954582 --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/info/refs @@ -0,0 +1,7 @@ +42e334efd04cd36eea6da0599913333c26116e1a537ca76e5b6e4af4dda00236 refs/heads/branch1 +5bc2249e32e0ba40a08879fba2bd4e97a13cb345831549f4bc5649525da8f6cc refs/heads/branch2 +9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 refs/heads/main +29a82d4fc02e19190fb489cc90d5730ed91970b49f4e39acda2798b3dd4f814e refs/tags/signed-tag +9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 refs/tags/signed-tag^{} +171822a62559f3aa28a00aa3785dbe915d6a8eb02712682740db44fc8bd2187a refs/tags/test +6aae864a3d1d0d6a5be0cc64028c1e7021e2632b031fd8eb82afc5a283d1c3d1 refs/tags/test^{} diff --git a/modules/git/tests/repos/repo1_bare_sha256/objects/info/commit-graph b/modules/git/tests/repos/repo1_bare_sha256/objects/info/commit-graph new file mode 100644 index 0000000000..2985d3e436 Binary files /dev/null and b/modules/git/tests/repos/repo1_bare_sha256/objects/info/commit-graph differ diff --git a/modules/git/tests/repos/repo1_bare_sha256/objects/info/packs b/modules/git/tests/repos/repo1_bare_sha256/objects/info/packs new file mode 100644 index 0000000000..c2d1bb88c5 --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/objects/info/packs @@ -0,0 +1,2 @@ +P pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.pack + diff --git a/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.bitmap b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.bitmap new file mode 100644 index 0000000000..535ba168c7 Binary files /dev/null and b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.bitmap differ diff --git a/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.idx b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.idx new file mode 100644 index 0000000000..ab45b6f312 Binary files /dev/null and b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.idx differ diff --git a/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.pack b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.pack new file mode 100644 index 0000000000..c77bf2024c Binary files /dev/null and b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.pack differ diff --git a/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.rev b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.rev new file mode 100644 index 0000000000..d24fd8e9ce Binary files /dev/null and b/modules/git/tests/repos/repo1_bare_sha256/objects/pack/pack-c01aa121b9c5e345fe0da2f9be78665970b0c38c6b495d5fc034bc7a7b95334b.rev differ diff --git a/modules/git/tests/repos/repo1_bare_sha256/packed-refs b/modules/git/tests/repos/repo1_bare_sha256/packed-refs new file mode 100644 index 0000000000..36c92ce3ba --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/packed-refs @@ -0,0 +1,8 @@ +# pack-refs with: peeled fully-peeled sorted +42e334efd04cd36eea6da0599913333c26116e1a537ca76e5b6e4af4dda00236 refs/heads/branch1 +5bc2249e32e0ba40a08879fba2bd4e97a13cb345831549f4bc5649525da8f6cc refs/heads/branch2 +9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 refs/heads/main +29a82d4fc02e19190fb489cc90d5730ed91970b49f4e39acda2798b3dd4f814e refs/tags/signed-tag +^9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 +171822a62559f3aa28a00aa3785dbe915d6a8eb02712682740db44fc8bd2187a refs/tags/test +^6aae864a3d1d0d6a5be0cc64028c1e7021e2632b031fd8eb82afc5a283d1c3d1 diff --git a/modules/git/tests/repos/repo1_bare_sha256/refs/heads/main b/modules/git/tests/repos/repo1_bare_sha256/refs/heads/main new file mode 100644 index 0000000000..b09fd5c560 --- /dev/null +++ b/modules/git/tests/repos/repo1_bare_sha256/refs/heads/main @@ -0,0 +1 @@ +9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 diff --git a/modules/git/tests/repos/repo5_pulls_sha256/HEAD b/modules/git/tests/repos/repo5_pulls_sha256/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/modules/git/tests/repos/repo5_pulls_sha256/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/modules/git/tests/repos/repo5_pulls_sha256/config b/modules/git/tests/repos/repo5_pulls_sha256/config new file mode 100644 index 0000000000..2388a50b2f --- /dev/null +++ b/modules/git/tests/repos/repo5_pulls_sha256/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 1 + filemode = true + bare = true +[extensions] + objectformat = sha256 diff --git a/modules/git/tests/repos/repo5_pulls_sha256/description b/modules/git/tests/repos/repo5_pulls_sha256/description new file mode 100644 index 0000000000..498b267a8c --- /dev/null +++ b/modules/git/tests/repos/repo5_pulls_sha256/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/modules/git/tests/repos/repo5_pulls_sha256/info/refs b/modules/git/tests/repos/repo5_pulls_sha256/info/refs new file mode 100644 index 0000000000..454e45de2d --- /dev/null +++ b/modules/git/tests/repos/repo5_pulls_sha256/info/refs @@ -0,0 +1,4 @@ +35ecd0f946c8baeb76fa5a3876f46bf35218655e2304d8505026fa4bfb496a4b refs/heads/main +35ecd0f946c8baeb76fa5a3876f46bf35218655e2304d8505026fa4bfb496a4b refs/heads/main-clone +7f50a4906503378b0bbb7d61bd2ca8d8d8ff4f7a2474980f99402d742ccc9665 refs/heads/test-patch-1 +1e35a51dc00fd7de730344c07061acfe80e8117e075ac979b6a29a3a045190ca refs/tags/v0.9.99 diff --git a/modules/git/tests/repos/repo5_pulls_sha256/objects/info/commit-graph b/modules/git/tests/repos/repo5_pulls_sha256/objects/info/commit-graph new file mode 100644 index 0000000000..8e5ef41ad6 Binary files /dev/null and b/modules/git/tests/repos/repo5_pulls_sha256/objects/info/commit-graph differ diff --git a/modules/git/tests/repos/repo5_pulls_sha256/objects/info/packs b/modules/git/tests/repos/repo5_pulls_sha256/objects/info/packs new file mode 100644 index 0000000000..6f51e7b9a8 --- /dev/null +++ b/modules/git/tests/repos/repo5_pulls_sha256/objects/info/packs @@ -0,0 +1,2 @@ +P pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.pack + diff --git a/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.bitmap b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.bitmap new file mode 100644 index 0000000000..38fca6e092 Binary files /dev/null and b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.bitmap differ diff --git a/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.idx b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.idx new file mode 100644 index 0000000000..fd43d04e0a Binary files /dev/null and b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.idx differ diff --git a/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.pack b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.pack new file mode 100644 index 0000000000..689318da32 Binary files /dev/null and b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.pack differ diff --git a/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.rev b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.rev new file mode 100644 index 0000000000..c0bac95376 Binary files /dev/null and b/modules/git/tests/repos/repo5_pulls_sha256/objects/pack/pack-bfe8f09d42ef5dd1610bf42641fe145d4a02b788eb26c31022a362312660a29d.rev differ diff --git a/modules/git/tests/repos/repo5_pulls_sha256/packed-refs b/modules/git/tests/repos/repo5_pulls_sha256/packed-refs new file mode 100644 index 0000000000..1525083592 --- /dev/null +++ b/modules/git/tests/repos/repo5_pulls_sha256/packed-refs @@ -0,0 +1,5 @@ +# pack-refs with: peeled fully-peeled sorted +35ecd0f946c8baeb76fa5a3876f46bf35218655e2304d8505026fa4bfb496a4b refs/heads/main +35ecd0f946c8baeb76fa5a3876f46bf35218655e2304d8505026fa4bfb496a4b refs/heads/main-clone +7f50a4906503378b0bbb7d61bd2ca8d8d8ff4f7a2474980f99402d742ccc9665 refs/heads/test-patch-1 +1e35a51dc00fd7de730344c07061acfe80e8117e075ac979b6a29a3a045190ca refs/tags/v0.9.99 diff --git a/modules/git/tests/repos/repo5_pulls_sha256/refs/heads/main b/modules/git/tests/repos/repo5_pulls_sha256/refs/heads/main new file mode 100644 index 0000000000..9b32e79e93 --- /dev/null +++ b/modules/git/tests/repos/repo5_pulls_sha256/refs/heads/main @@ -0,0 +1 @@ +35ecd0f946c8baeb76fa5a3876f46bf35218655e2304d8505026fa4bfb496a4b diff --git a/modules/git/tests/repos/repo6_blame_sha256/HEAD b/modules/git/tests/repos/repo6_blame_sha256/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/modules/git/tests/repos/repo6_blame_sha256/config b/modules/git/tests/repos/repo6_blame_sha256/config new file mode 100644 index 0000000000..2388a50b2f --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 1 + filemode = true + bare = true +[extensions] + objectformat = sha256 diff --git a/modules/git/tests/repos/repo6_blame_sha256/description b/modules/git/tests/repos/repo6_blame_sha256/description new file mode 100644 index 0000000000..498b267a8c --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/modules/git/tests/repos/repo6_blame_sha256/info/exclude b/modules/git/tests/repos/repo6_blame_sha256/info/exclude new file mode 100644 index 0000000000..a5196d1be8 --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/modules/git/tests/repos/repo6_blame_sha256/info/refs b/modules/git/tests/repos/repo6_blame_sha256/info/refs new file mode 100644 index 0000000000..bee6d1dce3 --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/info/refs @@ -0,0 +1 @@ +e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3 refs/heads/main diff --git a/modules/git/tests/repos/repo6_blame_sha256/objects/info/commit-graph b/modules/git/tests/repos/repo6_blame_sha256/objects/info/commit-graph new file mode 100644 index 0000000000..f963aa049d Binary files /dev/null and b/modules/git/tests/repos/repo6_blame_sha256/objects/info/commit-graph differ diff --git a/modules/git/tests/repos/repo6_blame_sha256/objects/info/packs b/modules/git/tests/repos/repo6_blame_sha256/objects/info/packs new file mode 100644 index 0000000000..73744cf8f7 --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/objects/info/packs @@ -0,0 +1,2 @@ +P pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.pack + diff --git a/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.bitmap b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.bitmap new file mode 100644 index 0000000000..c34487c53a Binary files /dev/null and b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.bitmap differ diff --git a/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.idx b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.idx new file mode 100644 index 0000000000..faaee22ff4 Binary files /dev/null and b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.idx differ diff --git a/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.pack b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.pack new file mode 100644 index 0000000000..626f081398 Binary files /dev/null and b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.pack differ diff --git a/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.rev b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.rev new file mode 100644 index 0000000000..56175555cd Binary files /dev/null and b/modules/git/tests/repos/repo6_blame_sha256/objects/pack/pack-fcb8a221b76025fd8415d3c562b611ac24312a5ffc3d3703d7c5cc906bdaee8e.rev differ diff --git a/modules/git/tests/repos/repo6_blame_sha256/packed-refs b/modules/git/tests/repos/repo6_blame_sha256/packed-refs new file mode 100644 index 0000000000..644269299f --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/packed-refs @@ -0,0 +1,2 @@ +# pack-refs with: peeled fully-peeled sorted +e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3 refs/heads/main diff --git a/modules/git/tests/repos/repo6_blame_sha256/refs/refs/main b/modules/git/tests/repos/repo6_blame_sha256/refs/refs/main new file mode 100644 index 0000000000..829662cdf5 --- /dev/null +++ b/modules/git/tests/repos/repo6_blame_sha256/refs/refs/main @@ -0,0 +1 @@ +e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3 diff --git a/modules/git/tests/repos/repo6_merge_sha256/HEAD b/modules/git/tests/repos/repo6_merge_sha256/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/modules/git/tests/repos/repo6_merge_sha256/config b/modules/git/tests/repos/repo6_merge_sha256/config new file mode 100644 index 0000000000..2388a50b2f --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 1 + filemode = true + bare = true +[extensions] + objectformat = sha256 diff --git a/modules/git/tests/repos/repo6_merge_sha256/description b/modules/git/tests/repos/repo6_merge_sha256/description new file mode 100644 index 0000000000..498b267a8c --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/modules/git/tests/repos/repo6_merge_sha256/info/exclude b/modules/git/tests/repos/repo6_merge_sha256/info/exclude new file mode 100644 index 0000000000..a5196d1be8 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/modules/git/tests/repos/repo6_merge_sha256/info/refs b/modules/git/tests/repos/repo6_merge_sha256/info/refs new file mode 100644 index 0000000000..7dae8a1be7 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/info/refs @@ -0,0 +1,4 @@ +d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1 refs/heads/main +b45258e9823233edea2d40d183742f29630e1e69300479fb4a55eabfe9b1d8bf refs/heads/merge/add_file +ff2b996e2fa366146300e4c9e51ccb6818147b360e46fa1437334f4a690955ce refs/heads/merge/modify_file +da1ded40dc8e5b7c564171f4bf2fc8370487decfb1cb6a99ef28f3ed73d09172 refs/heads/merge/remove_file diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/info/commit-graph b/modules/git/tests/repos/repo6_merge_sha256/objects/info/commit-graph new file mode 100644 index 0000000000..98068475e8 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/info/commit-graph differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/info/packs b/modules/git/tests/repos/repo6_merge_sha256/objects/info/packs new file mode 100644 index 0000000000..f3cf8197ce --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/objects/info/packs @@ -0,0 +1,3 @@ +P pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.pack +P pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.pack + diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.bitmap b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.bitmap new file mode 100644 index 0000000000..d1624a0780 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.bitmap differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.idx b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.idx new file mode 100644 index 0000000000..09b897d2db Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.idx differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.pack b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.pack new file mode 100644 index 0000000000..3b406be93f Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.pack differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.rev b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.rev new file mode 100644 index 0000000000..4a695fc2e5 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-2fff0848f8d8eab8f7902ac91ab6a096c7530f577d5c0a79c63d9ac2b44f7510.rev differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.idx b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.idx new file mode 100644 index 0000000000..3b58342e68 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.idx differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.mtimes b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.mtimes new file mode 100644 index 0000000000..a669a06e27 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.mtimes differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.pack b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.pack new file mode 100644 index 0000000000..a28808ee11 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.pack differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.rev b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.rev new file mode 100644 index 0000000000..c09bb3203b Binary files /dev/null and b/modules/git/tests/repos/repo6_merge_sha256/objects/pack/pack-65162b86afdbac3c566696d487e67bb2a4a5501ca1fa3528fad8a9474fba7e50.rev differ diff --git a/modules/git/tests/repos/repo6_merge_sha256/packed-refs b/modules/git/tests/repos/repo6_merge_sha256/packed-refs new file mode 100644 index 0000000000..b906893c52 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/packed-refs @@ -0,0 +1,5 @@ +# pack-refs with: peeled fully-peeled sorted +d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1 refs/heads/main +b45258e9823233edea2d40d183742f29630e1e69300479fb4a55eabfe9b1d8bf refs/heads/merge/add_file +ff2b996e2fa366146300e4c9e51ccb6818147b360e46fa1437334f4a690955ce refs/heads/merge/modify_file +da1ded40dc8e5b7c564171f4bf2fc8370487decfb1cb6a99ef28f3ed73d09172 refs/heads/merge/remove_file diff --git a/modules/git/tests/repos/repo6_merge_sha256/refs/heads/main b/modules/git/tests/repos/repo6_merge_sha256/refs/heads/main new file mode 100644 index 0000000000..c8c02921e1 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge_sha256/refs/heads/main @@ -0,0 +1 @@ +d2e5609f630dd8db500f5298d05d16def282412e3e66ed68cc7d0833b29129a1 diff --git a/modules/markup/html.go b/modules/markup/html.go index a64e4c565d..33dc1e9086 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -45,19 +45,19 @@ var ( // valid chars in encoded path and parameter: [-+~_%.a-zA-Z0-9/] - // sha1CurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae - // Although SHA1 hashes are 40 chars long, the regex matches the hash from 7 to 40 chars in length + // hashCurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae + // Although SHA1 hashes are 40 chars long, SHA256 are 64, the regex matches the hash from 7 to 64 chars in length // so that abbreviated hash links can be used as well. This matches git and GitHub usability. - sha1CurrentPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-f]{7,40})(?:\s|$|\)|\]|[.,](\s|$))`) + hashCurrentPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-f]{7,64})(?:\s|$|\)|\]|[.,](\s|$))`) // shortLinkPattern matches short but difficult to parse [[name|link|arg=test]] syntax shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`) // anySHA1Pattern splits url containing SHA into parts - anySHA1Pattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40})(/[-+~_%.a-zA-Z0-9/]+)?(#[-+~_%.a-zA-Z0-9]+)?`) + anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(#[-+~_%.a-zA-Z0-9]+)?`) // comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash" - comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,40})(\.\.\.?)([0-9a-f]{7,40})?(#[-+~_%.a-zA-Z0-9]+)?`) + comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`) validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) @@ -171,13 +171,13 @@ type processor func(ctx *RenderContext, node *html.Node) var defaultProcessors = []processor{ fullIssuePatternProcessor, comparePatternProcessor, - fullSha1PatternProcessor, + fullHashPatternProcessor, shortLinkProcessor, linkProcessor, mentionProcessor, issueIndexPatternProcessor, commitCrossReferencePatternProcessor, - sha1CurrentPatternProcessor, + hashCurrentPatternProcessor, emailAddressProcessor, emojiProcessor, emojiShortCodeProcessor, @@ -199,12 +199,12 @@ func PostProcess( var commitMessageProcessors = []processor{ fullIssuePatternProcessor, comparePatternProcessor, - fullSha1PatternProcessor, + fullHashPatternProcessor, linkProcessor, mentionProcessor, issueIndexPatternProcessor, commitCrossReferencePatternProcessor, - sha1CurrentPatternProcessor, + hashCurrentPatternProcessor, emailAddressProcessor, emojiProcessor, emojiShortCodeProcessor, @@ -231,12 +231,12 @@ func RenderCommitMessage( var commitMessageSubjectProcessors = []processor{ fullIssuePatternProcessor, comparePatternProcessor, - fullSha1PatternProcessor, + fullHashPatternProcessor, linkProcessor, mentionProcessor, issueIndexPatternProcessor, commitCrossReferencePatternProcessor, - sha1CurrentPatternProcessor, + hashCurrentPatternProcessor, emojiShortCodeProcessor, emojiProcessor, } @@ -273,7 +273,7 @@ func RenderIssueTitle( return renderProcessString(ctx, []processor{ issueIndexPatternProcessor, commitCrossReferencePatternProcessor, - sha1CurrentPatternProcessor, + hashCurrentPatternProcessor, emojiShortCodeProcessor, emojiProcessor, }, title) @@ -946,15 +946,15 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) { } } -// fullSha1PatternProcessor renders SHA containing URLs -func fullSha1PatternProcessor(ctx *RenderContext, node *html.Node) { +// fullHashPatternProcessor renders SHA containing URLs +func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } next := node.NextSibling for node != nil && node != next { - m := anySHA1Pattern.FindStringSubmatchIndex(node.Data) + m := anyHashPattern.FindStringSubmatchIndex(node.Data) if m == nil { return } @@ -1111,9 +1111,9 @@ func emojiProcessor(ctx *RenderContext, node *html.Node) { } } -// sha1CurrentPatternProcessor renders SHA1 strings to corresponding links that +// hashCurrentPatternProcessor renders SHA1 strings to corresponding links that // are assumed to be in the same repository. -func sha1CurrentPatternProcessor(ctx *RenderContext, node *html.Node) { +func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" || ctx.Metas["repoPath"] == "" { return } @@ -1124,7 +1124,7 @@ func sha1CurrentPatternProcessor(ctx *RenderContext, node *html.Node) { ctx.ShaExistCache = make(map[string]bool) } for node != nil && node != next && start < len(node.Data) { - m := sha1CurrentPattern.FindStringSubmatchIndex(node.Data[start:]) + m := hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:]) if m == nil { return } diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 5ba9561915..93ba9d7667 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -390,10 +390,10 @@ func TestRegExp_sha1CurrentPattern(t *testing.T) { } for _, testCase := range trueTestCases { - assert.True(t, sha1CurrentPattern.MatchString(testCase)) + assert.True(t, hashCurrentPattern.MatchString(testCase)) } for _, testCase := range falseTestCases { - assert.False(t, sha1CurrentPattern.MatchString(testCase)) + assert.False(t, hashCurrentPattern.MatchString(testCase)) } } @@ -427,7 +427,7 @@ func TestRegExp_anySHA1Pattern(t *testing.T) { } for k, v := range testCases { - assert.Equal(t, anySHA1Pattern.FindStringSubmatch(k)[1:], v) + assert.Equal(t, anyHashPattern.FindStringSubmatch(k)[1:], v) } } diff --git a/modules/packages/rpm/metadata.go b/modules/packages/rpm/metadata.go index 1ba4c73e8d..7fc47a53e6 100644 --- a/modules/packages/rpm/metadata.go +++ b/modules/packages/rpm/metadata.go @@ -15,7 +15,10 @@ import ( ) const ( - PropertyMetadata = "rpm.metadata" + PropertyMetadata = "rpm.metadata" + PropertyGroup = "rpm.group" + PropertyArchitecture = "rpm.architecture" + SettingKeyPrivate = "rpm.key.private" SettingKeyPublic = "rpm.key.public" diff --git a/modules/private/actions.go b/modules/private/actions.go index a22833632e..311a283650 100644 --- a/modules/private/actions.go +++ b/modules/private/actions.go @@ -14,16 +14,12 @@ type GenerateTokenRequest struct { } // GenerateActionsRunnerToken calls the internal GenerateActionsRunnerToken function -func GenerateActionsRunnerToken(ctx context.Context, scope string) (string, ResponseExtra) { +func GenerateActionsRunnerToken(ctx context.Context, scope string) (*ResponseText, ResponseExtra) { reqURL := setting.LocalURL + "api/internal/actions/generate_actions_runner_token" req := newInternalRequest(ctx, reqURL, "POST", GenerateTokenRequest{ Scope: scope, }) - resp, extra := requestJSONResp(req, &responseText{}) - if extra.HasError() { - return "", extra - } - return resp.Text, extra + return requestJSONResp(req, &ResponseText{}) } diff --git a/modules/private/hook.go b/modules/private/hook.go index 23e03896e4..cab8c81224 100644 --- a/modules/private/hook.go +++ b/modules/private/hook.go @@ -101,7 +101,7 @@ func HookPreReceive(ctx context.Context, ownerName, repoName string, opts HookOp reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName)) req := newInternalRequest(ctx, reqURL, "POST", opts) req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second) - _, extra := requestJSONResp(req, &responseText{}) + _, extra := requestJSONResp(req, &ResponseText{}) return extra } @@ -130,7 +130,7 @@ func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) R url.PathEscape(branch), ) req := newInternalRequest(ctx, reqURL, "POST") - _, extra := requestJSONResp(req, &responseText{}) + _, extra := requestJSONResp(req, &ResponseText{}) return extra } @@ -138,6 +138,6 @@ func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) R func SSHLog(ctx context.Context, isErr bool, msg string) error { reqURL := setting.LocalURL + "api/internal/ssh/log" req := newInternalRequest(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg}) - _, extra := requestJSONResp(req, &responseText{}) + _, extra := requestJSONResp(req, &ResponseText{}) return extra.Error } diff --git a/modules/private/key.go b/modules/private/key.go index 08762bd401..dcd1714856 100644 --- a/modules/private/key.go +++ b/modules/private/key.go @@ -15,20 +15,16 @@ func UpdatePublicKeyInRepo(ctx context.Context, keyID, repoID int64) error { // Ask for running deliver hook and test pull request tasks. reqURL := setting.LocalURL + fmt.Sprintf("api/internal/ssh/%d/update/%d", keyID, repoID) req := newInternalRequest(ctx, reqURL, "POST") - _, extra := requestJSONResp(req, &responseText{}) + _, extra := requestJSONResp(req, &ResponseText{}) return extra.Error } // AuthorizedPublicKeyByContent searches content as prefix (leak e-mail part) // and returns public key found. -func AuthorizedPublicKeyByContent(ctx context.Context, content string) (string, ResponseExtra) { +func AuthorizedPublicKeyByContent(ctx context.Context, content string) (*ResponseText, ResponseExtra) { // Ask for running deliver hook and test pull request tasks. reqURL := setting.LocalURL + "api/internal/ssh/authorized_keys" req := newInternalRequest(ctx, reqURL, "POST") req.Param("content", content) - resp, extra := requestJSONResp(req, &responseText{}) - if extra.HasError() { - return "", extra - } - return resp.Text, extra + return requestJSONResp(req, &ResponseText{}) } diff --git a/modules/private/mail.go b/modules/private/mail.go index ac55d6fe4d..08de5b7e28 100644 --- a/modules/private/mail.go +++ b/modules/private/mail.go @@ -20,7 +20,7 @@ type Email struct { // It accepts a list of usernames. // If DB contains these users it will send the email to them. // If to list == nil, it's supposed to send emails to every user present in DB -func SendEmail(ctx context.Context, subject, message string, to []string) (string, ResponseExtra) { +func SendEmail(ctx context.Context, subject, message string, to []string) (*ResponseText, ResponseExtra) { reqURL := setting.LocalURL + "api/internal/mail/send" req := newInternalRequest(ctx, reqURL, "POST", Email{ @@ -29,9 +29,5 @@ func SendEmail(ctx context.Context, subject, message string, to []string) (strin To: to, }) - resp, extra := requestJSONResp(req, &responseText{}) - if extra.HasError() { - return "", extra - } - return resp.Text, extra + return requestJSONResp(req, &ResponseText{}) } diff --git a/modules/private/request.go b/modules/private/request.go index 2bc43b972d..58cd261239 100644 --- a/modules/private/request.go +++ b/modules/private/request.go @@ -12,8 +12,8 @@ import ( "code.gitea.io/gitea/modules/json" ) -// responseText is used to get the response as text, instead of parsing it as JSON. -type responseText struct { +// ResponseText is used to get the response as text, instead of parsing it as JSON. +type ResponseText struct { Text string } @@ -50,7 +50,7 @@ func (re responseError) Error() string { // Caller should check the ResponseExtra.HasError() first to see whether the request fails. // // * If the "res" is a struct pointer, the response will be parsed as JSON -// * If the "res" is responseText pointer, the response will be stored as text in it +// * If the "res" is ResponseText pointer, the response will be stored as text in it // * If the "res" is responseCallback pointer, the callback function should set the ResponseExtra fields accordingly func requestJSONResp[T any](req *httplib.Request, res *T) (ret *T, extra ResponseExtra) { resp, err := req.Response() @@ -81,7 +81,7 @@ func requestJSONResp[T any](req *httplib.Request, res *T) (ret *T, extra Respons // now, the StatusCode must be 2xx var v any = res - if respText, ok := v.(*responseText); ok { + if respText, ok := v.(*ResponseText); ok { // get the whole response as a text string bs, err := io.ReadAll(resp.Body) if err != nil { @@ -119,7 +119,7 @@ func requestJSONResp[T any](req *httplib.Request, res *T) (ret *T, extra Respons // requestJSONClientMsg sends a request to the gitea server, server only responds text message status=200 with "success" body // If the request succeeds (200), the argument clientSuccessMsg will be used as ResponseExtra.UserMsg. func requestJSONClientMsg(req *httplib.Request, clientSuccessMsg string) ResponseExtra { - _, extra := requestJSONResp(req, &responseText{}) + _, extra := requestJSONResp(req, &ResponseText{}) if extra.HasError() { return extra } diff --git a/modules/references/references.go b/modules/references/references.go index 64a67d7da7..7758312564 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -39,7 +39,7 @@ var ( crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`) // crossReferenceCommitPattern matches a string that references a commit in a different repository // e.g. go-gitea/gitea@d8a994ef, go-gitea/gitea@d8a994ef243349f321568f9e36d5c3f444b99cae (7-40 characters) - crossReferenceCommitPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)@([0-9a-f]{7,40})(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`) + crossReferenceCommitPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)@([0-9a-f]{7,64})(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`) // spaceTrimmedPattern let's find the trailing space spaceTrimmedPattern = regexp.MustCompile(`(?:.*[0-9a-zA-Z-_])\s`) // timeLogPattern matches string for time tracking diff --git a/modules/references/references_test.go b/modules/references/references_test.go index b60d6b0459..ba7dda80cc 100644 --- a/modules/references/references_test.go +++ b/modules/references/references_test.go @@ -343,7 +343,7 @@ func TestFindRenderizableCommitCrossReference(t *testing.T) { }, }, { - Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd12340", // longer than 40 characters + Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd12341234512345123451234512345", // longer than 64 characters Expected: nil, }, { diff --git a/modules/setting/database.go b/modules/setting/database.go index 0b0488ce85..e200b15b2e 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -169,8 +169,8 @@ func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbsslMode s RawQuery: dbParam, } query := connURL.Query() - if strings.HasPrefix(dbHost, "/") { // looks like a unix socket - query.Add("host", dbHost) + if strings.HasPrefix(host, "/") { // looks like a unix socket + query.Add("host", host) connURL.Host = ":" + port } query.Set("sslmode", dbsslMode) diff --git a/modules/setting/database_test.go b/modules/setting/database_test.go index 14e0a6ac02..a742d54f8c 100644 --- a/modules/setting/database_test.go +++ b/modules/setting/database_test.go @@ -77,6 +77,14 @@ func Test_getPostgreSQLConnectionString(t *testing.T) { SSLMode: "false", Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false", }, + { + Host: "/tmp/pg.sock:6432", + User: "testuser", + Passwd: "pass", + Name: "gitea", + SSLMode: "false", + Output: "postgres://testuser:pass@:6432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false", + }, { Host: "localhost", User: "pgsqlusername", diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 3974c4db3a..51e175fba8 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -105,6 +105,9 @@ type Repository struct { AvatarURL string `json:"avatar_url"` Internal bool `json:"internal"` MirrorInterval string `json:"mirror_interval"` + // ObjectFormatName of the underlying git repository + // enum: sha1,sha256 + ObjectFormatName string `json:"object_format_name"` // swagger:strfmt date-time MirrorUpdated time.Time `json:"mirror_updated,omitempty"` RepoTransfer *RepoTransfer `json:"repo_transfer"` @@ -139,6 +142,9 @@ type CreateRepoOption struct { // TrustModel of the repository // enum: default,collaborator,committer,collaboratorcommitter TrustModel string `json:"trust_model"` + // ObjectFormatName of the underlying git repository + // enum: sha1,sha256 + ObjectFormatName string `json:"object_format_name" binding:"MaxSize(6)"` } // EditRepoOption options when editing a repository's properties diff --git a/modules/templates/util_string.go b/modules/templates/util_string.go index 613940ccdc..2771b1e223 100644 --- a/modules/templates/util_string.go +++ b/modules/templates/util_string.go @@ -4,7 +4,6 @@ package templates import ( - "regexp" "strings" "code.gitea.io/gitea/modules/base" @@ -26,10 +25,6 @@ func (su *StringUtils) Contains(s, substr string) bool { return strings.Contains(s, substr) } -func (su *StringUtils) ReplaceAllStringRegex(s, regex, new string) string { - return regexp.MustCompile(regex).ReplaceAllString(s, new) -} - func (su *StringUtils) Split(s, sep string) []string { return strings.Split(s, sep) } @@ -46,3 +41,7 @@ func (su *StringUtils) Cut(s, sep string) []any { func (su *StringUtils) EllipsisString(s string, max int) string { return base.EllipsisString(s, max) } + +func (su *StringUtils) ToUpper(s string) string { + return strings.ToUpper(s) +} diff --git a/modules/util/slice.go b/modules/util/slice.go index 6d63ab4a77..a7073fedee 100644 --- a/modules/util/slice.go +++ b/modules/util/slice.go @@ -4,6 +4,7 @@ package util import ( + "cmp" "slices" "strings" ) @@ -45,3 +46,10 @@ func SliceSortedEqual[T comparable](s1, s2 []T) bool { func SliceRemoveAll[T comparable](slice []T, target T) []T { return slices.DeleteFunc(slice, func(t T) bool { return t == target }) } + +// Sorted returns the sorted slice +// Note: The parameter is sorted inline. +func Sorted[S ~[]E, E cmp.Ordered](values S) S { + slices.Sort(values) + return values +} diff --git a/options/license/BSD-Source-beginning-file b/options/license/BSD-Source-beginning-file new file mode 100644 index 0000000000..6265f97608 --- /dev/null +++ b/options/license/BSD-Source-beginning-file @@ -0,0 +1,23 @@ +Copyright (c) 1997 Justin T. Gibbs. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer, + without modification, immediately at the beginning of the file. +2. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/options/license/Caldera-no-preamble b/options/license/Caldera-no-preamble new file mode 100644 index 0000000000..f70f34b32b --- /dev/null +++ b/options/license/Caldera-no-preamble @@ -0,0 +1,35 @@ +Copyright(C) Caldera International Inc. 2001-2002. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +Redistributions of source code and documentation must retain the above +copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +All advertising materials mentioning features or use of this software +must display the following acknowledgement: + + This product includes software developed or owned by Caldera + International, Inc. + +Neither the name of Caldera International, Inc. nor the names of other +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, +INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/options/license/HPND-MIT-disclaimer b/options/license/HPND-MIT-disclaimer new file mode 100644 index 0000000000..bf035915cf --- /dev/null +++ b/options/license/HPND-MIT-disclaimer @@ -0,0 +1,18 @@ + LICENSE + ======= + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the original copyright notices appear in all copies and that both +copyright notice and this permission notice appear in supporting +documentation, and that the name of the author not be used in advertising +or publicity pertaining to distribution of the software without specific +prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/options/license/LPD-document b/options/license/LPD-document new file mode 100644 index 0000000000..0b46392e2f --- /dev/null +++ b/options/license/LPD-document @@ -0,0 +1,8 @@ +Copyright (c) 1996 L. Peter Deutsch + +Permission is granted to copy and distribute this +document for any purpose and without charge, including +translations into other languages and incorporation +into compilations, provided that the copyright notice +and this notice are preserved, and that any substantive +changes or deletions from the original are clearly marked. diff --git a/options/license/TGPPL-1.0 b/options/license/TGPPL-1.0 new file mode 100644 index 0000000000..fbafe92c82 --- /dev/null +++ b/options/license/TGPPL-1.0 @@ -0,0 +1,181 @@ +======================================================= +Transitive Grace Period Public Licence ("TGPPL") v. 1.0 +======================================================= + +This Transitive Grace Period Public Licence (the "License") applies to any +original work of authorship (the "Original Work") whose owner (the +"Licensor") has placed the following licensing notice adjacent to the +copyright notice for the Original Work: + + *Licensed under the Transitive Grace Period Public Licence version 1.0* + +1. **Grant of Copyright License.** Licensor grants You a worldwide, + royalty-free, non-exclusive, sublicensable license, for the duration of + the copyright, to do the following: + + a. to reproduce the Original Work in copies, either alone or as part of a + collective work; + + b. to translate, adapt, alter, transform, modify, or arrange the Original + Work, thereby creating derivative works ("Derivative Works") based upon + the Original Work; + + c. to distribute or communicate copies of the Original Work and Derivative + Works to the public, with the proviso that copies of Original Work or + Derivative Works that You distribute or communicate shall be licensed + under this Transitive Grace Period Public Licence no later than 12 + months after You distributed or communicated said copies; + + d. to perform the Original Work publicly; and + + e. to display the Original Work publicly. + +2. **Grant of Patent License.** Licensor grants You a worldwide, + royalty-free, non-exclusive, sublicensable license, under patent claims + owned or controlled by the Licensor that are embodied in the Original + Work as furnished by the Licensor, for the duration of the patents, to + make, use, sell, offer for sale, have made, and import the Original Work + and Derivative Works. + +3. **Grant of Source Code License.** The term "Source Code" means the + preferred form of the Original Work for making modifications to it and + all available documentation describing how to modify the Original + Work. Licensor agrees to provide a machine-readable copy of the Source + Code of the Original Work along with each copy of the Original Work that + Licensor distributes. Licensor reserves the right to satisfy this + obligation by placing a machine-readable copy of the Source Code in an + information repository reasonably calculated to permit inexpensive and + convenient access by You for as long as Licensor continues to distribute + the Original Work. + +4. **Exclusions From License Grant.** Neither the names of Licensor, nor the + names of any contributors to the Original Work, nor any of their + trademarks or service marks, may be used to endorse or promote products + derived from this Original Work without express prior permission of the + Licensor. Except as expressly stated herein, nothing in this License + grants any license to Licensor's trademarks, copyrights, patents, trade + secrets or any other intellectual property. No patent license is granted + to make, use, sell, offer for sale, have made, or import embodiments of + any patent claims other than the licensed claims defined in Section 2. No + license is granted to the trademarks of Licensor even if such marks are + included in the Original Work. Nothing in this License shall be + interpreted to prohibit Licensor from licensing under terms different + from this License any Original Work that Licensor otherwise would have a + right to license. + +5. **External Deployment.** The term "External Deployment" means the use, + distribution, or communication of the Original Work or Derivative Works + in any way such that the Original Work or Derivative Works may be used by + anyone other than You, whether those works are distributed or + communicated to those persons or made available as an application + intended for use over a network. As an express condition for the grants + of license hereunder, You must treat any External Deployment by You of + the Original Work or a Derivative Work as a distribution under section + 1(c). + +6. **Attribution Rights.** You must retain, in the Source Code of any + Derivative Works that You create, all copyright, patent, or trademark + notices from the Source Code of the Original Work, as well as any notices + of licensing and any descriptive text identified therein as an + "Attribution Notice." You must cause the Source Code for any Derivative + Works that You create to carry a prominent Attribution Notice reasonably + calculated to inform recipients that You have modified the Original Work. + +7. **Warranty of Provenance and Disclaimer of Warranty.** Licensor warrants + that the copyright in and to the Original Work and the patent rights + granted herein by Licensor are owned by the Licensor or are sublicensed + to You under the terms of this License with the permission of the + contributor(s) of those copyrights and patent rights. Except as expressly + stated in the immediately preceding sentence, the Original Work is + provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, + either express or implied, including, without limitation, the warranties + of non-infringement, merchantability or fitness for a particular + purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH + YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this + License. No license to the Original Work is granted by this License + except under this disclaimer. + +8. **Limitation of Liability.** Under no circumstances and under no legal + theory, whether in tort (including negligence), contract, or otherwise, + shall the Licensor be liable to anyone for any indirect, special, + incidental, or consequential damages of any character arising as a result + of this License or the use of the Original Work including, without + limitation, damages for loss of goodwill, work stoppage, computer failure + or malfunction, or any and all other commercial damages or losses. This + limitation of liability shall not apply to the extent applicable law + prohibits such limitation. + +9. **Acceptance and Termination.** If, at any time, You expressly assented + to this License, that assent indicates your clear and irrevocable + acceptance of this License and all of its terms and conditions. If You + distribute or communicate copies of the Original Work or a Derivative + Work, You must make a reasonable effort under the circumstances to obtain + the express assent of recipients to the terms of this License. This + License conditions your rights to undertake the activities listed in + Section 1, including your right to create Derivative Works based upon the + Original Work, and doing so without honoring these terms and conditions + is prohibited by copyright law and international treaty. Nothing in this + License is intended to affect copyright exceptions and limitations + (including 'fair use' or 'fair dealing'). This License shall terminate + immediately and You may no longer exercise any of the rights granted to + You by this License upon your failure to honor the conditions in Section + 1(c). + +10. **Termination for Patent Action.** This License shall terminate + automatically and You may no longer exercise any of the rights granted to + You by this License as of the date You commence an action, including a + cross-claim or counterclaim, against Licensor or any licensee alleging + that the Original Work infringes a patent. This termination provision + shall not apply for an action alleging patent infringement by + combinations of the Original Work with other software or hardware. + +11. **Jurisdiction, Venue and Governing Law.** Any action or suit relating to + this License may be brought only in the courts of a jurisdiction wherein + the Licensor resides or in which Licensor conducts its primary business, + and under the laws of that jurisdiction excluding its conflict-of-law + provisions. The application of the United Nations Convention on Contracts + for the International Sale of Goods is expressly excluded. Any use of the + Original Work outside the scope of this License or after its termination + shall be subject to the requirements and penalties of copyright or patent + law in the appropriate jurisdiction. This section shall survive the + termination of this License. + +12. **Attorneys' Fees.** In any action to enforce the terms of this License + or seeking damages relating thereto, the prevailing party shall be + entitled to recover its costs and expenses, including, without + limitation, reasonable attorneys' fees and costs incurred in connection + with such action, including any appeal of such action. This section shall + survive the termination of this License. + +13. **Miscellaneous.** If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. + +14. **Definition of "You" in This License.** "You" throughout this License, + whether in upper or lower case, means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with you. For purposes of this + definition, "control" means (i) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + +15. **Right to Use.** You may use the Original Work in all ways not otherwise + restricted or conditioned by this License or by law, and Licensor + promises not to interfere with or be responsible for such uses by You. + +16. **Modification of This License.** This License is Copyright © 2007 Zooko + Wilcox-O'Hearn. Permission is granted to copy, distribute, or communicate + this License without modification. Nothing in this License permits You to + modify this License as applied to the Original Work or to Derivative + Works. However, You may modify the text of this License and copy, + distribute or communicate your modified version (the "Modified License") + and apply it to other original works of authorship subject to the + following conditions: (i) You may not indicate in any way that your + Modified License is the "Transitive Grace Period Public Licence" or + "TGPPL" and you may not use those names in the name of your Modified + License; and (ii) You must replace the notice specified in the first + paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to + the notice in this License. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 90e3ac503a..9a06bb0952 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -970,6 +970,8 @@ issue_labels_helper = Select an issue label set. license = License license_helper = Select a license file. license_helper_desc = A license governs what others can and can't do with your code. Not sure which one is right for your project? See Choose a license. +object_format = Object Format +object_format_helper = Object format of the repository. Cannot be changed later. SHA1 is most compatible. readme = README readme_helper = Select a README file template. readme_helper_desc = This is the place where you can write a complete description for your project. @@ -987,6 +989,7 @@ mirror_prune = Prune mirror_prune_desc = Remove obsolete remote-tracking references mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable periodic sync. (Minimum interval: %s) mirror_interval_invalid = The mirror interval is not valid. +mirror_sync = synced mirror_sync_on_commit = Sync when commits are pushed mirror_address = Clone From URL mirror_address_desc = Put any required credentials in the Authorization section. @@ -1037,6 +1040,7 @@ desc.public = Public desc.template = Template desc.internal = Internal desc.archived = Archived +desc.sha256 = SHA256 template.items = Template Items template.git_content = Git Content (Default Branch) @@ -1710,6 +1714,7 @@ pulls.select_commit_hold_shift_for_range = Select commit. Hold shift + click to pulls.review_only_possible_for_full_diff = Review is only possible when viewing the full diff pulls.filter_changes_by_commit = Filter by commit pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request. +pulls.nothing_to_compare_have_tag = The selected branch/tag are equal. pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty. pulls.has_pull_request = `A pull request between these branches already exists: %[2]s#%[3]d` pulls.create = Create Pull Request @@ -3413,6 +3418,9 @@ rpm.registry = Setup this registry from the command line: rpm.distros.redhat = on RedHat based distributions rpm.distros.suse = on SUSE based distributions rpm.install = To install the package, run the following command: +rpm.repository = Repository Info +rpm.repository.architectures = Architectures +rpm.repository.multiple_groups = This package is available in multiple groups. rubygems.install = To install the package using gem, run the following command: rubygems.install2 = or add it to the Gemfile: rubygems.dependencies.runtime = Runtime Dependencies diff --git a/package-lock.json b/package-lock.json index 5f4dc9bc2b..b299967eba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11160,9 +11160,9 @@ } }, "node_modules/vite": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz", - "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "dev": true, "dependencies": { "esbuild": "^0.19.3", diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 9026387129..d990ebb56a 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -512,7 +512,77 @@ func CommonRoutes() *web.Route { r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile) r.Get("/simple/{id}", pypi.PackageMetadata) }, reqPackageAccess(perm.AccessModeRead)) - r.Group("/rpm", RpmRoutes(r), reqPackageAccess(perm.AccessModeRead)) + r.Group("/rpm", func() { + r.Group("/repository.key", func() { + r.Head("", rpm.GetRepositoryKey) + r.Get("", rpm.GetRepositoryKey) + }) + + var ( + repoPattern = regexp.MustCompile(`\A(.*?)\.repo\z`) + uploadPattern = regexp.MustCompile(`\A(.*?)/upload\z`) + filePattern = regexp.MustCompile(`\A(.*?)/package/([^/]+)/([^/]+)/([^/]+)(?:/([^/]+\.rpm)|)\z`) + repoFilePattern = regexp.MustCompile(`\A(.*?)/repodata/([^/]+)\z`) + ) + + r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) { + path := ctx.Params("*") + isHead := ctx.Req.Method == "HEAD" + isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET" + isPut := ctx.Req.Method == "PUT" + isDelete := ctx.Req.Method == "DELETE" + + m := repoPattern.FindStringSubmatch(path) + if len(m) == 2 && isGetHead { + ctx.SetParams("group", strings.Trim(m[1], "/")) + rpm.GetRepositoryConfig(ctx) + return + } + + m = repoFilePattern.FindStringSubmatch(path) + if len(m) == 3 && isGetHead { + ctx.SetParams("group", strings.Trim(m[1], "/")) + ctx.SetParams("filename", m[2]) + if isHead { + rpm.CheckRepositoryFileExistence(ctx) + } else { + rpm.GetRepositoryFile(ctx) + } + return + } + + m = uploadPattern.FindStringSubmatch(path) + if len(m) == 2 && isPut { + reqPackageAccess(perm.AccessModeWrite)(ctx) + if ctx.Written() { + return + } + ctx.SetParams("group", strings.Trim(m[1], "/")) + rpm.UploadPackageFile(ctx) + return + } + + m = filePattern.FindStringSubmatch(path) + if len(m) == 6 && (isGetHead || isDelete) { + ctx.SetParams("group", strings.Trim(m[1], "/")) + ctx.SetParams("name", m[2]) + ctx.SetParams("version", m[3]) + ctx.SetParams("architecture", m[4]) + if isGetHead { + rpm.DownloadPackageFile(ctx) + } else { + reqPackageAccess(perm.AccessModeWrite)(ctx) + if ctx.Written() { + return + } + rpm.DeletePackageFile(ctx) + } + return + } + + ctx.Status(http.StatusNotFound) + }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/rubygems", func() { r.Get("/specs.4.8.gz", rubygems.EnumeratePackages) r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest) @@ -577,82 +647,6 @@ func CommonRoutes() *web.Route { return r } -// Support for uploading rpm packages with arbitrary depth paths -func RpmRoutes(r *web.Route) func() { - var ( - groupRepoInfo = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)\.repo\z`) - groupUpload = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/upload\z`) - groupRpm = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/package/([^/]+)/([^/]+)/([^/]+)(?:/([^/]+\.rpm)|)\z`) - groupMetadata = regexp.MustCompile(`\A((?:/(?:[^/]+))*|)/repodata/([^/]+)\z`) - ) - - return func() { - r.Methods("HEAD,GET,POST,PUT,PATCH,DELETE", "*", func(ctx *context.Context) { - path := ctx.Params("*") - isHead := ctx.Req.Method == "HEAD" - isGetHead := ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET" - isPut := ctx.Req.Method == "PUT" - isDelete := ctx.Req.Method == "DELETE" - - if path == "/repository.key" && isGetHead { - rpm.GetRepositoryKey(ctx) - return - } - - // get repo - m := groupRepoInfo.FindStringSubmatch(path) - if len(m) == 2 && isGetHead { - ctx.SetParams("group", strings.Trim(m[1], "/")) - rpm.GetRepositoryConfig(ctx) - return - } - // get meta - m = groupMetadata.FindStringSubmatch(path) - if len(m) == 3 && isGetHead { - ctx.SetParams("group", strings.Trim(m[1], "/")) - ctx.SetParams("filename", m[2]) - if isHead { - rpm.CheckRepositoryFileExistence(ctx) - } else { - rpm.GetRepositoryFile(ctx) - } - return - } - // upload - m = groupUpload.FindStringSubmatch(path) - if len(m) == 2 && isPut { - reqPackageAccess(perm.AccessModeWrite)(ctx) - if ctx.Written() { - return - } - ctx.SetParams("group", strings.Trim(m[1], "/")) - rpm.UploadPackageFile(ctx) - return - } - // rpm down/delete - m = groupRpm.FindStringSubmatch(path) - if len(m) == 6 { - ctx.SetParams("group", strings.Trim(m[1], "/")) - ctx.SetParams("name", m[2]) - ctx.SetParams("version", m[3]) - ctx.SetParams("architecture", m[4]) - if isGetHead { - rpm.DownloadPackageFile(ctx) - return - } else if isDelete { - reqPackageAccess(perm.AccessModeWrite)(ctx) - if ctx.Written() { - return - } - rpm.DeletePackageFile(ctx) - } - } - // default - ctx.Status(http.StatusNotFound) - }) - } -} - // ContainerRoutes provides endpoints that implement the OCI API to serve containers // These have to be mounted on `/v2/...` to comply with the OCI spec: // https://github.com/opencontainers/distribution-spec/blob/main/spec.md diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index dce3809264..8621242da4 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -114,11 +114,15 @@ func apiErrorDefined(ctx *context.Context, err *namedError) { }) } -// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost for anonymous access) +func apiUnauthorizedError(ctx *context.Context) { + ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`) + apiErrorDefined(ctx, errUnauthorized) +} + +// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost if anonymous access is enabled) func ReqContainerAccess(ctx *context.Context) { - if ctx.Doer == nil { - ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`) - apiErrorDefined(ctx, errUnauthorized) + if ctx.Doer == nil || (setting.Service.RequireSignInView && ctx.Doer.IsGhost()) { + apiUnauthorizedError(ctx) } } @@ -138,10 +142,15 @@ func DetermineSupport(ctx *context.Context) { } // Authenticate creates a token for the current user -// If the current user is anonymous, the ghost user is used +// If the current user is anonymous, the ghost user is used unless RequireSignInView is enabled. func Authenticate(ctx *context.Context) { u := ctx.Doer if u == nil { + if setting.Service.RequireSignInView { + apiUnauthorizedError(ctx) + return + } + u = user_model.NewGhostUser() } diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go index 75d19e2b43..5d06680552 100644 --- a/routers/api/packages/rpm/rpm.go +++ b/routers/api/packages/rpm/rpm.go @@ -34,13 +34,17 @@ func apiError(ctx *context.Context, status int, obj any) { // https://dnf.readthedocs.io/en/latest/conf_ref.html func GetRepositoryConfig(ctx *context.Context) { group := ctx.Params("group") + + var groupParts []string if group != "" { - group = fmt.Sprintf("/%s", group) + groupParts = strings.Split(group, "/") } + url := fmt.Sprintf("%sapi/packages/%s/rpm", setting.AppURL, ctx.Package.Owner.Name) - ctx.PlainText(http.StatusOK, `[gitea-`+ctx.Package.Owner.LowerName+strings.ReplaceAll(group, "/", "-")+`] -name=`+ctx.Package.Owner.Name+` - `+setting.AppName+strings.ReplaceAll(group, "/", " - ")+` -baseurl=`+url+group+`/ + + ctx.PlainText(http.StatusOK, `[gitea-`+strings.Join(append([]string{ctx.Package.Owner.LowerName}, groupParts...), "-")+`] +name=`+strings.Join(append([]string{ctx.Package.Owner.Name, setting.AppName}, groupParts...), " - ")+` +baseurl=`+strings.Join(append([]string{url}, groupParts...), "/")+` enabled=1 gpgcheck=1 gpgkey=`+url+`/repository.key`) @@ -157,7 +161,7 @@ func UploadPackageFile(ctx *context.Context) { Owner: ctx.Package.Owner, PackageType: packages_model.TypeRpm, Name: pck.Name, - Version: strings.Trim(fmt.Sprintf("%s/%s", group, pck.Version), "/"), + Version: pck.Version, }, Creator: ctx.Doer, Metadata: pck.VersionMetadata, @@ -171,7 +175,9 @@ func UploadPackageFile(ctx *context.Context) { Data: buf, IsLead: true, Properties: map[string]string{ - rpm_module.PropertyMetadata: string(fileMetadataRaw), + rpm_module.PropertyGroup: group, + rpm_module.PropertyArchitecture: pck.FileMetadata.Architecture, + rpm_module.PropertyMetadata: string(fileMetadataRaw), }, }, ) @@ -187,7 +193,7 @@ func UploadPackageFile(ctx *context.Context) { return } - if err := rpm_service.BuildRepositoryFiles(ctx, ctx.Package.Owner.ID, group); err != nil { + if err := rpm_service.BuildSpecificRepositoryFiles(ctx, ctx.Package.Owner.ID, group); err != nil { apiError(ctx, http.StatusInternalServerError, err) return } @@ -196,20 +202,20 @@ func UploadPackageFile(ctx *context.Context) { } func DownloadPackageFile(ctx *context.Context) { - group := ctx.Params("group") name := ctx.Params("name") version := ctx.Params("version") + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, PackageType: packages_model.TypeRpm, Name: name, - Version: strings.Trim(fmt.Sprintf("%s/%s", group, version), "/"), + Version: version, }, &packages_service.PackageFileInfo{ Filename: fmt.Sprintf("%s-%s.%s.rpm", name, version, ctx.Params("architecture")), - CompositeKey: group, + CompositeKey: ctx.Params("group"), }, ) if err != nil { @@ -229,6 +235,7 @@ func DeletePackageFile(webctx *context.Context) { name := webctx.Params("name") version := webctx.Params("version") architecture := webctx.Params("architecture") + var pd *packages_model.PackageDescriptor err := db.WithTx(webctx, func(ctx stdctx.Context) error { @@ -236,7 +243,7 @@ func DeletePackageFile(webctx *context.Context) { webctx.Package.Owner.ID, packages_model.TypeRpm, name, - strings.Trim(fmt.Sprintf("%s/%s", group, version), "/"), + version, ) if err != nil { return err @@ -286,7 +293,7 @@ func DeletePackageFile(webctx *context.Context) { notify_service.PackageDelete(webctx, webctx.Doer, pd) } - if err := rpm_service.BuildRepositoryFiles(webctx, webctx.Package.Owner.ID, group); err != nil { + if err := rpm_service.BuildSpecificRepositoryFiles(webctx, webctx.Package.Owner.ID, group); err != nil { apiError(webctx, http.StatusInternalServerError, err) return } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 8ce03cf29c..9810e461de 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -253,7 +253,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre DefaultBranch: opt.DefaultBranch, TrustModel: repo_model.ToTrustModel(opt.TrustModel), IsTemplate: opt.Template, - ObjectFormatName: git.Sha1ObjectFormat.Name(), + ObjectFormatName: opt.ObjectFormatName, }) if err != nil { if repo_model.IsErrRepoAlreadyExist(err) { diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go index f4e1a93a22..35ce215be4 100644 --- a/routers/web/admin/packages.go +++ b/routers/web/admin/packages.go @@ -108,6 +108,6 @@ func CleanupExpiredData(ctx *context.Context) { return } - ctx.Flash.Success(ctx.Tr("packages.cleanup.success")) + ctx.Flash.Success(ctx.Tr("admin.packages.cleanup.success")) ctx.Redirect(setting.AppSubURL + "/admin/packages") } diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 95b1062253..6dbc2c2cbc 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -244,6 +244,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio Title: title, Link: link, Description: desc, + IsPermaLink: "false", Author: &feeds.Author{ Name: act.ActUser.DisplayName(), Email: act.ActUser.GetEmail(), diff --git a/routers/web/githttp.go b/routers/web/githttp.go index 8d0d1ce03a..ab74e9a333 100644 --- a/routers/web/githttp.go +++ b/routers/web/githttp.go @@ -36,8 +36,8 @@ func gitHTTPRouters(m *web.Route) { m.Methods("GET,OPTIONS", "/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates")) m.Methods("GET,OPTIONS", "/objects/info/packs", repo.GetInfoPacks) m.Methods("GET,OPTIONS", "/objects/info/{file:[^/]*}", repo.GetTextFile("")) - m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject) - m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile) - m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile) + m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject) + m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile) + m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile) }, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb()) } diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 1cdae32a32..9cda30d23d 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -279,6 +279,17 @@ func Rerun(ctx *context_module.Context) { return } + // reset run's start and stop time when it is done + if run.Status.IsDone() { + run.PreviousDuration = run.Duration() + run.Started = 0 + run.Stopped = 0 + if err := actions_model.UpdateRun(ctx, run, "started", "stopped", "previous_duration"); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + } + job, jobs := getRunJobs(ctx, runIndex, jobIndex) if ctx.Written() { return diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index b5c550ae45..b64db91406 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -159,7 +159,6 @@ func Create(ctx *context.Context) { ctx.Data["private"] = getRepoPrivate(ctx) ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate ctx.Data["default_branch"] = setting.Repository.DefaultBranch - ctx.Data["hash_type"] = "sha1" ctxUser := checkContextUser(ctx, ctx.FormInt64("org")) if ctx.Written() { @@ -179,6 +178,8 @@ func Create(ctx *context.Context) { ctx.Data["CanCreateRepo"] = ctx.Doer.CanCreateRepo() ctx.Data["MaxCreationLimit"] = ctx.Doer.MaxCreationLimit() + ctx.Data["SupportedObjectFormats"] = git.SupportedObjectFormats + ctx.Data["DefaultObjectFormat"] = git.Sha1ObjectFormat ctx.HTML(http.StatusOK, tplCreate) } diff --git a/routers/web/user/package.go b/routers/web/user/package.go index d8da6a192e..708af3e43c 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/log" alpine_module "code.gitea.io/gitea/modules/packages/alpine" debian_module "code.gitea.io/gitea/modules/packages/debian" + rpm_module "code.gitea.io/gitea/modules/packages/rpm" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" @@ -195,9 +196,9 @@ func ViewPackageVersion(ctx *context.Context) { } } - ctx.Data["Branches"] = branches.Values() - ctx.Data["Repositories"] = repositories.Values() - ctx.Data["Architectures"] = architectures.Values() + ctx.Data["Branches"] = util.Sorted(branches.Values()) + ctx.Data["Repositories"] = util.Sorted(repositories.Values()) + ctx.Data["Architectures"] = util.Sorted(architectures.Values()) case packages_model.TypeDebian: distributions := make(container.Set[string]) components := make(container.Set[string]) @@ -216,9 +217,26 @@ func ViewPackageVersion(ctx *context.Context) { } } - ctx.Data["Distributions"] = distributions.Values() - ctx.Data["Components"] = components.Values() - ctx.Data["Architectures"] = architectures.Values() + ctx.Data["Distributions"] = util.Sorted(distributions.Values()) + ctx.Data["Components"] = util.Sorted(components.Values()) + ctx.Data["Architectures"] = util.Sorted(architectures.Values()) + case packages_model.TypeRpm: + groups := make(container.Set[string]) + architectures := make(container.Set[string]) + + for _, f := range pd.Files { + for _, pp := range f.Properties { + switch pp.Name { + case rpm_module.PropertyGroup: + groups.Add(pp.Value) + case rpm_module.PropertyArchitecture: + architectures.Add(pp.Value) + } + } + } + + ctx.Data["Groups"] = util.Sorted(groups.Values()) + ctx.Data["Architectures"] = util.Sorted(architectures.Values()) } var ( diff --git a/routers/web/web.go b/routers/web/web.go index 22f98d95de..ff0ce0c258 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1235,7 +1235,7 @@ func registerRoutes(m *web.Route) { Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost) m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch). Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost) - m.Combo("/_cherrypick/{sha:([a-f0-9]{7,40})}/*").Get(repo.CherryPick). + m.Combo("/_cherrypick/{sha:([a-f0-9]{7,64})}/*").Get(repo.CherryPick). Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost) }, repo.MustBeEditable) m.Group("", func() { @@ -1377,8 +1377,8 @@ func registerRoutes(m *web.Route) { m.Combo("/*"). Get(repo.Wiki). Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost) - m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) - m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff) + m.Get("/commit/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) + m.Get("/commit/{sha:[a-f0-9]{7,64}}.{ext:patch|diff}", repo.RawDiff) }, repo.MustEnableWiki, func(ctx *context.Context) { ctx.Data["PageIsWiki"] = true ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink() @@ -1498,9 +1498,9 @@ func registerRoutes(m *web.Route) { m.Group("", func() { m.Get("/graph", repo.Graph) - m.Get("/commit/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) - m.Get("/commit/{sha:([a-f0-9]{7,40})$}/load-branches-and-tags", repo.LoadBranchesAndTags) - m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick) + m.Get("/commit/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) + m.Get("/commit/{sha:([a-f0-9]{7,64})$}/load-branches-and-tags", repo.LoadBranchesAndTags) + m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, repo.CherryPick) }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed) @@ -1517,7 +1517,7 @@ func registerRoutes(m *web.Route) { m.Group("", func() { m.Get("/forks", repo.Forks) }, context.RepoRef(), reqRepoCodeReader) - m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) + m.Get("/commit/{sha:([a-f0-9]{7,64})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) }, ignSignIn, context.RepoAssignment, context.UnitTypes()) m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 0618f15602..2a3ffb76f3 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -159,24 +159,28 @@ func notify(ctx context.Context, input *notifyInput) error { workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload, - input.Event == webhook_module.HookEventPush && input.Ref == input.Repo.DefaultBranch, + input.Event == webhook_module.HookEventPush && git.RefName(input.Ref).BranchName() == input.Repo.DefaultBranch, ) if err != nil { return fmt.Errorf("DetectWorkflows: %w", err) } - if len(workflows) == 0 { - log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID) - } else { - for _, wf := range workflows { - if actionsConfig.IsWorkflowDisabled(wf.EntryName) { - log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName) - continue - } + log.Trace("repo %s with commit %s event %s find %d workflows and %d schedules", + input.Repo.RepoPath(), + commit.ID, + input.Event, + len(workflows), + len(schedules), + ) - if wf.TriggerEvent.Name != actions_module.GithubEventPullRequestTarget { - detectedWorkflows = append(detectedWorkflows, wf) - } + for _, wf := range workflows { + if actionsConfig.IsWorkflowDisabled(wf.EntryName) { + log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName) + continue + } + + if wf.TriggerEvent.Name != actions_module.GithubEventPullRequestTarget { + detectedWorkflows = append(detectedWorkflows, wf) } } diff --git a/modules/doctor/authorizedkeys.go b/services/doctor/authorizedkeys.go similarity index 100% rename from modules/doctor/authorizedkeys.go rename to services/doctor/authorizedkeys.go diff --git a/modules/doctor/breaking.go b/services/doctor/breaking.go similarity index 100% rename from modules/doctor/breaking.go rename to services/doctor/breaking.go diff --git a/modules/doctor/checkOldArchives.go b/services/doctor/checkOldArchives.go similarity index 100% rename from modules/doctor/checkOldArchives.go rename to services/doctor/checkOldArchives.go diff --git a/modules/doctor/dbconsistency.go b/services/doctor/dbconsistency.go similarity index 100% rename from modules/doctor/dbconsistency.go rename to services/doctor/dbconsistency.go diff --git a/modules/doctor/dbversion.go b/services/doctor/dbversion.go similarity index 100% rename from modules/doctor/dbversion.go rename to services/doctor/dbversion.go diff --git a/modules/doctor/doctor.go b/services/doctor/doctor.go similarity index 100% rename from modules/doctor/doctor.go rename to services/doctor/doctor.go diff --git a/modules/doctor/fix16961.go b/services/doctor/fix16961.go similarity index 100% rename from modules/doctor/fix16961.go rename to services/doctor/fix16961.go diff --git a/modules/doctor/fix16961_test.go b/services/doctor/fix16961_test.go similarity index 100% rename from modules/doctor/fix16961_test.go rename to services/doctor/fix16961_test.go diff --git a/modules/doctor/fix8312.go b/services/doctor/fix8312.go similarity index 100% rename from modules/doctor/fix8312.go rename to services/doctor/fix8312.go diff --git a/modules/doctor/heads.go b/services/doctor/heads.go similarity index 100% rename from modules/doctor/heads.go rename to services/doctor/heads.go diff --git a/modules/doctor/lfs.go b/services/doctor/lfs.go similarity index 100% rename from modules/doctor/lfs.go rename to services/doctor/lfs.go diff --git a/modules/doctor/mergebase.go b/services/doctor/mergebase.go similarity index 100% rename from modules/doctor/mergebase.go rename to services/doctor/mergebase.go diff --git a/modules/doctor/misc.go b/services/doctor/misc.go similarity index 100% rename from modules/doctor/misc.go rename to services/doctor/misc.go diff --git a/modules/doctor/paths.go b/services/doctor/paths.go similarity index 100% rename from modules/doctor/paths.go rename to services/doctor/paths.go diff --git a/modules/doctor/repository.go b/services/doctor/repository.go similarity index 100% rename from modules/doctor/repository.go rename to services/doctor/repository.go diff --git a/modules/doctor/storage.go b/services/doctor/storage.go similarity index 100% rename from modules/doctor/storage.go rename to services/doctor/storage.go diff --git a/modules/doctor/usertype.go b/services/doctor/usertype.go similarity index 100% rename from modules/doctor/usertype.go rename to services/doctor/usertype.go diff --git a/services/packages/cleanup/cleanup.go b/services/packages/cleanup/cleanup.go index 04ee6c4419..0ff8077bc9 100644 --- a/services/packages/cleanup/cleanup.go +++ b/services/packages/cleanup/cleanup.go @@ -19,6 +19,7 @@ import ( cargo_service "code.gitea.io/gitea/services/packages/cargo" container_service "code.gitea.io/gitea/services/packages/container" debian_service "code.gitea.io/gitea/services/packages/debian" + rpm_service "code.gitea.io/gitea/services/packages/rpm" ) // Task method to execute cleanup rules and cleanup expired package data @@ -127,6 +128,10 @@ func ExecuteCleanupRules(outerCtx context.Context) error { if err := alpine_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { return fmt.Errorf("CleanupRule [%d]: alpine.BuildAllRepositoryFiles failed: %w", pcr.ID, err) } + } else if pcr.Type == packages_model.TypeRpm { + if err := rpm_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { + return fmt.Errorf("CleanupRule [%d]: rpm.BuildAllRepositoryFiles failed: %w", pcr.ID, err) + } } } return nil diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go index 7a49efde4f..c52c8a5dd9 100644 --- a/services/packages/rpm/repository.go +++ b/services/packages/rpm/repository.go @@ -18,6 +18,7 @@ import ( "time" packages_model "code.gitea.io/gitea/models/packages" + rpm_model "code.gitea.io/gitea/models/packages/rpm" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" packages_module "code.gitea.io/gitea/modules/packages" @@ -96,6 +97,39 @@ func generateKeypair() (string, string, error) { return priv.String(), pub.String(), nil } +// BuildAllRepositoryFiles (re)builds all repository files for every available group +func BuildAllRepositoryFiles(ctx context.Context, ownerID int64) error { + pv, err := GetOrCreateRepositoryVersion(ctx, ownerID) + if err != nil { + return err + } + + // 1. Delete all existing repository files + pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID) + if err != nil { + return err + } + + for _, pf := range pfs { + if err := packages_service.DeletePackageFile(ctx, pf); err != nil { + return err + } + } + + // 2. (Re)Build repository files for existing packages + groups, err := rpm_model.GetGroups(ctx, ownerID) + if err != nil { + return err + } + for _, group := range groups { + if err := BuildSpecificRepositoryFiles(ctx, ownerID, group); err != nil { + return fmt.Errorf("failed to build repository files [%s]: %w", group, err) + } + } + + return nil +} + type repoChecksum struct { Value string `xml:",chardata"` Type string `xml:"type,attr"` @@ -126,7 +160,7 @@ type packageData struct { type packageCache = map[*packages_model.PackageFile]*packageData // BuildSpecificRepositoryFiles builds metadata files for the repository -func BuildRepositoryFiles(ctx context.Context, ownerID int64, compositeKey string) error { +func BuildSpecificRepositoryFiles(ctx context.Context, ownerID int64, group string) error { pv, err := GetOrCreateRepositoryVersion(ctx, ownerID) if err != nil { return err @@ -136,7 +170,7 @@ func BuildRepositoryFiles(ctx context.Context, ownerID int64, compositeKey strin OwnerID: ownerID, PackageType: packages_model.TypeRpm, Query: "%.rpm", - CompositeKey: compositeKey, + CompositeKey: group, }) if err != nil { return err @@ -195,15 +229,15 @@ func BuildRepositoryFiles(ctx context.Context, ownerID int64, compositeKey strin cache[pf] = pd } - primary, err := buildPrimary(ctx, pv, pfs, cache, compositeKey) + primary, err := buildPrimary(ctx, pv, pfs, cache, group) if err != nil { return err } - filelists, err := buildFilelists(ctx, pv, pfs, cache, compositeKey) + filelists, err := buildFilelists(ctx, pv, pfs, cache, group) if err != nil { return err } - other, err := buildOther(ctx, pv, pfs, cache, compositeKey) + other, err := buildOther(ctx, pv, pfs, cache, group) if err != nil { return err } @@ -217,12 +251,12 @@ func BuildRepositoryFiles(ctx context.Context, ownerID int64, compositeKey strin filelists, other, }, - compositeKey, + group, ) } // https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#repomd-xml -func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID int64, data []*repoData, compositeKey string) error { +func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID int64, data []*repoData, group string) error { type Repomd struct { XMLName xml.Name `xml:"repomd"` Xmlns string `xml:"xmlns,attr"` @@ -278,7 +312,7 @@ func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID &packages_service.PackageFileCreationInfo{ PackageFileInfo: packages_service.PackageFileInfo{ Filename: file.Name, - CompositeKey: compositeKey, + CompositeKey: group, }, Creator: user_model.NewGhostUser(), Data: file.Data, @@ -295,7 +329,7 @@ func buildRepomd(ctx context.Context, pv *packages_model.PackageVersion, ownerID } // https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#primary-xml -func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, compositeKey string) (*repoData, error) { +func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (*repoData, error) { type Version struct { Epoch string `xml:"epoch,attr"` Version string `xml:"ver,attr"` @@ -434,11 +468,11 @@ func buildPrimary(ctx context.Context, pv *packages_model.PackageVersion, pfs [] XmlnsRpm: "http://linux.duke.edu/metadata/rpm", PackageCount: len(pfs), Packages: packages, - }, compositeKey) + }, group) } // https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#filelists-xml -func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, compositeKey string) (*repoData, error) { //nolint:dupl +func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (*repoData, error) { //nolint:dupl type Version struct { Epoch string `xml:"epoch,attr"` Version string `xml:"ver,attr"` @@ -481,12 +515,11 @@ func buildFilelists(ctx context.Context, pv *packages_model.PackageVersion, pfs Xmlns: "http://linux.duke.edu/metadata/other", PackageCount: len(pfs), Packages: packages, - }, - compositeKey) + }, group) } // https://docs.pulpproject.org/en/2.19/plugins/pulp_rpm/tech-reference/rpm.html#other-xml -func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, compositeKey string) (*repoData, error) { //nolint:dupl +func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*packages_model.PackageFile, c packageCache, group string) (*repoData, error) { //nolint:dupl type Version struct { Epoch string `xml:"epoch,attr"` Version string `xml:"ver,attr"` @@ -529,7 +562,7 @@ func buildOther(ctx context.Context, pv *packages_model.PackageVersion, pfs []*p Xmlns: "http://linux.duke.edu/metadata/other", PackageCount: len(pfs), Packages: packages, - }, compositeKey) + }, group) } // writtenCounter counts all written bytes @@ -549,8 +582,10 @@ func (wc *writtenCounter) Written() int64 { return wc.written } -func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion, filetype string, obj any, compositeKey string) (*repoData, error) { +func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion, filetype string, obj any, group string) (*repoData, error) { content, _ := packages_module.NewHashedBuffer() + defer content.Close() + gzw := gzip.NewWriter(content) wc := &writtenCounter{} h := sha256.New() @@ -574,7 +609,7 @@ func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion, &packages_service.PackageFileCreationInfo{ PackageFileInfo: packages_service.PackageFileInfo{ Filename: filename, - CompositeKey: compositeKey, + CompositeKey: group, }, Creator: user_model.NewGhostUser(), Data: content, diff --git a/services/repository/branch.go b/services/repository/branch.go index 6ddc6badfa..c1e6625ed4 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -65,7 +65,8 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git Page: page, PageSize: pageSize, }, - Keyword: keyword, + Keyword: keyword, + ExcludeBranchNames: []string{repo.DefaultBranch}, } dbBranches, totalNumOfBranches, err := db.FindAndCount[git_model.Branch](ctx, branchOpts) @@ -73,8 +74,6 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git return nil, nil, 0, err } - branchOpts.ExcludeBranchNames = []string{repo.DefaultBranch} - if err := git_model.BranchList(dbBranches).LoadDeletedBy(ctx); err != nil { return nil, nil, 0, err } diff --git a/services/repository/create.go b/services/repository/create.go index bcf2c85c21..0e89573343 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -217,6 +217,10 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt } } + if opts.ObjectFormatName == "" { + opts.ObjectFormatName = git.Sha1ObjectFormat.Name() + } + repo := &repo_model.Repository{ OwnerID: u.ID, Owner: u, diff --git a/services/repository/fork.go b/services/repository/fork.go index 851a69c80f..a8ff2717b0 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -76,17 +76,18 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork defaultBranch = opts.SingleBranch } repo := &repo_model.Repository{ - OwnerID: owner.ID, - Owner: owner, - OwnerName: owner.Name, - Name: opts.Name, - LowerName: strings.ToLower(opts.Name), - Description: opts.Description, - DefaultBranch: defaultBranch, - IsPrivate: opts.BaseRepo.IsPrivate || opts.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate, - IsEmpty: opts.BaseRepo.IsEmpty, - IsFork: true, - ForkID: opts.BaseRepo.ID, + OwnerID: owner.ID, + Owner: owner, + OwnerName: owner.Name, + Name: opts.Name, + LowerName: strings.ToLower(opts.Name), + Description: opts.Description, + DefaultBranch: defaultBranch, + IsPrivate: opts.BaseRepo.IsPrivate || opts.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate, + IsEmpty: opts.BaseRepo.IsEmpty, + IsFork: true, + ForkID: opts.BaseRepo.ID, + ObjectFormatName: opts.BaseRepo.ObjectFormatName, } oldRepoPath := opts.BaseRepo.RepoPath() diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index 7102f73305..fdba0734a2 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -67,6 +67,9 @@ {{if .IsTemplate}} {{ctx.Locale.Tr "repo.desc.template"}} {{end}} + {{if eq .ObjectFormatName "sha256"}} + {{ctx.Locale.Tr "repo.desc.sha256"}} + {{end}} {{if .IsMirror}} {{svg "octicon-mirror"}} {{else if .IsFork}} diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl index b28aa02c1e..c51dcaa3ff 100644 --- a/templates/explore/repo_list.tmpl +++ b/templates/explore/repo_list.tmpl @@ -25,6 +25,9 @@ {{if .IsTemplate}} {{ctx.Locale.Tr "repo.desc.template"}} {{end}} + {{if eq .ObjectFormatName "sha256"}} + {{ctx.Locale.Tr "repo.desc.sha256"}} + {{end}}
diff --git a/templates/package/content/rpm.tmpl b/templates/package/content/rpm.tmpl index 4fd54a3197..0f128fd3fb 100644 --- a/templates/package/content/rpm.tmpl +++ b/templates/package/content/rpm.tmpl @@ -4,15 +4,21 @@
-
# {{ctx.Locale.Tr "packages.rpm.distros.redhat"}}
-{{$group_name:= StringUtils.ReplaceAllStringRegex .PackageDescriptor.Version.Version "(/[^/]+|[^/]*)\\z" "" -}}
-{{- if $group_name -}}
-{{- $group_name = (print "/" $group_name) -}}
-{{- end -}}
-dnf config-manager --add-repo 
+				
{{- if gt (len .Groups) 1 -}}
+# {{ctx.Locale.Tr "packages.rpm.repository.multiple_groups"}}
+
+{{end -}}
+# {{ctx.Locale.Tr "packages.rpm.distros.redhat"}}
+{{- range $group := .Groups}}
+	{{- if $group}}{{$group = print "/" $group}}{{end}}
+dnf config-manager --add-repo 
+{{- end}}
 
 # {{ctx.Locale.Tr "packages.rpm.distros.suse"}}
-zypper addrepo 
+{{- range $group := .Groups}} + {{- if $group}}{{$group = print "/" $group}}{{end}} +zypper addrepo +{{- end}}
@@ -30,6 +36,18 @@ zypper install {{$.PackageDescriptor.Package.Name}}
+

{{ctx.Locale.Tr "packages.rpm.repository"}}

+
+ + + + + + + +
{{ctx.Locale.Tr "packages.rpm.repository.architectures"}}
{{StringUtils.Join .Architectures ", "}}
+
+ {{if or .PackageDescriptor.Metadata.Summary .PackageDescriptor.Metadata.Description}}

{{ctx.Locale.Tr "packages.about"}}

{{if .PackageDescriptor.Metadata.Summary}}
{{.PackageDescriptor.Metadata.Summary}}
{{end}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 7bfed53124..7702770c40 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -3,7 +3,7 @@ {{ctx.Locale.Tr "repo.commits.author"}} - SHA1 + {{StringUtils.ToUpper $.Repository.ObjectFormatName}} {{ctx.Locale.Tr "repo.commits.message"}} {{ctx.Locale.Tr "repo.commits.date"}} diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 3b4b994be7..66f73fb398 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -185,6 +185,19 @@ {{ctx.Locale.Tr "repo.default_branch_helper"}}
+
+ + + {{ctx.Locale.Tr "repo.object_format_helper"}} +
diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index b4d96c3168..15574ad988 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -177,7 +177,7 @@
{{if .IsNothingToCompare}} - {{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived)}} + {{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived) .PageIsComparePull}}
{{ctx.Locale.Tr "repo.pulls.nothing_to_compare_and_allow_empty_pr"}}
@@ -185,8 +185,10 @@
{{template "repo/issue/new_form" .}}
- {{else}} + {{else if and .HeadIsBranch .BaseIsBranch}}
{{ctx.Locale.Tr "repo.pulls.nothing_to_compare"}}
+ {{else}} +
{{ctx.Locale.Tr "repo.pulls.nothing_to_compare_have_tag"}}
{{end}} {{else if and .PageIsComparePull (gt .CommitCount 0)}} {{if .HasPullRequest}} @@ -229,7 +231,7 @@ {{end}} {{$showDiffBox = true}} {{end}} - {{else}} + {{else if not .IsNothingToCompare}} {{$showDiffBox = true}} {{end}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index c362059ef3..dac30af600 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -27,14 +27,10 @@ {{ctx.Locale.Tr "repo.desc.template"}}
{{svg "octicon-repo-template" 18}}
{{end}} + {{if eq .ObjectFormatName "sha256"}} + {{ctx.Locale.Tr "repo.desc.sha256"}} + {{end}}
- {{if $.PullMirror}} -
- {{ctx.Locale.Tr "repo.mirror_from"}} - {{$.PullMirror.RemoteAddress}} - {{if $.PullMirror.UpdatedUnix}}{{ctx.Locale.Tr "repo.mirror_sync"}} {{TimeSinceUnix $.PullMirror.UpdatedUnix ctx.Locale}}{{end}} -
- {{end}} {{if not (or .IsBeingCreated .IsBroken)}}
@@ -147,7 +143,13 @@
{{end}} - {{if $.PullMirror}}
{{ctx.Locale.Tr "repo.mirror_from"}} {{$.PullMirror.RemoteAddress}}
{{end}} + {{if $.PullMirror}} +
+ {{ctx.Locale.Tr "repo.mirror_from"}} + {{$.PullMirror.RemoteAddress}} + {{if $.PullMirror.UpdatedUnix}}{{ctx.Locale.Tr "repo.mirror_sync"}} {{TimeSinceUnix $.PullMirror.UpdatedUnix ctx.Locale}}{{end}} +
+ {{end}} {{if .IsFork}}
{{ctx.Locale.Tr "repo.forked_from"}} {{.BaseRepo.FullName}}
{{end}} {{if .IsGenerated}}
{{ctx.Locale.Tr "repo.generated_from"}} {{(.TemplateRepo ctx).FullName}}
{{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 17e144bc1c..0ce35da96a 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -18777,6 +18777,15 @@ "uniqueItems": true, "x-go-name": "Name" }, + "object_format_name": { + "description": "ObjectFormatName of the underlying git repository", + "type": "string", + "enum": [ + "sha1", + "sha256" + ], + "x-go-name": "ObjectFormatName" + }, "private": { "description": "Whether the repository is private", "type": "boolean", @@ -22742,6 +22751,15 @@ "type": "string", "x-go-name": "Name" }, + "object_format_name": { + "description": "ObjectFormatName of the underlying git repository", + "type": "string", + "enum": [ + "sha1", + "sha256" + ], + "x-go-name": "ObjectFormatName" + }, "open_issues_count": { "type": "integer", "format": "int64", diff --git a/tests/gitea-repositories-meta/user2/test_commit_revert.git/refs/heads/main b/tests/gitea-repositories-meta/user2/test_commit_revert.git/refs/heads/main new file mode 100644 index 0000000000..ab80ca3ca6 --- /dev/null +++ b/tests/gitea-repositories-meta/user2/test_commit_revert.git/refs/heads/main @@ -0,0 +1 @@ +deebcbc752e540bab4ce3ee713d3fc8fdc35b2f7 diff --git a/tests/integration/api_packages_container_test.go b/tests/integration/api_packages_container_test.go index f32d33888b..509ad424e6 100644 --- a/tests/integration/api_packages_container_test.go +++ b/tests/integration/api_packages_container_test.go @@ -21,6 +21,7 @@ import ( container_module "code.gitea.io/gitea/modules/packages/container" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/minio/sha256-simd" @@ -106,6 +107,14 @@ func TestPackageContainer(t *testing.T) { req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)). AddTokenAuth(anonymousToken) MakeRequest(t, req, http.StatusOK) + + defer test.MockVariableValue(&setting.Service.RequireSignInView, true)() + + req = NewRequest(t, "GET", fmt.Sprintf("%sv2", setting.AppURL)) + MakeRequest(t, req, http.StatusUnauthorized) + + req = NewRequest(t, "GET", fmt.Sprintf("%sv2/token", setting.AppURL)) + MakeRequest(t, req, http.StatusUnauthorized) }) t.Run("User", func(t *testing.T) { diff --git a/tests/integration/api_packages_rpm_test.go b/tests/integration/api_packages_rpm_test.go index 822b0b040e..1dcec6099e 100644 --- a/tests/integration/api_packages_rpm_test.go +++ b/tests/integration/api_packages_rpm_test.go @@ -12,6 +12,7 @@ import ( "io" "net/http" "net/http/httptest" + "strings" "testing" "code.gitea.io/gitea/models/db" @@ -20,6 +21,7 @@ import ( user_model "code.gitea.io/gitea/models/user" rpm_module "code.gitea.io/gitea/modules/packages/rpm" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -73,346 +75,362 @@ Mu0UFYgZ/bYnuvn/vz4wtCz8qMwsHUvP0PX3tbYFUctAPdrY6tiiDtcCddDECahx7SuVNP5dpmb5 rootURL := fmt.Sprintf("/api/packages/%s/rpm", user.Name) - t.Run("RepositoryConfig", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + for _, group := range []string{"", "el9", "el9/stable"} { + t.Run(fmt.Sprintf("[Group:%s]", group), func(t *testing.T) { + var groupParts []string + if group != "" { + groupParts = strings.Split(group, "/") + } + groupURL := strings.Join(append([]string{rootURL}, groupParts...), "/") - req := NewRequest(t, "GET", rootURL+"/el9/stable.repo") - resp := MakeRequest(t, req, http.StatusOK) + t.Run("RepositoryConfig", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - expected := fmt.Sprintf(`[gitea-%s-el9-stable] -name=%s - %s - el9 - stable -baseurl=%sapi/packages/%s/rpm/el9/stable/ + req := NewRequest(t, "GET", groupURL+".repo") + resp := MakeRequest(t, req, http.StatusOK) + + expected := fmt.Sprintf(`[gitea-%s] +name=%s +baseurl=%s enabled=1 gpgcheck=1 -gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppName, setting.AppURL, user.Name, setting.AppURL, user.Name) +gpgkey=%sapi/packages/%s/rpm/repository.key`, + strings.Join(append([]string{user.LowerName}, groupParts...), "-"), + strings.Join(append([]string{user.Name, setting.AppName}, groupParts...), " - "), + util.URLJoin(setting.AppURL, groupURL), + setting.AppURL, + user.Name, + ) - assert.Equal(t, expected, resp.Body.String()) - }) + assert.Equal(t, expected, resp.Body.String()) + }) - t.Run("RepositoryKey", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + t.Run("RepositoryKey", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", rootURL+"/repository.key") - resp := MakeRequest(t, req, http.StatusOK) + req := NewRequest(t, "GET", rootURL+"/repository.key") + resp := MakeRequest(t, req, http.StatusOK) - assert.Equal(t, "application/pgp-keys", resp.Header().Get("Content-Type")) - assert.Contains(t, resp.Body.String(), "-----BEGIN PGP PUBLIC KEY BLOCK-----") - }) + assert.Equal(t, "application/pgp-keys", resp.Header().Get("Content-Type")) + assert.Contains(t, resp.Body.String(), "-----BEGIN PGP PUBLIC KEY BLOCK-----") + }) - t.Run("Upload", func(t *testing.T) { - url := rootURL + "/el9/stable/upload" + t.Run("Upload", func(t *testing.T) { + url := groupURL + "/upload" - req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)) - MakeRequest(t, req, http.StatusUnauthorized) + req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)) + MakeRequest(t, req, http.StatusUnauthorized) - req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). - AddBasicAuth(user.Name) - MakeRequest(t, req, http.StatusCreated) + req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusCreated) - pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm) - assert.NoError(t, err) - assert.Len(t, pvs, 1) + pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm) + assert.NoError(t, err) + assert.Len(t, pvs, 1) - pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) - assert.NoError(t, err) - assert.Nil(t, pd.SemVer) - assert.IsType(t, &rpm_module.VersionMetadata{}, pd.Metadata) - assert.Equal(t, packageName, pd.Package.Name) - assert.Equal(t, fmt.Sprintf("el9/stable/%s", packageVersion), pd.Version.Version) + pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) + assert.NoError(t, err) + assert.Nil(t, pd.SemVer) + assert.IsType(t, &rpm_module.VersionMetadata{}, 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, fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture), pfs[0].Name) - assert.True(t, pfs[0].IsLead) + pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) + assert.NoError(t, err) + assert.Len(t, pfs, 1) + assert.Equal(t, fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture), 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) + pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID) + assert.NoError(t, err) + assert.Equal(t, int64(len(content)), pb.Size) - req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). - AddBasicAuth(user.Name) - MakeRequest(t, req, http.StatusConflict) - }) + req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusConflict) + }) - t.Run("Download", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + t.Run("Download", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)) - resp := MakeRequest(t, req, http.StatusOK) + req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture)) + resp := MakeRequest(t, req, http.StatusOK) - assert.Equal(t, content, resp.Body.Bytes()) - }) + assert.Equal(t, content, resp.Body.Bytes()) + }) - t.Run("Repository", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + t.Run("Repository", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - url := rootURL + "/el9/stable/repodata" + url := groupURL + "/repodata" - req := NewRequest(t, "HEAD", url+"/dummy.xml") - MakeRequest(t, req, http.StatusNotFound) + req := NewRequest(t, "HEAD", url+"/dummy.xml") + MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "GET", url+"/dummy.xml") - MakeRequest(t, req, http.StatusNotFound) + req = NewRequest(t, "GET", url+"/dummy.xml") + MakeRequest(t, req, http.StatusNotFound) - t.Run("repomd.xml", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + t.Run("repomd.xml", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - req = NewRequest(t, "HEAD", url+"/repomd.xml") - MakeRequest(t, req, http.StatusOK) + req = NewRequest(t, "HEAD", url+"/repomd.xml") + MakeRequest(t, req, http.StatusOK) - req = NewRequest(t, "GET", url+"/repomd.xml") - resp := MakeRequest(t, req, http.StatusOK) + req = NewRequest(t, "GET", url+"/repomd.xml") + resp := MakeRequest(t, req, http.StatusOK) - type Repomd struct { - XMLName xml.Name `xml:"repomd"` - Xmlns string `xml:"xmlns,attr"` - XmlnsRpm string `xml:"xmlns:rpm,attr"` - Data []struct { - Type string `xml:"type,attr"` - Checksum struct { - Value string `xml:",chardata"` - Type string `xml:"type,attr"` - } `xml:"checksum"` - OpenChecksum struct { - Value string `xml:",chardata"` - Type string `xml:"type,attr"` - } `xml:"open-checksum"` - Location struct { - Href string `xml:"href,attr"` - } `xml:"location"` - Timestamp int64 `xml:"timestamp"` - Size int64 `xml:"size"` - OpenSize int64 `xml:"open-size"` - } `xml:"data"` - } + type Repomd struct { + XMLName xml.Name `xml:"repomd"` + Xmlns string `xml:"xmlns,attr"` + XmlnsRpm string `xml:"xmlns:rpm,attr"` + Data []struct { + Type string `xml:"type,attr"` + Checksum struct { + Value string `xml:",chardata"` + Type string `xml:"type,attr"` + } `xml:"checksum"` + OpenChecksum struct { + Value string `xml:",chardata"` + Type string `xml:"type,attr"` + } `xml:"open-checksum"` + Location struct { + Href string `xml:"href,attr"` + } `xml:"location"` + Timestamp int64 `xml:"timestamp"` + Size int64 `xml:"size"` + OpenSize int64 `xml:"open-size"` + } `xml:"data"` + } - var result Repomd - decodeXML(t, resp, &result) + var result Repomd + decodeXML(t, resp, &result) - assert.Len(t, result.Data, 3) - for _, d := range result.Data { - assert.Equal(t, "sha256", d.Checksum.Type) - assert.NotEmpty(t, d.Checksum.Value) - assert.Equal(t, "sha256", d.OpenChecksum.Type) - assert.NotEmpty(t, d.OpenChecksum.Value) - assert.NotEqual(t, d.Checksum.Value, d.OpenChecksum.Value) - assert.Greater(t, d.OpenSize, d.Size) + assert.Len(t, result.Data, 3) + for _, d := range result.Data { + assert.Equal(t, "sha256", d.Checksum.Type) + assert.NotEmpty(t, d.Checksum.Value) + assert.Equal(t, "sha256", d.OpenChecksum.Type) + assert.NotEmpty(t, d.OpenChecksum.Value) + assert.NotEqual(t, d.Checksum.Value, d.OpenChecksum.Value) + assert.Greater(t, d.OpenSize, d.Size) - switch d.Type { - case "primary": - assert.EqualValues(t, 722, d.Size) - assert.EqualValues(t, 1759, d.OpenSize) - assert.Equal(t, "repodata/primary.xml.gz", d.Location.Href) - case "filelists": - assert.EqualValues(t, 257, d.Size) - assert.EqualValues(t, 326, d.OpenSize) - assert.Equal(t, "repodata/filelists.xml.gz", d.Location.Href) - case "other": - assert.EqualValues(t, 306, d.Size) - assert.EqualValues(t, 394, d.OpenSize) - assert.Equal(t, "repodata/other.xml.gz", d.Location.Href) + switch d.Type { + case "primary": + assert.EqualValues(t, 722, d.Size) + assert.EqualValues(t, 1759, d.OpenSize) + assert.Equal(t, "repodata/primary.xml.gz", d.Location.Href) + case "filelists": + assert.EqualValues(t, 257, d.Size) + assert.EqualValues(t, 326, d.OpenSize) + assert.Equal(t, "repodata/filelists.xml.gz", d.Location.Href) + case "other": + assert.EqualValues(t, 306, d.Size) + assert.EqualValues(t, 394, d.OpenSize) + assert.Equal(t, "repodata/other.xml.gz", d.Location.Href) + } + } + }) + + t.Run("repomd.xml.asc", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req = NewRequest(t, "GET", url+"/repomd.xml.asc") + resp := MakeRequest(t, req, http.StatusOK) + + assert.Contains(t, resp.Body.String(), "-----BEGIN PGP SIGNATURE-----") + }) + + decodeGzipXML := func(t testing.TB, resp *httptest.ResponseRecorder, v any) { + t.Helper() + + zr, err := gzip.NewReader(resp.Body) + assert.NoError(t, err) + + assert.NoError(t, xml.NewDecoder(zr).Decode(v)) } - } + + t.Run("primary.xml.gz", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req = NewRequest(t, "GET", url+"/primary.xml.gz") + resp := MakeRequest(t, req, http.StatusOK) + + type EntryList struct { + Entries []*rpm_module.Entry `xml:"entry"` + } + + type Metadata struct { + XMLName xml.Name `xml:"metadata"` + Xmlns string `xml:"xmlns,attr"` + XmlnsRpm string `xml:"xmlns:rpm,attr"` + PackageCount int `xml:"packages,attr"` + Packages []struct { + XMLName xml.Name `xml:"package"` + Type string `xml:"type,attr"` + Name string `xml:"name"` + Architecture string `xml:"arch"` + Version struct { + Epoch string `xml:"epoch,attr"` + Version string `xml:"ver,attr"` + Release string `xml:"rel,attr"` + } `xml:"version"` + Checksum struct { + Checksum string `xml:",chardata"` + Type string `xml:"type,attr"` + Pkgid string `xml:"pkgid,attr"` + } `xml:"checksum"` + Summary string `xml:"summary"` + Description string `xml:"description"` + Packager string `xml:"packager"` + URL string `xml:"url"` + Time struct { + File uint64 `xml:"file,attr"` + Build uint64 `xml:"build,attr"` + } `xml:"time"` + Size struct { + Package int64 `xml:"package,attr"` + Installed uint64 `xml:"installed,attr"` + Archive uint64 `xml:"archive,attr"` + } `xml:"size"` + Location struct { + Href string `xml:"href,attr"` + } `xml:"location"` + Format struct { + License string `xml:"license"` + Vendor string `xml:"vendor"` + Group string `xml:"group"` + Buildhost string `xml:"buildhost"` + Sourcerpm string `xml:"sourcerpm"` + Provides EntryList `xml:"provides"` + Requires EntryList `xml:"requires"` + Conflicts EntryList `xml:"conflicts"` + Obsoletes EntryList `xml:"obsoletes"` + Files []*rpm_module.File `xml:"file"` + } `xml:"format"` + } `xml:"package"` + } + + var result Metadata + decodeGzipXML(t, resp, &result) + + assert.EqualValues(t, 1, result.PackageCount) + assert.Len(t, result.Packages, 1) + p := result.Packages[0] + assert.Equal(t, "rpm", p.Type) + assert.Equal(t, packageName, p.Name) + assert.Equal(t, packageArchitecture, p.Architecture) + assert.Equal(t, "YES", p.Checksum.Pkgid) + assert.Equal(t, "sha256", p.Checksum.Type) + assert.Equal(t, "f1d5d2ffcbe4a7568e98b864f40d923ecca084e9b9bcd5977ed6521c46d3fa4c", p.Checksum.Checksum) + assert.Equal(t, "https://gitea.io", p.URL) + assert.EqualValues(t, len(content), p.Size.Package) + assert.EqualValues(t, 13, p.Size.Installed) + assert.EqualValues(t, 272, p.Size.Archive) + assert.Equal(t, fmt.Sprintf("package/%s/%s/%s/%s", packageName, packageVersion, packageArchitecture, fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture)), p.Location.Href) + f := p.Format + assert.Equal(t, "MIT", f.License) + assert.Len(t, f.Provides.Entries, 2) + assert.Len(t, f.Requires.Entries, 7) + assert.Empty(t, f.Conflicts.Entries) + assert.Empty(t, f.Obsoletes.Entries) + assert.Len(t, f.Files, 1) + }) + + t.Run("filelists.xml.gz", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req = NewRequest(t, "GET", url+"/filelists.xml.gz") + resp := MakeRequest(t, req, http.StatusOK) + + type Filelists struct { + XMLName xml.Name `xml:"filelists"` + Xmlns string `xml:"xmlns,attr"` + PackageCount int `xml:"packages,attr"` + Packages []struct { + Pkgid string `xml:"pkgid,attr"` + Name string `xml:"name,attr"` + Architecture string `xml:"arch,attr"` + Version struct { + Epoch string `xml:"epoch,attr"` + Version string `xml:"ver,attr"` + Release string `xml:"rel,attr"` + } `xml:"version"` + Files []*rpm_module.File `xml:"file"` + } `xml:"package"` + } + + var result Filelists + decodeGzipXML(t, resp, &result) + + assert.EqualValues(t, 1, result.PackageCount) + assert.Len(t, result.Packages, 1) + p := result.Packages[0] + assert.NotEmpty(t, p.Pkgid) + assert.Equal(t, packageName, p.Name) + assert.Equal(t, packageArchitecture, p.Architecture) + assert.Len(t, p.Files, 1) + f := p.Files[0] + assert.Equal(t, "/usr/local/bin/hello", f.Path) + }) + + t.Run("other.xml.gz", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req = NewRequest(t, "GET", url+"/other.xml.gz") + resp := MakeRequest(t, req, http.StatusOK) + + type Other struct { + XMLName xml.Name `xml:"otherdata"` + Xmlns string `xml:"xmlns,attr"` + PackageCount int `xml:"packages,attr"` + Packages []struct { + Pkgid string `xml:"pkgid,attr"` + Name string `xml:"name,attr"` + Architecture string `xml:"arch,attr"` + Version struct { + Epoch string `xml:"epoch,attr"` + Version string `xml:"ver,attr"` + Release string `xml:"rel,attr"` + } `xml:"version"` + Changelogs []*rpm_module.Changelog `xml:"changelog"` + } `xml:"package"` + } + + var result Other + decodeGzipXML(t, resp, &result) + + assert.EqualValues(t, 1, result.PackageCount) + assert.Len(t, result.Packages, 1) + p := result.Packages[0] + assert.NotEmpty(t, p.Pkgid) + assert.Equal(t, packageName, p.Name) + assert.Equal(t, packageArchitecture, p.Architecture) + assert.Len(t, p.Changelogs, 1) + c := p.Changelogs[0] + assert.Equal(t, "KN4CK3R ", c.Author) + assert.EqualValues(t, 1678276800, c.Date) + assert.Equal(t, "- Changelog message.", c.Text) + }) + }) + + t.Run("Delete", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture)) + MakeRequest(t, req, http.StatusUnauthorized) + + req = NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusNoContent) + + pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm) + assert.NoError(t, err) + assert.Empty(t, pvs) + req = NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusNotFound) + }) }) - - t.Run("repomd.xml.asc", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req = NewRequest(t, "GET", url+"/repomd.xml.asc") - resp := MakeRequest(t, req, http.StatusOK) - - assert.Contains(t, resp.Body.String(), "-----BEGIN PGP SIGNATURE-----") - }) - - decodeGzipXML := func(t testing.TB, resp *httptest.ResponseRecorder, v any) { - t.Helper() - - zr, err := gzip.NewReader(resp.Body) - assert.NoError(t, err) - - assert.NoError(t, xml.NewDecoder(zr).Decode(v)) - } - - t.Run("primary.xml.gz", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req = NewRequest(t, "GET", url+"/primary.xml.gz") - resp := MakeRequest(t, req, http.StatusOK) - - type EntryList struct { - Entries []*rpm_module.Entry `xml:"entry"` - } - - type Metadata struct { - XMLName xml.Name `xml:"metadata"` - Xmlns string `xml:"xmlns,attr"` - XmlnsRpm string `xml:"xmlns:rpm,attr"` - PackageCount int `xml:"packages,attr"` - Packages []struct { - XMLName xml.Name `xml:"package"` - Type string `xml:"type,attr"` - Name string `xml:"name"` - Architecture string `xml:"arch"` - Version struct { - Epoch string `xml:"epoch,attr"` - Version string `xml:"ver,attr"` - Release string `xml:"rel,attr"` - } `xml:"version"` - Checksum struct { - Checksum string `xml:",chardata"` - Type string `xml:"type,attr"` - Pkgid string `xml:"pkgid,attr"` - } `xml:"checksum"` - Summary string `xml:"summary"` - Description string `xml:"description"` - Packager string `xml:"packager"` - URL string `xml:"url"` - Time struct { - File uint64 `xml:"file,attr"` - Build uint64 `xml:"build,attr"` - } `xml:"time"` - Size struct { - Package int64 `xml:"package,attr"` - Installed uint64 `xml:"installed,attr"` - Archive uint64 `xml:"archive,attr"` - } `xml:"size"` - Location struct { - Href string `xml:"href,attr"` - } `xml:"location"` - Format struct { - License string `xml:"license"` - Vendor string `xml:"vendor"` - Group string `xml:"group"` - Buildhost string `xml:"buildhost"` - Sourcerpm string `xml:"sourcerpm"` - Provides EntryList `xml:"provides"` - Requires EntryList `xml:"requires"` - Conflicts EntryList `xml:"conflicts"` - Obsoletes EntryList `xml:"obsoletes"` - Files []*rpm_module.File `xml:"file"` - } `xml:"format"` - } `xml:"package"` - } - - var result Metadata - decodeGzipXML(t, resp, &result) - - assert.EqualValues(t, 1, result.PackageCount) - assert.Len(t, result.Packages, 1) - p := result.Packages[0] - assert.Equal(t, "rpm", p.Type) - assert.Equal(t, packageName, p.Name) - assert.Equal(t, packageArchitecture, p.Architecture) - assert.Equal(t, "YES", p.Checksum.Pkgid) - assert.Equal(t, "sha256", p.Checksum.Type) - assert.Equal(t, "f1d5d2ffcbe4a7568e98b864f40d923ecca084e9b9bcd5977ed6521c46d3fa4c", p.Checksum.Checksum) - assert.Equal(t, "https://gitea.io", p.URL) - assert.EqualValues(t, len(content), p.Size.Package) - assert.EqualValues(t, 13, p.Size.Installed) - assert.EqualValues(t, 272, p.Size.Archive) - assert.Equal(t, fmt.Sprintf("package/%s/%s/%s/%s", packageName, packageVersion, packageArchitecture, fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture)), p.Location.Href) - f := p.Format - assert.Equal(t, "MIT", f.License) - assert.Len(t, f.Provides.Entries, 2) - assert.Len(t, f.Requires.Entries, 7) - assert.Empty(t, f.Conflicts.Entries) - assert.Empty(t, f.Obsoletes.Entries) - assert.Len(t, f.Files, 1) - }) - - t.Run("filelists.xml.gz", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req = NewRequest(t, "GET", url+"/filelists.xml.gz") - resp := MakeRequest(t, req, http.StatusOK) - - type Filelists struct { - XMLName xml.Name `xml:"filelists"` - Xmlns string `xml:"xmlns,attr"` - PackageCount int `xml:"packages,attr"` - Packages []struct { - Pkgid string `xml:"pkgid,attr"` - Name string `xml:"name,attr"` - Architecture string `xml:"arch,attr"` - Version struct { - Epoch string `xml:"epoch,attr"` - Version string `xml:"ver,attr"` - Release string `xml:"rel,attr"` - } `xml:"version"` - Files []*rpm_module.File `xml:"file"` - } `xml:"package"` - } - - var result Filelists - decodeGzipXML(t, resp, &result) - - assert.EqualValues(t, 1, result.PackageCount) - assert.Len(t, result.Packages, 1) - p := result.Packages[0] - assert.NotEmpty(t, p.Pkgid) - assert.Equal(t, packageName, p.Name) - assert.Equal(t, packageArchitecture, p.Architecture) - assert.Len(t, p.Files, 1) - f := p.Files[0] - assert.Equal(t, "/usr/local/bin/hello", f.Path) - }) - - t.Run("other.xml.gz", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req = NewRequest(t, "GET", url+"/other.xml.gz") - resp := MakeRequest(t, req, http.StatusOK) - - type Other struct { - XMLName xml.Name `xml:"otherdata"` - Xmlns string `xml:"xmlns,attr"` - PackageCount int `xml:"packages,attr"` - Packages []struct { - Pkgid string `xml:"pkgid,attr"` - Name string `xml:"name,attr"` - Architecture string `xml:"arch,attr"` - Version struct { - Epoch string `xml:"epoch,attr"` - Version string `xml:"ver,attr"` - Release string `xml:"rel,attr"` - } `xml:"version"` - Changelogs []*rpm_module.Changelog `xml:"changelog"` - } `xml:"package"` - } - - var result Other - decodeGzipXML(t, resp, &result) - - assert.EqualValues(t, 1, result.PackageCount) - assert.Len(t, result.Packages, 1) - p := result.Packages[0] - assert.NotEmpty(t, p.Pkgid) - assert.Equal(t, packageName, p.Name) - assert.Equal(t, packageArchitecture, p.Architecture) - assert.Len(t, p.Changelogs, 1) - c := p.Changelogs[0] - assert.Equal(t, "KN4CK3R ", c.Author) - assert.EqualValues(t, 1678276800, c.Date) - assert.Equal(t, "- Changelog message.", c.Text) - }) - }) - - t.Run("Delete", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - req := NewRequest(t, "DELETE", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)) - MakeRequest(t, req, http.StatusUnauthorized) - - req = NewRequest(t, "DELETE", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)). - AddBasicAuth(user.Name) - MakeRequest(t, req, http.StatusNoContent) - - pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm) - assert.NoError(t, err) - assert.Empty(t, pvs) - req = NewRequest(t, "DELETE", fmt.Sprintf("%s/el9/stable/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)). - AddBasicAuth(user.Name) - MakeRequest(t, req, http.StatusNotFound) - }) + } } diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js index 15e5b21204..f8d0c0cac0 100644 --- a/web_src/js/bootstrap.js +++ b/web_src/js/bootstrap.js @@ -8,10 +8,21 @@ __webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`; export function showGlobalErrorMessage(msg) { const pageContent = document.querySelector('.page-content'); if (!pageContent) return; - const el = document.createElement('div'); - el.innerHTML = `
`; - el.childNodes[0].textContent = msg; - pageContent.prepend(el.childNodes[0]); + + // compact the message to a data attribute to avoid too many duplicated messages + const msgCompact = msg.replace(/\W/g, '').trim(); + let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`); + if (!msgDiv) { + const el = document.createElement('div'); + el.innerHTML = `
`; + msgDiv = el.childNodes[0]; + } + // merge duplicated messages into "the message (count)" format + const msgCount = Number(msgDiv.getAttribute(`data-global-error-msg-count`)) + 1; + msgDiv.setAttribute(`data-global-error-msg-compact`, msgCompact); + msgDiv.setAttribute(`data-global-error-msg-count`, msgCount.toString()); + msgDiv.textContent = msg + (msgCount > 1 ? ` (${msgCount})` : ''); + pageContent.prepend(msgDiv); } /** diff --git a/web_src/js/bootstrap.test.js b/web_src/js/bootstrap.test.js new file mode 100644 index 0000000000..a6b901b92c --- /dev/null +++ b/web_src/js/bootstrap.test.js @@ -0,0 +1,12 @@ +import {showGlobalErrorMessage} from './bootstrap.js'; + +test('showGlobalErrorMessage', () => { + document.body.innerHTML = '
'; + showGlobalErrorMessage('test msg 1'); + showGlobalErrorMessage('test msg 2'); + showGlobalErrorMessage('test msg 1'); // duplicated + + expect(document.body.innerHTML).toContain('>test msg 1 (2)<'); + expect(document.body.innerHTML).toContain('>test msg 2<'); + expect(document.querySelectorAll('.js-global-error').length).toEqual(2); +}); diff --git a/web_src/js/test/setup.js b/web_src/js/test/setup.js index 52355c7adc..6fb0f5dc8f 100644 --- a/web_src/js/test/setup.js +++ b/web_src/js/test/setup.js @@ -1,3 +1,5 @@ +window.__webpack_public_path__ = ''; + window.config = { csrfToken: 'test-csrf-token-123456', pageData: {},