File: shared_context_and_examples.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (174 lines) | stat: -rw-r--r-- 5,645 bytes parent folder | download
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# frozen_string_literal: true

RSpec.shared_context 'with simulated pipeline attributes and shared project and user' do
  let(:ci_server_host) { 'gitlab.com' }
  let(:ci_project_namespace) { 'gitlab-org' }
  let(:ci_project_path) { "#{ci_project_namespace}/#{ci_project_name}" }
  let(:ci_project_name) { 'gitlab' }
  let(:ci_pipeline_source) { 'push' }

  let(:variables_attributes_base) do
    [
      { key: 'CI_SERVER_HOST', value: ci_server_host },
      { key: 'CI_PROJECT_NAMESPACE', value: ci_project_namespace },
      { key: 'CI_PROJECT_PATH', value: ci_project_path },
      { key: 'CI_PROJECT_NAME', value: ci_project_name },
      { key: 'CI_PIPELINE_SOURCE', value: ci_pipeline_source },
      { key: 'GITLAB_INTERNAL', value: 'true' }
    ]
  end

  let(:jobs) { pipeline.stages.flat_map { |s| s.statuses.map(&:name) } }

  let_it_be(:group) { create(:group, path: 'gitlab-org') }
  let_it_be(:gitlab_org_gitlab_project) { create(:project, :empty_repo, group: group, path: 'gitlab') }
  let_it_be(:user) { create(:user) }
  let_it_be(:ci_glob) { Dir.glob("{.gitlab-ci.yml,.gitlab/**/*.yml}").freeze }
  let_it_be(:ci_glob_with_common_file_globs) { [*ci_glob, 'lib/api/lint.rb', 'doc/index.md'] }
  let_it_be(:master_branch) { 'master' }

  around do |example|
    with_net_connect_allowed { example.run } # creating pipeline requires network call to fetch templates
  end

  before_all do
    gitlab_org_gitlab_project.add_developer(user)

    sync_local_files_to_project(
      gitlab_org_gitlab_project,
      user,
      master_branch,
      files: ci_glob_with_common_file_globs
    )
  end

  before do
    # delete once we have a migration to permanently increase limit
    stub_application_setting(max_yaml_size_bytes: 2.megabytes)
  end
end

RSpec.shared_context 'with simulated MR pipeline attributes' do
  let(:ci_pipeline_source) { 'merge_request_event' }
  let(:ci_merge_request_event_type) { 'merged_result' }
  let(:mr_labels) { [] }

  let(:mr_pipeline_variables_attributes) do
    [
      *variables_attributes_base,
      { key: 'CI_COMMIT_REF_NAME', value: source_branch },
      { key: 'CI_MERGE_REQUEST_EVENT_TYPE', value: ci_merge_request_event_type }
    ]
  end

  let(:create_pipeline_service) { Ci::CreatePipelineService.new(target_project, user, ref: target_branch) }

  let(:source_project) { gitlab_org_gitlab_project }
  let(:target_project) { gitlab_org_gitlab_project }
  let(:source_branch) { "feature_branch_ci_#{SecureRandom.uuid}" }
  let(:target_branch) { master_branch }

  let(:merge_request) do
    create(:labeled_merge_request,
      source_project: source_project,
      source_branch: source_branch,
      target_project: target_project,
      target_branch: target_branch,
      labels: mr_labels.map { |label_title| create(:label, title: label_title) }
    )
  end

  before do
    file_change_actions = changed_files.map do |file_path|
      action = source_project.repository.blob_at(source_branch, file_path).nil? ? :create : :update
      {
        action: action,
        file_path: file_path,
        content: 'content'
      }
    end

    source_project.repository.commit_files(
      user,
      branch_name: source_branch,
      message: 'changes files',
      actions: file_change_actions
    )
  end

  after do
    source_project.repository.delete_branch(source_branch)
  end
end

RSpec.shared_examples 'default branch pipeline' do
  it 'is valid' do
    expect(pipeline.yaml_errors).to be nil
    expect(pipeline.status).to eq('created')
    expect(jobs).to include(expected_job_name)
  end
end

RSpec.shared_examples 'merge request pipeline' do
  it "succeeds with expected job" do
    expect(pipeline.yaml_errors).to be nil
    expect(pipeline.status).to eq('created')
    expect(jobs).to include(expected_job_name)
  end
end

RSpec.shared_examples 'merge train pipeline' do
  let(:ci_merge_request_event_type) { 'merge_train' }

  it "succeeds with expected job" do
    expect(pipeline.yaml_errors).to be nil
    expect(pipeline.status).to eq('created')
    expect(jobs).to include('pre-merge-checks')
    expect(jobs).not_to include('upload-frontend-fixtures')
  end
end

module CiConfigurationValidationHelper
  def sync_local_files_to_project(project, user, branch_name, files:)
    actions = []

    entries = project.repository.tree(branch_name, recursive: true).entries
    entries.map! { |e| e.dir? ? project.repository.tree(branch_name, e.path, recursive: true).entries : e }
    current_files = entries.flatten.select(&:file?).map(&:path).uniq

    # Delete old
    actions.concat (current_files - files).map { |file| { action: :delete, file_path: file } }
    # Add new
    actions.concat (files - current_files).map { |file|
                     { action: :create, file_path: file, content: read_file(file) }
                   }

    # Update changed
    (current_files & files).each do |file|
      content = read_file(file)
      if content != project.repository.blob_data_at(branch_name, file)
        actions << { action: :update, file_path: file, content: content }
      end
    end

    if actions.any?
      puts "Syncing files to #{project.full_path} #{branch_name} branch"
      project.repository.commit_files(user, branch_name: branch_name, message: 'syncing', actions: actions)
    else
      puts "No file syncing needed"
    end
  end

  def read_file(file, ignore_ci_component: true)
    content = File.read(file)

    return content unless ignore_ci_component

    fake_job = <<~YAML
    .ignore:
      script: echo ok
    YAML

    file.end_with?('.yml') && %r{^\s*- component:.*CI_SERVER_}.match?(content) ? fake_job : content
  end
end