diff --git a/plugins/doc.go b/plugins/doc.go new file mode 100644 index 0000000..6cc1988 --- /dev/null +++ b/plugins/doc.go @@ -0,0 +1,3 @@ +// Package plugins contains magefile targets that perform common tasks +// that all plugins need to support +package plugins diff --git a/plugins/magefile.go b/plugins/magefile.go new file mode 100644 index 0000000..e810bff --- /dev/null +++ b/plugins/magefile.go @@ -0,0 +1,111 @@ +package plugins + +import ( + "fmt" + "os" + "path/filepath" + + "get.porter.sh/magefiles/ci" + "get.porter.sh/magefiles/porter" + "get.porter.sh/magefiles/releases" + "github.com/carolynvs/magex/mgx" + "github.com/carolynvs/magex/shx" + "github.com/carolynvs/magex/xplat" + "github.com/magefile/mage/mg" +) + +type Magefile struct { + Pkg string + PluginName string + BinDir string +} + +// NewMagefile creates a magefile helper for a plugin +func NewMagefile(pkg, pluginName, binDir string) Magefile { + return Magefile{Pkg: pkg, PluginName: pluginName, BinDir: binDir} +} + +var must = shx.CommandBuilder{StopOnError: true} + +// ConfigureAgent sets up a CI worker to use Go and mage. +func (m Magefile) ConfigureAgent() { + mgx.Must(ci.ConfigureAgent()) +} + +// Build the mixin +func (m Magefile) Build() { + must.RunV("go", "mod", "tidy") + releases.BuildPlugin(m.PluginName) +} + +// XBuildAll cross-compiles the mixin before a release +func (m Magefile) XBuildAll() { + releases.XBuildAll(m.Pkg, m.PluginName, m.BinDir) + releases.PrepareMixinForPublish(m.PluginName) +} + +// TestUnit runs unit tests +func (m Magefile) TestUnit() { + v := "" + if mg.Verbose() { + v = "-v" + } + must.Command("go", "test", v, "./pkg/...").CollapseArgs().RunV() +} + +// Test runs a full suite of tests +func (m Magefile) Test() { + m.TestUnit() + + // Check that we can call `plugin version` + m.Build() + must.RunV(filepath.Join(m.BinDir, m.PluginName+xplat.FileExt()), "version") +} + +// Publish the plugin and its plugin feed +func (m Magefile) Publish() { + mg.SerialDeps(m.PublishBinaries, m.PublishPluginFeed) +} + +// PublishBinaries uploads cross-compiled binaries to a GitHub release +// Requires PORTER_RELEASE_REPOSITORY to be set to github.com/USERNAME/REPO +func (m Magefile) PublishBinaries() { + mg.SerialDeps(porter.UseBinForPorterHome, porter.EnsurePorter) + releases.PreparePluginForPublish(m.PluginName) + releases.PublishPlugin(m.PluginName) +} + +// Publish a plugin feed +// Requires PORTER_PACKAGES_REMOTE to be set to git@github.com:USERNAME/REPO.git +func (m Magefile) PublishPluginFeed() { + mg.SerialDeps(porter.UseBinForPorterHome, porter.EnsurePorter) + releases.PublishPluginFeed(m.PluginName) +} + +// TestPublish uploads release artifacts to a fork of the pluin's repo. +// If your plugin is officially hosted in a repository under your username, you will need to manually +// override PORTER_RELEASE_REPOSITORY and PORTER_PACKAGES_REMOTE to test out publishing safely. +func (m Magefile) TestPublish(username string) { + pluginRepo := fmt.Sprintf("github.com/%s/%s-plugins", username, m.PluginName) + pkgRepo := fmt.Sprintf("https://github.com/%s/packages.git", username) + fmt.Printf("Publishing a release to %s and committing a plugin feed to %s\n", pluginRepo, pkgRepo) + fmt.Printf("If you use different repository names, set %s and %s then call mage Publish instead.\n", releases.ReleaseRepository, releases.PackagesRemote) + os.Setenv(releases.ReleaseRepository, pluginRepo) + os.Setenv(releases.PackagesRemote, pkgRepo) + + m.Publish() +} + +// Install the plugin +func (m Magefile) Install() { + porterHome := porter.GetPorterHome() + fmt.Printf("Installing the %s plugin into %s\n", m.PluginName, porterHome) + + os.MkdirAll(filepath.Join(porterHome, "plugins", m.PluginName), 0770) + mgx.Must(shx.Copy(filepath.Join(m.BinDir, m.PluginName+xplat.FileExt()), filepath.Join(porterHome, "mixins", m.PluginName))) +} + +// Clean removes generated build files +func (m Magefile) Clean() { + os.RemoveAll("bin") +} diff --git a/plugins/magefile_test.go b/plugins/magefile_test.go new file mode 100644 index 0000000..836a787 --- /dev/null +++ b/plugins/magefile_test.go @@ -0,0 +1,25 @@ +package plugins + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInstall(t *testing.T) { + magefile := NewMagefile("github.com/mymixin/test-mixin", "testmixin", "testdata/bin/mixins/testmixin") + + // Change the porter home to a safe place for the test to write to + require.NoError(t, os.MkdirAll("testdata/porter_home", 0770)) + os.Setenv("PORTER_HOME", "testdata/porter_home") + defer os.Unsetenv("PORTER_HOME") + + magefile.Install() + + assert.DirExists(t, "testdata/porter_home/mixins/testmixin", "The mixin directory doesn't exist") + assert.FileExists(t, "testdata/porter_home/mixins/testmixin/testmixin", "The client wasn't installed") + assert.DirExists(t, "testdata/porter_home/mixins/testmixin/runtimes", "The mixin runtimes directory doesn't exist") + assert.FileExists(t, "testdata/porter_home/mixins/testmixin/runtimes/testmixin-runtime", "The runtime wasn't installed") +} diff --git a/plugins/testdata/bin/plugins/testplugin/testplugin b/plugins/testdata/bin/plugins/testplugin/testplugin new file mode 100644 index 0000000..e69de29 diff --git a/releases/build.go b/releases/build.go index 0803f5e..f2da62f 100644 --- a/releases/build.go +++ b/releases/build.go @@ -22,6 +22,13 @@ func getLDFLAGS(pkg string) string { return fmt.Sprintf("-w -X %s/pkg.Version=%s -X %s/pkg.Commit=%s", pkg, info.Version, pkg, info.Commit) } +func getPluginLDFLAGS() string { + info := LoadMetadata() + + pkg := "get.porter.sh/porter/pkg/plugins/pluginbuilder" + return fmt.Sprintf("-w -X %s.Version=%s -X %s.Commit=%s", pkg, info.Version, pkg, info.Commit) +} + func build(pkgName, cmd, outPath, goos, goarch string) error { ldflags := getLDFLAGS(pkgName) @@ -51,6 +58,20 @@ func BuildClient(pkg string, name string, binDir string) error { return build(pkg, name, outPath, runtime.GOOS, runtime.GOARCH) } +func BuildPlugin(name string) error { + srcPath := "." + goos := runtime.GOOS + goarch := runtime.GOARCH + ldflags := getPluginLDFLAGS() + + outPath := filepath.Join("bin/plugins/", name, name, fileExt(goos)) + os.MkdirAll(filepath.Dir(outPath), 0770) + + return shx.Command("go", "build", "-ldflags", ldflags, "-o", outPath, srcPath). + Env("CGO_ENABLED=0", "GO111MODULE=on", "GOOS="+goos, "GOARCH="+goarch). + RunV() +} + func BuildAll(pkg string, name string, binDir string) error { var g errgroup.Group g.Go(func() error {