Run validation in the selected module
This commit is contained in:
136
internal/gotool/owner.go
Normal file
136
internal/gotool/owner.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package gotool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Owner lazily discovers and reuses one Go tool while its inherited
|
||||
// environment and persistent settings file remain unchanged.
|
||||
type Owner struct {
|
||||
directory string
|
||||
tool *Tool
|
||||
env map[string]string
|
||||
file string
|
||||
contents []byte
|
||||
fileFound bool
|
||||
}
|
||||
|
||||
// NewOwner creates an operation-local Go tool owner without running Go.
|
||||
func NewOwner(directory string) *Owner {
|
||||
return &Owner{directory: directory}
|
||||
}
|
||||
|
||||
// Acquire returns the operation's Go tool, rediscovering settings when their
|
||||
// process or persistent inputs have changed.
|
||||
func (owner *Owner) Acquire(ctx context.Context) (*Tool, error) {
|
||||
if owner.tool != nil {
|
||||
changed, err := owner.inputsChanged()
|
||||
if err == nil && !changed {
|
||||
return owner.tool, nil
|
||||
}
|
||||
}
|
||||
if err := owner.discover(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return owner.tool, nil
|
||||
}
|
||||
|
||||
// Refresh checks the owner's inputs after a consumer callback and rediscovers
|
||||
// settings when the callback changed the environment or persistent file.
|
||||
func (owner *Owner) Refresh(ctx context.Context) error {
|
||||
if owner.tool == nil {
|
||||
return nil
|
||||
}
|
||||
changed, err := owner.inputsChanged()
|
||||
if err == nil && !changed {
|
||||
return nil
|
||||
}
|
||||
return owner.discover(ctx)
|
||||
}
|
||||
|
||||
func (owner *Owner) discover(ctx context.Context) error {
|
||||
environment := inheritedEnvironment()
|
||||
tool, err := New(ctx, owner.directory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settingsFile, err := resolveSettingsFile(owner.directory, tool.settingsFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contents, found, err := readSettingsFile(settingsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read Go settings file %q: %w", settingsFile, err)
|
||||
}
|
||||
owner.tool = tool
|
||||
owner.env = environment
|
||||
owner.file = settingsFile
|
||||
owner.contents = contents
|
||||
owner.fileFound = found
|
||||
return nil
|
||||
}
|
||||
|
||||
func (owner *Owner) inputsChanged() (bool, error) {
|
||||
if !sameEnvironment(owner.env, inheritedEnvironment()) {
|
||||
return true, nil
|
||||
}
|
||||
contents, found, err := readSettingsFile(owner.file)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return found != owner.fileFound || !bytes.Equal(contents, owner.contents), nil
|
||||
}
|
||||
|
||||
func inheritedEnvironment() map[string]string {
|
||||
values := make(map[string]string)
|
||||
for _, entry := range os.Environ() {
|
||||
key, value, ok := strings.Cut(entry, "=")
|
||||
if ok {
|
||||
values[key] = value
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func sameEnvironment(left, right map[string]string) bool {
|
||||
if len(left) != len(right) {
|
||||
return false
|
||||
}
|
||||
for key, value := range left {
|
||||
other, ok := right[key]
|
||||
if !ok || other != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func resolveSettingsFile(directory, filename string) (string, error) {
|
||||
if filename == "" || filename == "off" {
|
||||
return "", nil
|
||||
}
|
||||
if !filepath.IsAbs(filename) {
|
||||
filename = filepath.Join(directory, filename)
|
||||
}
|
||||
return filepath.Abs(filename)
|
||||
}
|
||||
|
||||
func readSettingsFile(filename string) ([]byte, bool, error) {
|
||||
if filename == "" {
|
||||
return nil, false, nil
|
||||
}
|
||||
contents, err := os.ReadFile(filename)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return contents, true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user