#!/usr/bin/env ruby require 'net/http' require 'json' require 'cgi' module Trigger OMNIBUS_PROJECT_PATH = 'gitlab-org/omnibus-gitlab'.freeze CNG_PROJECT_PATH = 'gitlab-org/build/CNG'.freeze TOKEN = ENV['BUILD_TRIGGER_TOKEN'] def self.ee? ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md') end class Omnibus def initialize @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::OMNIBUS_PROJECT_PATH)}/trigger/pipeline") @params = env_params.merge(file_params).merge(token: Trigger::TOKEN) end def invoke! res = Net::HTTP.post_form(@uri, @params) id = JSON.parse(res.body)['id'] project = Trigger::OMNIBUS_PROJECT_PATH if id puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}" puts "Waiting for downstream pipeline status" else raise "Trigger failed! The response from the trigger is: #{res.body}" end Trigger::Pipeline.new(project, id) end private def env_params { "ref" => ENV["OMNIBUS_BRANCH"] || "master", "variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"], "variables[ALTERNATIVE_SOURCES]" => true, "variables[ee]" => Trigger.ee? ? 'true' : 'false', "variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"], "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}" } end def file_params Hash.new.tap do |params| Dir.glob("*_VERSION").each do |version_file| params["variables[#{version_file}]"] = File.read(version_file).strip end end end end class CNG def initialize @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::CNG_PROJECT_PATH)}/trigger/pipeline") @ref_name = ENV['CI_COMMIT_REF_NAME'] @username = ENV['GITLAB_USER_NAME'] @project_name = ENV['CI_PROJECT_NAME'] @job_id = ENV['CI_JOB_ID'] @params = env_params.merge(file_params).merge(token: Trigger::TOKEN) end # # Trigger a pipeline # def invoke! res = Net::HTTP.post_form(@uri, @params) id = JSON.parse(res.body)['id'] project = Trigger::CNG_PROJECT_PATH if id puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}" puts "Waiting for downstream pipeline status" else raise "Trigger failed! The response from the trigger is: #{res.body}" end Trigger::Pipeline.new(project, id) end private def env_params params = { "ref" => ENV["CNG_BRANCH"] || "master", "variables[TRIGGERED_USER]" => @username, "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{@project_name}/-/jobs/#{@job_id}" } if Trigger.ee? params["variables[GITLAB_EE_VERSION]"] = @ref_name params["variables[EE_PIPELINE]"] = 'true' else params["variables[GITLAB_CE_VERSION]"] = @ref_name params["variables[CE_PIPELINE]"] = 'true' end params end # Read version files from all components def file_params Dir.glob("*_VERSION").each_with_object({}) do |version_file, params| raw_version = File.read(version_file).strip # if the version matches semver format, treat it as a tag and prepend `v` version = if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/) "v#{raw_version}" else raw_version end params["variables[#{version_file}]"] = version end end end class Pipeline INTERVAL = 60 # seconds MAX_DURATION = 3600 * 3 # 3 hours def initialize(project, id) @start = Time.now.to_i @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/pipelines/#{id}") end def wait! loop do raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout? case status when :created, :pending, :running print "." sleep INTERVAL when :success puts "Pipeline succeeded in #{duration} minutes!" break else raise "Pipeline did not succeed!" end STDOUT.flush end end def timeout? Time.now.to_i > (@start + MAX_DURATION) end def duration (Time.now.to_i - @start) / 60 end def status req = Net::HTTP::Get.new(@uri) req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN'] res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: true) do |http| http.request(req) end JSON.parse(res.body)['status'].to_s.to_sym rescue JSON::ParserError # Ignore GitLab API hiccups. If GitLab is really down, we'll hit the job # timeout anyway. :running end end end case ARGV[0] when 'omnibus' Trigger::Omnibus.new.invoke!.wait! when 'cng' Trigger::CNG.new.invoke!.wait! else puts "Please provide a valid option: omnibus - Triggers a pipeline that builds the omnibus-gitlab package cng - Triggers a pipeline that builds images used by the GitLab helm chart" end