github.com/szyn/goreleaser@v0.76.1-0.20180517112710-333da09a1297/pipeline/scoop/scoop.go (about)

     1  // Package scoop provides a Pipe that generates a scoop.sh App Manifest and pushes it to a bucket
     2  package scoop
     3  
     4  import (
     5  	"bytes"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/goreleaser/goreleaser/context"
    11  	"github.com/goreleaser/goreleaser/internal/artifact"
    12  	"github.com/goreleaser/goreleaser/internal/client"
    13  	"github.com/goreleaser/goreleaser/pipeline"
    14  )
    15  
    16  // ErrNoWindows when there is no build for windows (goos doesn't contain windows)
    17  var ErrNoWindows = errors.New("scoop requires a windows build")
    18  
    19  // Pipe for build
    20  type Pipe struct{}
    21  
    22  func (Pipe) String() string {
    23  	return "creating Scoop Manifest"
    24  }
    25  
    26  // Run the pipe
    27  func (Pipe) Run(ctx *context.Context) error {
    28  	client, err := client.NewGitHub(ctx)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	return doRun(ctx, client)
    33  }
    34  
    35  // Default sets the pipe defaults
    36  func (Pipe) Default(ctx *context.Context) error {
    37  	if ctx.Config.Scoop.CommitAuthor.Name == "" {
    38  		ctx.Config.Scoop.CommitAuthor.Name = "goreleaserbot"
    39  	}
    40  	if ctx.Config.Scoop.CommitAuthor.Email == "" {
    41  		ctx.Config.Scoop.CommitAuthor.Email = "goreleaser@carlosbecker.com"
    42  	}
    43  	return nil
    44  }
    45  
    46  func doRun(ctx *context.Context, client client.Client) error {
    47  	if ctx.Config.Scoop.Bucket.Name == "" {
    48  		return pipeline.Skip("scoop section is not configured")
    49  	}
    50  	if ctx.Config.Archive.Format == "binary" {
    51  		return pipeline.Skip("archive format is binary")
    52  	}
    53  
    54  	var archives = ctx.Artifacts.Filter(
    55  		artifact.And(
    56  			artifact.ByGoos("windows"),
    57  			artifact.ByType(artifact.UploadableArchive),
    58  		),
    59  	).List()
    60  	if len(archives) == 0 {
    61  		return ErrNoWindows
    62  	}
    63  
    64  	path := ctx.Config.ProjectName + ".json"
    65  
    66  	content, err := buildManifest(ctx, client, archives)
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	if ctx.SkipPublish {
    72  		return pipeline.ErrSkipPublishEnabled
    73  	}
    74  	if ctx.Config.Release.Draft {
    75  		return pipeline.Skip("release is marked as draft")
    76  	}
    77  	return client.CreateFile(
    78  		ctx,
    79  		ctx.Config.Scoop.CommitAuthor,
    80  		ctx.Config.Scoop.Bucket,
    81  		content,
    82  		path,
    83  		fmt.Sprintf("Scoop update for %s version %s", ctx.Config.ProjectName, ctx.Git.CurrentTag),
    84  	)
    85  }
    86  
    87  // Manifest represents a scoop.sh App Manifest, more info:
    88  // https://github.com/lukesampson/scoop/wiki/App-Manifests
    89  type Manifest struct {
    90  	Version      string              `json:"version"`               // The version of the app that this manifest installs.
    91  	Architecture map[string]Resource `json:"architecture"`          // `architecture`: If the app has 32- and 64-bit versions, architecture can be used to wrap the differences.
    92  	Homepage     string              `json:"homepage,omitempty"`    // `homepage`: The home page for the program.
    93  	License      string              `json:"license,omitempty"`     // `license`: The software license for the program. For well-known licenses, this will be a string like "MIT" or "GPL2". For custom licenses, this should be the URL of the license.
    94  	Description  string              `json:"description,omitempty"` // Description of the app
    95  }
    96  
    97  // Resource represents a combination of a url and a binary name for an architecture
    98  type Resource struct {
    99  	URL string `json:"url"` // URL to the archive
   100  	Bin string `json:"bin"` // name of binary inside the archive
   101  }
   102  
   103  func buildManifest(ctx *context.Context, client client.Client, artifacts []artifact.Artifact) (result bytes.Buffer, err error) {
   104  	manifest := Manifest{
   105  		Version:      ctx.Version,
   106  		Architecture: make(map[string]Resource),
   107  		Homepage:     ctx.Config.Scoop.Homepage,
   108  		License:      ctx.Config.Scoop.License,
   109  		Description:  ctx.Config.Scoop.Description,
   110  	}
   111  
   112  	for _, artifact := range artifacts {
   113  		var arch = "64bit"
   114  		if artifact.Goarch == "386" {
   115  			arch = "32bit"
   116  		}
   117  		manifest.Architecture[arch] = Resource{
   118  			URL: getDownloadURL(ctx, ctx.Config.GitHubURLs.Download, artifact.Name),
   119  			Bin: ctx.Config.Builds[0].Binary + ".exe",
   120  		}
   121  	}
   122  
   123  	data, err := json.MarshalIndent(manifest, "", "    ")
   124  	if err != nil {
   125  		return
   126  	}
   127  	_, err = result.Write(data)
   128  	return
   129  }
   130  
   131  func getDownloadURL(ctx *context.Context, githubURL, file string) string {
   132  	return fmt.Sprintf(
   133  		"%s/%s/%s/releases/download/%s/%s",
   134  		githubURL,
   135  		ctx.Config.Release.GitHub.Owner,
   136  		ctx.Config.Release.GitHub.Name,
   137  		ctx.Git.CurrentTag,
   138  		file,
   139  	)
   140  }