2020-01-20 23:01:19 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repository
import (
"fmt"
"os"
"path/filepath"
2022-08-04 10:28:32 +03:00
"runtime"
2020-01-20 23:01:19 +03:00
2021-07-28 12:42:56 +03:00
"code.gitea.io/gitea/modules/git"
2020-01-20 23:01:19 +03:00
"code.gitea.io/gitea/modules/setting"
2020-08-11 23:05:34 +03:00
"code.gitea.io/gitea/modules/util"
2020-01-20 23:01:19 +03:00
)
2020-04-06 13:44:47 +03:00
func getHookTemplates ( ) ( hookNames , hookTpls , giteaHookTpls [ ] string ) {
hookNames = [ ] string { "pre-receive" , "update" , "post-receive" }
hookTpls = [ ] string {
2021-10-21 12:22:43 +03:00
// for pre-receive
2021-05-07 16:19:09 +03:00
fmt . Sprintf ( ` # ! / usr / bin / env % s
2021-10-21 12:22:43 +03:00
# AUTO GENERATED BY GITEA , DO NOT MODIFY
2021-05-07 16:19:09 +03:00
data = $ ( cat )
exitcodes = ""
hookname = $ ( basename $ 0 )
GIT_DIR = $ { GIT_DIR : - $ ( dirname $ 0 ) / . . }
for hook in $ { GIT_DIR } / hooks / $ { hookname } . d / * ; do
2021-10-21 12:22:43 +03:00
test - x "${hook}" && test - f "${hook}" || continue
echo "${data}" | "${hook}"
exitcodes = "${exitcodes} $?"
2021-05-07 16:19:09 +03:00
done
for i in $ { exitcodes } ; do
2021-10-21 12:22:43 +03:00
[ $ { i } - eq 0 ] || exit $ { i }
2021-05-07 16:19:09 +03:00
done
` , setting . ScriptType ) ,
2021-10-21 12:22:43 +03:00
// for update
2021-05-07 16:19:09 +03:00
fmt . Sprintf ( ` # ! / usr / bin / env % s
2021-10-21 12:22:43 +03:00
# AUTO GENERATED BY GITEA , DO NOT MODIFY
2021-05-07 16:19:09 +03:00
exitcodes = ""
hookname = $ ( basename $ 0 )
GIT_DIR = $ { GIT_DIR : - $ ( dirname $ 0 / . . ) }
for hook in $ { GIT_DIR } / hooks / $ { hookname } . d / * ; do
2021-10-21 12:22:43 +03:00
test - x "${hook}" && test - f "${hook}" || continue
"${hook}" $ 1 $ 2 $ 3
exitcodes = "${exitcodes} $?"
2021-05-07 16:19:09 +03:00
done
for i in $ { exitcodes } ; do
2021-10-21 12:22:43 +03:00
[ $ { i } - eq 0 ] || exit $ { i }
2021-05-07 16:19:09 +03:00
done
` , setting . ScriptType ) ,
2021-10-21 12:22:43 +03:00
// for post-receive
2021-05-07 16:19:09 +03:00
fmt . Sprintf ( ` # ! / usr / bin / env % s
2021-10-21 12:22:43 +03:00
# AUTO GENERATED BY GITEA , DO NOT MODIFY
2021-05-07 16:19:09 +03:00
data = $ ( cat )
exitcodes = ""
hookname = $ ( basename $ 0 )
GIT_DIR = $ { GIT_DIR : - $ ( dirname $ 0 ) / . . }
for hook in $ { GIT_DIR } / hooks / $ { hookname } . d / * ; do
2021-10-21 12:22:43 +03:00
test - x "${hook}" && test - f "${hook}" || continue
echo "${data}" | "${hook}"
exitcodes = "${exitcodes} $?"
2021-05-07 16:19:09 +03:00
done
for i in $ { exitcodes } ; do
2021-10-21 12:22:43 +03:00
[ $ { i } - eq 0 ] || exit $ { i }
2021-05-07 16:19:09 +03:00
done
` , setting . ScriptType ) ,
2020-04-06 13:44:47 +03:00
}
2021-10-21 12:22:43 +03:00
2020-04-06 13:44:47 +03:00
giteaHookTpls = [ ] string {
2021-10-21 12:22:43 +03:00
// for pre-receive
fmt . Sprintf ( ` # ! / usr / bin / env % s
# AUTO GENERATED BY GITEA , DO NOT MODIFY
% s hook -- config = % s pre - receive
` , setting . ScriptType , util . ShellEscape ( setting . AppPath ) , util . ShellEscape ( setting . CustomConf ) ) ,
// for update
fmt . Sprintf ( ` # ! / usr / bin / env % s
# AUTO GENERATED BY GITEA , DO NOT MODIFY
% s hook -- config = % s update $ 1 $ 2 $ 3
` , setting . ScriptType , util . ShellEscape ( setting . AppPath ) , util . ShellEscape ( setting . CustomConf ) ) ,
// for post-receive
fmt . Sprintf ( ` # ! / usr / bin / env % s
# AUTO GENERATED BY GITEA , DO NOT MODIFY
% s hook -- config = % s post - receive
` , setting . ScriptType , util . ShellEscape ( setting . AppPath ) , util . ShellEscape ( setting . CustomConf ) ) ,
2020-04-06 13:44:47 +03:00
}
2021-07-28 12:42:56 +03:00
if git . SupportProcReceive {
hookNames = append ( hookNames , "proc-receive" )
hookTpls = append ( hookTpls ,
2021-10-21 12:22:43 +03:00
fmt . Sprintf ( ` # ! / usr / bin / env % s
# AUTO GENERATED BY GITEA , DO NOT MODIFY
% s hook -- config = % s proc - receive
` , setting . ScriptType , util . ShellEscape ( setting . AppPath ) , util . ShellEscape ( setting . CustomConf ) ) )
2021-07-28 12:42:56 +03:00
giteaHookTpls = append ( giteaHookTpls , "" )
}
2022-06-20 13:02:49 +03:00
return hookNames , hookTpls , giteaHookTpls
2020-04-06 13:44:47 +03:00
}
2020-01-20 23:01:19 +03:00
// CreateDelegateHooks creates all the hooks scripts for the repo
func CreateDelegateHooks ( repoPath string ) error {
return createDelegateHooks ( repoPath )
}
// createDelegateHooks creates all the hooks scripts for the repo
func createDelegateHooks ( repoPath string ) ( err error ) {
2020-04-06 13:44:47 +03:00
hookNames , hookTpls , giteaHookTpls := getHookTemplates ( )
2020-01-20 23:01:19 +03:00
hookDir := filepath . Join ( repoPath , "hooks" )
for i , hookName := range hookNames {
oldHookPath := filepath . Join ( hookDir , hookName )
newHookPath := filepath . Join ( hookDir , hookName + ".d" , "gitea" )
if err := os . MkdirAll ( filepath . Join ( hookDir , hookName + ".d" ) , os . ModePerm ) ; err != nil {
return fmt . Errorf ( "create hooks dir '%s': %v" , filepath . Join ( hookDir , hookName + ".d" ) , err )
}
// WARNING: This will override all old server-side hooks
2020-08-11 23:05:34 +03:00
if err = util . Remove ( oldHookPath ) ; err != nil && ! os . IsNotExist ( err ) {
2020-01-20 23:01:19 +03:00
return fmt . Errorf ( "unable to pre-remove old hook file '%s' prior to rewriting: %v " , oldHookPath , err )
}
2022-01-20 20:46:10 +03:00
if err = os . WriteFile ( oldHookPath , [ ] byte ( hookTpls [ i ] ) , 0 o777 ) ; err != nil {
2020-01-20 23:01:19 +03:00
return fmt . Errorf ( "write old hook file '%s': %v" , oldHookPath , err )
}
2020-04-06 13:44:47 +03:00
if err = ensureExecutable ( oldHookPath ) ; err != nil {
return fmt . Errorf ( "Unable to set %s executable. Error %v" , oldHookPath , err )
}
2020-08-11 23:05:34 +03:00
if err = util . Remove ( newHookPath ) ; err != nil && ! os . IsNotExist ( err ) {
2020-01-20 23:01:19 +03:00
return fmt . Errorf ( "unable to pre-remove new hook file '%s' prior to rewriting: %v" , newHookPath , err )
}
2022-01-20 20:46:10 +03:00
if err = os . WriteFile ( newHookPath , [ ] byte ( giteaHookTpls [ i ] ) , 0 o777 ) ; err != nil {
2020-01-20 23:01:19 +03:00
return fmt . Errorf ( "write new hook file '%s': %v" , newHookPath , err )
}
2020-04-06 13:44:47 +03:00
if err = ensureExecutable ( newHookPath ) ; err != nil {
return fmt . Errorf ( "Unable to set %s executable. Error %v" , oldHookPath , err )
}
2020-01-20 23:01:19 +03:00
}
return nil
}
2020-04-06 13:44:47 +03:00
func checkExecutable ( filename string ) bool {
2022-08-04 10:28:32 +03:00
// windows has no concept of a executable bit
if runtime . GOOS == "windows" {
return true
}
2020-04-06 13:44:47 +03:00
fileInfo , err := os . Stat ( filename )
if err != nil {
return false
}
2022-01-20 20:46:10 +03:00
return ( fileInfo . Mode ( ) & 0 o100 ) > 0
2020-04-06 13:44:47 +03:00
}
func ensureExecutable ( filename string ) error {
fileInfo , err := os . Stat ( filename )
if err != nil {
return err
}
2022-01-20 20:46:10 +03:00
if ( fileInfo . Mode ( ) & 0 o100 ) > 0 {
2020-04-06 13:44:47 +03:00
return nil
}
2022-01-20 20:46:10 +03:00
mode := fileInfo . Mode ( ) | 0 o100
2020-04-06 13:44:47 +03:00
return os . Chmod ( filename , mode )
}
// CheckDelegateHooks checks the hooks scripts for the repo
func CheckDelegateHooks ( repoPath string ) ( [ ] string , error ) {
hookNames , hookTpls , giteaHookTpls := getHookTemplates ( )
hookDir := filepath . Join ( repoPath , "hooks" )
results := make ( [ ] string , 0 , 10 )
for i , hookName := range hookNames {
oldHookPath := filepath . Join ( hookDir , hookName )
newHookPath := filepath . Join ( hookDir , hookName + ".d" , "gitea" )
cont := false
2020-11-28 05:42:08 +03:00
isExist , err := util . IsExist ( oldHookPath )
if err != nil {
results = append ( results , fmt . Sprintf ( "unable to check if %s exists. Error: %v" , oldHookPath , err ) )
}
if err == nil && ! isExist {
2020-04-06 13:44:47 +03:00
results = append ( results , fmt . Sprintf ( "old hook file %s does not exist" , oldHookPath ) )
cont = true
}
2020-11-28 05:42:08 +03:00
isExist , err = util . IsExist ( oldHookPath + ".d" )
if err != nil {
results = append ( results , fmt . Sprintf ( "unable to check if %s exists. Error: %v" , oldHookPath + ".d" , err ) )
}
if err == nil && ! isExist {
2020-04-06 13:44:47 +03:00
results = append ( results , fmt . Sprintf ( "hooks directory %s does not exist" , oldHookPath + ".d" ) )
cont = true
}
2020-11-28 05:42:08 +03:00
isExist , err = util . IsExist ( newHookPath )
if err != nil {
results = append ( results , fmt . Sprintf ( "unable to check if %s exists. Error: %v" , newHookPath , err ) )
}
if err == nil && ! isExist {
2020-04-06 13:44:47 +03:00
results = append ( results , fmt . Sprintf ( "new hook file %s does not exist" , newHookPath ) )
cont = true
}
if cont {
continue
}
2021-09-22 08:38:34 +03:00
contents , err := os . ReadFile ( oldHookPath )
2020-04-06 13:44:47 +03:00
if err != nil {
return results , err
}
if string ( contents ) != hookTpls [ i ] {
results = append ( results , fmt . Sprintf ( "old hook file %s is out of date" , oldHookPath ) )
}
if ! checkExecutable ( oldHookPath ) {
results = append ( results , fmt . Sprintf ( "old hook file %s is not executable" , oldHookPath ) )
}
2021-09-22 08:38:34 +03:00
contents , err = os . ReadFile ( newHookPath )
2020-04-06 13:44:47 +03:00
if err != nil {
return results , err
}
if string ( contents ) != giteaHookTpls [ i ] {
results = append ( results , fmt . Sprintf ( "new hook file %s is out of date" , newHookPath ) )
}
if ! checkExecutable ( newHookPath ) {
results = append ( results , fmt . Sprintf ( "new hook file %s is not executable" , newHookPath ) )
}
}
return results , nil
}