1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-26 04:22:05 +02:00
2017-09-14 21:19:56 -03:00

152 lines
4.2 KiB
Go

// Package snapcraft implements the Pipe interface providing Snapcraft bindings.
package snapcraft
import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/apex/log"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/linux"
"github.com/goreleaser/goreleaser/pipeline"
"golang.org/x/sync/errgroup"
yaml "gopkg.in/yaml.v2"
)
// ErrNoSnapcraft is shown when snapcraft cannot be found in $PATH
var ErrNoSnapcraft = errors.New("snapcraft not present in $PATH")
// ErrNoDescription is shown when no description provided
var ErrNoDescription = errors.New("no description provided for snapcraft")
// ErrNoSummary is shown when no summary provided
var ErrNoSummary = errors.New("no summary provided for snapcraft")
// Metadata to generate the snap package
type Metadata struct {
Name string
Version string
Summary string
Description string
Grade string `yaml:",omitempty"`
Confinement string `yaml:",omitempty"`
Architectures []string
Apps map[string]AppMetadata
}
// AppMetadata for the binaries that will be in the snap package
type AppMetadata struct {
Command string
Plugs []string `yaml:",omitempty"`
Daemon string `yaml:",omitempty"`
}
// Pipe for snapcraft packaging
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Creating Linux packages with snapcraft"
}
// Run the pipe
func (Pipe) Run(ctx *context.Context) error {
if ctx.Config.Snapcraft.Summary == "" && ctx.Config.Snapcraft.Description == "" {
return pipeline.Skip("no summary nor description were provided")
}
if ctx.Config.Snapcraft.Summary == "" {
return ErrNoSummary
}
if ctx.Config.Snapcraft.Description == "" {
return ErrNoDescription
}
_, err := exec.LookPath("snapcraft")
if err != nil {
return ErrNoSnapcraft
}
var g errgroup.Group
for platform, groups := range ctx.Binaries {
if !strings.Contains(platform, "linux") {
log.WithField("platform", platform).Debug("skipped non-linux builds for snapcraft")
continue
}
arch := linux.Arch(platform)
for folder, binaries := range groups {
g.Go(func() error {
return create(ctx, folder, arch, binaries)
})
}
}
return g.Wait()
}
func create(ctx *context.Context, folder, arch string, binaries []context.Binary) error {
var log = log.WithField("arch", arch)
// prime is the directory that then will be compressed to make the .snap package.
var folderDir = filepath.Join(ctx.Config.Dist, folder)
var primeDir = filepath.Join(folderDir, "prime")
var metaDir = filepath.Join(primeDir, "meta")
if err := os.MkdirAll(metaDir, 0755); err != nil {
return err
}
var file = filepath.Join(primeDir, "meta", "snap.yaml")
log.WithField("file", file).Debug("creating snap metadata")
var metadata = &Metadata{
Version: ctx.Version,
Summary: ctx.Config.Snapcraft.Summary,
Description: ctx.Config.Snapcraft.Description,
Grade: ctx.Config.Snapcraft.Grade,
Confinement: ctx.Config.Snapcraft.Confinement,
Architectures: []string{arch},
Apps: make(map[string]AppMetadata),
}
if ctx.Config.Snapcraft.Name != "" {
metadata.Name = ctx.Config.Snapcraft.Name
} else {
metadata.Name = ctx.Config.ProjectName
}
for _, binary := range binaries {
log.WithField("path", binary.Path).
WithField("name", binary.Name).
Debug("passed binary to snapcraft")
appMetadata := AppMetadata{
Command: binary.Name,
}
if configAppMetadata, ok := ctx.Config.Snapcraft.Apps[binary.Name]; ok {
appMetadata.Plugs = configAppMetadata.Plugs
appMetadata.Daemon = configAppMetadata.Daemon
}
metadata.Apps[binary.Name] = appMetadata
destBinaryPath := filepath.Join(primeDir, filepath.Base(binary.Path))
if err := os.Link(binary.Path, destBinaryPath); err != nil {
return err
}
}
out, err := yaml.Marshal(metadata)
if err != nil {
return err
}
if err = ioutil.WriteFile(file, out, 0644); err != nil {
return err
}
var snap = filepath.Join(ctx.Config.Dist, folder+".snap")
var cmd = exec.Command("snapcraft", "snap", primeDir, "--output", snap)
if out, err = cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to generate snap package: %s", string(out))
}
ctx.AddArtifact(snap)
return nil
}