github.com/triarius/goreleaser@v1.12.5/internal/pipe/checksums/checksums.go (about)

     1  // Package checksums provides a Pipe that creates .checksums files for
     2  // each artifact.
     3  package checksums
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"strings"
    12  	"sync"
    13  
    14  	"github.com/caarlos0/log"
    15  	"github.com/triarius/goreleaser/internal/artifact"
    16  	"github.com/triarius/goreleaser/internal/extrafiles"
    17  	"github.com/triarius/goreleaser/internal/semerrgroup"
    18  	"github.com/triarius/goreleaser/internal/tmpl"
    19  	"github.com/triarius/goreleaser/pkg/context"
    20  )
    21  
    22  var (
    23  	errNoArtifacts = errors.New("there are no artifacts to sign")
    24  	lock           sync.Mutex
    25  )
    26  
    27  // Pipe for checksums.
    28  type Pipe struct{}
    29  
    30  func (Pipe) String() string                 { return "calculating checksums" }
    31  func (Pipe) Skip(ctx *context.Context) bool { return ctx.Config.Checksum.Disable }
    32  
    33  // Default sets the pipe defaults.
    34  func (Pipe) Default(ctx *context.Context) error {
    35  	if ctx.Config.Checksum.NameTemplate == "" {
    36  		ctx.Config.Checksum.NameTemplate = "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
    37  	}
    38  	if ctx.Config.Checksum.Algorithm == "" {
    39  		ctx.Config.Checksum.Algorithm = "sha256"
    40  	}
    41  	return nil
    42  }
    43  
    44  // Run the pipe.
    45  func (Pipe) Run(ctx *context.Context) error {
    46  	filename, err := tmpl.New(ctx).Apply(ctx.Config.Checksum.NameTemplate)
    47  	if err != nil {
    48  		return err
    49  	}
    50  	filepath := filepath.Join(ctx.Config.Dist, filename)
    51  	if err := refresh(ctx, filepath); err != nil {
    52  		if errors.Is(err, errNoArtifacts) {
    53  			return nil
    54  		}
    55  		return err
    56  	}
    57  	ctx.Artifacts.Add(&artifact.Artifact{
    58  		Type: artifact.Checksum,
    59  		Path: filepath,
    60  		Name: filename,
    61  		Extra: map[string]interface{}{
    62  			artifact.ExtraRefresh: func() error {
    63  				log.WithField("file", filename).Info("refreshing checksums")
    64  				return refresh(ctx, filepath)
    65  			},
    66  		},
    67  	})
    68  	return nil
    69  }
    70  
    71  func refresh(ctx *context.Context, filepath string) error {
    72  	lock.Lock()
    73  	defer lock.Unlock()
    74  	filter := artifact.Or(
    75  		artifact.ByType(artifact.UploadableArchive),
    76  		artifact.ByType(artifact.UploadableBinary),
    77  		artifact.ByType(artifact.UploadableSourceArchive),
    78  		artifact.ByType(artifact.LinuxPackage),
    79  		artifact.ByType(artifact.SBOM),
    80  	)
    81  	if len(ctx.Config.Checksum.IDs) > 0 {
    82  		filter = artifact.And(filter, artifact.ByIDs(ctx.Config.Checksum.IDs...))
    83  	}
    84  
    85  	artifactList := ctx.Artifacts.Filter(filter).List()
    86  
    87  	extraFiles, err := extrafiles.Find(ctx, ctx.Config.Checksum.ExtraFiles)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	for name, path := range extraFiles {
    93  		artifactList = append(artifactList, &artifact.Artifact{
    94  			Name: name,
    95  			Path: path,
    96  			Type: artifact.UploadableFile,
    97  		})
    98  	}
    99  
   100  	if len(artifactList) == 0 {
   101  		return errNoArtifacts
   102  	}
   103  
   104  	g := semerrgroup.New(ctx.Parallelism)
   105  	sumLines := make([]string, len(artifactList))
   106  	for i, artifact := range artifactList {
   107  		i := i
   108  		artifact := artifact
   109  		g.Go(func() error {
   110  			sumLine, err := checksums(ctx.Config.Checksum.Algorithm, artifact)
   111  			if err != nil {
   112  				return err
   113  			}
   114  			sumLines[i] = sumLine
   115  			return nil
   116  		})
   117  	}
   118  
   119  	err = g.Wait()
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	file, err := os.OpenFile(
   125  		filepath,
   126  		os.O_APPEND|os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
   127  		0o644,
   128  	)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	defer file.Close()
   133  
   134  	// sort to ensure the signature is deterministic downstream
   135  	sort.Strings(sumLines)
   136  	_, err = file.WriteString(strings.Join(sumLines, ""))
   137  	return err
   138  }
   139  
   140  func checksums(algorithm string, artifact *artifact.Artifact) (string, error) {
   141  	log.WithField("file", artifact.Name).Debug("checksumming")
   142  	sha, err := artifact.Checksum(algorithm)
   143  	if err != nil {
   144  		return "", err
   145  	}
   146  	return fmt.Sprintf("%v  %v\n", sha, artifact.Name), nil
   147  }