Monorepo for Tangled tangled.org
12

Configure Feed

Select the types of activity you want to include in your feed.

1// heavily inspired by gitea's model 2 3package hook 4 5import ( 6 "errors" 7 "fmt" 8 "io/fs" 9 "os" 10 "path/filepath" 11 "strings" 12 13 "github.com/go-git/go-git/v5" 14) 15 16var ErrNoGitRepo = errors.New("not a git repo") 17var ErrCreatingHookDir = errors.New("failed to create hooks directory") 18var ErrCreatingHook = errors.New("failed to create hook") 19var ErrCreatingDelegate = errors.New("failed to create delegate hook") 20 21type config struct { 22 scanPath string 23 internalApi string 24} 25 26type setupOpt func(*config) 27 28func WithScanPath(scanPath string) setupOpt { 29 return func(c *config) { 30 c.scanPath = scanPath 31 } 32} 33 34func WithInternalApi(api string) setupOpt { 35 return func(c *config) { 36 c.internalApi = api 37 } 38} 39 40func Config(opts ...setupOpt) config { 41 config := config{} 42 for _, o := range opts { 43 o(&config) 44 } 45 return config 46} 47 48// setup hooks for all users 49// 50// directory structure is typically like so: 51// 52// did:plc:repo1 53// did:plc:repo2 54// did:web:repo1 55func Setup(config config) error { 56 // iterate over all directories in current directory: 57 repoDirs, err := os.ReadDir(config.scanPath) 58 if errors.Is(err, fs.ErrNotExist) { 59 return os.MkdirAll(config.scanPath, 0755) 60 } 61 if err != nil { 62 return err 63 } 64 65 for _, repo := range repoDirs { 66 if !repo.IsDir() { 67 continue 68 } 69 70 did := repo.Name() 71 if !strings.HasPrefix(did, "did:") { 72 continue 73 } 74 75 userPath := filepath.Join(config.scanPath, did) 76 if err := SetupRepo(config, userPath); err != nil { 77 return err 78 } 79 } 80 81 return nil 82} 83 84// setup hook in /scanpath/did:plc:repo 85func SetupRepo(config config, path string) error { 86 if _, err := git.PlainOpen(path); err != nil { 87 return fmt.Errorf("%s: %w", path, ErrNoGitRepo) 88 } 89 90 preReceiveD := filepath.Join(path, "hooks", "post-receive.d") 91 if err := os.MkdirAll(preReceiveD, 0755); err != nil { 92 return fmt.Errorf("%s: %w", preReceiveD, ErrCreatingHookDir) 93 } 94 95 notify := filepath.Join(preReceiveD, "40-notify.sh") 96 if err := mkHook(config, notify); err != nil { 97 return fmt.Errorf("%s: %w", notify, ErrCreatingHook) 98 } 99 100 delegate := filepath.Join(path, "hooks", "post-receive") 101 if err := mkDelegate(delegate); err != nil { 102 return fmt.Errorf("%s: %w", delegate, ErrCreatingDelegate) 103 } 104 105 return nil 106} 107 108func mkHook(config config, hookPath string) error { 109 executablePath, err := os.Executable() 110 if err != nil { 111 return err 112 } 113 114 hookContent := fmt.Sprintf(`#!/usr/bin/env bash 115# AUTO GENERATED BY KNOT, DO NOT MODIFY 116push_options=() 117for ((i=0; i<GIT_PUSH_OPTION_COUNT; i++)); do 118 option_var="GIT_PUSH_OPTION_$i" 119 push_options+=(-push-option "${!option_var}") 120done 121%s hook -git-dir "$GIT_DIR" -user-did "$GIT_USER_DID" -user-handle "$GIT_USER_HANDLE" -internal-api "%s" "${push_options[@]}" post-receive 122 `, executablePath, config.internalApi) 123 124 return os.WriteFile(hookPath, []byte(hookContent), 0755) 125} 126 127func mkDelegate(path string) error { 128 content := fmt.Sprintf(`#!/usr/bin/env bash 129# AUTO GENERATED BY KNOT, DO NOT MODIFY 130data=$(cat) 131exitcodes="" 132hookname=$(basename $0) 133GIT_DIR="$PWD" 134 135for hook in ${GIT_DIR}/hooks/${hookname}.d/*; do 136 test -x "${hook}" && test -f "${hook}" || continue 137 echo "${data}" | "${hook}" 138 exitcodes="${exitcodes} $?" 139done 140 141for i in ${exitcodes}; do 142 [ ${i} -eq 0 ] || exit ${i} 143done 144 `) 145 146 return os.WriteFile(path, []byte(content), 0755) 147}