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  }