Skip to content

Commit

Permalink
support before_build hook (#455)
Browse files Browse the repository at this point in the history
  • Loading branch information
metrue committed Feb 11, 2020
1 parent 9b3e857 commit bdc454e
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 4 deletions.
4 changes: 2 additions & 2 deletions .golangci.yml
@@ -1,6 +1,6 @@
run:
deadline: 10m
timeout: 10m
deadline: 20m
timeout: 20m
issues-exit-code: 1
tests: true
skip-dirs:
Expand Down
15 changes: 14 additions & 1 deletion assets/dockerfiles/base/node/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions assets/dockerfiles/base/node/package.json
Expand Up @@ -10,6 +10,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@koa/cors": "^2.2.3",
"get-port": "^3.2.0",
"is-generator-function": "^1.0.6",
"koa": "^2.3.0",
Expand Down
7 changes: 7 additions & 0 deletions handlers/image.go
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/metrue/fx/constants"
containerruntimes "github.com/metrue/fx/container_runtimes"
"github.com/metrue/fx/context"
"github.com/metrue/fx/hook"
"github.com/metrue/fx/packer"
"github.com/metrue/fx/pkg/spinner"
"github.com/metrue/fx/utils"
Expand Down Expand Up @@ -39,6 +40,9 @@ func BuildImage(ctx context.Contexter) (err error) {
if err := packer.Pack(workdir, sources...); err != nil {
return err
}
if err := hook.RunBeforeBuildHook(workdir); err != nil {
return err
}
}

docker := ctx.Get("docker").(containerruntimes.ContainerRuntime)
Expand Down Expand Up @@ -68,6 +72,9 @@ func ExportImage(ctx context.Contexter) (err error) {
if err := packer.Pack(outputDir, sources...); err != nil {
return err
}
if err := hook.RunBeforeBuildHook(outputDir); err != nil {
return err
}
}

log.Infof("exported to %v: %v", outputDir, constants.CheckedSymbol)
Expand Down
1 change: 1 addition & 0 deletions hook/.hooks/before_build
@@ -0,0 +1 @@
npm install
15 changes: 15 additions & 0 deletions hook/fixture/package.json
@@ -0,0 +1,15 @@
{
"name": "fixture",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"leftpad": "0.0.1"
}
}
73 changes: 73 additions & 0 deletions hook/hook.go
@@ -0,0 +1,73 @@
package hook

import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"

"github.com/metrue/fx/utils"
)

// Hooker defines hook interface
type Hooker interface {
Run() error
}

// Hook to run
type Hook struct {
name string
script string
}

// New a hook
func New(name string, script string, workdir string) *Hook {
return &Hook{
name: name,
script: script,
}
}

// Run execute a hook
func (h *Hook) Run(workdir string) error {
var script string
if !utils.IsRegularFile(h.script) {
hookScript, err := ioutil.TempFile(os.TempDir(), "fx-hook-script-")
if err != nil {
return err
}
defer os.Remove(hookScript.Name())

content := []byte(h.script)
if _, err = hookScript.Write(content); err != nil {
return err
}
if err := hookScript.Close(); err != nil {
return err
}
script = hookScript.Name()
} else {
absScript, err := filepath.Abs(h.script)
if err != nil {
return err
}
script = absScript
}

cmd := exec.Command("/bin/sh", script)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if workdir != "" {
cmd.Dir = workdir
}

if err := cmd.Run(); err != nil {
return err
}
return nil
}

// Name hook name
func (h *Hook) Name() string {
return h.name
}
54 changes: 54 additions & 0 deletions hook/hook_manager.go
@@ -0,0 +1,54 @@
package hook

import (
"os"
"path/filepath"

"github.com/metrue/fx/utils"
)

// HookNameBeforeBuild before build hook
const HookNameBeforeBuild = "before_build"

// RunBeforeBuildHook trigger before_build hook
func RunBeforeBuildHook(workdir string) error {
hooks, err := descovery("")
if err != nil {
return err
}
for _, h := range hooks {
if h.Name() == HookNameBeforeBuild {
if err := h.Run(workdir); err != nil {
return err
}
}
}
return nil
}

func descovery(hookdir string) ([]*Hook, error) {
if hookdir == "" {
dir, err := os.Getwd()
if err != nil {
return nil, err
}
hookdir = filepath.Join(dir, ".hooks")
}

hooks := []*Hook{}
if !utils.IsDir(hookdir) {
return hooks, nil
}
if err := filepath.Walk(hookdir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Name() == HookNameBeforeBuild {
hooks = append(hooks, New("before_build", path, ""))
}
return nil
}); err != nil {
return nil, err
}
return hooks, nil
}
40 changes: 40 additions & 0 deletions hook/hook_manager_test.go
@@ -0,0 +1,40 @@
package hook

import (
"os"
"path/filepath"
"testing"
)

func TestHookManager(t *testing.T) {
t.Run("descovery in default hookdir .hooks", func(t *testing.T) {
hooks, err := descovery("")
if err != nil {
t.Fatal(err)
}

if len(hooks) != 1 {
t.Fatalf("should have one hook, but got %d", len(hooks))
}

if hooks[0].Name() != HookNameBeforeBuild {
t.Fatalf("should be before_build hook, but got %s", hooks[0].Name())
}
})

t.Run("descovery in empty hookdir", func(t *testing.T) {
hooks, err := descovery(filepath.Join(os.TempDir(), ".hooks"))
if err != nil {
t.Fatal(err)
}
if len(hooks) != 0 {
t.Fatalf("should get 0 hooks, but got %d", len(hooks))
}
})

t.Run("run before_build hook", func(t *testing.T) {
if err := RunBeforeBuildHook("fixture"); err != nil {
t.Fatal(err)
}
})
}
21 changes: 21 additions & 0 deletions hook/hook_test.go
@@ -0,0 +1,21 @@
package hook

import (
"testing"
)

func TestHook(t *testing.T) {
t.Run("text", func(t *testing.T) {
h := New("before_build", "npm install leftpad", "fixture")
if err := h.Run("fixture"); err != nil {
t.Fatal(err)
}
})

t.Run("script", func(t *testing.T) {
h := New("before_build", ".hooks/before_build", "fixture")
if err := h.Run("fixture"); err != nil {
t.Fatal(err)
}
})
}
4 changes: 4 additions & 0 deletions middlewares/build.go
Expand Up @@ -7,6 +7,7 @@ import (

containerruntimes "github.com/metrue/fx/container_runtimes"
"github.com/metrue/fx/context"
"github.com/metrue/fx/hook"
"github.com/metrue/fx/packer"
"github.com/metrue/fx/pkg/spinner"
"github.com/metrue/fx/types"
Expand Down Expand Up @@ -54,6 +55,9 @@ func Build(ctx context.Contexter) (err error) {
if err := packer.Pack(workdir, sources...); err != nil {
return err
}
if err := hook.RunBeforeBuildHook(workdir); err != nil {
return err
}
}

cloudType := ctx.Get("cloud_type").(string)
Expand Down
2 changes: 1 addition & 1 deletion packer/a_packer-packr.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packer/images/node/app.js
@@ -1,8 +1,12 @@
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const cors = require('@koa/cors');
const fx = require('./fx');

const app = new Koa();
app.use(cors({
origin: '*',
}));
app.use(bodyParser());
app.use(fx);

Expand Down

0 comments on commit bdc454e

Please sign in to comment.