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

satellite.rb « satellite « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 398643d68de2c07a56ba30181786f693dae6219a (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
module Gitlab
  module Satellite
    autoload :DeleteFileAction, 'gitlab/satellite/files/delete_file_action'
    autoload :EditFileAction,   'gitlab/satellite/files/edit_file_action'
    autoload :FileAction,       'gitlab/satellite/files/file_action'
    autoload :NewFileAction,    'gitlab/satellite/files/new_file_action'

    class CheckoutFailed < StandardError; end
    class CommitFailed < StandardError; end
    class PushFailed < StandardError; end

    class Satellite
      include Gitlab::Popen

      PARKING_BRANCH = "__parking_branch"

      attr_accessor :project

      def initialize(project)
        @project = project
      end

      def log(message)
        Gitlab::Satellite::Logger.error(message)
      end

      def clear_and_update!
        project.ensure_satellite_exists

        @repo = nil
        clear_working_dir!
        delete_heads!
        remove_remotes!
        update_from_source!
      end

      def create
        output, status = popen(%W(git clone -- #{project.repository.path_to_repo} #{path}),
                               Gitlab.config.satellites.path)

        log("PID: #{project.id}: git clone #{project.repository.path_to_repo} #{path}")
        log("PID: #{project.id}: -> #{output}")

        if status.zero?
          true
        else
          log("Failed to create satellite for #{project.name_with_namespace}")
          false
        end
      end

      def exists?
        File.exists? path
      end

      # * Locks the satellite
      # * Changes the current directory to the satellite's working dir
      # * Yields
      def lock
        project.ensure_satellite_exists

        File.open(lock_file, "w+") do |f|
          begin
            f.flock File::LOCK_EX
            yield
          ensure
            f.flock File::LOCK_UN
          end
        end
      end

      def lock_file
        create_locks_dir unless File.exists?(lock_files_dir)
        File.join(lock_files_dir, "satellite_#{project.id}.lock")
      end

      def path
        File.join(Gitlab.config.satellites.path, project.path_with_namespace)
      end

      def repo
        project.ensure_satellite_exists

        @repo ||= Grit::Repo.new(path)
      end

      def destroy
        FileUtils.rm_rf(path)
      end

      private

      # Clear the working directory
      def clear_working_dir!
        repo.git.reset(hard: true)
        repo.git.clean(f: true, d: true, x: true)
      end

      # Deletes all branches except the parking branch
      #
      # This ensures we have no name clashes or issues updating branches when
      # working with the satellite.
      def delete_heads!
        heads = repo.heads.map(&:name)

        # update or create the parking branch
        repo.git.checkout(default_options({ B: true }), PARKING_BRANCH)

        # remove the parking branch from the list of heads ...
        heads.delete(PARKING_BRANCH)
        # ... and delete all others
        heads.each { |head| repo.git.branch(default_options({ D: true }), head) }
      end

      # Deletes all remotes except origin
      #
      # This ensures we have no remote name clashes or issues updating branches when
      # working with the satellite.
      def remove_remotes!
        remotes = repo.git.remote.split(' ')
        remotes.delete('origin')
        remotes.each { |name| repo.git.remote(default_options,'rm', name)}
      end

      # Updates the satellite from bare repo
      #
      # Note: this will only update remote branches (i.e. origin/*)
      def update_from_source!
        repo.git.remote(default_options, 'set-url', :origin, project.repository.path_to_repo)
        repo.git.fetch(default_options, :origin)
      end

      def default_options(options = {})
        { raise: true, timeout: true }.merge(options)
      end

      # Create directory for storing
      # satellites lock files
      def create_locks_dir
        FileUtils.mkdir_p(lock_files_dir)
      end

      def lock_files_dir
        @lock_files_dir ||= File.join(Gitlab.config.satellites.path, "tmp")
      end
    end
  end
end