mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-19 20:57:53 +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
|
- deb
|
||||||
dependencies:
|
dependencies:
|
||||||
- git
|
- 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
|
language: go
|
||||||
go: 1.8.3
|
go: 1.8.3
|
||||||
install:
|
install:
|
||||||
- make setup
|
- make setup
|
||||||
- gem install fpm
|
- 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:
|
script:
|
||||||
- make test
|
- make test
|
||||||
- test -n "$TRAVIS_TAG" || go run main.go --skip-validate --skip-publish
|
- 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"`
|
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
|
// Snapshot config
|
||||||
type Snapshot struct {
|
type Snapshot struct {
|
||||||
NameTemplate string `yaml:"name_template,omitempty"`
|
NameTemplate string `yaml:"name_template,omitempty"`
|
||||||
@ -139,6 +150,7 @@ type Project struct {
|
|||||||
Builds []Build `yaml:",omitempty"`
|
Builds []Build `yaml:",omitempty"`
|
||||||
Archive Archive `yaml:",omitempty"`
|
Archive Archive `yaml:",omitempty"`
|
||||||
FPM FPM `yaml:",omitempty"`
|
FPM FPM `yaml:",omitempty"`
|
||||||
|
Snapcraft Snapcraft `yaml:",omitempty"`
|
||||||
Snapshot Snapshot `yaml:",omitempty"`
|
Snapshot Snapshot `yaml:",omitempty"`
|
||||||
|
|
||||||
// this is a hack ¯\_(ツ)_/¯
|
// this is a hack ¯\_(ツ)_/¯
|
||||||
@ -191,6 +203,7 @@ func checkOverflows(config Project) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
checker.check(config.FPM.XXX, "fpm")
|
checker.check(config.FPM.XXX, "fpm")
|
||||||
|
checker.check(config.Snapcraft.XXX, "snapcraft")
|
||||||
checker.check(config.Release.XXX, "release")
|
checker.check(config.Release.XXX, "release")
|
||||||
checker.check(config.Release.GitHub.XXX, "release.github")
|
checker.check(config.Release.GitHub.XXX, "release.github")
|
||||||
checker.check(config.SingleBuild.XXX, "build")
|
checker.check(config.SingleBuild.XXX, "build")
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/goreleaser/goreleaser/pipeline/fpm"
|
"github.com/goreleaser/goreleaser/pipeline/fpm"
|
||||||
"github.com/goreleaser/goreleaser/pipeline/git"
|
"github.com/goreleaser/goreleaser/pipeline/git"
|
||||||
"github.com/goreleaser/goreleaser/pipeline/release"
|
"github.com/goreleaser/goreleaser/pipeline/release"
|
||||||
|
"github.com/goreleaser/goreleaser/pipeline/snapcraft"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ var pipes = []pipeline.Pipe{
|
|||||||
build.Pipe{}, // build
|
build.Pipe{}, // build
|
||||||
archive.Pipe{}, // archive (tar.gz, zip, etc)
|
archive.Pipe{}, // archive (tar.gz, zip, etc)
|
||||||
fpm.Pipe{}, // archive via fpm (deb, rpm, etc)
|
fpm.Pipe{}, // archive via fpm (deb, rpm, etc)
|
||||||
|
snapcraft.Pipe{}, // archive via snapcraft (snap)
|
||||||
checksums.Pipe{}, // checksums of the files
|
checksums.Pipe{}, // checksums of the files
|
||||||
release.Pipe{}, // release to github
|
release.Pipe{}, // release to github
|
||||||
brew.Pipe{}, // push to brew tap
|
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