github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/env/env.go (about)

     1  // Package env implements the Pipe interface providing validation of
     2  // missing environment variables needed by the release process.
     3  package env
     4  
     5  import (
     6  	"bufio"
     7  	"errors"
     8  	"fmt"
     9  	"io/fs"
    10  	"os"
    11  	"strings"
    12  
    13  	"github.com/caarlos0/log"
    14  	"github.com/goreleaser/goreleaser/internal/logext"
    15  	"github.com/goreleaser/goreleaser/internal/skips"
    16  	"github.com/goreleaser/goreleaser/internal/tmpl"
    17  	"github.com/goreleaser/goreleaser/pkg/context"
    18  	homedir "github.com/mitchellh/go-homedir"
    19  )
    20  
    21  // ErrMissingToken indicates an error when GITHUB_TOKEN, GITLAB_TOKEN and GITEA_TOKEN are all missing in the environment.
    22  var ErrMissingToken = errors.New("missing GITHUB_TOKEN, GITLAB_TOKEN and GITEA_TOKEN")
    23  
    24  // ErrMultipleTokens indicates that multiple tokens are defined. ATM only one of them if allowed.
    25  // See https://github.com/goreleaser/goreleaser/pull/809
    26  type ErrMultipleTokens struct {
    27  	tokens []string
    28  }
    29  
    30  func (e ErrMultipleTokens) Error() string {
    31  	return fmt.Sprintf("multiple tokens found, but only one is allowed: %s\n\nLearn more at https://goreleaser.com/errors/multiple-tokens\n", strings.Join(e.tokens, ", "))
    32  }
    33  
    34  // Pipe for env.
    35  type Pipe struct{}
    36  
    37  func (Pipe) String() string {
    38  	return "loading environment variables"
    39  }
    40  
    41  func setDefaultTokenFiles(ctx *context.Context) {
    42  	env := &ctx.Config.EnvFiles
    43  	if env.GitHubToken == "" {
    44  		env.GitHubToken = "~/.config/goreleaser/github_token"
    45  	}
    46  	if env.GitLabToken == "" {
    47  		env.GitLabToken = "~/.config/goreleaser/gitlab_token"
    48  	}
    49  	if env.GiteaToken == "" {
    50  		env.GiteaToken = "~/.config/goreleaser/gitea_token"
    51  	}
    52  }
    53  
    54  // Run the pipe.
    55  func (Pipe) Run(ctx *context.Context) error {
    56  	templ := tmpl.New(ctx).WithEnvS(os.Environ())
    57  	tEnv := []string{}
    58  	for i := range ctx.Config.Env {
    59  		env, err := templ.Apply(ctx.Config.Env[i])
    60  		if err != nil {
    61  			return err
    62  		}
    63  		tEnv = append(tEnv, env)
    64  	}
    65  	for k, v := range context.ToEnv(tEnv) {
    66  		ctx.Env[k] = v
    67  	}
    68  
    69  	setDefaultTokenFiles(ctx)
    70  	githubToken, githubTokenErr := loadEnv("GITHUB_TOKEN", ctx.Config.EnvFiles.GitHubToken)
    71  	gitlabToken, gitlabTokenErr := loadEnv("GITLAB_TOKEN", ctx.Config.EnvFiles.GitLabToken)
    72  	giteaToken, giteaTokenErr := loadEnv("GITEA_TOKEN", ctx.Config.EnvFiles.GiteaToken)
    73  
    74  	forceToken := ctx.Config.ForceToken
    75  	if forceToken == "" {
    76  		forceToken = os.Getenv("GORELEASER_FORCE_TOKEN")
    77  	}
    78  	switch strings.ToLower(forceToken) {
    79  	case "github":
    80  		gitlabToken = ""
    81  		giteaToken = ""
    82  	case "gitlab":
    83  		githubToken = ""
    84  		giteaToken = ""
    85  	case "gitea":
    86  		githubToken = ""
    87  		gitlabToken = ""
    88  	default:
    89  		var tokens []string
    90  		if githubToken != "" {
    91  			tokens = append(tokens, "GITHUB_TOKEN")
    92  		}
    93  		if gitlabToken != "" {
    94  			tokens = append(tokens, "GITLAB_TOKEN")
    95  		}
    96  		if giteaToken != "" {
    97  			tokens = append(tokens, "GITEA_TOKEN")
    98  		}
    99  		if len(tokens) > 1 {
   100  			return ErrMultipleTokens{tokens}
   101  		}
   102  	}
   103  
   104  	noTokens := githubToken == "" && gitlabToken == "" && giteaToken == ""
   105  	noTokenErrs := githubTokenErr == nil && gitlabTokenErr == nil && giteaTokenErr == nil
   106  
   107  	if err := checkErrors(ctx, noTokens, noTokenErrs, gitlabTokenErr, githubTokenErr, giteaTokenErr); err != nil {
   108  		return err
   109  	}
   110  
   111  	if gitlabToken != "" {
   112  		log.Debug("token type: gitlab")
   113  		ctx.TokenType = context.TokenTypeGitLab
   114  		ctx.Token = gitlabToken
   115  	}
   116  
   117  	if giteaToken != "" {
   118  		log.Debug("token type: gitea")
   119  		ctx.TokenType = context.TokenTypeGitea
   120  		ctx.Token = giteaToken
   121  	}
   122  
   123  	if githubToken != "" {
   124  		log.Debug("token type: github")
   125  		ctx.Token = githubToken
   126  	}
   127  
   128  	if ctx.TokenType == "" {
   129  		ctx.TokenType = context.TokenTypeGitHub
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func checkErrors(ctx *context.Context, noTokens, noTokenErrs bool, gitlabTokenErr, githubTokenErr, giteaTokenErr error) error {
   136  	if ctx.SkipTokenCheck || skips.Any(ctx, skips.Publish) {
   137  		return nil
   138  	}
   139  	if b, err := tmpl.New(ctx).Bool(ctx.Config.Release.Disable); err != nil || b {
   140  		return err
   141  	}
   142  
   143  	if noTokens && noTokenErrs {
   144  		return ErrMissingToken
   145  	}
   146  
   147  	if gitlabTokenErr != nil {
   148  		return fmt.Errorf("failed to load gitlab token: %w", gitlabTokenErr)
   149  	}
   150  
   151  	if githubTokenErr != nil {
   152  		return fmt.Errorf("failed to load github token: %w", githubTokenErr)
   153  	}
   154  
   155  	if giteaTokenErr != nil {
   156  		return fmt.Errorf("failed to load gitea token: %w", giteaTokenErr)
   157  	}
   158  	return nil
   159  }
   160  
   161  func loadEnv(env, path string) (string, error) {
   162  	val := os.Getenv(env)
   163  	if val != "" {
   164  		log.Infof("using token from %s", logext.Keyword("$"+env))
   165  		return val, nil
   166  	}
   167  	path, err := homedir.Expand(path)
   168  	if err != nil {
   169  		return "", err
   170  	}
   171  	f, err := os.Open(path) // #nosec
   172  	if errors.Is(err, fs.ErrNotExist) {
   173  		return "", nil
   174  	}
   175  	if err != nil {
   176  		return "", err
   177  	}
   178  	defer f.Close()
   179  	log.Infof("using token from %s", logext.Keyword(path))
   180  	bts, _, err := bufio.NewReader(f).ReadLine()
   181  	return string(bts), err
   182  }