package main // see `man 5 githooks` for details import ( "bytes" "errors" "fmt" "io" "log" "os" "os/exec" "path/filepath" "strings" ) func main() { log.SetFlags(0) defer log.Println("Exiting craft") log.Println("Starting craft") hookType := getHookType() log.Printf("Git hook type: %s\n", hookType) repo := getRepositoryPath() log.Printf("Git repository: %s\n", repo) for { eof, update := getUpdate() if eof { break } log.Printf("Git ref update: %s\n", update) commit := update.updated workbench := prepareWorkbench(repo, commit) defer os.RemoveAll(workbench) craft(workbench) } } func runCommand(dir string, name string, args ...string) { log.Printf("%s %s\n", name, strings.Join(args, " ")) command := exec.Command(name, args...) command.Dir = dir command.Stderr = os.Stderr command.Stdout = os.Stdout err := command.Run() if err != nil { log.Fatal(err) } } func craft(workbench string) { script := `#!/bin/sh source craft.sh build ` cmd := exec.Command("busybox", "ash") cmd.Dir = workbench cmd.Stdin = bytes.NewBufferString(script) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { log.Fatal(err) } } func prepareWorkbench(repo string, commit string) string { workbench, err := os.MkdirTemp("", "*-craft") if err != nil { log.Fatal(err) } runCommand( workbench, "git", "clone", repo, workbench, ) runCommand( workbench, "git", "--git-dir", filepath.Join(workbench, ".git"), "--work-tree", workbench, "checkout", commit, ) runCommand( workbench, "git", "--git-dir", filepath.Join(workbench, ".git"), "--work-tree", workbench, "submodule", "update", "--depth=1", "--init", "--recursive", ) return workbench } func getHookType() string { args := len(os.Args) if args != 1 { log.Fatalf("Expected zero arguments but %d were given", args-1) } prefix := `hooks/` path := os.Args[0] hookType := strings.TrimPrefix(path, prefix) if path == hookType { log.Fatalf("Hook path '%s' has no '%s' prefix", path, prefix) } validateHookType(hookType) return hookType } func validateHookType(hookType string) { validTypes := [...]string{ `post-receive`, } for _, currentType := range validTypes { if hookType == currentType { return } } log.Fatalf("Not supported Git hook type '%s'\n", hookType) } func getRepositoryPath() string { cwd, err := os.Getwd() if err != nil { log.Fatal(err) } return cwd } type update struct { old, updated, ref string } func (u update) String() string { ret := fmt.Sprintf("'%s' from '%s' to '%s'", u.ref, u.old, u.updated) return ret } func getUpdate() (bool, update) { var u update _, err := fmt.Scanf("%s %s %s\n", &u.old, &u.updated, &u.ref) if err != nil { if errors.Is(err, io.EOF) { return true, u } else { log.Fatal(err) } } return false, u }