Initial support for push options (#12169)
* Initial support for push options Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix misspelling 🤦 Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix formatting after conflict resolution * defer close git repo * According the GitLab documentation, git >= 2.10 Signed-off-by: jolheiser <john.olheiser@gmail.com> * Words are hard. Thanks @mrsdizzie 😅 Co-authored-by: mrsdizzie <info@mrsdizzie.com> * Only update if there are push options Signed-off-by: jolheiser <john.olheiser@gmail.com> Co-authored-by: mrsdizzie <info@mrsdizzie.com>
This commit is contained in:
		
							parent
							
								
									e7d65cbc6e
								
							
						
					
					
						commit
						43a397ce9a
					
				
					 6 changed files with 118 additions and 0 deletions
				
			
		|  | @ -127,6 +127,12 @@ var checklist = []check{ | ||||||
| 		isDefault: false, | 		isDefault: false, | ||||||
| 		f:         runDoctorUserStarNum, | 		f:         runDoctorUserStarNum, | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		title:     "Enable push options", | ||||||
|  | 		name:      "enable-push-options", | ||||||
|  | 		isDefault: false, | ||||||
|  | 		f:         runDoctorEnablePushOptions, | ||||||
|  | 	}, | ||||||
| 	// more checks please append here
 | 	// more checks please append here
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -605,3 +611,28 @@ func runDoctorCheckDBConsistency(ctx *cli.Context) ([]string, error) { | ||||||
| 
 | 
 | ||||||
| 	return results, nil | 	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 | ||||||
|  | 		} | ||||||
|  | 		defer r.Close() | ||||||
|  | 
 | ||||||
|  | 		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 repositories.", prefix, numRepos)}, err | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								cmd/hook.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								cmd/hook.go
									
									
									
									
									
								
							|  | @ -178,6 +178,7 @@ Gitea or set your environment appropriately.`, "") | ||||||
| 		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), | 		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), | ||||||
| 		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory), | 		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory), | ||||||
| 		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath), | 		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath), | ||||||
|  | 		GitPushOptions:                  pushOptions(), | ||||||
| 		ProtectedBranchID:               prID, | 		ProtectedBranchID:               prID, | ||||||
| 		IsDeployKey:                     isDeployKey, | 		IsDeployKey:                     isDeployKey, | ||||||
| 	} | 	} | ||||||
|  | @ -326,6 +327,7 @@ Gitea or set your environment appropriately.`, "") | ||||||
| 		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), | 		GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories), | ||||||
| 		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory), | 		GitObjectDirectory:              os.Getenv(private.GitObjectDirectory), | ||||||
| 		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath), | 		GitQuarantinePath:               os.Getenv(private.GitQuarantinePath), | ||||||
|  | 		GitPushOptions:                  pushOptions(), | ||||||
| 	} | 	} | ||||||
| 	oldCommitIDs := make([]string, hookBatchSize) | 	oldCommitIDs := make([]string, hookBatchSize) | ||||||
| 	newCommitIDs := make([]string, hookBatchSize) | 	newCommitIDs := make([]string, hookBatchSize) | ||||||
|  | @ -438,3 +440,17 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) { | ||||||
| 		os.Stderr.Sync() | 		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 | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								docs/content/doc/usage/push-options.en-us.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								docs/content/doc/usage/push-options.en-us.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 visibility to public:   | ||||||
|  | ```shell | ||||||
|  | git push -o repo.private=false -u origin master | ||||||
|  | ``` | ||||||
|  | @ -120,6 +120,12 @@ func Init(ctx context.Context) error { | ||||||
| 		return err | 		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", ">=") { | 	if version.Compare(gitVersion, "2.18", ">=") { | ||||||
| 		if err := checkAndSetConfig("core.commitGraph", "true", true); err != nil { | 		if err := checkAndSetConfig("core.commitGraph", "true", true); err != nil { | ||||||
| 			return err | 			return err | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | @ -19,8 +20,28 @@ const ( | ||||||
| 	GitAlternativeObjectDirectories = "GIT_ALTERNATE_OBJECT_DIRECTORIES" | 	GitAlternativeObjectDirectories = "GIT_ALTERNATE_OBJECT_DIRECTORIES" | ||||||
| 	GitObjectDirectory              = "GIT_OBJECT_DIRECTORY" | 	GitObjectDirectory              = "GIT_OBJECT_DIRECTORY" | ||||||
| 	GitQuarantinePath               = "GIT_QUARANTINE_PATH" | 	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
 | // HookOptions represents the options for the Hook calls
 | ||||||
| type HookOptions struct { | type HookOptions struct { | ||||||
| 	OldCommitIDs                    []string | 	OldCommitIDs                    []string | ||||||
|  | @ -31,6 +52,7 @@ type HookOptions struct { | ||||||
| 	GitObjectDirectory              string | 	GitObjectDirectory              string | ||||||
| 	GitAlternativeObjectDirectories string | 	GitAlternativeObjectDirectories string | ||||||
| 	GitQuarantinePath               string | 	GitQuarantinePath               string | ||||||
|  | 	GitPushOptions                  GitPushOptions | ||||||
| 	ProtectedBranchID               int64 | 	ProtectedBranchID               int64 | ||||||
| 	IsDeployKey                     bool | 	IsDeployKey                     bool | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -436,6 +436,18 @@ func HookPostReceive(ctx *macaron.Context, opts private.HookOptions) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Push Options
 | ||||||
|  | 	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 { | ||||||
|  | 			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)) | 	results := make([]private.HookPostReceiveBranchResult, 0, len(opts.OldCommitIDs)) | ||||||
| 
 | 
 | ||||||
| 	// We have to reload the repo in case its state is changed above
 | 	// We have to reload the repo in case its state is changed above
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue