From dee0ac7e3ad988c9860422ad4d9428eeb235f100 Mon Sep 17 00:00:00 2001 From: jolheiser Date: Mon, 6 Jul 2020 17:03:33 -0500 Subject: [PATCH 1/7] Initial support for push options Signed-off-by: jolheiser --- cmd/doctor.go | 30 +++++++++++++++++++ cmd/hook.go | 16 ++++++++++ docs/content/doc/usage/push-options.en-us.md | 31 ++++++++++++++++++++ modules/git/git.go | 3 ++ modules/private/hook.go | 22 ++++++++++++++ routers/private/hook.go | 12 ++++++++ 6 files changed, 114 insertions(+) create mode 100644 docs/content/doc/usage/push-options.en-us.md diff --git a/cmd/doctor.go b/cmd/doctor.go index 45a50c82660d4..7644678d8b28f 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -120,6 +120,12 @@ var checklist = []check{ isDefault: false, f: runDoctorPRMergeBase, }, + { + title: "Enable push options", + name: "enable-push-options", + isDefault: false, + f: runDoctorEnablePushOptions, + }, // more checks please append here } @@ -594,3 +600,27 @@ func runDoctorCheckDBConsistency(ctx *cli.Context) ([]string, error) { return results, nil } + +func runDoctorEnablePushOptions(ctx *cli.Context) ([]string, error) { + numRepos := 0 + _, err := iterateRepositories(func(repo *models.Repository) ([]string, error) { + numRepos++ + r, err := git.OpenRepository(repo.RepoPath()) + if err != nil { + return nil, err + } + + if ctx.Bool("fix") { + _, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunInDir(r.Path) + return nil, err + } + + return nil, nil + }) + + var prefix string + if !ctx.Bool("fix") { + prefix = "DRY RUN: " + } + return []string{fmt.Sprintf("%sEnabled push options for %d respositories.", prefix, numRepos)}, err +} diff --git a/cmd/hook.go b/cmd/hook.go index f5658de7cd75c..863ed832a9ba0 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -178,6 +178,7 @@ Gitea or set your environment appropriately.`, "") GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), GitObjectDirectory: os.Getenv(private.GitObjectDirectory), GitQuarantinePath: os.Getenv(private.GitQuarantinePath), + GitPushOptions: pushOptions(), ProtectedBranchID: prID, IsDeployKey: isDeployKey, } @@ -326,6 +327,7 @@ Gitea or set your environment appropriately.`, "") GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), GitObjectDirectory: os.Getenv(private.GitObjectDirectory), GitQuarantinePath: os.Getenv(private.GitQuarantinePath), + GitPushOptions: pushOptions(), } oldCommitIDs := make([]string, hookBatchSize) newCommitIDs := make([]string, hookBatchSize) @@ -438,3 +440,17 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) { os.Stderr.Sync() } } + +func pushOptions() map[string]string { + opts := make(map[string]string) + if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil { + for idx := 0; idx < pushCount; idx++ { + opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx)) + kv := strings.SplitN(opt, "=", 2) + if len(kv) == 2 { + opts[kv[0]] = kv[1] + } + } + } + return opts +} diff --git a/docs/content/doc/usage/push-options.en-us.md b/docs/content/doc/usage/push-options.en-us.md new file mode 100644 index 0000000000000..9041659d2e116 --- /dev/null +++ b/docs/content/doc/usage/push-options.en-us.md @@ -0,0 +1,31 @@ +--- +date: "2020-07-06T16:00:00+02:00" +title: "Usage: Push Options" +slug: "push-options" +weight: 15 +toc: true +draft: false +menu: + sidebar: + parent: "usage" + name: "Push Options" + weight: 15 + identifier: "push-options" +--- + +# Push Options + +In Gitea `1.13`, support for some [push options](https://git-scm.com/docs/git-push#Documentation/git-push.txt--oltoptiongt) +were added. + + +## Supported Options + +- `repo.private` (true|false) - Change the repository's visibility. +This is particularly useful when combined with push-to-create. +- `repo.template` (true|false) - Change whether the repository is a template. + +Example of changing a repository's visibilit to public: +```shell +git push -o repo.private=false -u origin master +``` diff --git a/modules/git/git.go b/modules/git/git.go index 6f231cee7a40a..9814128eb2e99 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -119,6 +119,9 @@ func Init(ctx context.Context) error { if err := checkAndSetConfig("core.quotePath", "false", true); err != nil { return err } + if err := checkAndSetConfig("receive.advertisePushOptions", "true", true); err != nil { + return err + } if version.Compare(gitVersion, "2.18", ">=") { if err := checkAndSetConfig("core.commitGraph", "true", true); err != nil { diff --git a/modules/private/hook.go b/modules/private/hook.go index 010fc4d72453a..84d66943ba255 100644 --- a/modules/private/hook.go +++ b/modules/private/hook.go @@ -9,6 +9,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" "time" "code.gitea.io/gitea/modules/setting" @@ -19,8 +20,28 @@ const ( GitAlternativeObjectDirectories = "GIT_ALTERNATE_OBJECT_DIRECTORIES" GitObjectDirectory = "GIT_OBJECT_DIRECTORY" GitQuarantinePath = "GIT_QUARANTINE_PATH" + GitPushOptionCount = "GIT_PUSH_OPTION_COUNT" ) +// GitPushOptions is a wrapper around a map[string]string +type GitPushOptions map[string]string + +// GitPushOptions keys +const ( + GitPushOptionRepoPrivate = "repo.private" + GitPushOptionRepoTemplate = "repo.template" +) + +// Bool checks for a key in the map and parses as a boolean +func (g GitPushOptions) Bool(key string, def bool) bool { + if val, ok := g[key]; ok { + if b, err := strconv.ParseBool(val); err == nil { + return b + } + } + return def +} + // HookOptions represents the options for the Hook calls type HookOptions struct { OldCommitIDs []string @@ -31,6 +52,7 @@ type HookOptions struct { GitObjectDirectory string GitAlternativeObjectDirectories string GitQuarantinePath string + GitPushOptions GitPushOptions ProtectedBranchID int64 IsDeployKey bool } diff --git a/routers/private/hook.go b/routers/private/hook.go index 4b57aff588f52..a7c9e64f7031d 100644 --- a/routers/private/hook.go +++ b/routers/private/hook.go @@ -434,6 +434,18 @@ func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) { } } + // Push Options + if repo != nil { + repo.IsPrivate = opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate, repo.IsPrivate) + repo.IsTemplate = opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate, repo.IsTemplate) + if err := models.UpdateRepositoryCols(repo, "is_private", "is_template"); err != nil { + log.Error("Failed to Update: %s/%s Error: %v", ownerName, repoName, err) + ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{ + Err: fmt.Sprintf("Failed to Update: %s/%s Error: %v", ownerName, repoName, err), + }) + } + } + results := make([]private.HookPostReceiveBranchResult, 0, len(opts.OldCommitIDs)) // We have to reload the repo in case its state is changed above From dda785a784d14f6cc7d26806df22348795087391 Mon Sep 17 00:00:00 2001 From: jolheiser Date: Mon, 6 Jul 2020 17:12:29 -0500 Subject: [PATCH 2/7] =?UTF-8?q?Fix=20misspelling=20=F0=9F=A4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: jolheiser --- cmd/doctor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/doctor.go b/cmd/doctor.go index 7644678d8b28f..b32d83ff8e310 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -622,5 +622,5 @@ func runDoctorEnablePushOptions(ctx *cli.Context) ([]string, error) { if !ctx.Bool("fix") { prefix = "DRY RUN: " } - return []string{fmt.Sprintf("%sEnabled push options for %d respositories.", prefix, numRepos)}, err + return []string{fmt.Sprintf("%sEnabled push options for %d repositories.", prefix, numRepos)}, err } From 66f87b4511919764f1d4546af37aef5acdd416b4 Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Wed, 8 Jul 2020 13:52:46 -0500 Subject: [PATCH 3/7] Fix formatting after conflict resolution --- cmd/doctor.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/doctor.go b/cmd/doctor.go index c706facfc3868..27726bf63920f 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -126,12 +126,12 @@ var checklist = []check{ isDefault: false, f: runDoctorUserStarNum, }, - { - title: "Enable push options", + { + title: "Enable push options", name: "enable-push-options", isDefault: false, f: runDoctorEnablePushOptions, - } + }, // more checks please append here } From c4726bd324fb762601031e7603872765f71d2695 Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Thu, 9 Jul 2020 09:51:07 -0500 Subject: [PATCH 4/7] defer close git repo --- cmd/doctor.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/doctor.go b/cmd/doctor.go index 27726bf63920f..1461a2caa608c 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -619,6 +619,7 @@ func runDoctorEnablePushOptions(ctx *cli.Context) ([]string, error) { if err != nil { return nil, err } + defer r.Close() if ctx.Bool("fix") { _, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunInDir(r.Path) From b1f47c4ab6dc1db0b7f7a57ee2a851d4cbe9b943 Mon Sep 17 00:00:00 2001 From: jolheiser Date: Mon, 17 Aug 2020 21:07:46 -0500 Subject: [PATCH 5/7] According the GitLab documentation, git >= 2.10 Signed-off-by: jolheiser --- modules/git/git.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/git/git.go b/modules/git/git.go index 9814128eb2e99..1061bdb0d5253 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -119,8 +119,11 @@ func Init(ctx context.Context) error { if err := checkAndSetConfig("core.quotePath", "false", true); err != nil { return err } - if err := checkAndSetConfig("receive.advertisePushOptions", "true", true); err != nil { - return err + + if version.Compare(gitVersion, "2.10", ">=") { + if err := checkAndSetConfig("receive.advertisePushOptions", "true", true); err != nil { + return err + } } if version.Compare(gitVersion, "2.18", ">=") { From 054aaf1a89ab0199f0a389c3f0051ab27216b96f Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Mon, 17 Aug 2020 21:13:23 -0500 Subject: [PATCH 6/7] Words are hard. Thanks @mrsdizzie :sweat_smile: Co-authored-by: mrsdizzie --- docs/content/doc/usage/push-options.en-us.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/doc/usage/push-options.en-us.md b/docs/content/doc/usage/push-options.en-us.md index 9041659d2e116..439d13b42f88e 100644 --- a/docs/content/doc/usage/push-options.en-us.md +++ b/docs/content/doc/usage/push-options.en-us.md @@ -25,7 +25,7 @@ were added. This is particularly useful when combined with push-to-create. - `repo.template` (true|false) - Change whether the repository is a template. -Example of changing a repository's visibilit to public: +Example of changing a repository's visibility to public: ```shell git push -o repo.private=false -u origin master ``` From 8158a85c523ed8dada19c2fbebdcdee9f6ab7419 Mon Sep 17 00:00:00 2001 From: jolheiser Date: Thu, 20 Aug 2020 17:07:41 -0500 Subject: [PATCH 7/7] Only update if there are push options Signed-off-by: jolheiser --- routers/private/hook.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/private/hook.go b/routers/private/hook.go index a7c9e64f7031d..cfefb8f82cca0 100644 --- a/routers/private/hook.go +++ b/routers/private/hook.go @@ -435,7 +435,7 @@ func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) { } // Push Options - if repo != nil { + if repo != nil && len(opts.GitPushOptions) > 0 { repo.IsPrivate = opts.GitPushOptions.Bool(private.GitPushOptionRepoPrivate, repo.IsPrivate) repo.IsTemplate = opts.GitPushOptions.Bool(private.GitPushOptionRepoTemplate, repo.IsTemplate) if err := models.UpdateRepositoryCols(repo, "is_private", "is_template"); err != nil {