require './spec/support/sidekiq' require './spec/support/test_env' class Gitlab::Seeder::CycleAnalytics def initialize(project, perf: false) @project = project @user = User.admins.first @issue_count = perf ? 1000 : 5 stub_git_pre_receive! end # The GitLab API needn't be running for the fixtures to be # created. Since we're performing a number of git actions # here (like creating a branch or committing a file), we need # to disable the `pre_receive` hook in order to remove this # dependency on the GitLab API. def stub_git_pre_receive! Gitlab::Git::HooksService.class_eval do def run_hook(name) [true, ''] end end end def seed_metrics! @issue_count.times do |index| # Issue Timecop.travel 5.days.from_now title = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" issue = Issue.create(project: @project, title: title, author: @user) issue_metrics = issue.metrics # Milestones / Labels Timecop.travel 5.days.from_now if index.even? issue_metrics.first_associated_with_milestone_at = rand(6..12).hours.from_now else issue_metrics.first_added_to_board_at = rand(6..12).hours.from_now end # Commit Timecop.travel 5.days.from_now issue_metrics.first_mentioned_in_commit_at = rand(6..12).hours.from_now # MR Timecop.travel 5.days.from_now branch_name = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" @project.repository.add_branch(@user, branch_name, 'master') merge_request = MergeRequest.create(target_project: @project, source_project: @project, source_branch: branch_name, target_branch: 'master', title: branch_name, author: @user) merge_request_metrics = merge_request.metrics # MR closing issues Timecop.travel 5.days.from_now MergeRequestsClosingIssues.create!(issue: issue, merge_request: merge_request) # Merge Timecop.travel 5.days.from_now merge_request_metrics.merged_at = rand(6..12).hours.from_now # Start build Timecop.travel 5.days.from_now merge_request_metrics.latest_build_started_at = rand(6..12).hours.from_now # Finish build Timecop.travel 5.days.from_now merge_request_metrics.latest_build_finished_at = rand(6..12).hours.from_now # Deploy to production Timecop.travel 5.days.from_now merge_request_metrics.first_deployed_to_production_at = rand(6..12).hours.from_now issue_metrics.save! merge_request_metrics.save! print '.' end end def seed! Sidekiq::Worker.skipping_transaction_check do Sidekiq::Testing.inline! do issues = create_issues puts '.' # Stage 1 Timecop.travel 5.days.from_now add_milestones_and_list_labels(issues) print '.' # Stage 2 Timecop.travel 5.days.from_now branches = mention_in_commits(issues) print '.' # Stage 3 Timecop.travel 5.days.from_now merge_requests = create_merge_requests_closing_issues(issues, branches) print '.' # Stage 4 Timecop.travel 5.days.from_now run_builds(merge_requests) print '.' # Stage 5 Timecop.travel 5.days.from_now merge_merge_requests(merge_requests) print '.' # Stage 6 / 7 Timecop.travel 5.days.from_now deploy_to_production(merge_requests) print '.' end end print '.' end private def create_issues Array.new(@issue_count) do issue_params = { title: "Cycle Analytics: #{FFaker::Lorem.sentence(6)}", description: FFaker::Lorem.sentence, state: 'opened', assignees: [@project.team.users.sample] } Issues::CreateService.new(@project, @project.team.users.sample, issue_params).execute end end def add_milestones_and_list_labels(issues) issues.shuffle.map.with_index do |issue, index| Timecop.travel 12.hours.from_now if index.even? issue.update(milestone: @project.milestones.sample) else label_name = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" list_label = FactoryBot.create(:label, title: label_name, project: issue.project) FactoryBot.create(:list, board: FactoryBot.create(:board, project: issue.project), label: list_label) issue.update(labels: [list_label]) end issue end end def mention_in_commits(issues) issues.map do |issue| Timecop.travel 12.hours.from_now branch_name = filename = "#{FFaker::Product.brand}-#{FFaker::Product.brand}-#{rand(1000)}" issue.project.repository.add_branch(@user, branch_name, 'master') commit_sha = issue.project.repository.create_file(@user, filename, "content", message: "Commit for #{issue.to_reference}", branch_name: branch_name) issue.project.repository.commit(commit_sha) GitPushService.new(issue.project, @user, oldrev: issue.project.repository.commit("master").sha, newrev: commit_sha, ref: 'refs/heads/master').execute branch_name end end def create_merge_requests_closing_issues(issues, branches) issues.zip(branches).map do |issue, branch| Timecop.travel 12.hours.from_now opts = { title: 'Cycle Analytics merge_request', description: "Fixes #{issue.to_reference}", source_branch: branch, target_branch: 'master' } MergeRequests::CreateService.new(issue.project, @user, opts).execute end end def run_builds(merge_requests) merge_requests.each do |merge_request| Timecop.travel 12.hours.from_now service = Ci::CreatePipelineService.new(merge_request.project, @user, ref: "refs/heads/#{merge_request.source_branch}") pipeline = service.execute(:push, ignore_skip_ci: true, save_on_errors: false) pipeline.run! Timecop.travel rand(1..6).hours.from_now pipeline.succeed! PipelineMetricsWorker.new.perform(pipeline.id) end end def merge_merge_requests(merge_requests) merge_requests.each do |merge_request| Timecop.travel 12.hours.from_now MergeRequests::MergeService.new(merge_request.project, @user).execute(merge_request) end end def deploy_to_production(merge_requests) merge_requests.each do |merge_request| next unless merge_request.head_pipeline Timecop.travel 12.hours.from_now job = merge_request.head_pipeline.builds.where.not(environment: nil).last CreateDeploymentService.new(job).execute end end end Gitlab::Seeder.quiet do flag = 'SEED_CYCLE_ANALYTICS' if ENV[flag] Project.find_each do |project| # This seed naively assumes that every project has a repository, and every # repository has a `master` branch, which may be the case for a pristine # GDK seed, but is almost never true for a GDK that's actually had # development performed on it. next unless project.repository_exists? next unless project.repository.commit('master') seeder = Gitlab::Seeder::CycleAnalytics.new(project) seeder.seed! end elsif ENV['CYCLE_ANALYTICS_PERF_TEST'] seeder = Gitlab::Seeder::CycleAnalytics.new(Project.order(:id).first, perf: true) seeder.seed! elsif ENV['CYCLE_ANALYTICS_POPULATE_METRICS_DIRECTLY'] seeder = Gitlab::Seeder::CycleAnalytics.new(Project.order(:id).first, perf: true) seeder.seed_metrics! else puts "Skipped. Use the `#{flag}` environment variable to enable." end end