1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
# frozen_string_literal: true
module Gitlab
module Git
class Hook
def self.directory
Gitlab.config.git.hooks_directory
end
def self.legacy_hooks_directory
File.join(Gitlab.config.gitlab_shell.path, 'hooks')
end
GL_PROTOCOL = 'web'
attr_reader :name, :path, :repository
def initialize(name, repository)
@name = name
@repository = repository
@path = File.join(self.class.directory, name)
end
def repo_path
repository.path
end
def exists?
File.exist?(path)
end
def trigger(gl_id, gl_username, oldrev, newrev, ref, push_options: nil, transaction: nil)
return [true, nil] unless exists?
Bundler.with_clean_env do
case name
when "pre-receive", "post-receive"
call_receive_hook(gl_id, gl_username, oldrev, newrev, ref, push_options, transaction)
when "update"
call_update_hook(gl_id, gl_username, oldrev, newrev, ref)
end
end
end
private
def call_stdin_hook(args, input, env)
exit_status = false
exit_message = nil
options = {
chdir: repo_path
}
Open3.popen3(env, path, *args, options) do |stdin, stdout, stderr, wait_thr|
exit_status = true
stdin.sync = true
# in git, hooks may just exit without reading stdin. We catch the
# exception to avoid a broken pipe warning
begin
input.lines do |line|
stdin.puts line
end
rescue Errno::EPIPE
end
stdin.close
unless wait_thr.value == 0
exit_status = false
exit_message = retrieve_error_message(stderr, stdout)
end
end
[exit_status, exit_message]
end
def call_receive_hook(gl_id, gl_username, oldrev, newrev, ref, push_options, transaction)
changes = [oldrev, newrev, ref].join(" ")
vars = env_base_vars(gl_id, gl_username)
vars.merge!(push_options.env_data) if push_options
vars.merge!(transaction.env_vars) if transaction
call_stdin_hook([], changes, vars)
end
def call_update_hook(gl_id, gl_username, oldrev, newrev, ref)
options = {
chdir: repo_path
}
args = [ref, oldrev, newrev]
vars = env_base_vars(gl_id, gl_username)
stdout, stderr, status = Open3.capture3(vars, path, *args, options)
[status.success?, stderr.presence || stdout]
end
def retrieve_error_message(stderr, stdout)
err_message = stderr.read
err_message = err_message.blank? ? stdout.read : err_message
err_message
end
def env_base_vars(gl_id, gl_username)
{
'GITALY_GITLAB_SHELL_DIR' => Gitlab.config.gitlab_shell.path,
'GITLAB_SHELL_DIR' => Gitlab.config.gitlab_shell.path,
'GITALY_LOG_DIR' => Gitlab.config.logging.dir,
'GITALY_LOG_LEVEL' => Gitlab.config.logging.level,
'GITALY_LOG_FORMAT' => Gitlab.config.logging.format,
'GITALY_RUBY_DIR' => Gitlab.config.gitaly.ruby_dir,
'GITALY_BIN_DIR' => Gitlab.config.gitaly.bin_dir,
'GL_ID' => gl_id,
'GL_USERNAME' => gl_username,
'GL_REPOSITORY' => repository.gl_repository,
'GL_PROJECT_PATH' => repository.gl_project_path,
'GL_PROTOCOL' => GL_PROTOCOL,
'PWD' => repo_path,
'GIT_DIR' => repo_path,
'GITALY_REPO' => repository.gitaly_repository.to_json,
'GITALY_SOCKET' => Gitlab.config.gitaly.internal_socket,
'GITALY_GO_POSTRECEIVE' => repository.feature_enabled?('go-postreceive-hook').to_s
}
end
end
end
end
|