github.com/yogeshkumararora/slsa-github-generator@v1.10.1-0.20240520161934-11278bd5afb4/internal/builders/go/pkg/config.go (about)

     1  // Copyright 2022 SLSA Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pkg
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	"gopkg.in/yaml.v3"
    25  
    26  	"github.com/yogeshkumararora/slsa-github-generator/internal/utils"
    27  )
    28  
    29  var supportedVersions = map[int]bool{
    30  	1: true,
    31  }
    32  
    33  type goReleaserConfigFile struct {
    34  	Main    *string  `yaml:"main"`
    35  	Dir     *string  `yaml:"dir"`
    36  	Goos    string   `yaml:"goos"`
    37  	Goarch  string   `yaml:"goarch"`
    38  	Binary  string   `yaml:"binary"`
    39  	Env     []string `yaml:"env"`
    40  	Flags   []string `yaml:"flags"`
    41  	Ldflags []string `yaml:"ldflags"`
    42  	Version int      `yaml:"version"`
    43  }
    44  
    45  // GoReleaserConfig tracks configuration for goreleaser.
    46  type GoReleaserConfig struct {
    47  	Env     map[string]string
    48  	Main    *string
    49  	Dir     *string
    50  	Goos    string
    51  	Goarch  string
    52  	Binary  string
    53  	Flags   []string
    54  	Ldflags []string
    55  }
    56  
    57  var (
    58  	// ErrUnsupportedVersion indicates an unsupported Go builder version.
    59  	ErrUnsupportedVersion = errors.New("unsupported version")
    60  
    61  	// ErrInvalidDirectory indicates an invalid directory.
    62  	ErrInvalidDirectory = errors.New("invalid directory")
    63  
    64  	// ErrInvalidEnvironmentVariable indicates  an invalid environment variable.
    65  	ErrInvalidEnvironmentVariable = errors.New("invalid environment variable")
    66  )
    67  
    68  func configFromString(b []byte) (*GoReleaserConfig, error) {
    69  	var cf goReleaserConfigFile
    70  	if err := yaml.Unmarshal(b, &cf); err != nil {
    71  		return nil, fmt.Errorf("yaml.Unmarshal: %w", err)
    72  	}
    73  
    74  	return fromConfig(&cf)
    75  }
    76  
    77  // ConfigFromFile reads the file located at path and builds a GoReleaserConfig
    78  // from it.
    79  func ConfigFromFile(path string) (*GoReleaserConfig, error) {
    80  	if err := validatePath(path); err != nil {
    81  		return nil, fmt.Errorf("validate path: %q: %w", path, err)
    82  	}
    83  
    84  	cfg, err := os.ReadFile(filepath.Clean(path))
    85  	if err != nil {
    86  		return nil, fmt.Errorf("%q: os.ReadFile: %w", path, err)
    87  	}
    88  
    89  	c, err := configFromString(cfg)
    90  	if err != nil {
    91  		return nil, fmt.Errorf("configfromstring: %q: %w", path, err)
    92  	}
    93  	return c, nil
    94  }
    95  
    96  func fromConfig(cf *goReleaserConfigFile) (*GoReleaserConfig, error) {
    97  	if err := validateVersion(cf); err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	if err := validateMain(cf); err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	if err := validateDir(cf); err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	cfg := GoReleaserConfig{
   110  		Goos:    cf.Goos,
   111  		Goarch:  cf.Goarch,
   112  		Flags:   cf.Flags,
   113  		Ldflags: cf.Ldflags,
   114  		Binary:  cf.Binary,
   115  		Main:    cf.Main,
   116  		Dir:     cf.Dir,
   117  	}
   118  
   119  	if err := cfg.setEnvs(cf); err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	return &cfg, nil
   124  }
   125  
   126  func validatePath(path string) error {
   127  	err := utils.PathIsUnderCurrentDirectory(path)
   128  	if err != nil {
   129  		return convertPathError(err, "PathIsUnderCurrentDirectory")
   130  	}
   131  	return nil
   132  }
   133  
   134  func validateDir(cf *goReleaserConfigFile) error {
   135  	if cf.Dir == nil {
   136  		return nil
   137  	}
   138  	return validatePath(*cf.Dir)
   139  }
   140  
   141  func validateMain(cf *goReleaserConfigFile) error {
   142  	if cf.Main == nil {
   143  		return nil
   144  	}
   145  
   146  	// Validate the main path is under the current directory.
   147  	if err := utils.PathIsUnderCurrentDirectory(*cf.Main); err != nil {
   148  		return convertPathError(err, "PathIsUnderCurrentDirectory")
   149  	}
   150  	return nil
   151  }
   152  
   153  func convertPathError(e error, msg string) error {
   154  	if e != nil {
   155  		if errors.Is(e, utils.ErrInternal) ||
   156  			errors.Is(e, utils.ErrInvalidPath) {
   157  			return fmt.Errorf("%w: %v", ErrInvalidDirectory, e)
   158  		}
   159  		return fmt.Errorf("%s: %w", msg, e)
   160  	}
   161  	return e
   162  }
   163  
   164  func validateVersion(cf *goReleaserConfigFile) error {
   165  	_, exists := supportedVersions[cf.Version]
   166  	if !exists {
   167  		return fmt.Errorf("%w: '%d'", ErrUnsupportedVersion, cf.Version)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  func (r *GoReleaserConfig) setEnvs(cf *goReleaserConfigFile) error {
   174  	m := make(map[string]string)
   175  	for _, e := range cf.Env {
   176  		name, value, present := strings.Cut(e, "=")
   177  		if !present {
   178  			return fmt.Errorf("%w: '%s' contains no '='", ErrInvalidEnvironmentVariable, e)
   179  		}
   180  		m[name] = value
   181  	}
   182  
   183  	if len(m) > 0 {
   184  		r.Env = m
   185  	}
   186  
   187  	return nil
   188  }