# frozen_string_literal: true require 'json' require 'excon' require 'sshkey' module RakeCircleCI class URLs def initialize(opts) @base_url = opts[:base_url] @api_token = opts[:api_token] @project_slug = opts[:project_slug] end def follow_url "#{@base_url}/v1.1/project/#{@project_slug}/follow?" \ "circle-token=#{@api_token}" end def env_vars_url "#{@base_url}/v2/project/#{@project_slug}/envvar" end def env_var_url(name) "#{@base_url}/v2/project/#{@project_slug}/envvar/#{name}" end def settings_url "#{@base_url}/v1.1/project/#{@project_slug}/settings?" \ "circle-token=#{@api_token}" end def ssh_keys_url "#{@base_url}/v1.1/project/#{@project_slug}/ssh-key?" \ "circle-token=#{@api_token}" end def checkout_keys_url "#{@base_url}/v1.1/project/#{@project_slug}/checkout-key" end def checkout_key_url(fingerprint) "#{@base_url}/v1.1/project/#{@project_slug}/checkout-key/#{fingerprint}" end end class HTTPClient def get(url, opts) assert_successful(Excon.get(url, opts)) end def post(url, opts) assert_successful(Excon.post(url, opts)) end def delete(url, opts) assert_successful(Excon.delete(url, opts)) end private def assert_successful(response) unless response.status >= 200 && response.status < 300 host = response.data[:host] path = response.data[:path] status = response.status reason = response.data[:reason_phrase] raise "Unsuccessful request: #{host}#{path} #{status} #{reason}" end response end end class Client def initialize(opts) @urls = URLs.new(opts) @api_token = opts[:api_token] @http = HTTPClient.new end def follow_project @http.post(@urls.follow_url, { headers: }) end def find_env_vars response = @http.get(@urls.env_vars_url, { headers: }) body = JSON.parse(response.body) body['items'].map { |item| item['name'] } end def create_env_var(name, value) body = JSON.dump(name:, value:) @http.post(@urls.env_vars_url, { body:, headers: }) end def delete_env_var(name) @http.delete(@urls.env_var_url(name), { headers: }) end def delete_env_vars env_vars = find_env_vars env_vars.each do |env_var| delete_env_var(env_var) end end def find_ssh_keys response = @http.get(@urls.settings_url, { headers: }) body = JSON.parse(response.body, symbolize_names: true) body[:ssh_keys] end def create_ssh_key(private_key, opts = {}) body = { fingerprint: SSHKey.new(private_key).sha1_fingerprint, private_key: } body = body.merge(hostname: opts[:hostname]) if opts[:hostname] body = JSON.dump(body) @http.post(@urls.ssh_keys_url, { body:, headers: }) end def delete_ssh_key(fingerprint, opts = {}) body = { fingerprint: } body = body.merge(hostname: opts[:hostname]) if opts[:hostname] body = JSON.dump(body) @http.delete(@urls.ssh_keys_url, { body:, headers: }) end def delete_ssh_keys ssh_keys = find_ssh_keys ssh_keys.each do |ssh_key| fingerprint = ssh_key[:fingerprint] hostname = ssh_key[:hostname] options = hostname && { hostname: } args = [fingerprint, options].compact delete_ssh_key(*args) end end def find_checkout_keys response = @http.get(@urls.checkout_keys_url, { headers: }) JSON.parse(response.body, symbolize_names: true) end def create_checkout_key(type) type_strings = { deploy_key: 'deploy-key', github_user_key: 'github-user-key' } body = JSON.dump(type: type_strings[type.to_sym] || type.to_s) @http.post(@urls.checkout_keys_url, { body:, headers: }) end def delete_checkout_key(fingerprint) @http.delete(@urls.checkout_key_url(fingerprint), { headers: }) end def delete_checkout_keys checkout_keys = find_checkout_keys checkout_keys.each do |checkout_key| delete_checkout_key(checkout_key[:fingerprint]) end end private def headers { 'Circle-Token': @api_token, 'Content-Type': 'application/json', Accept: 'application/json' } end end end