Make hook status printing configurable with delay (#9641)
* Delay printing hook statuses until after 1 second * Move to a 5s delay, wrapped writer structure and add config * Update cmd/hook.go * Apply suggestions from code review * Update cmd/hook.go Co-authored-by: Antoine GIRARD <sapk@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									83f9359a75
								
							
						
					
					
						commit
						65baacf227
					
				
					 3 changed files with 128 additions and 26 deletions
				
			
		
							
								
								
									
										148
									
								
								cmd/hook.go
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								cmd/hook.go
									
									
									
									
									
								
							|  | @ -8,10 +8,12 @@ import ( | |||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
|  | @ -58,6 +60,85 @@ var ( | |||
| 	} | ||||
| ) | ||||
| 
 | ||||
| type delayWriter struct { | ||||
| 	internal io.Writer | ||||
| 	buf      *bytes.Buffer | ||||
| 	timer    *time.Timer | ||||
| } | ||||
| 
 | ||||
| func newDelayWriter(internal io.Writer, delay time.Duration) *delayWriter { | ||||
| 	timer := time.NewTimer(delay) | ||||
| 	return &delayWriter{ | ||||
| 		internal: internal, | ||||
| 		buf:      &bytes.Buffer{}, | ||||
| 		timer:    timer, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (d *delayWriter) Write(p []byte) (n int, err error) { | ||||
| 	if d.buf != nil { | ||||
| 		select { | ||||
| 		case <-d.timer.C: | ||||
| 			_, err := d.internal.Write(d.buf.Bytes()) | ||||
| 			if err != nil { | ||||
| 				return 0, err | ||||
| 			} | ||||
| 			d.buf = nil | ||||
| 			return d.internal.Write(p) | ||||
| 		default: | ||||
| 			return d.buf.Write(p) | ||||
| 		} | ||||
| 	} | ||||
| 	return d.internal.Write(p) | ||||
| } | ||||
| 
 | ||||
| func (d *delayWriter) WriteString(s string) (n int, err error) { | ||||
| 	if d.buf != nil { | ||||
| 		select { | ||||
| 		case <-d.timer.C: | ||||
| 			_, err := d.internal.Write(d.buf.Bytes()) | ||||
| 			if err != nil { | ||||
| 				return 0, err | ||||
| 			} | ||||
| 			d.buf = nil | ||||
| 			return d.internal.Write([]byte(s)) | ||||
| 		default: | ||||
| 			return d.buf.WriteString(s) | ||||
| 		} | ||||
| 	} | ||||
| 	return d.internal.Write([]byte(s)) | ||||
| } | ||||
| 
 | ||||
| func (d *delayWriter) Close() error { | ||||
| 	if d == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	stopped := d.timer.Stop() | ||||
| 	if stopped { | ||||
| 		return nil | ||||
| 	} | ||||
| 	select { | ||||
| 	case <-d.timer.C: | ||||
| 	default: | ||||
| 	} | ||||
| 	if d.buf == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	_, err := d.internal.Write(d.buf.Bytes()) | ||||
| 	d.buf = nil | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| type nilWriter struct{} | ||||
| 
 | ||||
| func (n *nilWriter) Write(p []byte) (int, error) { | ||||
| 	return len(p), nil | ||||
| } | ||||
| 
 | ||||
| func (n *nilWriter) WriteString(s string) (int, error) { | ||||
| 	return len(s), nil | ||||
| } | ||||
| 
 | ||||
| func runHookPreReceive(c *cli.Context) error { | ||||
| 	if os.Getenv(models.EnvIsInternal) == "true" { | ||||
| 		return nil | ||||
|  | @ -101,6 +182,18 @@ Gitea or set your environment appropriately.`, "") | |||
| 	total := 0 | ||||
| 	lastline := 0 | ||||
| 
 | ||||
| 	var out io.Writer | ||||
| 	out = &nilWriter{} | ||||
| 	if setting.Git.VerbosePush { | ||||
| 		if setting.Git.VerbosePushDelay > 0 { | ||||
| 			dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay) | ||||
| 			defer dWriter.Close() | ||||
| 			out = dWriter | ||||
| 		} else { | ||||
| 			out = os.Stdout | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for scanner.Scan() { | ||||
| 		// TODO: support news feeds for wiki
 | ||||
| 		if isWiki { | ||||
|  | @ -124,12 +217,10 @@ Gitea or set your environment appropriately.`, "") | |||
| 			newCommitIDs[count] = newCommitID | ||||
| 			refFullNames[count] = refFullName | ||||
| 			count++ | ||||
| 			fmt.Fprintf(os.Stdout, "*") | ||||
| 			os.Stdout.Sync() | ||||
| 			fmt.Fprintf(out, "*") | ||||
| 
 | ||||
| 			if count >= hookBatchSize { | ||||
| 				fmt.Fprintf(os.Stdout, " Checking %d branches\n", count) | ||||
| 				os.Stdout.Sync() | ||||
| 				fmt.Fprintf(out, " Checking %d branches\n", count) | ||||
| 
 | ||||
| 				hookOptions.OldCommitIDs = oldCommitIDs | ||||
| 				hookOptions.NewCommitIDs = newCommitIDs | ||||
|  | @ -147,12 +238,10 @@ Gitea or set your environment appropriately.`, "") | |||
| 				lastline = 0 | ||||
| 			} | ||||
| 		} else { | ||||
| 			fmt.Fprintf(os.Stdout, ".") | ||||
| 			os.Stdout.Sync() | ||||
| 			fmt.Fprintf(out, ".") | ||||
| 		} | ||||
| 		if lastline >= hookBatchSize { | ||||
| 			fmt.Fprintf(os.Stdout, "\n") | ||||
| 			os.Stdout.Sync() | ||||
| 			fmt.Fprintf(out, "\n") | ||||
| 			lastline = 0 | ||||
| 		} | ||||
| 	} | ||||
|  | @ -162,8 +251,7 @@ Gitea or set your environment appropriately.`, "") | |||
| 		hookOptions.NewCommitIDs = newCommitIDs[:count] | ||||
| 		hookOptions.RefFullNames = refFullNames[:count] | ||||
| 
 | ||||
| 		fmt.Fprintf(os.Stdout, " Checking %d branches\n", count) | ||||
| 		os.Stdout.Sync() | ||||
| 		fmt.Fprintf(out, " Checking %d branches\n", count) | ||||
| 
 | ||||
| 		statusCode, msg := private.HookPreReceive(username, reponame, hookOptions) | ||||
| 		switch statusCode { | ||||
|  | @ -173,14 +261,11 @@ Gitea or set your environment appropriately.`, "") | |||
| 			fail(msg, "") | ||||
| 		} | ||||
| 	} else if lastline > 0 { | ||||
| 		fmt.Fprintf(os.Stdout, "\n") | ||||
| 		os.Stdout.Sync() | ||||
| 		fmt.Fprintf(out, "\n") | ||||
| 		lastline = 0 | ||||
| 	} | ||||
| 
 | ||||
| 	fmt.Fprintf(os.Stdout, "Checked %d references in total\n", total) | ||||
| 	os.Stdout.Sync() | ||||
| 
 | ||||
| 	fmt.Fprintf(out, "Checked %d references in total\n", total) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  | @ -206,6 +291,19 @@ Gitea or set your environment appropriately.`, "") | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var out io.Writer | ||||
| 	var dWriter *delayWriter | ||||
| 	out = &nilWriter{} | ||||
| 	if setting.Git.VerbosePush { | ||||
| 		if setting.Git.VerbosePushDelay > 0 { | ||||
| 			dWriter = newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay) | ||||
| 			defer dWriter.Close() | ||||
| 			out = dWriter | ||||
| 		} else { | ||||
| 			out = os.Stdout | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// the environment setted on serv command
 | ||||
| 	repoUser := os.Getenv(models.EnvRepoUsername) | ||||
| 	isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true") | ||||
|  | @ -241,7 +339,7 @@ Gitea or set your environment appropriately.`, "") | |||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		fmt.Fprintf(os.Stdout, ".") | ||||
| 		fmt.Fprintf(out, ".") | ||||
| 		oldCommitIDs[count] = string(fields[0]) | ||||
| 		newCommitIDs[count] = string(fields[1]) | ||||
| 		refFullNames[count] = string(fields[2]) | ||||
|  | @ -250,16 +348,15 @@ Gitea or set your environment appropriately.`, "") | |||
| 		} | ||||
| 		count++ | ||||
| 		total++ | ||||
| 		os.Stdout.Sync() | ||||
| 
 | ||||
| 		if count >= hookBatchSize { | ||||
| 			fmt.Fprintf(os.Stdout, " Processing %d references\n", count) | ||||
| 			os.Stdout.Sync() | ||||
| 			fmt.Fprintf(out, " Processing %d references\n", count) | ||||
| 			hookOptions.OldCommitIDs = oldCommitIDs | ||||
| 			hookOptions.NewCommitIDs = newCommitIDs | ||||
| 			hookOptions.RefFullNames = refFullNames | ||||
| 			resp, err := private.HookPostReceive(repoUser, repoName, hookOptions) | ||||
| 			if resp == nil { | ||||
| 				_ = dWriter.Close() | ||||
| 				hookPrintResults(results) | ||||
| 				fail("Internal Server Error", err) | ||||
| 			} | ||||
|  | @ -277,9 +374,9 @@ Gitea or set your environment appropriately.`, "") | |||
| 				fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 		fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total) | ||||
| 		os.Stdout.Sync() | ||||
| 		fmt.Fprintf(out, "Processed %d references in total\n", total) | ||||
| 
 | ||||
| 		_ = dWriter.Close() | ||||
| 		hookPrintResults(results) | ||||
| 		return nil | ||||
| 	} | ||||
|  | @ -288,19 +385,18 @@ Gitea or set your environment appropriately.`, "") | |||
| 	hookOptions.NewCommitIDs = newCommitIDs[:count] | ||||
| 	hookOptions.RefFullNames = refFullNames[:count] | ||||
| 
 | ||||
| 	fmt.Fprintf(os.Stdout, " Processing %d references\n", count) | ||||
| 	os.Stdout.Sync() | ||||
| 	fmt.Fprintf(out, " Processing %d references\n", count) | ||||
| 
 | ||||
| 	resp, err := private.HookPostReceive(repoUser, repoName, hookOptions) | ||||
| 	if resp == nil { | ||||
| 		_ = dWriter.Close() | ||||
| 		hookPrintResults(results) | ||||
| 		fail("Internal Server Error", err) | ||||
| 	} | ||||
| 	wasEmpty = wasEmpty || resp.RepoWasEmpty | ||||
| 	results = append(results, resp.Results...) | ||||
| 
 | ||||
| 	fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total) | ||||
| 	os.Stdout.Sync() | ||||
| 	fmt.Fprintf(out, "Processed %d references in total\n", total) | ||||
| 
 | ||||
| 	if wasEmpty && masterPushed { | ||||
| 		// We need to tell the repo to reset the default branch to master
 | ||||
|  | @ -309,7 +405,7 @@ Gitea or set your environment appropriately.`, "") | |||
| 			fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	_ = dWriter.Close() | ||||
| 	hookPrintResults(results) | ||||
| 
 | ||||
| 	return nil | ||||
|  |  | |||
|  | @ -522,6 +522,8 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` | |||
| - `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view. | ||||
| - `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/ | ||||
| - `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1 | ||||
| - `VERBOSE_PUSH`: **true**: Print status information about pushes as they are being processed. | ||||
| - `VERBOSE_PUSH_DELAY`: **5s**: Only print verbose information if push takes longer than this delay. | ||||
| 
 | ||||
| ## Git - Timeout settings (`git.timeout`) | ||||
| - `DEFAUlT`: **360**: Git operations default timeout seconds. | ||||
|  |  | |||
|  | @ -21,6 +21,8 @@ var ( | |||
| 		MaxGitDiffLines           int | ||||
| 		MaxGitDiffLineCharacters  int | ||||
| 		MaxGitDiffFiles           int | ||||
| 		VerbosePush               bool | ||||
| 		VerbosePushDelay          time.Duration | ||||
| 		GCArgs                    []string `ini:"GC_ARGS" delim:" "` | ||||
| 		EnableAutoGitWireProtocol bool | ||||
| 		Timeout                   struct { | ||||
|  | @ -36,6 +38,8 @@ var ( | |||
| 		MaxGitDiffLines:           1000, | ||||
| 		MaxGitDiffLineCharacters:  5000, | ||||
| 		MaxGitDiffFiles:           100, | ||||
| 		VerbosePush:               true, | ||||
| 		VerbosePushDelay:          5 * time.Second, | ||||
| 		GCArgs:                    []string{}, | ||||
| 		EnableAutoGitWireProtocol: true, | ||||
| 		Timeout: struct { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue