This repository has been archived by the owner on Nov 19, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from KrauseFx/feature/configuration-2.0
[WIP] Brand new configuration for everything
- Loading branch information
Showing
8 changed files
with
320 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
require 'fastlane_core/configuration/config_item' | ||
require 'fastlane_core/configuration/commander_generator' | ||
|
||
module FastlaneCore | ||
class Configuration | ||
def self.create(available_options, values) | ||
Configuration.new(available_options, values) | ||
end | ||
|
||
# Setting up | ||
|
||
def initialize(available_options, values) | ||
@available_options = available_options | ||
@values = values | ||
|
||
verify_input_types | ||
verify_value_exists | ||
verify_no_duplicates | ||
end | ||
|
||
def verify_input_types | ||
raise "available_options parameter must be an array of ConfigItems".red unless @available_options.kind_of?Array | ||
@available_options.each do |item| | ||
raise "available_options parameter must be an array of ConfigItems".red unless item.kind_of?ConfigItem | ||
end | ||
raise "values parameter must be a hash".red unless @values.kind_of?Hash | ||
end | ||
|
||
def verify_value_exists | ||
# Make sure the given value keys exist | ||
@values.each do |key, value| | ||
option = option_for_key(key) | ||
if option | ||
option.verify!(value) # Call the verify block for it too | ||
else | ||
raise "Could not find available option '#{key}' in the list of available options #{@available_options.collect { |a| a.key }}".red | ||
end | ||
end | ||
end | ||
|
||
def verify_no_duplicates | ||
# Make sure a key was not used multiple times | ||
@available_options.each do |current| | ||
count = @available_options.select { |option| option.key == current.key }.count | ||
raise "Multiple entries for configuration key '#{current.key}' found!".red if count > 1 | ||
|
||
unless current.short_option.to_s.empty? | ||
count = @available_options.select { |option| option.short_option == current.short_option }.count | ||
raise "Multiple entries for short_option '#{current.short_option}' found!".red if count > 1 | ||
end | ||
end | ||
end | ||
|
||
# Using the class | ||
|
||
# Returns the value for a certain key. fastlane_core tries to fetch the value from different sources | ||
def fetch(key) | ||
raise "Key '#{key}' must be a symbol. Example :app_id.".red unless key.kind_of?Symbol | ||
|
||
option = option_for_key(key) | ||
raise "Could not find option for key :#{key}. Available keys: #{@available_options.collect { |a| a.key }}".red unless option | ||
|
||
# `if value == nil` instead of ||= because false is also a valid value | ||
|
||
value ||= @values[key] | ||
# TODO: configuration files | ||
value = ENV[key.to_s] if value == nil | ||
value = option.default_value if value == nil | ||
value = false if (value == nil and not option.is_string) # by default boolean flags are false | ||
|
||
while value == nil and !option.optional | ||
value = ask("#{option.description}: ") | ||
# Also store this value to use it from now on | ||
begin | ||
set(key, value) | ||
rescue Exception => ex | ||
puts ex | ||
value = nil | ||
end | ||
end | ||
|
||
value | ||
end | ||
|
||
# Overwrites or sets a new value for a given key | ||
def set(key, value) | ||
raise "Key '#{key}' must be a symbol. Example :app_id.".red unless key.kind_of?Symbol | ||
option = option_for_key(key) | ||
|
||
unless option | ||
raise "Could not find available option '#{key}' in the list of !available options #{@available_options.collect { |a| a.key }}".red | ||
end | ||
|
||
option.verify!(value) | ||
|
||
@values[key] = value | ||
true | ||
end | ||
|
||
# Returns the config_item object for a given key | ||
def option_for_key(key) | ||
@available_options.find { |o| o.key == key } | ||
end | ||
|
||
# Aliases `[key]` to `fetch(key)` because Ruby can do it. | ||
alias_method :[], :fetch | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require 'commander' | ||
|
||
module FastlaneCore | ||
class CommanderGenerator | ||
include Commander::Methods | ||
|
||
def generate(options) | ||
options.each do |option| | ||
appendix = (option.is_string ? "STRING" : "") | ||
type = (option.is_string ? String : nil) | ||
short_option = option.short_option || "-#{option.key.to_s[0]}" | ||
global_option short_option, "--#{option.key} #{appendix}", type, (option.description + " (#{option.env_name})") | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
module FastlaneCore | ||
class ConfigItem | ||
attr_accessor :key, :env_name, :description, :short_option, :default_value, :verify_block, :is_string, :optional | ||
|
||
# Creates a new option | ||
# @param key (Symbol) the key which is used as command paramters or key in the fastlane tools | ||
# @param env_name (String) the name of the environment variable, which is only used if no other values were found | ||
# @param description (String) A description shown to the user | ||
# @param short_option (String) A string of length 1 which is used for the command parameters (e.g. -f) | ||
# @param default_value the value which is used if there was no given values and no environment values | ||
# @param verify_block an optional block which is called when a new value is set. | ||
# Check value is valid. This could be type checks or if a folder/file exists | ||
# You have to raise a specific exception if something goes wrong. Append .red after the string | ||
# @param is_string (String) is that parameter a string? | ||
# @param optional (Boolean) is false by default. If set to true, also string values will not be asked to the user | ||
def initialize(key: nil, env_name: nil, description: nil, short_option: nil, default_value: nil, verify_block: nil, is_string: true, optional: false) | ||
raise "key must be a symbol" unless key.kind_of?Symbol | ||
raise "env_name must be a String" unless env_name.kind_of?String | ||
if short_option | ||
raise "short_option must be a String of length 1" unless (short_option.kind_of?String and short_option.gsub('-', '').length == 1) | ||
end | ||
if description | ||
raise "Do not let descriptions end with a '.', since it's used for user inputs as well".red if (description[-1] == '.') | ||
end | ||
|
||
@key = key | ||
@env_name = env_name | ||
@description = description | ||
@short_option = short_option | ||
@default_value = default_value | ||
@verify_block = verify_block | ||
@is_string = is_string | ||
@optional = optional | ||
end | ||
|
||
|
||
# This will raise an exception if the value is not valid | ||
def verify!(value) | ||
raise "Invalid value '#{value}' for option '#{self}'".red unless is_valid?value | ||
true | ||
end | ||
|
||
# Make sure, the value is valid (based on the verify block) | ||
# Returns false if that's not the case | ||
def is_valid?(value) | ||
# we also allow nil values, which do not have to be verified. | ||
if value | ||
if @is_string | ||
raise "Please pass a path as String".red unless value.kind_of?String | ||
end | ||
|
||
if @verify_block | ||
begin | ||
@verify_block.call(value) | ||
rescue Exception => ex | ||
Helper.log.fatal "Error setting value '#{value}' for option '#{@key}'".red | ||
raise ex | ||
end | ||
end | ||
end | ||
|
||
true | ||
end | ||
|
||
def to_s | ||
[@key, @description].join(": ") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
module FastlaneCore | ||
VERSION = "0.2.1" | ||
VERSION = "0.3.0" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
describe FastlaneCore do | ||
describe FastlaneCore::Configuration do | ||
describe "Create a new Configuration Manager" do | ||
|
||
it "raises an error if no hash is given" do | ||
expect { | ||
FastlaneCore::Configuration.create([], "string") | ||
}.to raise_error "values parameter must be a hash".red | ||
end | ||
|
||
it "raises an error if no array is given" do | ||
expect { | ||
FastlaneCore::Configuration.create("string", {}) | ||
}.to raise_error "available_options parameter must be an array of ConfigItems".red | ||
end | ||
|
||
it "raises an error if array contains invalid elements" do | ||
expect { | ||
FastlaneCore::Configuration.create(["string"], {}) | ||
}.to raise_error "available_options parameter must be an array of ConfigItems".red | ||
end | ||
|
||
|
||
it "raises an error if the option of a given value is not available" do | ||
expect { | ||
FastlaneCore::Configuration.create([], {cert_name: "value"}) | ||
}.to raise_error "Could not find available option 'cert_name' in the list of available options []".red | ||
end | ||
|
||
it "raises an error if a description ends with a ." do | ||
expect { | ||
FastlaneCore::Configuration.create([FastlaneCore::ConfigItem.new( | ||
key: :cert_name, | ||
env_name: "asdf", | ||
description: "Set the profile name.")], {}) | ||
}.to raise_error "Do not let descriptions end with a '.', since it's used for user inputs as well".red | ||
end | ||
|
||
it "raises an error if a a key was used twice" do | ||
expect { | ||
FastlaneCore::Configuration.create([FastlaneCore::ConfigItem.new( | ||
key: :cert_name, | ||
env_name: "asdf"), | ||
FastlaneCore::ConfigItem.new( | ||
key: :cert_name, | ||
env_name: "asdf")], {}) | ||
}.to raise_error "Multiple entries for configuration key 'cert_name' found!".red | ||
end | ||
|
||
describe "Use a valid Configuration Manager" do | ||
before do | ||
@options = [ | ||
FastlaneCore::ConfigItem.new(key: :cert_name, | ||
env_name: "SIGH_PROVISIONING_PROFILE_NAME", | ||
description: "Set the profile name", | ||
default_value: "production_default", | ||
verify_block: nil), | ||
FastlaneCore::ConfigItem.new(key: :output, | ||
env_name: "SIGH_OUTPUT_PATH", | ||
description: "Directory in which the profile should be stored", | ||
default_value: ".", | ||
verify_block: Proc.new do |value| | ||
raise "Could not find output directory '#{value}'".red unless File.exists?(value) | ||
end) | ||
] | ||
@values = { | ||
cert_name: "asdf", | ||
output: ".." | ||
} | ||
@config = FastlaneCore::Configuration.create(@options, @values) | ||
end | ||
|
||
describe "fetch" do | ||
it "raises an error if a non symbol was given" do | ||
expect { | ||
@config.fetch(123) | ||
}.to raise_error "Key '123' must be a symbol. Example :app_id.".red | ||
end | ||
|
||
it "raises an error if this option does not exist" do | ||
expect { | ||
@config[:asdfasdf] | ||
}.to raise_error "Could not find option for key :asdfasdf. Available keys: [:cert_name, :output]".red | ||
end | ||
|
||
it "returns the value for the given key if given" do | ||
expect(@config.fetch(:cert_name)).to eq(@values[:cert_name]) | ||
end | ||
|
||
it "returns the value for the given key if given using []" do | ||
expect(@config[:cert_name]).to eq(@values[:cert_name]) | ||
end | ||
|
||
it "returns the default value if nothing else was given" do | ||
@config.set(:cert_name, nil) | ||
expect(@config[:cert_name]).to eq("production_default") | ||
end | ||
end | ||
|
||
describe "verify_block" do | ||
it "throws an error if the key doesn't exist" do | ||
expect { | ||
@config.set(:non_existing, "value") | ||
}.to raise_error("Could not find available option 'non_existing' in the list of !available options [:cert_name, :output]".red) | ||
end | ||
|
||
it "throws an error if it's invalid" do | ||
expect { | ||
@config.set(:output, 132) | ||
}.to raise_error("Please pass a path as String".red) | ||
end | ||
|
||
it "allows valid updates" do | ||
new_val = "../../" | ||
expect(@config.set(:output, new_val)).to eq(true) | ||
expect(@config[:output]).to eq(new_val) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |