feat: Production readiness improvements for WHOOSH council formation
Major security, observability, and configuration improvements:
## Security Hardening
- Implemented configurable CORS (no more wildcards)
- Added comprehensive auth middleware for admin endpoints
- Enhanced webhook HMAC validation
- Added input validation and rate limiting
- Security headers and CSP policies
## Configuration Management
- Made N8N webhook URL configurable (WHOOSH_N8N_BASE_URL)
- Replaced all hardcoded endpoints with environment variables
- Added feature flags for LLM vs heuristic composition
- Gitea fetch hardening with EAGER_FILTER and FULL_RESCAN options
## API Completeness
- Implemented GetCouncilComposition function
- Added GET /api/v1/councils/{id} endpoint
- Council artifacts API (POST/GET /api/v1/councils/{id}/artifacts)
- /admin/health/details endpoint with component status
- Database lookup for repository URLs (no hardcoded fallbacks)
## Observability & Performance
- Added OpenTelemetry distributed tracing with goal/pulse correlation
- Performance optimization database indexes
- Comprehensive health monitoring
- Enhanced logging and error handling
## Infrastructure
- Production-ready P2P discovery (replaces mock implementation)
- Removed unused Redis configuration
- Enhanced Docker Swarm integration
- Added migration files for performance indexes
## Code Quality
- Comprehensive input validation
- Graceful error handling and failsafe fallbacks
- Backwards compatibility maintained
- Following security best practices
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
			
			
This commit is contained in:
		
							
								
								
									
										462
									
								
								vendor/golang.org/x/tools/internal/gocommand/invoke.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										462
									
								
								vendor/golang.org/x/tools/internal/gocommand/invoke.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,462 @@ | ||||
| // Copyright 2020 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // Package gocommand is a helper for calling the go command. | ||||
| package gocommand | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	exec "golang.org/x/sys/execabs" | ||||
|  | ||||
| 	"golang.org/x/tools/internal/event" | ||||
| 	"golang.org/x/tools/internal/event/keys" | ||||
| 	"golang.org/x/tools/internal/event/label" | ||||
| 	"golang.org/x/tools/internal/event/tag" | ||||
| ) | ||||
|  | ||||
| // An Runner will run go command invocations and serialize | ||||
| // them if it sees a concurrency error. | ||||
| type Runner struct { | ||||
| 	// once guards the runner initialization. | ||||
| 	once sync.Once | ||||
|  | ||||
| 	// inFlight tracks available workers. | ||||
| 	inFlight chan struct{} | ||||
|  | ||||
| 	// serialized guards the ability to run a go command serially, | ||||
| 	// to avoid deadlocks when claiming workers. | ||||
| 	serialized chan struct{} | ||||
| } | ||||
|  | ||||
| const maxInFlight = 10 | ||||
|  | ||||
| func (runner *Runner) initialize() { | ||||
| 	runner.once.Do(func() { | ||||
| 		runner.inFlight = make(chan struct{}, maxInFlight) | ||||
| 		runner.serialized = make(chan struct{}, 1) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // 1.13: go: updates to go.mod needed, but contents have changed | ||||
| // 1.14: go: updating go.mod: existing contents have changed since last read | ||||
| var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`) | ||||
|  | ||||
| // verb is an event label for the go command verb. | ||||
| var verb = keys.NewString("verb", "go command verb") | ||||
|  | ||||
| func invLabels(inv Invocation) []label.Label { | ||||
| 	return []label.Label{verb.Of(inv.Verb), tag.Directory.Of(inv.WorkingDir)} | ||||
| } | ||||
|  | ||||
| // Run is a convenience wrapper around RunRaw. | ||||
| // It returns only stdout and a "friendly" error. | ||||
| func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) { | ||||
| 	ctx, done := event.Start(ctx, "gocommand.Runner.Run", invLabels(inv)...) | ||||
| 	defer done() | ||||
|  | ||||
| 	stdout, _, friendly, _ := runner.RunRaw(ctx, inv) | ||||
| 	return stdout, friendly | ||||
| } | ||||
|  | ||||
| // RunPiped runs the invocation serially, always waiting for any concurrent | ||||
| // invocations to complete first. | ||||
| func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error { | ||||
| 	ctx, done := event.Start(ctx, "gocommand.Runner.RunPiped", invLabels(inv)...) | ||||
| 	defer done() | ||||
|  | ||||
| 	_, err := runner.runPiped(ctx, inv, stdout, stderr) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // RunRaw runs the invocation, serializing requests only if they fight over | ||||
| // go.mod changes. | ||||
| func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { | ||||
| 	ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...) | ||||
| 	defer done() | ||||
| 	// Make sure the runner is always initialized. | ||||
| 	runner.initialize() | ||||
|  | ||||
| 	// First, try to run the go command concurrently. | ||||
| 	stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) | ||||
|  | ||||
| 	// If we encounter a load concurrency error, we need to retry serially. | ||||
| 	if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { | ||||
| 		return stdout, stderr, friendlyErr, err | ||||
| 	} | ||||
| 	event.Error(ctx, "Load concurrency error, will retry serially", err) | ||||
|  | ||||
| 	// Run serially by calling runPiped. | ||||
| 	stdout.Reset() | ||||
| 	stderr.Reset() | ||||
| 	friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) | ||||
| 	return stdout, stderr, friendlyErr, err | ||||
| } | ||||
|  | ||||
| func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { | ||||
| 	// Wait for 1 worker to become available. | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return nil, nil, nil, ctx.Err() | ||||
| 	case runner.inFlight <- struct{}{}: | ||||
| 		defer func() { <-runner.inFlight }() | ||||
| 	} | ||||
|  | ||||
| 	stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{} | ||||
| 	friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr) | ||||
| 	return stdout, stderr, friendlyErr, err | ||||
| } | ||||
|  | ||||
| func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { | ||||
| 	// Make sure the runner is always initialized. | ||||
| 	runner.initialize() | ||||
|  | ||||
| 	// Acquire the serialization lock. This avoids deadlocks between two | ||||
| 	// runPiped commands. | ||||
| 	select { | ||||
| 	case <-ctx.Done(): | ||||
| 		return nil, ctx.Err() | ||||
| 	case runner.serialized <- struct{}{}: | ||||
| 		defer func() { <-runner.serialized }() | ||||
| 	} | ||||
|  | ||||
| 	// Wait for all in-progress go commands to return before proceeding, | ||||
| 	// to avoid load concurrency errors. | ||||
| 	for i := 0; i < maxInFlight; i++ { | ||||
| 		select { | ||||
| 		case <-ctx.Done(): | ||||
| 			return nil, ctx.Err() | ||||
| 		case runner.inFlight <- struct{}{}: | ||||
| 			// Make sure we always "return" any workers we took. | ||||
| 			defer func() { <-runner.inFlight }() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return inv.runWithFriendlyError(ctx, stdout, stderr) | ||||
| } | ||||
|  | ||||
| // An Invocation represents a call to the go command. | ||||
| type Invocation struct { | ||||
| 	Verb       string | ||||
| 	Args       []string | ||||
| 	BuildFlags []string | ||||
|  | ||||
| 	// If ModFlag is set, the go command is invoked with -mod=ModFlag. | ||||
| 	ModFlag string | ||||
|  | ||||
| 	// If ModFile is set, the go command is invoked with -modfile=ModFile. | ||||
| 	ModFile string | ||||
|  | ||||
| 	// If Overlay is set, the go command is invoked with -overlay=Overlay. | ||||
| 	Overlay string | ||||
|  | ||||
| 	// If CleanEnv is set, the invocation will run only with the environment | ||||
| 	// in Env, not starting with os.Environ. | ||||
| 	CleanEnv   bool | ||||
| 	Env        []string | ||||
| 	WorkingDir string | ||||
| 	Logf       func(format string, args ...interface{}) | ||||
| } | ||||
|  | ||||
| func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { | ||||
| 	rawError = i.run(ctx, stdout, stderr) | ||||
| 	if rawError != nil { | ||||
| 		friendlyError = rawError | ||||
| 		// Check for 'go' executable not being found. | ||||
| 		if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound { | ||||
| 			friendlyError = fmt.Errorf("go command required, not found: %v", ee) | ||||
| 		} | ||||
| 		if ctx.Err() != nil { | ||||
| 			friendlyError = ctx.Err() | ||||
| 		} | ||||
| 		friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error { | ||||
| 	log := i.Logf | ||||
| 	if log == nil { | ||||
| 		log = func(string, ...interface{}) {} | ||||
| 	} | ||||
|  | ||||
| 	goArgs := []string{i.Verb} | ||||
|  | ||||
| 	appendModFile := func() { | ||||
| 		if i.ModFile != "" { | ||||
| 			goArgs = append(goArgs, "-modfile="+i.ModFile) | ||||
| 		} | ||||
| 	} | ||||
| 	appendModFlag := func() { | ||||
| 		if i.ModFlag != "" { | ||||
| 			goArgs = append(goArgs, "-mod="+i.ModFlag) | ||||
| 		} | ||||
| 	} | ||||
| 	appendOverlayFlag := func() { | ||||
| 		if i.Overlay != "" { | ||||
| 			goArgs = append(goArgs, "-overlay="+i.Overlay) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch i.Verb { | ||||
| 	case "env", "version": | ||||
| 		goArgs = append(goArgs, i.Args...) | ||||
| 	case "mod": | ||||
| 		// mod needs the sub-verb before flags. | ||||
| 		goArgs = append(goArgs, i.Args[0]) | ||||
| 		appendModFile() | ||||
| 		goArgs = append(goArgs, i.Args[1:]...) | ||||
| 	case "get": | ||||
| 		goArgs = append(goArgs, i.BuildFlags...) | ||||
| 		appendModFile() | ||||
| 		goArgs = append(goArgs, i.Args...) | ||||
|  | ||||
| 	default: // notably list and build. | ||||
| 		goArgs = append(goArgs, i.BuildFlags...) | ||||
| 		appendModFile() | ||||
| 		appendModFlag() | ||||
| 		appendOverlayFlag() | ||||
| 		goArgs = append(goArgs, i.Args...) | ||||
| 	} | ||||
| 	cmd := exec.Command("go", goArgs...) | ||||
| 	cmd.Stdout = stdout | ||||
| 	cmd.Stderr = stderr | ||||
|  | ||||
| 	// cmd.WaitDelay was added only in go1.20 (see #50436). | ||||
| 	if waitDelay := reflect.ValueOf(cmd).Elem().FieldByName("WaitDelay"); waitDelay.IsValid() { | ||||
| 		// https://go.dev/issue/59541: don't wait forever copying stderr | ||||
| 		// after the command has exited. | ||||
| 		// After CL 484741 we copy stdout manually, so we we'll stop reading that as | ||||
| 		// soon as ctx is done. However, we also don't want to wait around forever | ||||
| 		// for stderr. Give a much-longer-than-reasonable delay and then assume that | ||||
| 		// something has wedged in the kernel or runtime. | ||||
| 		waitDelay.Set(reflect.ValueOf(30 * time.Second)) | ||||
| 	} | ||||
|  | ||||
| 	// On darwin the cwd gets resolved to the real path, which breaks anything that | ||||
| 	// expects the working directory to keep the original path, including the | ||||
| 	// go command when dealing with modules. | ||||
| 	// The Go stdlib has a special feature where if the cwd and the PWD are the | ||||
| 	// same node then it trusts the PWD, so by setting it in the env for the child | ||||
| 	// process we fix up all the paths returned by the go command. | ||||
| 	if !i.CleanEnv { | ||||
| 		cmd.Env = os.Environ() | ||||
| 	} | ||||
| 	cmd.Env = append(cmd.Env, i.Env...) | ||||
| 	if i.WorkingDir != "" { | ||||
| 		cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir) | ||||
| 		cmd.Dir = i.WorkingDir | ||||
| 	} | ||||
|  | ||||
| 	defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) | ||||
|  | ||||
| 	return runCmdContext(ctx, cmd) | ||||
| } | ||||
|  | ||||
| // DebugHangingGoCommands may be set by tests to enable additional | ||||
| // instrumentation (including panics) for debugging hanging Go commands. | ||||
| // | ||||
| // See golang/go#54461 for details. | ||||
| var DebugHangingGoCommands = false | ||||
|  | ||||
| // runCmdContext is like exec.CommandContext except it sends os.Interrupt | ||||
| // before os.Kill. | ||||
| func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) { | ||||
| 	// If cmd.Stdout is not an *os.File, the exec package will create a pipe and | ||||
| 	// copy it to the Writer in a goroutine until the process has finished and | ||||
| 	// either the pipe reaches EOF or command's WaitDelay expires. | ||||
| 	// | ||||
| 	// However, the output from 'go list' can be quite large, and we don't want to | ||||
| 	// keep reading (and allocating buffers) if we've already decided we don't | ||||
| 	// care about the output. We don't want to wait for the process to finish, and | ||||
| 	// we don't wait to wait for the WaitDelay to expire either. | ||||
| 	// | ||||
| 	// Instead, if cmd.Stdout requires a copying goroutine we explicitly replace | ||||
| 	// it with a pipe (which is an *os.File), which we can close in order to stop | ||||
| 	// copying output as soon as we realize we don't care about it. | ||||
| 	var stdoutW *os.File | ||||
| 	if cmd.Stdout != nil { | ||||
| 		if _, ok := cmd.Stdout.(*os.File); !ok { | ||||
| 			var stdoutR *os.File | ||||
| 			stdoutR, stdoutW, err = os.Pipe() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			prevStdout := cmd.Stdout | ||||
| 			cmd.Stdout = stdoutW | ||||
|  | ||||
| 			stdoutErr := make(chan error, 1) | ||||
| 			go func() { | ||||
| 				_, err := io.Copy(prevStdout, stdoutR) | ||||
| 				if err != nil { | ||||
| 					err = fmt.Errorf("copying stdout: %w", err) | ||||
| 				} | ||||
| 				stdoutErr <- err | ||||
| 			}() | ||||
| 			defer func() { | ||||
| 				// We started a goroutine to copy a stdout pipe. | ||||
| 				// Wait for it to finish, or terminate it if need be. | ||||
| 				var err2 error | ||||
| 				select { | ||||
| 				case err2 = <-stdoutErr: | ||||
| 					stdoutR.Close() | ||||
| 				case <-ctx.Done(): | ||||
| 					stdoutR.Close() | ||||
| 					// Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close | ||||
| 					// should cause the Read call in io.Copy to unblock and return | ||||
| 					// immediately, but we still need to receive from stdoutErr to confirm | ||||
| 					// that it has happened. | ||||
| 					<-stdoutErr | ||||
| 					err2 = ctx.Err() | ||||
| 				} | ||||
| 				if err == nil { | ||||
| 					err = err2 | ||||
| 				} | ||||
| 			}() | ||||
|  | ||||
| 			// Per https://pkg.go.dev/os/exec#Cmd, “If Stdout and Stderr are the | ||||
| 			// same writer, and have a type that can be compared with ==, at most | ||||
| 			// one goroutine at a time will call Write.” | ||||
| 			// | ||||
| 			// Since we're starting a goroutine that writes to cmd.Stdout, we must | ||||
| 			// also update cmd.Stderr so that it still holds. | ||||
| 			func() { | ||||
| 				defer func() { recover() }() | ||||
| 				if cmd.Stderr == prevStdout { | ||||
| 					cmd.Stderr = cmd.Stdout | ||||
| 				} | ||||
| 			}() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	err = cmd.Start() | ||||
| 	if stdoutW != nil { | ||||
| 		// The child process has inherited the pipe file, | ||||
| 		// so close the copy held in this process. | ||||
| 		stdoutW.Close() | ||||
| 		stdoutW = nil | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	resChan := make(chan error, 1) | ||||
| 	go func() { | ||||
| 		resChan <- cmd.Wait() | ||||
| 	}() | ||||
|  | ||||
| 	// If we're interested in debugging hanging Go commands, stop waiting after a | ||||
| 	// minute and panic with interesting information. | ||||
| 	debug := DebugHangingGoCommands | ||||
| 	if debug { | ||||
| 		timer := time.NewTimer(1 * time.Minute) | ||||
| 		defer timer.Stop() | ||||
| 		select { | ||||
| 		case err := <-resChan: | ||||
| 			return err | ||||
| 		case <-timer.C: | ||||
| 			HandleHangingGoCommand(cmd.Process) | ||||
| 		case <-ctx.Done(): | ||||
| 		} | ||||
| 	} else { | ||||
| 		select { | ||||
| 		case err := <-resChan: | ||||
| 			return err | ||||
| 		case <-ctx.Done(): | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Cancelled. Interrupt and see if it ends voluntarily. | ||||
| 	if err := cmd.Process.Signal(os.Interrupt); err == nil { | ||||
| 		// (We used to wait only 1s but this proved | ||||
| 		// fragile on loaded builder machines.) | ||||
| 		timer := time.NewTimer(5 * time.Second) | ||||
| 		defer timer.Stop() | ||||
| 		select { | ||||
| 		case err := <-resChan: | ||||
| 			return err | ||||
| 		case <-timer.C: | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Didn't shut down in response to interrupt. Kill it hard. | ||||
| 	// TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT | ||||
| 	// on certain platforms, such as unix. | ||||
| 	if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) && debug { | ||||
| 		log.Printf("error killing the Go command: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return <-resChan | ||||
| } | ||||
|  | ||||
| func HandleHangingGoCommand(proc *os.Process) { | ||||
| 	switch runtime.GOOS { | ||||
| 	case "linux", "darwin", "freebsd", "netbsd": | ||||
| 		fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND | ||||
|  | ||||
| The gopls test runner has detected a hanging go command. In order to debug | ||||
| this, the output of ps and lsof/fstat is printed below. | ||||
|  | ||||
| See golang/go#54461 for more details.`) | ||||
|  | ||||
| 		fmt.Fprintln(os.Stderr, "\nps axo ppid,pid,command:") | ||||
| 		fmt.Fprintln(os.Stderr, "-------------------------") | ||||
| 		psCmd := exec.Command("ps", "axo", "ppid,pid,command") | ||||
| 		psCmd.Stdout = os.Stderr | ||||
| 		psCmd.Stderr = os.Stderr | ||||
| 		if err := psCmd.Run(); err != nil { | ||||
| 			panic(fmt.Sprintf("running ps: %v", err)) | ||||
| 		} | ||||
|  | ||||
| 		listFiles := "lsof" | ||||
| 		if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" { | ||||
| 			listFiles = "fstat" | ||||
| 		} | ||||
|  | ||||
| 		fmt.Fprintln(os.Stderr, "\n"+listFiles+":") | ||||
| 		fmt.Fprintln(os.Stderr, "-----") | ||||
| 		listFilesCmd := exec.Command(listFiles) | ||||
| 		listFilesCmd.Stdout = os.Stderr | ||||
| 		listFilesCmd.Stderr = os.Stderr | ||||
| 		if err := listFilesCmd.Run(); err != nil { | ||||
| 			panic(fmt.Sprintf("running %s: %v", listFiles, err)) | ||||
| 		} | ||||
| 	} | ||||
| 	panic(fmt.Sprintf("detected hanging go command (pid %d): see golang/go#54461 for more details", proc.Pid)) | ||||
| } | ||||
|  | ||||
| func cmdDebugStr(cmd *exec.Cmd) string { | ||||
| 	env := make(map[string]string) | ||||
| 	for _, kv := range cmd.Env { | ||||
| 		split := strings.SplitN(kv, "=", 2) | ||||
| 		if len(split) == 2 { | ||||
| 			k, v := split[0], split[1] | ||||
| 			env[k] = v | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var args []string | ||||
| 	for _, arg := range cmd.Args { | ||||
| 		quoted := strconv.Quote(arg) | ||||
| 		if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") { | ||||
| 			args = append(args, quoted) | ||||
| 		} else { | ||||
| 			args = append(args, arg) | ||||
| 		} | ||||
| 	} | ||||
| 	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " ")) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Claude Code
					Claude Code