summaryrefslogtreecommitdiff
path: root/scripts/api/get_job_id
blob: c7fe859db916b1f0f2452d37b937a0815267877f (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
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'rubygems'
require 'gitlab'
require 'optparse'

class JobFinder
  DEFAULT_OPTIONS = {
    project: ENV['CI_PROJECT_ID'],
    pipeline_id: ENV['CI_PIPELINE_ID'],
    pipeline_query: {},
    job_query: {},
    api_token: ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
  }.freeze

  def initialize(options)
    @project = options.delete(:project)
    @pipeline_query = options.delete(:pipeline_query)
    @job_query = options.delete(:job_query)
    @pipeline_id = options.delete(:pipeline_id)
    @job_name = options.delete(:job_name)
    @artifact_path = options.delete(:artifact_path)

    # Force the token to be a string so that if api_token is nil, it's set to '', allowing unauthenticated requests (for forks).
    api_token = options.delete(:api_token).to_s

    warn "No API token given." if api_token.empty?

    Gitlab.configure do |config|
      config.endpoint = 'https://gitlab.com/api/v4'
      config.private_token = api_token
    end
  end

  def execute
    find_job_with_artifact || find_job_with_filtered_pipelines || find_job_in_pipeline
  end

  private

  attr_reader :project, :pipeline_query, :job_query, :pipeline_id, :job_name, :artifact_path

  def find_job_with_artifact
    return if artifact_path.nil?

    Gitlab.pipelines(project, pipeline_query_params).auto_paginate do |pipeline|
      Gitlab.pipeline_jobs(project, pipeline.id, job_query_params).auto_paginate do |job|
        return job if found_job_with_artifact?(job) # rubocop:disable Cop/AvoidReturnFromBlocks
      end
    end

    raise 'Job not found!'
  end

  def find_job_with_filtered_pipelines
    return if pipeline_query.empty?

    Gitlab.pipelines(project, pipeline_query_params).auto_paginate do |pipeline|
      Gitlab.pipeline_jobs(project, pipeline.id, job_query_params).auto_paginate do |job|
        return job if found_job_by_name?(job) # rubocop:disable Cop/AvoidReturnFromBlocks
      end
    end

    raise 'Job not found!'
  end

  def find_job_in_pipeline
    return unless pipeline_id

    Gitlab.pipeline_jobs(project, pipeline_id, job_query_params).auto_paginate do |job|
      return job if found_job_by_name?(job) # rubocop:disable Cop/AvoidReturnFromBlocks
    end

    raise 'Job not found!'
  end

  def found_job_with_artifact?(job)
    artifact_url = "https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/jobs/#{job.id}/artifacts/#{artifact_path}"
    response = HTTParty.head(artifact_url) # rubocop:disable Gitlab/HTTParty
    response.success?
  end

  def found_job_by_name?(job)
    job.name == job_name
  end

  def pipeline_query_params
    @pipeline_query_params ||= { per_page: 100, **pipeline_query }
  end

  def job_query_params
    @job_query_params ||= { per_page: 100, **job_query }
  end
end

if $0 == __FILE__
  options = JobFinder::DEFAULT_OPTIONS.dup

  OptionParser.new do |opts|
    opts.on("-p", "--project PROJECT", String, "Project where to find the job (defaults to $CI_PROJECT_ID)") do |value|
      options[:project] = value
    end

    opts.on("-i", "--pipeline-id pipeline_id", String, "A pipeline ID (defaults to $CI_PIPELINE_ID)") do |value|
      options[:pipeline_id] = value
    end

    opts.on("-q", "--pipeline-query pipeline_query", String, "Query to pass to the Pipeline API request") do |value|
      options[:pipeline_query].merge!(Hash[*value.split('=')])
    end

    opts.on("-Q", "--job-query job_query", String, "Query to pass to the Job API request") do |value|
      options[:job_query].merge!(Hash[*value.split('=')])
    end

    opts.on("-j", "--job-name job_name", String, "A job name that needs to exist in the found pipeline") do |value|
      options[:job_name] = value
    end

    opts.on("-a", "--artifact-path ARTIFACT_PATH", String, "A valid artifact path") do |value|
      options[:artifact_path] = value
    end

    opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope") do |value|
      options[:api_token] = value
    end

    opts.on("-h", "--help", "Prints this help") do
      puts opts
      exit
    end
  end.parse!

  job = JobFinder.new(options).execute

  return if job.nil?

  puts job.id
end