2018-02-10 12:38:07 +00:00
// Package scoop provides a Pipe that generates a scoop.sh App Manifest and pushes it to a bucket
package scoop
import (
"bytes"
"encoding/json"
"errors"
2020-04-21 16:11:18 -03:00
"path/filepath"
2020-02-05 19:05:43 -06:00
"strings"
2018-02-10 12:38:07 +00:00
2020-04-27 21:51:05 -03:00
"github.com/apex/log"
2018-02-10 12:38:07 +00:00
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/client"
2018-09-12 14:18:01 -03:00
"github.com/goreleaser/goreleaser/internal/pipe"
2018-08-21 04:06:55 +03:00
"github.com/goreleaser/goreleaser/internal/tmpl"
2018-08-14 23:50:20 -03:00
"github.com/goreleaser/goreleaser/pkg/context"
2018-02-10 12:38:07 +00:00
)
2020-05-26 00:48:10 -03:00
// ErrNoWindows when there is no build for windows (goos doesn't contain windows).
2018-02-10 12:38:07 +00:00
var ErrNoWindows = errors . New ( "scoop requires a windows build" )
2020-05-26 00:48:10 -03:00
// ErrTokenTypeNotImplementedForScoop indicates that a new token type was not implemented for this pipe.
2019-08-13 20:28:03 +02:00
var ErrTokenTypeNotImplementedForScoop = errors . New ( "token type not implemented for scoop pipe" )
2020-05-26 00:48:10 -03:00
// Pipe for build.
2018-02-10 12:38:07 +00:00
type Pipe struct { }
func ( Pipe ) String ( ) string {
2020-03-06 01:25:09 -03:00
return "scoop manifests"
2018-02-10 12:38:07 +00:00
}
2020-05-26 00:48:10 -03:00
// Publish scoop manifest.
2018-10-10 12:47:31 -03:00
func ( Pipe ) Publish ( ctx * context . Context ) error {
2020-04-29 15:09:00 -03:00
if ctx . SkipPublish {
return pipe . ErrSkipPublishEnabled
}
2020-07-06 21:12:41 +01:00
2019-06-29 16:02:40 +02:00
client , err := client . New ( ctx )
2018-02-10 12:38:07 +00:00
if err != nil {
return err
}
return doRun ( ctx , client )
}
2020-05-26 00:48:10 -03:00
// Default sets the pipe defaults.
2018-02-10 12:38:07 +00:00
func ( Pipe ) Default ( ctx * context . Context ) error {
2018-09-30 11:04:32 -03:00
if ctx . Config . Scoop . Name == "" {
ctx . Config . Scoop . Name = ctx . Config . ProjectName
}
2018-02-10 12:38:07 +00:00
if ctx . Config . Scoop . CommitAuthor . Name == "" {
ctx . Config . Scoop . CommitAuthor . Name = "goreleaserbot"
}
if ctx . Config . Scoop . CommitAuthor . Email == "" {
ctx . Config . Scoop . CommitAuthor . Email = "goreleaser@carlosbecker.com"
}
2019-08-13 20:28:03 +02:00
2020-04-29 13:45:18 -07:00
if ctx . Config . Scoop . CommitMessageTemplate == "" {
ctx . Config . Scoop . CommitMessageTemplate = "Scoop update for {{ .ProjectName }} version {{ .Tag }}"
}
2018-02-10 12:38:07 +00:00
return nil
}
2020-07-06 21:12:41 +01:00
func doRun ( ctx * context . Context , cl client . Client ) error {
scoop := ctx . Config . Scoop
if scoop . Bucket . Name == "" {
2018-09-12 14:18:01 -03:00
return pipe . Skip ( "scoop section is not configured" )
2018-02-10 12:38:07 +00:00
}
2019-08-13 20:28:03 +02:00
2020-07-06 21:12:41 +01:00
if scoop . Bucket . Token != "" {
token , err := tmpl . New ( ctx ) . ApplySingleEnvOnly ( scoop . Bucket . Token )
if err != nil {
return err
}
log . Debug ( "using custom token to publish scoop manifest" )
c , err := client . NewWithToken ( ctx , token )
if err != nil {
return err
}
cl = c
}
2019-08-13 20:28:03 +02:00
2019-12-29 15:02:15 -03:00
// TODO: multiple archives
if ctx . Config . Archives [ 0 ] . Format == "binary" {
2018-09-12 14:18:01 -03:00
return pipe . Skip ( "archive format is binary" )
2018-02-10 12:38:07 +00:00
}
var archives = ctx . Artifacts . Filter (
artifact . And (
artifact . ByGoos ( "windows" ) ,
artifact . ByType ( artifact . UploadableArchive ) ,
) ,
) . List ( )
if len ( archives ) == 0 {
return ErrNoWindows
}
2020-07-06 21:12:41 +01:00
var path = scoop . Name + ".json"
2018-02-10 12:38:07 +00:00
2020-07-06 21:12:41 +01:00
data , err := dataFor ( ctx , cl , archives )
2020-07-06 14:48:17 +01:00
if err != nil {
return err
}
content , err := doBuildManifest ( data )
2018-02-10 12:38:07 +00:00
if err != nil {
return err
}
2018-03-01 01:12:58 -03:00
if ctx . SkipPublish {
2018-09-12 14:18:01 -03:00
return pipe . ErrSkipPublishEnabled
2020-02-05 19:05:43 -06:00
}
2020-07-06 21:12:41 +01:00
if strings . TrimSpace ( scoop . SkipUpload ) == "true" {
2020-02-05 19:05:43 -06:00
return pipe . Skip ( "scoop.skip_upload is true" )
2020-02-05 22:10:16 -03:00
}
2020-07-06 21:12:41 +01:00
if strings . TrimSpace ( scoop . SkipUpload ) == "auto" && ctx . Semver . Prerelease != "" {
2020-02-05 22:10:16 -03:00
return pipe . Skip ( "release is prerelease" )
2018-02-10 12:38:07 +00:00
}
if ctx . Config . Release . Draft {
2018-09-12 14:18:01 -03:00
return pipe . Skip ( "release is marked as draft" )
2018-02-10 12:38:07 +00:00
}
2019-08-29 01:34:09 +02:00
if ctx . Config . Release . Disable {
return pipe . Skip ( "release is disabled" )
}
2020-04-29 13:45:18 -07:00
commitMessage , err := tmpl . New ( ctx ) .
2020-07-06 21:12:41 +01:00
Apply ( scoop . CommitMessageTemplate )
2020-04-29 13:45:18 -07:00
if err != nil {
return err
}
2020-07-06 21:12:41 +01:00
repo := client . RepoFromRef ( scoop . Bucket )
return cl . CreateFile (
2018-02-10 12:38:07 +00:00
ctx ,
2020-07-06 21:12:41 +01:00
scoop . CommitAuthor ,
repo ,
2019-06-26 14:12:33 -03:00
content . Bytes ( ) ,
2018-02-15 00:13:57 -02:00
path ,
2020-04-29 13:45:18 -07:00
commitMessage ,
2018-02-15 00:13:57 -02:00
)
2018-02-10 12:38:07 +00:00
}
2020-05-26 00:48:10 -03:00
// Manifest represents a scoop.sh App Manifest.
// more info: https://github.com/lukesampson/scoop/wiki/App-Manifests
2018-02-10 12:38:07 +00:00
type Manifest struct {
2020-12-05 07:16:32 -08:00
Version string ` json:"version" ` // The version of the app that this manifest installs.
Architecture map [ string ] Resource ` json:"architecture" ` // `architecture`: If the app has 32- and 64-bit versions, architecture can be used to wrap the differences.
Homepage string ` json:"homepage,omitempty" ` // `homepage`: The home page for the program.
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.
Description string ` json:"description,omitempty" ` // Description of the app
Persist [ ] string ` json:"persist,omitempty" ` // Persist data between updates
PreInstall [ ] string ` json:"pre_install,omitempty" ` // An array of strings, of the commands to be executed before an application is installed.
PostInstall [ ] string ` json:"post_install,omitempty" ` // An array of strings, of the commands to be executed after an application is installed.
2018-02-10 12:38:07 +00:00
}
2020-05-26 00:48:10 -03:00
// Resource represents a combination of a url and a binary name for an architecture.
2018-02-10 12:38:07 +00:00
type Resource struct {
2019-01-05 12:30:26 -02:00
URL string ` json:"url" ` // URL to the archive
Bin [ ] string ` json:"bin" ` // name of binary inside the archive
Hash string ` json:"hash" ` // the archive checksum
2018-02-10 12:38:07 +00:00
}
2020-07-06 14:48:17 +01:00
func doBuildManifest ( manifest Manifest ) ( bytes . Buffer , error ) {
2018-08-20 22:20:04 -03:00
var result bytes . Buffer
2020-07-06 14:48:17 +01:00
data , err := json . MarshalIndent ( manifest , "" , " " )
if err != nil {
return result , err
}
_ , err = result . Write ( data )
return result , err
}
func dataFor ( ctx * context . Context , cl client . Client , artifacts [ ] * artifact . Artifact ) ( Manifest , error ) {
2018-08-20 22:20:04 -03:00
var manifest = Manifest {
2018-02-10 12:38:07 +00:00
Version : ctx . Version ,
2018-12-12 18:24:22 -02:00
Architecture : map [ string ] Resource { } ,
2018-02-10 12:38:07 +00:00
Homepage : ctx . Config . Scoop . Homepage ,
License : ctx . Config . Scoop . License ,
Description : ctx . Config . Scoop . Description ,
2018-08-25 23:24:42 +03:00
Persist : ctx . Config . Scoop . Persist ,
2020-12-05 07:16:32 -08:00
PreInstall : ctx . Config . Scoop . PreInstall ,
PostInstall : ctx . Config . Scoop . PostInstall ,
2018-02-10 12:38:07 +00:00
}
2019-08-13 20:28:03 +02:00
if ctx . Config . Scoop . URLTemplate == "" {
2020-07-06 14:48:17 +01:00
url , err := cl . ReleaseURLTemplate ( ctx )
if err != nil {
if client . IsNotImplementedErr ( err ) {
return manifest , ErrTokenTypeNotImplementedForScoop
}
return manifest , err
2019-08-13 20:28:03 +02:00
}
2020-07-06 14:48:17 +01:00
ctx . Config . Scoop . URLTemplate = url
2019-08-13 20:28:03 +02:00
}
2018-02-10 12:38:07 +00:00
for _ , artifact := range artifacts {
2021-04-18 20:40:23 +02:00
if artifact . Goos != "windows" {
continue
}
var arch string
switch {
case artifact . Goarch == "386" :
2018-02-15 00:13:57 -02:00
arch = "32bit"
2021-04-18 20:40:23 +02:00
case artifact . Goarch == "amd64" :
arch = "64bit"
default :
continue
2018-02-15 00:13:57 -02:00
}
2018-08-21 04:06:55 +03:00
2018-08-20 22:20:04 -03:00
url , err := tmpl . New ( ctx ) .
WithArtifact ( artifact , map [ string ] string { } ) .
Apply ( ctx . Config . Scoop . URLTemplate )
2018-08-21 04:06:55 +03:00
if err != nil {
2020-07-06 14:48:17 +01:00
return manifest , err
2018-08-21 04:06:55 +03:00
}
2019-02-04 17:27:51 -02:00
sum , err := artifact . Checksum ( "sha256" )
2018-09-06 13:20:12 +03:00
if err != nil {
2020-07-06 14:48:17 +01:00
return manifest , err
2018-09-06 13:20:12 +03:00
}
2020-04-27 21:51:05 -03:00
log . WithFields ( log . Fields {
"artifactExtras" : artifact . Extra ,
"fromURLTemplate" : ctx . Config . Scoop . URLTemplate ,
"templatedBrewURL" : url ,
"sum" : sum ,
} ) . Debug ( "scoop url templating" )
2018-02-15 00:13:57 -02:00
manifest . Architecture [ arch ] = Resource {
2018-09-06 13:20:12 +03:00
URL : url ,
2019-01-05 12:30:26 -02:00
Bin : binaries ( artifact ) ,
2018-09-06 13:20:12 +03:00
Hash : sum ,
2018-02-10 12:38:07 +00:00
}
}
2020-07-06 14:48:17 +01:00
return manifest , nil
2018-02-10 12:38:07 +00:00
}
2019-01-05 12:30:26 -02:00
2019-08-12 17:44:48 -03:00
func binaries ( a * artifact . Artifact ) [ ] string {
2019-01-05 12:30:26 -02:00
// nolint: prealloc
var bins [ ] string
2020-04-21 16:11:18 -03:00
var wrap = a . ExtraOr ( "WrappedIn" , "" ) . ( string )
2019-08-12 17:44:48 -03:00
for _ , b := range a . ExtraOr ( "Builds" , [ ] * artifact . Artifact { } ) . ( [ ] * artifact . Artifact ) {
2020-04-21 16:11:18 -03:00
bins = append ( bins , filepath . Join ( wrap , b . Name ) )
2019-01-05 12:30:26 -02:00
}
return bins
}