github.com/sudo-bmitch/version-bump@v0.0.0-20240503123857-70b0e3f646dd/internal/action/action.go (about)

     1  // Package action processes the result of a scanner match, the source, and the
     2  // configuration to take an action (log, modify the version).
     3  package action
     4  
     5  import (
     6  	"fmt"
     7  
     8  	"github.com/sudo-bmitch/version-bump/internal/config"
     9  	"github.com/sudo-bmitch/version-bump/internal/lockfile"
    10  	"github.com/sudo-bmitch/version-bump/internal/source"
    11  )
    12  
    13  // Opts specifies runtime configuration inputs and outputs
    14  type Opts struct {
    15  	Action  runAction       // which action to run
    16  	DryRun  bool            // when set, lock file and scanned files are unchanged
    17  	Locks   *lockfile.Locks // lock entries to use or set
    18  	Changes []*Change       // results of the run
    19  }
    20  
    21  type runAction int
    22  
    23  const (
    24  	ActionScan   runAction = iota // scan: search files for versions and saves to lock
    25  	ActionCheck                   // check: scans for versions and compares to source
    26  	ActionSet                     // set: updates a version without checking the source
    27  	ActionUpdate                  // update: modifies versions using sources
    28  	ActionReset                   // reset: sets versions to the lock value without checking source
    29  )
    30  
    31  // Change lists changes found or made to scanned files
    32  type Change struct {
    33  	Filename string // filename modified
    34  	Source   string // name of the source
    35  	Scan     string // name of the scan
    36  	Key      string // key of the scan
    37  	Orig     string // previous version
    38  	New      string // new version
    39  }
    40  
    41  type Action struct {
    42  	opts *Opts
    43  	conf config.Config
    44  }
    45  
    46  func New(opts *Opts, conf config.Config) *Action {
    47  	return &Action{
    48  		opts: opts,
    49  		conf: conf,
    50  	}
    51  }
    52  
    53  // Done should be called after all HandleMatch calls are finished.
    54  // It will perform any final steps.
    55  func (a *Action) Done() error {
    56  	// TODO: is this needed?
    57  	return nil
    58  }
    59  
    60  // HandleMatch processes a scan result, checking the sources and config, and returning the resulting action
    61  // Output:
    62  // - change bool: should the scan modify the version
    63  // - version string: version the scan should use
    64  // - err error: not nil on any failure
    65  func (a *Action) HandleMatch(filename string, scan string, sourceName string, version string, data config.SourceTmplData) (bool, string, error) {
    66  	if _, ok := a.conf.Sources[sourceName]; !ok {
    67  		return false, "", fmt.Errorf("source not found: %s", sourceName)
    68  	}
    69  	s, err := source.Get(*a.conf.Sources[sourceName])
    70  	if err != nil {
    71  		return false, "", fmt.Errorf("could not get source %s: %w", sourceName, err)
    72  	}
    73  	data.SourceArgs = a.conf.Sources[sourceName].Args
    74  	key, err := s.Key(data)
    75  	if err != nil {
    76  		return false, "", fmt.Errorf("could not get key for source %s: %w", sourceName, err)
    77  	}
    78  	// determine curVer
    79  	var curVer string
    80  	switch a.opts.Action {
    81  	case ActionCheck, ActionUpdate:
    82  		// query from source
    83  		curVer, err = s.Get(data)
    84  		if err != nil {
    85  			return false, "", fmt.Errorf("could not get current version from source %s: %w", sourceName, err)
    86  		}
    87  	case ActionSet, ActionReset:
    88  		// TODO: get curVer from lock, requires getting the key from the source
    89  
    90  	case ActionScan:
    91  		// scan doesn't change the version, set from file contents
    92  		curVer = version
    93  	}
    94  
    95  	// store any changes when version != curVer
    96  	if version != curVer {
    97  		a.opts.Changes = append(a.opts.Changes, &Change{
    98  			Filename: filename,
    99  			Source:   sourceName,
   100  			Scan:     scan,
   101  			Key:      key,
   102  			Orig:     version,
   103  			New:      curVer,
   104  		})
   105  	}
   106  
   107  	// for dry-run, never return a change
   108  	if a.opts.DryRun || a.opts.Action == ActionCheck {
   109  		return false, version, nil
   110  	}
   111  
   112  	// update lock file
   113  	switch a.opts.Action {
   114  	case ActionScan, ActionUpdate:
   115  		err = a.opts.Locks.Set(sourceName, key, curVer)
   116  		if err != nil {
   117  			return false, "", fmt.Errorf("could not set lock for %s/%s: %w", sourceName, key, err)
   118  		}
   119  	}
   120  	return version != curVer, curVer, nil
   121  }