class Fastlane::Actions::GithubActionAction

Public Class Methods

authors() click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 286
def self.authors
  ["josdholtz"]
end
available_options() click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 294
def self.available_options
  [
    FastlaneCore::ConfigItem.new(key: :server_url,
                                 env_name: "FL_GITHUB_API_SERVER_URL",
                                 description: "The server url. e.g. 'https://your.internal.github.host/api/v3' (Default: 'https://api.github.com')",
                                 default_value: "https://api.github.com",
                                 optional: true,
                                 verify_block: proc do |value|
                                   UI.user_error!("Please include the protocol in the server url, e.g. https://your.github.server/api/v3") unless value.include?("//")
                                 end),
    FastlaneCore::ConfigItem.new(key: :api_token,
                                 env_name: "FL_GITHUB_API_TOKEN",
                                 description: "Personal API Token for GitHub - generate one at https://github.com/settings/tokens",
                                 sensitive: true,
                                 code_gen_sensitive: true,
                                 is_string: true,
                                 default_value: ENV["GITHUB_API_TOKEN"],
                                 default_value_dynamic: true,
                                 optional: false),
    FastlaneCore::ConfigItem.new(key: :org,
                                 env_name: "FL_GITHUB_ACTIONS_ORG",
                                 description: "Name of organization of the repository for GitHub Actions"),
    FastlaneCore::ConfigItem.new(key: :repo,
                                 env_name: "FL_GITHUB_ACTIONS_REPO",
                                 description: "Name of repository for GitHub Actions"),
    FastlaneCore::ConfigItem.new(key: :match_org,
                                 env_name: "FL_GITHUB_ACTIONS_MATCH_ORG",
                                 description: "Name of organization of the match repository",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :match_repo,
                                 env_name: "FL_GITHUB_ACTIONS_MATCH_REPO",
                                 description: "Name of match repository",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :dotenv_paths,
                                 env_name: "FL_GITHUB_ACTINOS_DOTENV_PATHS",
                                 description: "Paths of .env files to parse",
                                 optional: true,
                                 type: Array,
                                 verify_block: proc do |values|
                                   values.each do |value|
                                     UI.user_error!("Path #{value} doesn't exist") unless File.exist?(value)
                                   end 
                                 end),
  ]
end
check_for_setup_ci_in_fastfile() click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 25
def self.check_for_setup_ci_in_fastfile
  fastfiles = Dir.glob("./*/Fastfile").map do |path|
    File.absolute_path(path)
  end
 
  fastfiles.each do |path|
    content = File.read(path)

    if !content.include?("setup_ci")
      UI.confirm("`setup_ci` is not detected for '#{path}'. Do you still want to continue on?")
    end
  end

end
deploy_key_title() click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 21
def self.deploy_key_title
  "Match Deploy Key (created by fastalne-plugin-github_actions)"
end
description() click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 282
def self.description
  "Helper to setup GitHub actions for fastlane and match"
end
details() click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 290
def self.details
  "Helper to setup GitHub actions for fastlane and match"
end
encrypt_secret(key64, secret) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 269
def self.encrypt_secret(key64, secret)
  require "rbnacl"
  require "base64"

  key = Base64.decode64(key64)
  public_key = RbNaCl::PublicKey.new(key)

  box = RbNaCl::Boxes::Sealed.from_public_key(public_key)
  encrypted_secret = box.encrypt(secret)

  return Base64.strict_encode64(encrypted_secret)
end
generate_deploy_key(params) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 125
def self.generate_deploy_key(params)
  if params[:match_org].to_s == "" && params[:match_repo].to_s == ""
    UI.message("Skipping Deploy Key generation...")
    return {}
  elsif params[:match_org].to_s == ""
    UI.user_error!("`match_org` also needs to be specified")
  elsif params[:match_repo].to_s == ""
    UI.user_error!("`match_repo` also needs to be specified")
  end

  get_deploy_keys_resp = self.match_repo_get(params, "/keys")

  sleep(1)

  deploy_keys = get_deploy_keys_resp[:json] || []
  deploy_keys.each do |deploy_key|
    if deploy_key["title"] == deploy_key_title
      if UI.confirm("Deploy Key for the match repo already exists... Delete it?")
        self.match_repo_delete(params, "/keys/#{deploy_key["id"]}")
        UI.message("Deleted existing Deploy Key")
        sleep(1)
      else
        return {}
      end
    end
  end

  require 'sshkey'
  k = SSHKey.generate()

  body = {
    title: deploy_key_title,
    key: k.ssh_public_key,
    read_only: true
  }
  post_deploy_key_resp = self.match_repo_post(params, "/keys", body)
  UI.message("Created Deploy Key")
  
  sleep(3)
 
  secrets = {}
  secrets[match_deploy_key] = k.private_key  
  return secrets
end
generate_workflow_template(params, secret_names) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 40
def self.generate_workflow_template(params, secret_names)
  workflows_dir = File.absolute_path(".github/workflows")
  workflow_path = File.join(workflows_dir, 'fastlane.yml')

  if File.exist?(workflow_path)
    if UI.confirm("File already exists at #{workflow_path}. Do you want to overwrite?")
      UI.message("Overwriting #{workflow_path}")
    else
      UI.message("Cancelled workflow template generation...")
      return
    end
  end

  require 'fastlane/erb_template_helper'
  include ERB::Util
    
  spaces = " " * 10

  use_match = secret_names.include?(match_deploy_key)
    
  #
  # Clone test secrets and commands
  #
  if use_match
    clone_test_secrets = [
      'GIT_SSH_COMMAND: "ssh -o StrictHostKeyChecking=no"',
      "#{match_deploy_key}: ${{ secrets.#{match_deploy_key} }}"
    ].map do |secret|
      "#{spaces}#{secret}"
    end.join("\n")

    clone_test_commands = [
      'eval "$(ssh-agent -s)"',
      "ssh-add - <<< \"${#{match_deploy_key}}\"",
      "git clone git@github.com:#{params[:match_org]}/#{params[:match_repo]}.git",
      "ls #{params[:match_repo]}"
    ].map do |command|
      "#{spaces}#{command}"
    end.join("\n")
  end

  #
  # Secrets and commands
  #
  secrets = secret_names.map do |secret_name|
    "#{secret_name}: ${{ secrets.#{secret_name}  }}"
  end

  if use_match
    secrets << 'GIT_SSH_COMMAND: "ssh -o StrictHostKeyChecking=no"'
    secrets << 'MATCH_READONLY: true'
  end

  secrets = secrets.map do |secret|
    "#{spaces}#{secret}"
  end.join("\n")

  commands = []
  if use_match
    commands = [
      'eval "$(ssh-agent -s)"',
      "ssh-add - <<< \"${#{match_deploy_key}}\"",
    ]
  end

  commands << 'bundle exec fastlane test'
  commands = commands.map do |command|
    "#{spaces}#{command}"
  end.join("\n")

  puts "secret size: #{secrets.size}"

  workflow_template = Helper::GithubActionHelper.load("workflow_template")
  workflow_render = Helper::GithubActionHelper.render(workflow_template, {
    use_match: use_match,
    clone_test_secrets: clone_test_secrets,
    clone_test_commands: clone_test_commands,
    secrets: secrets,
    commands: commands
  })

  FileUtils.mkdir_p(workflows_dir)
  File.write(workflow_path, workflow_render)
end
is_supported?(platform) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 340
def self.is_supported?(platform)
  true
end
match_deploy_key() click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 17
def self.match_deploy_key
  "MATCH_DEPLOY_KEY"  
end
match_repo_delete(params, path) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 259
def self.match_repo_delete(params, path)
  return other_action.github_api(
    server_url: params[:server_url],
    api_token: params[:api_token],
    http_method: "DELETE",
    path: "/repos/#{params[:match_org]}/#{params[:match_repo]}#{path}",
    body: {},
  )
end
match_repo_get(params, path) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 239
def self.match_repo_get(params, path)
  return other_action.github_api(
    server_url: params[:server_url],
    api_token: params[:api_token],
    http_method: "GET",
    path: "/repos/#{params[:match_org]}/#{params[:match_repo]}#{path}",
    body: {},
  )
end
match_repo_post(params, path, body) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 249
def self.match_repo_post(params, path, body)
  return other_action.github_api(
    server_url: params[:server_url],
    api_token: params[:api_token],
    http_method: "POST",
    path: "/repos/#{params[:match_org]}/#{params[:match_repo]}#{path}",
    body: body,
  )
end
parse_dotenvs(params) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 207
def self.parse_dotenvs(params)
  dotenv_paths = (params[:dotenv_paths] || [])

  if dotenv_paths.empty?
    UI.message "No dotenv paths to parse..."
    return {}
  end

  require "dotenv"
  return Dotenv.parse(*dotenv_paths)
end
post_secrets(params, additional_secrets) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 170
def self.post_secrets(params, additional_secrets)
  public_key_resp = self.repo_get(params, "/actions/secrets/public-key")
  key_id = public_key_resp[:json]["key_id"]
  key64 = public_key_resp[:json]["key"]

  secrets = self.parse_dotenvs(params)
  secrets = secrets.merge(additional_secrets || {})

  encrypted_secrets = {}
  secrets.each do |k,v|
    encrypted_value = self.encrypt_secret(key64, v)
    encrypted_secrets[k] = encrypted_value
  end

  existing_secrets_resp = self.repo_get(params, "/actions/secrets")
  existing_secret_names = existing_secrets_resp[:json]["secrets"].map do |secret|
    secret["name"].to_s
  end

  encrypted_secrets.reject! do |k,v|
    if existing_secret_names.include?(k.to_s)
      !UI.confirm("Overwrite #{k}?")
    end
  end

  encrypted_secrets.each do |k,v|
    body = {
      key_id: key_id,
      encrypted_value: v
    }
    self.repo_put(params, "/actions/secrets/#{k}", body)
    UI.message("Saving secret #{k}")
  end

  return secrets.keys
end
repo_get(params, path) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 219
def self.repo_get(params, path)
  return other_action.github_api(
    server_url: params[:server_url],
    api_token: params[:api_token],
    http_method: "GET",
    path: "/repos/#{params[:org]}/#{params[:repo]}#{path}",
    body: {},
  )
end
repo_put(params, path, body) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 229
def self.repo_put(params, path, body)
  return other_action.github_api(
    server_url: params[:server_url],
    api_token: params[:api_token],
    http_method: "PUT",
    path: "/repos/#{params[:org]}/#{params[:repo]}#{path}",
    body: body,
  )
end
run(params) click to toggle source
# File lib/fastlane/plugin/github_action/actions/github_action_action.rb, line 7
def self.run(params)
  UI.message("The github_actions plugin is working!")

  self.check_for_setup_ci_in_fastfile

  additional_secrets = self.generate_deploy_key(params)
  secret_names = self.post_secrets(params, additional_secrets)
  self.generate_workflow_template(params, secret_names)
end