github.com/ahmet2mir/goreleaser@v0.180.3-0.20210927151101-8e5ee5a9b8c5/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 // BrewTap is an uploadable homebrew tap recipe file. 52 BrewTap 53 // ScoopManifest is an uploadable scoop manifest file. 54 ScoopManifest 55 ) 56 57 func (t Type) String() string { 58 switch t { 59 case UploadableArchive: 60 return "Archive" 61 case UploadableFile: 62 return "File" 63 case UploadableBinary, Binary: 64 return "Binary" 65 case LinuxPackage: 66 return "Linux Package" 67 case PublishableDockerImage, DockerImage: 68 return "Docker Image" 69 case DockerManifest: 70 return "Docker Manifest" 71 case PublishableSnapcraft, Snapcraft: 72 return "Snap" 73 case Checksum: 74 return "Checksum" 75 case Signature: 76 return "Signature" 77 case UploadableSourceArchive: 78 return "Source" 79 case BrewTap: 80 return "Brew Tap" 81 case ScoopManifest: 82 return "Scoop Manifest" 83 default: 84 return "unknown" 85 } 86 } 87 88 // Artifact represents an artifact and its relevant info. 89 type Artifact struct { 90 Name string 91 Path string 92 Goos string 93 Goarch string 94 Goarm string 95 Gomips string 96 Type Type 97 Extra map[string]interface{} 98 } 99 100 // ExtraOr returns the Extra field with the given key or the or value specified 101 // if it is nil. 102 func (a Artifact) ExtraOr(key string, or interface{}) interface{} { 103 if a.Extra[key] == nil { 104 return or 105 } 106 return a.Extra[key] 107 } 108 109 // Checksum calculates the checksum of the artifact. 110 // nolint: gosec 111 func (a Artifact) Checksum(algorithm string) (string, error) { 112 log.Debugf("calculating checksum for %s", a.Path) 113 file, err := os.Open(a.Path) 114 if err != nil { 115 return "", fmt.Errorf("failed to checksum: %w", err) 116 } 117 defer file.Close() 118 var h hash.Hash 119 switch algorithm { 120 case "crc32": 121 h = crc32.NewIEEE() 122 case "md5": 123 h = md5.New() 124 case "sha224": 125 h = sha256.New224() 126 case "sha384": 127 h = sha512.New384() 128 case "sha256": 129 h = sha256.New() 130 case "sha1": 131 h = sha1.New() 132 case "sha512": 133 h = sha512.New() 134 default: 135 return "", fmt.Errorf("invalid algorithm: %s", algorithm) 136 } 137 138 if _, err := io.Copy(h, file); err != nil { 139 return "", fmt.Errorf("failed to checksum: %w", err) 140 } 141 return hex.EncodeToString(h.Sum(nil)), nil 142 } 143 144 // Artifacts is a list of artifacts. 145 type Artifacts struct { 146 items []*Artifact 147 lock *sync.Mutex 148 } 149 150 // New return a new list of artifacts. 151 func New() Artifacts { 152 return Artifacts{ 153 items: []*Artifact{}, 154 lock: &sync.Mutex{}, 155 } 156 } 157 158 // List return the actual list of artifacts. 159 func (artifacts Artifacts) List() []*Artifact { 160 return artifacts.items 161 } 162 163 // GroupByPlatform groups the artifacts by their platform. 164 func (artifacts Artifacts) GroupByPlatform() map[string][]*Artifact { 165 result := map[string][]*Artifact{} 166 for _, a := range artifacts.items { 167 plat := a.Goos + a.Goarch + a.Goarm + a.Gomips 168 result[plat] = append(result[plat], a) 169 } 170 return result 171 } 172 173 // Add safely adds a new artifact to an artifact list. 174 func (artifacts *Artifacts) Add(a *Artifact) { 175 artifacts.lock.Lock() 176 defer artifacts.lock.Unlock() 177 log.WithFields(log.Fields{ 178 "name": a.Name, 179 "path": a.Path, 180 "type": a.Type, 181 }).Debug("added new artifact") 182 artifacts.items = append(artifacts.items, a) 183 } 184 185 // Filter defines an artifact filter which can be used within the Filter 186 // function. 187 type Filter func(a *Artifact) bool 188 189 // ByGoos is a predefined filter that filters by the given goos. 190 func ByGoos(s string) Filter { 191 return func(a *Artifact) bool { 192 return a.Goos == s 193 } 194 } 195 196 // ByGoarch is a predefined filter that filters by the given goarch. 197 func ByGoarch(s string) Filter { 198 return func(a *Artifact) bool { 199 return a.Goarch == s 200 } 201 } 202 203 // ByGoarm is a predefined filter that filters by the given goarm. 204 func ByGoarm(s string) Filter { 205 return func(a *Artifact) bool { 206 return a.Goarm == s 207 } 208 } 209 210 // ByType is a predefined filter that filters by the given type. 211 func ByType(t Type) Filter { 212 return func(a *Artifact) bool { 213 return a.Type == t 214 } 215 } 216 217 // ByFormats filters artifacts by a `Format` extra field. 218 func ByFormats(formats ...string) Filter { 219 filters := make([]Filter, 0, len(formats)) 220 for _, format := range formats { 221 format := format 222 filters = append(filters, func(a *Artifact) bool { 223 return a.ExtraOr("Format", "") == format 224 }) 225 } 226 return Or(filters...) 227 } 228 229 // ByIDs filter artifacts by an `ID` extra field. 230 func ByIDs(ids ...string) Filter { 231 filters := make([]Filter, 0, len(ids)) 232 for _, id := range ids { 233 id := id 234 filters = append(filters, func(a *Artifact) bool { 235 // checksum and source archive are always for all artifacts, so return always true. 236 return a.Type == Checksum || 237 a.Type == UploadableSourceArchive || 238 a.ExtraOr("ID", "") == id 239 }) 240 } 241 return Or(filters...) 242 } 243 244 // Or performs an OR between all given filters. 245 func Or(filters ...Filter) Filter { 246 return func(a *Artifact) bool { 247 for _, f := range filters { 248 if f(a) { 249 return true 250 } 251 } 252 return false 253 } 254 } 255 256 // And performs an AND between all given filters. 257 func And(filters ...Filter) Filter { 258 return func(a *Artifact) bool { 259 for _, f := range filters { 260 if !f(a) { 261 return false 262 } 263 } 264 return true 265 } 266 } 267 268 // Filter filters the artifact list, returning a new instance. 269 // There are some pre-defined filters but anything of the Type Filter 270 // is accepted. 271 // You can compose filters by using the And and Or filters. 272 func (artifacts *Artifacts) Filter(filter Filter) Artifacts { 273 if filter == nil { 274 return *artifacts 275 } 276 277 result := New() 278 for _, a := range artifacts.items { 279 if filter(a) { 280 result.items = append(result.items, a) 281 } 282 } 283 return result 284 } 285 286 // Paths returns the artifact.Path of the current artifact list. 287 func (artifacts Artifacts) Paths() []string { 288 var result []string 289 for _, artifact := range artifacts.List() { 290 result = append(result, artifact.Path) 291 } 292 return result 293 }