github.com/amane3/goreleaser@v0.182.0/internal/artifact/artifact.go (about) 1 // Package artifact provides the core artifact storage for goreleaser. 2 package artifact 3 4 // nolint: gosec 5 import ( 6 "crypto/md5" 7 "crypto/sha1" 8 "crypto/sha256" 9 "crypto/sha512" 10 "encoding/hex" 11 "fmt" 12 "hash" 13 "hash/crc32" 14 "io" 15 "os" 16 "sync" 17 18 "github.com/apex/log" 19 ) 20 21 // Type defines the type of an artifact. 22 type Type int 23 24 const ( 25 // UploadableArchive a tar.gz/zip archive to be uploaded. 26 UploadableArchive Type = iota 27 // UploadableBinary is a binary file to be uploaded. 28 UploadableBinary 29 // UploadableFile is any file that can be uploaded. 30 UploadableFile 31 // Binary is a binary (output of a gobuild). 32 Binary 33 // LinuxPackage is a linux package generated by nfpm. 34 LinuxPackage 35 // PublishableSnapcraft is a snap package yet to be published. 36 PublishableSnapcraft 37 // Snapcraft is a published snap package. 38 Snapcraft 39 // PublishableDockerImage is a Docker image yet to be published. 40 PublishableDockerImage 41 // DockerImage is a published Docker image. 42 DockerImage 43 // DockerManifest is a published Docker manifest. 44 DockerManifest 45 // Checksum is a checksums file. 46 Checksum 47 // Signature is a signature file. 48 Signature 49 // UploadableSourceArchive is the archive with the current commit source code. 50 UploadableSourceArchive 51 ) 52 53 func (t Type) String() string { 54 switch t { 55 case UploadableArchive: 56 return "Archive" 57 case UploadableFile: 58 return "File" 59 case UploadableBinary, Binary: 60 return "Binary" 61 case LinuxPackage: 62 return "Linux Package" 63 case PublishableDockerImage, DockerImage: 64 return "Docker Image" 65 case DockerManifest: 66 return "Docker Manifest" 67 case PublishableSnapcraft, Snapcraft: 68 return "Snap" 69 case Checksum: 70 return "Checksum" 71 case Signature: 72 return "Signature" 73 case UploadableSourceArchive: 74 return "Source" 75 default: 76 return "unknown" 77 } 78 } 79 80 // Artifact represents an artifact and its relevant info. 81 type Artifact struct { 82 Name string 83 Path string 84 Goos string 85 Goarch string 86 Goarm string 87 Gomips string 88 Type Type 89 Extra map[string]interface{} 90 } 91 92 // ExtraOr returns the Extra field with the given key or the or value specified 93 // if it is nil. 94 func (a Artifact) ExtraOr(key string, or interface{}) interface{} { 95 if a.Extra[key] == nil { 96 return or 97 } 98 return a.Extra[key] 99 } 100 101 // Checksum calculates the checksum of the artifact. 102 // nolint: gosec 103 func (a Artifact) Checksum(algorithm string) (string, error) { 104 log.Debugf("calculating checksum for %s", a.Path) 105 file, err := os.Open(a.Path) 106 if err != nil { 107 return "", fmt.Errorf("failed to checksum: %w", err) 108 } 109 defer file.Close() 110 var h hash.Hash 111 switch algorithm { 112 case "crc32": 113 h = crc32.NewIEEE() 114 case "md5": 115 h = md5.New() 116 case "sha224": 117 h = sha256.New224() 118 case "sha384": 119 h = sha512.New384() 120 case "sha256": 121 h = sha256.New() 122 case "sha1": 123 h = sha1.New() 124 case "sha512": 125 h = sha512.New() 126 default: 127 return "", fmt.Errorf("invalid algorithm: %s", algorithm) 128 } 129 _, err = io.Copy(h, file) 130 if err != nil { 131 return "", fmt.Errorf("failed to checksum: %w", err) 132 } 133 return hex.EncodeToString(h.Sum(nil)), nil 134 } 135 136 // Artifacts is a list of artifacts. 137 type Artifacts struct { 138 items []*Artifact 139 lock *sync.Mutex 140 } 141 142 // New return a new list of artifacts. 143 func New() Artifacts { 144 return Artifacts{ 145 items: []*Artifact{}, 146 lock: &sync.Mutex{}, 147 } 148 } 149 150 // List return the actual list of artifacts. 151 func (artifacts Artifacts) List() []*Artifact { 152 return artifacts.items 153 } 154 155 // GroupByPlatform groups the artifacts by their platform. 156 func (artifacts Artifacts) GroupByPlatform() map[string][]*Artifact { 157 var result = map[string][]*Artifact{} 158 for _, a := range artifacts.items { 159 plat := a.Goos + a.Goarch + a.Goarm + a.Gomips 160 result[plat] = append(result[plat], a) 161 } 162 return result 163 } 164 165 // Add safely adds a new artifact to an artifact list. 166 func (artifacts *Artifacts) Add(a *Artifact) { 167 artifacts.lock.Lock() 168 defer artifacts.lock.Unlock() 169 log.WithFields(log.Fields{ 170 "name": a.Name, 171 "path": a.Path, 172 "type": a.Type, 173 }).Debug("added new artifact") 174 artifacts.items = append(artifacts.items, a) 175 } 176 177 // Filter defines an artifact filter which can be used within the Filter 178 // function. 179 type Filter func(a *Artifact) bool 180 181 // ByGoos is a predefined filter that filters by the given goos. 182 func ByGoos(s string) Filter { 183 return func(a *Artifact) bool { 184 return a.Goos == s 185 } 186 } 187 188 // ByGoarch is a predefined filter that filters by the given goarch. 189 func ByGoarch(s string) Filter { 190 return func(a *Artifact) bool { 191 return a.Goarch == s 192 } 193 } 194 195 // ByGoarm is a predefined filter that filters by the given goarm. 196 func ByGoarm(s string) Filter { 197 return func(a *Artifact) bool { 198 return a.Goarm == s 199 } 200 } 201 202 // ByType is a predefined filter that filters by the given type. 203 func ByType(t Type) Filter { 204 return func(a *Artifact) bool { 205 return a.Type == t 206 } 207 } 208 209 // ByFormats filters artifacts by a `Format` extra field. 210 func ByFormats(formats ...string) Filter { 211 var filters = make([]Filter, 0, len(formats)) 212 for _, format := range formats { 213 format := format 214 filters = append(filters, func(a *Artifact) bool { 215 return a.ExtraOr("Format", "") == format 216 }) 217 } 218 return Or(filters...) 219 } 220 221 // ByIDs filter artifacts by an `ID` extra field. 222 func ByIDs(ids ...string) Filter { 223 var filters = make([]Filter, 0, len(ids)) 224 for _, id := range ids { 225 id := id 226 filters = append(filters, func(a *Artifact) bool { 227 // checksum and source archive are always for all artifacts, so return always true. 228 return a.Type == Checksum || 229 a.Type == UploadableSourceArchive || 230 a.ExtraOr("ID", "") == id 231 }) 232 } 233 return Or(filters...) 234 } 235 236 // Or performs an OR between all given filters. 237 func Or(filters ...Filter) Filter { 238 return func(a *Artifact) bool { 239 for _, f := range filters { 240 if f(a) { 241 return true 242 } 243 } 244 return false 245 } 246 } 247 248 // And performs an AND between all given filters. 249 func And(filters ...Filter) Filter { 250 return func(a *Artifact) bool { 251 for _, f := range filters { 252 if !f(a) { 253 return false 254 } 255 } 256 return true 257 } 258 } 259 260 // Filter filters the artifact list, returning a new instance. 261 // There are some pre-defined filters but anything of the Type Filter 262 // is accepted. 263 // You can compose filters by using the And and Or filters. 264 func (artifacts *Artifacts) Filter(filter Filter) Artifacts { 265 if filter == nil { 266 return *artifacts 267 } 268 269 var result = New() 270 for _, a := range artifacts.items { 271 if filter(a) { 272 result.items = append(result.items, a) 273 } 274 } 275 return result 276 }