Welcome to mirror list, hosted at ThFree Co, Russian Federation.

hook.rb « git « gitlab « lib « ruby - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: dd3875d2f7760433e0782094df72865b97dacc5c (plain)
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