The codebase that powers boop.cat boop.cat
at main 127 lines 2.7 kB view raw
1// Copyright 2025 boop.cat 2// Licensed under the Apache License, Version 2.0 3// See LICENSE file for details. 4 5package deploy 6 7import ( 8 "context" 9 "fmt" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14) 15 16func ensureDir(path string) error { 17 return os.MkdirAll(path, 0755) 18} 19 20func GitClone(ctx context.Context, repoURL, branch, targetDir string, depth int, logger func(string)) error { 21 if err := os.RemoveAll(targetDir); err != nil { 22 return fmt.Errorf("failed to clear target dir: %w", err) 23 } 24 if err := ensureDir(filepath.Dir(targetDir)); err != nil { 25 return fmt.Errorf("failed to create parent dir: %w", err) 26 } 27 28 args := []string{"clone", "--no-tags", "--depth", fmt.Sprintf("%d", depth)} 29 if branch != "" { 30 args = append(args, "--branch", branch) 31 } 32 args = append(args, repoURL, targetDir) 33 34 cmd := exec.CommandContext(ctx, "git", args...) 35 cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0") 36 37 stdout, _ := cmd.StdoutPipe() 38 stderr, _ := cmd.StderrPipe() 39 40 if err := cmd.Start(); err != nil { 41 return err 42 } 43 44 sanitize := func(s string) string { 45 46 if strings.Contains(s, "@github.com") { 47 parts := strings.Split(s, "@github.com") 48 if len(parts) > 1 { 49 50 lastSpace := strings.LastIndex(parts[0], " ") 51 if lastSpace != -1 { 52 return parts[0][:lastSpace+1] + "***" + "@github.com" + parts[1] 53 } 54 55 lastSlash := strings.LastIndex(parts[0], "//") 56 if lastSlash != -1 { 57 return parts[0][:lastSlash+2] + "***" + "@github.com" + parts[1] 58 } 59 } 60 } 61 return s 62 } 63 64 go func() { 65 buf := make([]byte, 1024) 66 for { 67 n, err := stdout.Read(buf) 68 if n > 0 && logger != nil { 69 logger(sanitize(string(buf[:n]))) 70 } 71 if err != nil { 72 break 73 } 74 } 75 }() 76 77 go func() { 78 buf := make([]byte, 1024) 79 for { 80 n, err := stderr.Read(buf) 81 if n > 0 && logger != nil { 82 logger(sanitize(string(buf[:n]))) 83 } 84 if err != nil { 85 break 86 } 87 } 88 }() 89 90 return cmd.Wait() 91} 92 93func GitCheckout(targetDir, ref string) error { 94 cmd := exec.Command("git", "checkout", ref) 95 cmd.Dir = targetDir 96 return cmd.Run() 97} 98 99type CommitInfo struct { 100 SHA string 101 Message string 102 Author string 103} 104 105func GitCurrentHead(targetDir string) (*CommitInfo, error) { 106 107 shaCmd := exec.Command("git", "rev-parse", "HEAD") 108 shaCmd.Dir = targetDir 109 shaOut, err := shaCmd.Output() 110 if err != nil { 111 return nil, err 112 } 113 114 msgCmd := exec.Command("git", "log", "-1", "--pretty=%s") 115 msgCmd.Dir = targetDir 116 msgOut, _ := msgCmd.Output() 117 118 authCmd := exec.Command("git", "log", "-1", "--pretty=%an <%ae>") 119 authCmd.Dir = targetDir 120 authOut, _ := authCmd.Output() 121 122 return &CommitInfo{ 123 SHA: strings.TrimSpace(string(shaOut)), 124 Message: strings.TrimSpace(string(msgOut)), 125 Author: strings.TrimSpace(string(authOut)), 126 }, nil 127}