code.gitea.io/gitea@v1.19.3/modules/git/hook.go (about)

     1  // Copyright 2015 The Gogs Authors. All rights reserved.
     2  // Copyright 2021 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package git
     6  
     7  import (
     8  	"errors"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"code.gitea.io/gitea/modules/log"
    15  	"code.gitea.io/gitea/modules/util"
    16  )
    17  
    18  // hookNames is a list of Git server hooks' name that are supported.
    19  var hookNames = []string{
    20  	"pre-receive",
    21  	"update",
    22  	"post-receive",
    23  }
    24  
    25  // ErrNotValidHook error when a git hook is not valid
    26  var ErrNotValidHook = errors.New("not a valid Git hook")
    27  
    28  // IsValidHookName returns true if given name is a valid Git hook.
    29  func IsValidHookName(name string) bool {
    30  	for _, hn := range hookNames {
    31  		if hn == name {
    32  			return true
    33  		}
    34  	}
    35  	return false
    36  }
    37  
    38  // Hook represents a Git hook.
    39  type Hook struct {
    40  	name     string
    41  	IsActive bool   // Indicates whether repository has this hook.
    42  	Content  string // Content of hook if it's active.
    43  	Sample   string // Sample content from Git.
    44  	path     string // Hook file path.
    45  }
    46  
    47  // GetHook returns a Git hook by given name and repository.
    48  func GetHook(repoPath, name string) (*Hook, error) {
    49  	if !IsValidHookName(name) {
    50  		return nil, ErrNotValidHook
    51  	}
    52  	h := &Hook{
    53  		name: name,
    54  		path: path.Join(repoPath, "hooks", name+".d", name),
    55  	}
    56  	samplePath := filepath.Join(repoPath, "hooks", name+".sample")
    57  	if isFile(h.path) {
    58  		data, err := os.ReadFile(h.path)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  		h.IsActive = true
    63  		h.Content = string(data)
    64  	} else if isFile(samplePath) {
    65  		data, err := os.ReadFile(samplePath)
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  		h.Sample = string(data)
    70  	}
    71  	return h, nil
    72  }
    73  
    74  // Name return the name of the hook
    75  func (h *Hook) Name() string {
    76  	return h.name
    77  }
    78  
    79  // Update updates hook settings.
    80  func (h *Hook) Update() error {
    81  	if len(strings.TrimSpace(h.Content)) == 0 {
    82  		if isExist(h.path) {
    83  			err := util.Remove(h.path)
    84  			if err != nil {
    85  				return err
    86  			}
    87  		}
    88  		h.IsActive = false
    89  		return nil
    90  	}
    91  	d := filepath.Dir(h.path)
    92  	if err := os.MkdirAll(d, os.ModePerm); err != nil {
    93  		return err
    94  	}
    95  
    96  	err := os.WriteFile(h.path, []byte(strings.ReplaceAll(h.Content, "\r", "")), os.ModePerm)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	h.IsActive = true
   101  	return nil
   102  }
   103  
   104  // ListHooks returns a list of Git hooks of given repository.
   105  func ListHooks(repoPath string) (_ []*Hook, err error) {
   106  	if !isDir(path.Join(repoPath, "hooks")) {
   107  		return nil, errors.New("hooks path does not exist")
   108  	}
   109  
   110  	hooks := make([]*Hook, len(hookNames))
   111  	for i, name := range hookNames {
   112  		hooks[i], err = GetHook(repoPath, name)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  	}
   117  	return hooks, nil
   118  }
   119  
   120  const (
   121  	// HookPathUpdate hook update path
   122  	HookPathUpdate = "hooks/update"
   123  )
   124  
   125  // SetUpdateHook writes given content to update hook of the repository.
   126  func SetUpdateHook(repoPath, content string) (err error) {
   127  	log.Debug("Setting update hook: %s", repoPath)
   128  	hookPath := path.Join(repoPath, HookPathUpdate)
   129  	isExist, err := util.IsExist(hookPath)
   130  	if err != nil {
   131  		log.Debug("Unable to check if %s exists. Error: %v", hookPath, err)
   132  		return err
   133  	}
   134  	if isExist {
   135  		err = util.Remove(hookPath)
   136  	} else {
   137  		err = os.MkdirAll(path.Dir(hookPath), os.ModePerm)
   138  	}
   139  	if err != nil {
   140  		return err
   141  	}
   142  	return os.WriteFile(hookPath, []byte(content), 0o777)
   143  }