BigW Consortium Gitlab

test_env.rb 9.82 KB
Newer Older
1 2
require 'rspec/mocks'

3 4 5
module TestEnv
  extend self

6 7
  # When developing the seed repository, comment out the branch you will modify.
  BRANCH_SHA = {
8
    'signed-commits'                     => '5d4a1cb',
9 10
    'not-merged-branch'                  => 'b83d6e3',
    'branch-merged'                      => '498214d',
11
    'empty-branch'                       => '7efb185',
12
    'ends-with.json'                     => '98b0d8b',
13 14 15 16 17 18 19
    'flatten-dir'                        => 'e56497b',
    'feature'                            => '0b4bc9a',
    'feature_conflict'                   => 'bb5206f',
    'fix'                                => '48f0be4',
    'improve/awesome'                    => '5937ac0',
    'markdown'                           => '0ed8c6c',
    'lfs'                                => 'be93687',
20
    'master'                             => 'b83d6e3',
21
    'merge-test'                         => '5937ac0',
22 23 24 25 26
    "'test'"                             => 'e56497b',
    'orphaned-branch'                    => '45127a9',
    'binary-encoding'                    => '7b1cf43',
    'gitattributes'                      => '5a62481',
    'expand-collapse-diffs'              => '4842455',
27
    'symlink-expand-diff'                => '81e6355',
28 29 30
    'expand-collapse-files'              => '025db92',
    'expand-collapse-lines'              => '238e82d',
    'video'                              => '8879059',
31
    'add-balsamiq-file'                  => 'b89b56d',
32
    'crlf-diff'                          => '5938907',
Sean McGivern committed
33
    'conflict-start'                     => '824be60',
34 35
    'conflict-resolvable'                => '1450cd6',
    'conflict-binary-file'               => '259a6fb',
Sean McGivern committed
36
    'conflict-contains-conflict-markers' => '78a3086',
37
    'conflict-missing-side'              => 'eb227b3',
38
    'conflict-non-utf8'                  => 'd0a293c',
39
    'conflict-too-large'                 => '39fa04f',
40
    'deleted-image-test'                 => '6c17798',
41
    'wip'                                => 'b9238ee',
42
    'csv'                                => '3dd0896',
43
    'v1.1.0'                             => 'b83d6e3',
Phil Hughes committed
44
    'add-ipython-files'                  => '93ee732',
45 46
    'add-pdf-file'                       => 'e774ebd',
    'add-pdf-text-binary'                => '79faa7b'
47
  }.freeze
48

49 50 51 52
  # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
  # need to keep all the branches in sync.
  # We currently only need a subset of the branches
  FORKED_BRANCH_SHA = {
53 54 55 56
    'add-submodule-version-bump' => '3f547c0',
    'master'                     => '5937ac0',
    'remove-submodule'           => '2a33e0c',
    'conflict-resolvable-fork'   => '404fa3f'
57
  }.freeze
58

59 60
  TMP_TEST_PATH = Rails.root.join('tmp', 'tests', '**')

61 62
  # Test environment
  #
63
  # See gitlab.yml.example test section for paths
64
  #
65
  def init(opts = {})
66 67 68
    # Disable mailer for spinach tests
    disable_mailer if opts[:mailer] == false

Robert Speicher committed
69
    clean_test_path
70

71 72 73
    # Setup GitLab shell for test instance
    setup_gitlab_shell

74
    setup_gitaly
75

76 77
    # Create repository for FactoryGirl.create(:project)
    setup_factory_repo
78

79 80
    # Create repository for FactoryGirl.create(:forked_project_with_submodules)
    setup_forked_repo
81 82
  end

83 84 85 86
  def cleanup
    stop_gitaly
  end

87
  def disable_mailer
88 89
    allow_any_instance_of(NotificationService).to receive(:mailer)
      .and_return(double.as_null_object)
90
  end
91

92
  def enable_mailer
93 94
    allow_any_instance_of(NotificationService).to receive(:mailer)
      .and_call_original
95 96
  end

97
  def disable_pre_receive
98
    allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
99 100
  end

Robert Speicher committed
101 102 103 104
  # Clean /tmp/tests
  #
  # Keeps gitlab-shell and gitlab-test
  def clean_test_path
105
    Dir[TMP_TEST_PATH].each do |entry|
106
      unless File.basename(entry) =~ /\A(gitaly|gitlab-(shell|test|test_bare|test-fork|test-fork_bare))\z/
Robert Speicher committed
107 108 109
        FileUtils.rm_rf(entry)
      end
    end
110 111 112 113

    FileUtils.mkdir_p(repos_path)
    FileUtils.mkdir_p(backup_path)
    FileUtils.mkdir_p(pages_path)
Robert Speicher committed
114 115
  end

116 117 118 119 120 121 122 123
  def clean_gitlab_test_path
    Dir[TMP_TEST_PATH].each do |entry|
      if File.basename(entry) =~ /\A(gitlab-(test|test_bare|test-fork|test-fork_bare))\z/
        FileUtils.rm_rf(entry)
      end
    end
  end

124
  def setup_gitlab_shell
125 126 127
    puts "\n==> Setting up Gitlab Shell..."
    start = Time.now
    gitlab_shell_dir = Gitlab.config.gitlab_shell.path
128
    shell_needs_update = component_needs_update?(gitlab_shell_dir,
129 130
      Gitlab::Shell.version_required)

131
    unless !shell_needs_update || system('rake', 'gitlab:shell:install')
132
      puts "\nGitLab Shell failed to install, cleaning up #{gitlab_shell_dir}!\n"
133
      FileUtils.rm_rf(gitlab_shell_dir)
134
      exit 1
Robert Speicher committed
135
    end
136 137

    puts "    GitLab Shell setup in #{Time.now - start} seconds...\n"
138
  end
139

140
  def setup_gitaly
141 142
    puts "\n==> Setting up Gitaly..."
    start = Time.now
143
    socket_path = Gitlab::GitalyClient.address('default').sub(/\Aunix:/, '')
144
    gitaly_dir = File.dirname(socket_path)
145 146

    if gitaly_dir_stale?(gitaly_dir)
147
      puts "    Gitaly is outdated, cleaning up #{gitaly_dir}!"
148
      FileUtils.rm_rf(gitaly_dir)
149 150
    end

151 152
    gitaly_needs_update = component_needs_update?(gitaly_dir,
      Gitlab::GitalyClient.expected_server_version)
153

154
    unless !gitaly_needs_update || system('rake', "gitlab:gitaly:install[#{gitaly_dir}]")
155
      puts "\nGitaly failed to install, cleaning up #{gitaly_dir}!\n"
156
      FileUtils.rm_rf(gitaly_dir)
157
      exit 1
158 159
    end

160
    start_gitaly(gitaly_dir)
161
    puts "    Gitaly setup in #{Time.now - start} seconds...\n"
162 163
  end

164 165
  def gitaly_dir_stale?(dir)
    gitaly_executable = File.join(dir, 'gitaly')
166 167 168
    return false unless File.exist?(gitaly_executable)

    File.mtime(gitaly_executable) < File.mtime(Rails.root.join('GITALY_SERVER_VERSION'))
169 170
  end

171
  def start_gitaly(gitaly_dir)
172 173 174 175 176 177 178
    if ENV['CI'].present?
      # Gitaly has been spawned outside this process already
      return
    end

    spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s
    @gitaly_pid = Bundler.with_original_env { IO.popen([spawn_script], &:read).to_i }
179 180 181 182 183 184 185 186
  end

  def stop_gitaly
    return unless @gitaly_pid

    Process.kill('KILL', @gitaly_pid)
  end

187
  def setup_factory_repo
188 189 190 191 192 193 194 195 196 197 198
    setup_repo(factory_repo_path, factory_repo_path_bare, factory_repo_name,
               BRANCH_SHA)
  end

  # This repo has a submodule commit that is not present in the main test
  # repository.
  def setup_forked_repo
    setup_repo(forked_repo_path, forked_repo_path_bare, forked_repo_name,
               FORKED_BRANCH_SHA)
  end

199
  def setup_repo(repo_path, repo_path_bare, repo_name, refs)
200
    clone_url = "https://gitlab.com/gitlab-org/#{repo_name}.git"
201

202
    unless File.directory?(repo_path)
203
      system(*%W(#{Gitlab.config.git.bin_path} clone -q #{clone_url} #{repo_path}))
204 205
    end

206
    set_repo_refs(repo_path, refs)
207

208 209 210 211
    unless File.directory?(repo_path_bare)
      # We must copy bare repositories because we will push to them.
      system(git_env, *%W(#{Gitlab.config.git.bin_path} clone -q --bare #{repo_path} #{repo_path_bare}))
    end
212 213
  end

214
  def copy_repo(project, bare_repo:, refs:)
215
    target_repo_path = File.expand_path(project.repository_storage_path + "/#{project.full_path}.git")
216
    FileUtils.mkdir_p(target_repo_path)
217
    FileUtils.cp_r("#{File.expand_path(bare_repo)}/.", target_repo_path)
218
    FileUtils.chmod_R 0755, target_repo_path
219
    set_repo_refs(target_repo_path, refs)
220 221
  end

222
  def repos_path
223
    Gitlab.config.repositories.storages.default['path']
224
  end
225

226 227 228 229
  def backup_path
    Gitlab.config.backup.path
  end

230 231 232 233
  def pages_path
    Gitlab.config.pages.path
  end

234 235 236 237
  # When no cached assets exist, manually hit the root path to create them
  #
  # Otherwise they'd be created by the first test, often timing out and
  # causing a transient test failure
238
  def eager_load_driver_server
239 240
    return unless defined?(Capybara)

241
    puts "Starting the Capybara driver server..."
242
    Capybara.current_session.visit '/'
243 244
  end

245 246 247 248 249 250 251 252
  def factory_repo_path_bare
    "#{factory_repo_path}_bare"
  end

  def forked_repo_path_bare
    "#{forked_repo_path}_bare"
  end

253 254 255 256 257 258 259 260
  def with_empty_bare_repository(name = nil)
    path = Rails.root.join('tmp/tests', name || 'empty-bare-repository').to_s

    yield(Rugged::Repository.init_at(path, :bare))
  ensure
    FileUtils.rm_rf(path)
  end

261 262 263
  private

  def factory_repo_path
264 265 266
    @factory_repo_path ||= Rails.root.join('tmp', 'tests', factory_repo_name)
  end

267 268 269
  def factory_repo_name
    'gitlab-test'
  end
270

271 272 273 274 275 276 277 278
  def forked_repo_path
    @forked_repo_path ||= Rails.root.join('tmp', 'tests', forked_repo_name)
  end

  def forked_repo_name
    'gitlab-test-fork'
  end

279 280 281
  # Prevent developer git configurations from being persisted to test
  # repositories
  def git_env
282
    { 'GIT_TEMPLATE_DIR' => '' }
283
  end
284 285

  def set_repo_refs(repo_path, branch_sha)
286
    instructions = branch_sha.map { |branch, sha| "update refs/heads/#{branch}\x00#{sha}\x00" }.join("\x00") << "\x00"
287 288
    update_refs = %W(#{Gitlab.config.git.bin_path} update-ref --stdin -z)
    reset = proc do
289 290 291 292
      Dir.chdir(repo_path) do
        IO.popen(update_refs, "w") { |io| io.write(instructions) }
        $?.success?
      end
293 294
    end

295 296 297 298 299 300
    # Try to reset without fetching to avoid using the network.
    unless reset.call
      raise 'Could not fetch test seed repository.' unless system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} fetch origin))

      # Before we used Git clone's --mirror option, bare repos could end up
      # with missing refs, clearing them and retrying should fix the issue.
301
      cleanup && clean_gitlab_test_path && init unless reset.call
302 303
    end
  end
304

305 306
  def component_needs_update?(component_folder, expected_version)
    version = File.read(File.join(component_folder, 'VERSION')).strip
307 308 309 310

    # Notice that this will always yield true when using branch versions
    # (`=branch_name`), but that actually makes sure the server is always based
    # on the latest branch revision.
311
    version != expected_version
312 313 314
  rescue Errno::ENOENT
    true
  end
315
end