-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
348 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package hooks | ||
|
||
import ( | ||
"context" | ||
"reflect" | ||
) | ||
|
||
type Runner interface { | ||
RunAfter(ctx context.Context, params Params) error | ||
RunBefore(ctx context.Context, params Params) error | ||
} | ||
|
||
// Params contains environment variables for hook commands. All values | ||
// should be in string format. | ||
type Params struct { | ||
Remote string `env:"GLMT_REMOTE"` | ||
Branch string `env:"GLMT_BRANCH"` | ||
MergeRequestURL string `env:"GLMT_MR_URL"` | ||
Project string `env:"GLMT_PROJECT"` | ||
Username string `env:"GLMT_USERNAME"` | ||
} | ||
|
||
// Env specifies the environment by the params. Each entry is of the | ||
// form "key=value". | ||
func (h Params) Env() (env []string) { | ||
rt := reflect.TypeOf(h) | ||
rv := reflect.ValueOf(h) | ||
|
||
env = make([]string, rt.NumField()) | ||
for i := 0; i < rt.NumField(); i++ { | ||
env[i] = rt.Field(i).Tag.Get("env") + "=" + rv.Field(i).String() | ||
} | ||
|
||
return env | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package hooks_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"gitlab.com/gitlab-merge-tool/glmt/internal/hooks" | ||
) | ||
|
||
func TestHookParamsEnv(t *testing.T) { | ||
const exp = "GLMT_BRANCH=master" | ||
|
||
params := hooks.Params{ | ||
Branch: "master", | ||
} | ||
|
||
env := params.Env() | ||
|
||
var got bool | ||
for _, envVal := range env { | ||
if envVal == exp { | ||
got = true | ||
break | ||
} | ||
} | ||
|
||
if !got { | ||
t.Fatal(env) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Package impl implements hooks.Runner. | ||
package impl | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"os/exec" | ||
"time" | ||
|
||
"gitlab.com/gitlab-merge-tool/glmt/internal/config" | ||
"gitlab.com/gitlab-merge-tool/glmt/internal/hooks" | ||
) | ||
|
||
type Hooks struct { | ||
afterCommands map[string][]string | ||
beforeCommands map[string][]string | ||
|
||
timeout time.Duration | ||
|
||
stdout io.Writer | ||
stderr io.Writer | ||
} | ||
|
||
func NewHooks(cfg config.Hooks, stdout io.Writer, stderr io.Writer) *Hooks { | ||
const defaultTimeout = 5 * time.Second | ||
|
||
timeout := time.Duration(cfg.Timeout) | ||
if timeout == 0 { | ||
timeout = defaultTimeout | ||
} | ||
|
||
return &Hooks{ | ||
afterCommands: filterCommands(cfg.AfterCommands), | ||
beforeCommands: filterCommands(cfg.BeforeCommands), | ||
|
||
timeout: timeout, | ||
|
||
stdout: stdout, | ||
stderr: stderr, | ||
} | ||
} | ||
|
||
func (h Hooks) RunAfter(ctx context.Context, params hooks.Params) (err error) { | ||
return h.run(ctx, h.afterCommands, params) | ||
} | ||
|
||
func (h Hooks) RunBefore(ctx context.Context, params hooks.Params) error { | ||
return h.run(ctx, h.beforeCommands, params) | ||
} | ||
|
||
func (h Hooks) run( | ||
ctx context.Context, | ||
commands map[string][]string, | ||
params hooks.Params, | ||
) (err error) { | ||
env := params.Env() | ||
|
||
var cancel context.CancelFunc | ||
ctx, cancel = context.WithTimeout(ctx, h.timeout) | ||
defer cancel() | ||
|
||
for name, cmd := range commands { | ||
cmdProc := exec.CommandContext(ctx, cmd[0], cmd[1:]...) | ||
cmdProc.Env = env | ||
cmdProc.Stdout = h.stdout | ||
cmdProc.Stderr = h.stderr | ||
|
||
err = cmdProc.Run() | ||
if err != nil { | ||
return fmt.Errorf("%s: running command: %w", name, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// filterCommands removes empty commands. | ||
func filterCommands(commands map[string][]string) (filtered map[string][]string) { | ||
filtered = make(map[string][]string, len(commands)) | ||
|
||
for name, cmd := range commands { | ||
if len(cmd) == 0 { | ||
continue | ||
} | ||
|
||
filtered[name] = cmd | ||
} | ||
|
||
return filtered | ||
} |
Oops, something went wrong.