mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
Merge pull request #317 from elopio/snapcraft
add the snapcraft pipeline
This commit is contained in:
commit
e45c4a9cb7
@ -41,3 +41,11 @@ fpm:
|
||||
- deb
|
||||
dependencies:
|
||||
- git
|
||||
snapcraft:
|
||||
summary: Deliver Go binaries as fast and easily as possible
|
||||
description: |
|
||||
GoReleaser builds Go binaries for several platforms, creates a GitHub
|
||||
release and then pushes a Homebrew formula to a repository. All that
|
||||
wrapped in your favorite CI.
|
||||
grade: stable
|
||||
confinement: classic
|
||||
|
@ -1,8 +1,14 @@
|
||||
dist: trusty
|
||||
sudo: required
|
||||
language: go
|
||||
go: 1.8.3
|
||||
install:
|
||||
- make setup
|
||||
- gem install fpm
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install --yes snapd
|
||||
- export PATH=/snap/bin:$PATH
|
||||
- sudo snap install snapcraft --candidate --classic
|
||||
script:
|
||||
- make test
|
||||
- test -n "$TRAVIS_TAG" || go run main.go --skip-validate --skip-publish
|
||||
|
@ -123,6 +123,17 @@ type FPM struct {
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// Snapcraft config
|
||||
type Snapcraft struct {
|
||||
Summary string `yaml:",omitempty"`
|
||||
Description string `yaml:",omitempty"`
|
||||
Grade string `yaml:",omitempty"`
|
||||
Confinement string `yaml:",omitempty"`
|
||||
|
||||
// Capture all undefined fields and should be empty after loading
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}
|
||||
|
||||
// Snapshot config
|
||||
type Snapshot struct {
|
||||
NameTemplate string `yaml:"name_template,omitempty"`
|
||||
@ -133,13 +144,14 @@ type Snapshot struct {
|
||||
|
||||
// Project includes all project configuration
|
||||
type Project struct {
|
||||
ProjectName string `yaml:"project_name,omitempty"`
|
||||
Release Release `yaml:",omitempty"`
|
||||
Brew Homebrew `yaml:",omitempty"`
|
||||
Builds []Build `yaml:",omitempty"`
|
||||
Archive Archive `yaml:",omitempty"`
|
||||
FPM FPM `yaml:",omitempty"`
|
||||
Snapshot Snapshot `yaml:",omitempty"`
|
||||
ProjectName string `yaml:"project_name,omitempty"`
|
||||
Release Release `yaml:",omitempty"`
|
||||
Brew Homebrew `yaml:",omitempty"`
|
||||
Builds []Build `yaml:",omitempty"`
|
||||
Archive Archive `yaml:",omitempty"`
|
||||
FPM FPM `yaml:",omitempty"`
|
||||
Snapcraft Snapcraft `yaml:",omitempty"`
|
||||
Snapshot Snapshot `yaml:",omitempty"`
|
||||
|
||||
// this is a hack ¯\_(ツ)_/¯
|
||||
SingleBuild Build `yaml:"build,omitempty"`
|
||||
@ -191,6 +203,7 @@ func checkOverflows(config Project) error {
|
||||
}
|
||||
}
|
||||
checker.check(config.FPM.XXX, "fpm")
|
||||
checker.check(config.Snapcraft.XXX, "snapcraft")
|
||||
checker.check(config.Release.XXX, "release")
|
||||
checker.check(config.Release.GitHub.XXX, "release.github")
|
||||
checker.check(config.SingleBuild.XXX, "build")
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/pipeline/fpm"
|
||||
"github.com/goreleaser/goreleaser/pipeline/git"
|
||||
"github.com/goreleaser/goreleaser/pipeline/release"
|
||||
"github.com/goreleaser/goreleaser/pipeline/snapcraft"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
@ -31,6 +32,7 @@ var pipes = []pipeline.Pipe{
|
||||
build.Pipe{}, // build
|
||||
archive.Pipe{}, // archive (tar.gz, zip, etc)
|
||||
fpm.Pipe{}, // archive via fpm (deb, rpm, etc)
|
||||
snapcraft.Pipe{}, // archive via snapcraft (snap)
|
||||
checksums.Pipe{}, // checksums of the files
|
||||
release.Pipe{}, // release to github
|
||||
brew.Pipe{}, // push to brew tap
|
||||
|
142
pipeline/snapcraft/snapcraft.go
Normal file
142
pipeline/snapcraft/snapcraft.go
Normal file
@ -0,0 +1,142 @@
|
||||
// Package snapcraft implements the Pipe interface providing Snapcraft bindings.
|
||||
package snapcraft
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
"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")
|
||||
|
||||
// SnapcraftMetadata to generate the snap package
|
||||
type SnapcraftMetadata struct {
|
||||
Name string
|
||||
Version string
|
||||
Summary string
|
||||
Description string
|
||||
Grade string `yaml:",omitempty"`
|
||||
Confinement string `yaml:",omitempty"`
|
||||
Architectures []string
|
||||
Apps map[string]AppsMetadata
|
||||
}
|
||||
|
||||
// AppsMetadata for the binaries that will be in the snap package
|
||||
type AppsMetadata struct {
|
||||
Command string
|
||||
// Plugs []string
|
||||
// Daemon string
|
||||
}
|
||||
|
||||
// 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 == "" {
|
||||
log.Error("no snapcraft summary defined, skipping")
|
||||
return nil
|
||||
}
|
||||
if ctx.Config.Snapcraft.Summary == "" {
|
||||
log.Error("no snapcraft description defined, skipping")
|
||||
return nil
|
||||
}
|
||||
_, 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 := archFor(platform)
|
||||
for folder, binaries := range groups {
|
||||
g.Go(func() error {
|
||||
return create(ctx, folder, arch, binaries)
|
||||
})
|
||||
}
|
||||
}
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func archFor(key string) string {
|
||||
switch {
|
||||
case strings.Contains(key, "amd64"):
|
||||
return "amd64"
|
||||
case strings.Contains(key, "386"):
|
||||
return "i386"
|
||||
case strings.Contains(key, "arm64"):
|
||||
return "arm64"
|
||||
case strings.Contains(key, "arm6"):
|
||||
return "armhf"
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func create(ctx *context.Context, folder, arch string, binaries []context.Binary) error {
|
||||
// prime is the directory that then will be compressed to make the .snap package.
|
||||
folderDir := filepath.Join(ctx.Config.Dist, folder)
|
||||
primeDir := filepath.Join(folderDir, "prime")
|
||||
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).Info("creating snap metadata")
|
||||
|
||||
var metadata = &SnapcraftMetadata{
|
||||
Name: ctx.Config.ProjectName,
|
||||
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]AppsMetadata),
|
||||
}
|
||||
|
||||
for _, binary := range binaries {
|
||||
log.WithField("path", binary.Path).
|
||||
WithField("name", binary.Name).
|
||||
Info("passed binary to snapcraft")
|
||||
metadata.Apps[binary.Name] = AppsMetadata{Command: binary.Name}
|
||||
|
||||
destBinaryPath := filepath.Join(primeDir, filepath.Base(binary.Path))
|
||||
os.Link(binary.Path, destBinaryPath)
|
||||
}
|
||||
out, err := yaml.Marshal(metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = ioutil.WriteFile(file, out, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
snap := metadata.Name + "_" + metadata.Version + "_" + arch + ".snap"
|
||||
cmd := exec.Command("snapcraft", "snap", "prime", "--output", snap)
|
||||
cmd.Dir = folderDir
|
||||
if out, err = cmd.CombinedOutput(); err != nil {
|
||||
return errors.New(string(out))
|
||||
}
|
||||
ctx.AddArtifact(filepath.Join(folderDir, snap))
|
||||
return nil
|
||||
}
|
85
pipeline/snapcraft/snapcraft_test.go
Normal file
85
pipeline/snapcraft/snapcraft_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
package snapcraft
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/goreleaser/config"
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDescription(t *testing.T) {
|
||||
assert.NotEmpty(t, Pipe{}.Description())
|
||||
}
|
||||
|
||||
func TestRunPipeNoSummary(t *testing.T) {
|
||||
var assert = assert.New(t)
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{
|
||||
Snapcraft: config.Snapcraft{
|
||||
Description: "dummy",
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestRunPipeNoDescription(t *testing.T) {
|
||||
var assert = assert.New(t)
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{
|
||||
Snapcraft: config.Snapcraft{
|
||||
Summary: "dummy",
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestRunPipe(t *testing.T) {
|
||||
var assert = assert.New(t)
|
||||
folder, err := ioutil.TempDir("", "archivetest")
|
||||
assert.NoError(err)
|
||||
var dist = filepath.Join(folder, "dist")
|
||||
assert.NoError(os.Mkdir(dist, 0755))
|
||||
assert.NoError(os.Mkdir(filepath.Join(dist, "mybin"), 0755))
|
||||
var binPath = filepath.Join(dist, "mybin", "mybin")
|
||||
_, err = os.Create(binPath)
|
||||
assert.NoError(err)
|
||||
var ctx = &context.Context{
|
||||
Version: "testversion",
|
||||
Config: config.Project{
|
||||
ProjectName: "mybin",
|
||||
Dist: dist,
|
||||
Snapcraft: config.Snapcraft{
|
||||
Summary: "test summary",
|
||||
Description: "test description",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, plat := range []string{"linuxamd64", "linux386", "darwinamd64"} {
|
||||
ctx.AddBinary(plat, "mybin", "mybin", binPath)
|
||||
}
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestNoSnapcraftInPath(t *testing.T) {
|
||||
var assert = assert.New(t)
|
||||
var path = os.Getenv("PATH")
|
||||
defer func() {
|
||||
assert.NoError(os.Setenv("PATH", path))
|
||||
}()
|
||||
assert.NoError(os.Setenv("PATH", ""))
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{
|
||||
Snapcraft: config.Snapcraft{
|
||||
Summary: "dummy",
|
||||
Description: "dummy",
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.EqualError(Pipe{}.Run(ctx), ErrNoSnapcraft.Error())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user