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 }