arkana-bitrise 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 18b9c3836b57659a724f0c32a2448b21344bb9512fbd434df315a86b740a6129
4
+ data.tar.gz: 102f2dd14df51e0e78079c0485577bb2d881d8b4a5430708c1099a078311b943
5
+ SHA512:
6
+ metadata.gz: 74a62ba2fbecf0cea025590c11e79c754461822644d9a80a420d438b9ae82666ce478854c59ae036f74aeda668280e4429ab2fe16e641039750f7d68bf04e44b
7
+ data.tar.gz: 7fd1ab9b7dce867112b1dd4380ba6ec39c54395c013d1f71558a16840a45403162f52490259472f82506a8ebc331c20c7cee1a61047b3683040e447c972ba096
data/bin/arkana ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "arkana"
5
+ require "arkana/models/arguments"
6
+
7
+ arguments = Arguments.new # Parses ARGV
8
+ Arkana.run(arguments)
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "arkana"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require_relative "models/config"
5
+ require_relative "helpers/ui"
6
+
7
+ # The config parser is responsible for parsing user CLI arguments, reading the config file and returning a `Config` object.
8
+ module ConfigParser
9
+ # Parses the config file defined by the user (if any), returning a Config object.
10
+ #
11
+ # @return [Config] the config parsed from the user's options.
12
+ def self.parse(arguments)
13
+ yaml = YAML.load_file(arguments.config_filepath)
14
+ config = Config.new(yaml)
15
+ config.current_flavor = arguments.flavor
16
+ config.dotenv_filepath = arguments.dotenv_filepath
17
+ UI.warn("Dotenv file was specified but couldn't be found at '#{config.dotenv_filepath}'") if config.dotenv_filepath && !File.exist?(config.dotenv_filepath)
18
+ config
19
+ end
20
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "arkana/helpers/string"
4
+
5
+ # The encoder is responsible for finding the env vars for given keys, encoding them, and creating Secrets based on the generated data.
6
+ module Encoder
7
+ # Fetches values of each key from ENV, and encodes them using the given salt.
8
+ #
9
+ # @return [Secret[]] an array of Secret objects, which contain their keys and encoded values.
10
+ def self.encode!(keys:, salt:, current_flavor:, environments:)
11
+ keys.map do |key|
12
+ secret = find_secret!(key: key, current_flavor: current_flavor)
13
+ encoded_value = encode(secret, salt.raw)
14
+ secret_type = Type.new(string_value: secret)
15
+ protocol_key = protocol_key(key: key, environments: environments)
16
+ Secret.new(key: key, protocol_key: protocol_key, encoded_value: encoded_value, type: secret_type)
17
+ end
18
+ end
19
+
20
+ # Encodes the given string, using the given cipher.
21
+ def self.encode(string, cipher)
22
+ bytes = string.encode("utf-8")
23
+ result = []
24
+ (0...bytes.length).each do |index|
25
+ byte = bytes[index].ord # Convert to its codepoint representation
26
+ result << (byte ^ cipher[index % cipher.length]) # XOR operation with a value of the cipher array.
27
+ end
28
+
29
+ encoded_key = []
30
+ result.each do |element|
31
+ # Warning: this might be specific to Swift implementation. When generating code for other languages, beware.
32
+ encoded_key << format("%#x", element) # Format the binary number to "0xAB" format.
33
+ end
34
+
35
+ encoded_key.join(", ")
36
+ end
37
+
38
+ def self.find_secret!(key:, current_flavor:)
39
+ flavor_key = "#{current_flavor.capitalize_first_letter}#{key}" if current_flavor
40
+ secret = ENV[flavor_key] if flavor_key
41
+ secret ||= ENV[key] || raise("Secret '#{flavor_key || key}' was declared but couldn't be found in the environment variables nor in the specified dotenv file.")
42
+ secret
43
+ end
44
+
45
+ def self.protocol_key(key:, environments:)
46
+ environments.filter_map do |env|
47
+ key.delete_suffix(env) if key.end_with?(env)
48
+ end.first || key
49
+ end
50
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dotenv"
4
+
5
+ # This helper is a mere utility used to facilitate and orchestrate the loading of multiple Dotenv files.
6
+ module DotenvHelper
7
+ # Loads the appropriate dotenv file(s).
8
+ def self.load(config)
9
+ Dotenv.load(config.dotenv_filepath) if config.dotenv_filepath
10
+ # Must be loaded after loading the `config.dotenv_filepath` so they override each other in the right order
11
+ Dotenv.load(flavor_dotenv_filepath(config)) if config.current_flavor
12
+ end
13
+
14
+ def self.flavor_dotenv_filepath(config)
15
+ dotenv_dirname = File.dirname(config.dotenv_filepath)
16
+ flavor_dotenv_filename = ".env.#{config.current_flavor.downcase}"
17
+ File.join(dotenv_dirname, flavor_dotenv_filename)
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # String extensions and utilities.
4
+ class String
5
+ # Returns a string converted from an assumed PascalCase, to camelCase.
6
+ def camel_case
7
+ return self if empty?
8
+
9
+ copy = dup
10
+ copy[0] = copy[0].downcase
11
+ copy
12
+ end
13
+
14
+ # Returns a string with its first character capitalized.
15
+ def capitalize_first_letter
16
+ return self if empty?
17
+
18
+ copy = dup
19
+ copy[0] = copy[0].upcase
20
+ copy
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Utilities to reduce the amount of boilerplate code in `.swift.erb` template files.
4
+ module SwiftTemplateHelper
5
+ def self.swift_type(type)
6
+ case type
7
+ when :string then "String"
8
+ when :boolean then "Bool"
9
+ else raise "Unknown variable type '#{type}' received.'"
10
+ end
11
+ end
12
+
13
+ def self.protocol_getter(declaration_strategy)
14
+ case declaration_strategy
15
+ when "lazy var" then "mutating get"
16
+ when "var", "let" then "get"
17
+ else raise "Unknown declaration strategy '#{declaration_strategy}' received.'"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "colorize"
5
+
6
+ # Contains utilities related to display information to the user on the Terminal (the user's interface).
7
+ module UI
8
+ # Logs the message in red color and raise an error with the given message.
9
+ def self.crash(message)
10
+ logger.fatal(message.red)
11
+ raise message
12
+ end
13
+
14
+ # Logs the message in cyan color.
15
+ def self.debug(message)
16
+ logger.debug(message.cyan)
17
+ end
18
+
19
+ # Logs the message in green color.
20
+ def self.success(message)
21
+ logger.info(message.green)
22
+ end
23
+
24
+ # Logs the message in yellow color.
25
+ def self.warn(message)
26
+ logger.warn(message.yellow)
27
+ end
28
+
29
+ # Logger used to log all the messages.
30
+ def self.logger
31
+ @logger ||= Logger.new($stdout)
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ # Model that parses and documents the CLI options, using `OptionParser`.
6
+ class Arguments
7
+ # @returns [string]
8
+ attr_reader :config_filepath
9
+ # @returns [string]
10
+ attr_reader :dotenv_filepath
11
+ # @returns [string]
12
+ attr_reader :flavor
13
+
14
+ def initialize
15
+ # Default values
16
+ @config_filepath = ".arkana.yml"
17
+ @dotenv_filepath = ".env" if File.exist?(".env")
18
+ @flavor = nil
19
+
20
+ OptionParser.new do |opt|
21
+ opt.on("-c", "--config-filepath /path/to/your/.arkana.yml", "Path to your config file. Defaults to '.arkana.yml'") do |o|
22
+ @config_filepath = o
23
+ end
24
+ opt.on("-e", "--dotenv-filepath /path/to/your/.env", "Path to your dotenv file. Defaults to '.env' if one exists.") do |o|
25
+ @dotenv_filepath = o
26
+ end
27
+ opt.on("-f", "--flavor FrostedFlakes", "Flavors are useful, for instance, when generating secrets for white-label projects. See the README for more information") do |o|
28
+ @flavor = o
29
+ end
30
+ end.parse!
31
+ end
32
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Model used to hold all the configuration set up by the user (both from CLI arguments and from the config file).
4
+ class Config
5
+ # @returns [string[]]
6
+ attr_reader :environments
7
+ # @returns [string[]]
8
+ attr_reader :global_secrets
9
+ # @returns [string[]]
10
+ attr_reader :environment_secrets
11
+ # @returns [string]
12
+ attr_reader :import_name
13
+ # @returns [string]
14
+ attr_reader :namespace
15
+ # @returns [string]
16
+ attr_reader :pod_name
17
+ # @returns [string]
18
+ attr_reader :result_path
19
+ # @returns [string[]]
20
+ attr_reader :flavors
21
+ # @returns [string]
22
+ attr_reader :swift_declaration_strategy
23
+ # @returns [boolean]
24
+ attr_reader :should_generate_unit_tests
25
+ # @returns [string]
26
+ attr_reader :package_manager
27
+
28
+ # @returns [string]
29
+ attr_accessor :current_flavor
30
+ # @returns [string]
31
+ attr_accessor :dotenv_filepath
32
+
33
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
34
+ def initialize(yaml)
35
+ @environments = (yaml["environments"] || []).map(&:capitalize)
36
+ @environment_secrets = yaml["environment_secrets"] || []
37
+ @global_secrets = yaml["global_secrets"] || []
38
+ default_name = "ArkanaKeys"
39
+ @namespace = yaml["namespace"] || default_name
40
+ @import_name = yaml["import_name"] || default_name
41
+ @pod_name = yaml["pod_name"] || default_name
42
+ @result_path = yaml["result_path"] || default_name
43
+ @flavors = yaml["flavors"] || []
44
+ @swift_declaration_strategy = yaml["swift_declaration_strategy"] || "let"
45
+ @should_generate_unit_tests = yaml["should_generate_unit_tests"]
46
+ @should_generate_unit_tests = true if @should_generate_unit_tests.nil?
47
+ @package_manager = yaml["package_manager"] || "spm"
48
+ end
49
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
50
+
51
+ # TODO: Consider renaming this and environment_secrets, cuz they're confusing.
52
+ def environment_keys
53
+ result = environment_secrets.map do |k|
54
+ environments.map { |e| k + e }
55
+ end
56
+ result.flatten
57
+ end
58
+
59
+ def all_keys
60
+ global_secrets + environment_keys
61
+ end
62
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "type"
4
+
5
+ # Model used to hold the metadata for the salt bytes.
6
+ class Salt
7
+ attr_reader :raw
8
+ # Salt, formatted as a string of comma-separated hexadecimal numbers, e.g. "0x94, 0x11, 0x1b, 0x6, 0x49, 0, 0xa7"
9
+ attr_reader :formatted
10
+
11
+ def initialize(raw:)
12
+ @raw = raw
13
+ formatted_salt = []
14
+ raw.each do |element|
15
+ # Warning: this might be specific to Swift implementation. When generating code for other languages, beware.
16
+ formatted_salt << format("%#x", element)
17
+ end
18
+ @formatted = formatted_salt.join(", ")
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "type"
4
+
5
+ # Model used to hold the metadata for a secret, such as its env var key, its protocol key, its type, and encoded value.
6
+ class Secret
7
+ # @returns [string]
8
+ attr_accessor :key
9
+ # The same string as the key, except without the "environment" suffix.
10
+ # Used in the declaration of the protocol that defines the environment secrets.
11
+ # @returns [string]
12
+ attr_accessor :protocol_key
13
+ # Encoded value, formatted as a string of comma-separated hexadecimal numbers, e.g. "0x94, 0x11, 0x1b, 0x6, 0x49, 0, 0xa7"
14
+ # @returns [string]
15
+ attr_reader :encoded_value
16
+ # The type of the variable, as a language-agnostic enum.
17
+ # @returns [Type]
18
+ attr_reader :type
19
+
20
+ def initialize(key:, protocol_key:, encoded_value:, type:)
21
+ @key = key
22
+ @protocol_key = protocol_key
23
+ @encoded_value = encoded_value
24
+ @type = type
25
+ end
26
+
27
+ def environment
28
+ return :global if key == protocol_key
29
+ return key.delete_prefix(protocol_key) if key.start_with?(protocol_key)
30
+
31
+ raise("Precondition failure: the protocol_key '#{protocol_key}' is not the same as the key '#{key}' nor is its prefix. This state shouldn't be possible.")
32
+ end
33
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "arkana/models/secret"
4
+
5
+ # A class that defines template arguments in a language-agnostic form.
6
+ class TemplateArguments
7
+ # Generates template arguments for the given secrets and config specifications.
8
+ def initialize(environment_secrets:, global_secrets:, config:, salt:)
9
+ # The environments declared in the config file.
10
+ @environments = config.environments
11
+ # The salt used to encode all the secrets.
12
+ @salt = salt
13
+ # The encoded environment-specific secrets.
14
+ @environment_secrets = environment_secrets
15
+ # The encoded global secrets.
16
+ @global_secrets = global_secrets
17
+ # Name of the import statements (or the interfaces' prefixes).
18
+ @import_name = config.import_name
19
+ # Name of the pod that should be generated.
20
+ @pod_name = config.pod_name
21
+ # The top level namespace in which the keys will be generated. Often an enum.
22
+ @namespace = config.namespace
23
+ # The property declaration strategy declared in the config file.
24
+ @swift_declaration_strategy = config.swift_declaration_strategy
25
+ # Whether unit tests should be generated.
26
+ @should_generate_unit_tests = config.should_generate_unit_tests
27
+ end
28
+
29
+ def environment_protocol_secrets(environment)
30
+ @environment_secrets.select do |secret|
31
+ secret.environment == environment
32
+ end
33
+ end
34
+
35
+ # Generates a new test secret for a given key, using the salt stored.
36
+ def generate_test_secret(key:)
37
+ # Yes, we encode the key as the value because this is just for testing purposes
38
+ encoded_value = Encoder.encode(key, @salt.raw)
39
+ Secret.new(key: key, protocol_key: key, encoded_value: encoded_value, type: :string)
40
+ end
41
+
42
+ # Expose private `binding` method.
43
+ # rubocop:disable Naming/AccessorMethodName
44
+ def get_binding
45
+ binding
46
+ end
47
+ # rubocop:enable Naming/AccessorMethodName
48
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A namespace that defines language-agnostic variable types.
4
+ module Type
5
+ STRING = :string
6
+ BOOLEAN = :boolean
7
+
8
+ def self.new(string_value:)
9
+ case string_value
10
+ when "true", "false"
11
+ BOOLEAN
12
+ else
13
+ STRING
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require_relative "models/salt"
5
+
6
+ # Responsible for generating the salt.
7
+ module SaltGenerator
8
+ SALT_LENGTH = 64
9
+
10
+ # Generates and returns a new Salt object.
11
+ #
12
+ # @return [Salt] the salt that was just generated.
13
+ def self.generate
14
+ chars = SecureRandom.random_bytes(SALT_LENGTH).chars # Convert array of random bytes to array of characters
15
+ codepoints = chars.map(&:ord) # Convert each character to its codepoint representation
16
+ Salt.new(raw: codepoints)
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "fileutils"
5
+ require_relative "helpers/string"
6
+
7
+ # Responsible for generating Swift source and test files.
8
+ module SwiftCodeGenerator
9
+ # Generates Swift code and test files for the given template arguments.
10
+ def self.generate(template_arguments:, config:)
11
+ interface_swift_package_dir = File.join(config.result_path, "#{config.import_name}Interfaces")
12
+ set_up_interfaces_swift_package(interface_swift_package_dir, template_arguments, config)
13
+
14
+ swift_package_dir = File.join(config.result_path, config.import_name)
15
+ set_up_swift_package(swift_package_dir, template_arguments, config)
16
+ end
17
+
18
+ def self.set_up_interfaces_swift_package(path, template_arguments, config)
19
+ dirname = File.dirname(__FILE__)
20
+ readme_template = File.read("#{dirname}/templates/interfaces_readme.erb")
21
+ package_template = File.read("#{dirname}/templates/interfaces_package.swift.erb")
22
+ podspec_template = File.read("#{dirname}/templates/interfaces.podspec.erb")
23
+ sources_dir = File.join(path, "Sources")
24
+ source_template = File.read("#{dirname}/templates/arkana_protocol.swift.erb")
25
+ FileUtils.mkdir_p(path)
26
+ FileUtils.mkdir_p(sources_dir)
27
+ render(podspec_template, template_arguments, File.join(path, "#{config.pod_name.capitalize_first_letter}Interfaces.podspec")) if config.package_manager == "cocoapods"
28
+ render(readme_template, template_arguments, File.join(path, "README.md"))
29
+ render(package_template, template_arguments, File.join(path, "Package.swift"))
30
+ render(source_template, template_arguments, File.join(sources_dir, "#{config.import_name}Interfaces.swift"))
31
+ end
32
+
33
+ def self.set_up_swift_package(path, template_arguments, config)
34
+ dirname = File.dirname(__FILE__)
35
+ readme_template = File.read("#{dirname}/templates/readme.erb")
36
+ package_template = File.read("#{dirname}/templates/package.swift.erb")
37
+ sources_dir = File.join(path, "Sources")
38
+ source_template = File.read("#{dirname}/templates/arkana.swift.erb")
39
+ tests_dir = File.join(path, "Tests") if config.should_generate_unit_tests
40
+ tests_template = File.read("#{dirname}/templates/arkana_tests.swift.erb")
41
+ podspec_template = File.read("#{dirname}/templates/arkana.podspec.erb")
42
+ FileUtils.mkdir_p(path)
43
+ FileUtils.mkdir_p(sources_dir)
44
+ FileUtils.mkdir_p(tests_dir) if config.should_generate_unit_tests
45
+ render(readme_template, template_arguments, File.join(path, "README.md"))
46
+ render(package_template, template_arguments, File.join(path, "Package.swift"))
47
+ render(podspec_template, template_arguments, File.join(path, "#{config.pod_name.capitalize_first_letter}.podspec")) if config.package_manager == "cocoapods"
48
+ render(source_template, template_arguments, File.join(sources_dir, "#{config.import_name}.swift"))
49
+ render(tests_template, template_arguments, File.join(tests_dir, "#{config.import_name}Tests.swift")) if config.should_generate_unit_tests
50
+ end
51
+
52
+ def self.render(template, template_arguments, destination_file)
53
+ renderer = ERB.new(template, trim_mode: ">") # Don't automatically add newlines at the end of each template tag
54
+ result = renderer.result(template_arguments.get_binding)
55
+ File.write(destination_file, result)
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DO NOT MODIFY
4
+ # Automatically generated by Arkana (https://github.com/rogerluan/arkana)
5
+
6
+ Pod::Spec.new do |spec|
7
+ spec.name = '<%= @pod_name %>'
8
+ spec.summary = 'Contains env vars and secrets. Autogenerated by Arkana.'
9
+ spec.homepage = 'https://github.com/rogerluan/arkana'
10
+ spec.version = '1.0.0'
11
+ spec.authors = { 'Arkana' => 'https://github.com/rogerluan/arkana' }
12
+ spec.source = { path: "./" }
13
+ spec.source_files = 'Sources/**/*.swift'
14
+ spec.ios.deployment_target = '11.0'
15
+ spec.swift_version = '5.0'
16
+
17
+ spec.dependency '<%= @pod_name %>Interfaces', '~> 1.0.0'
18
+ end
@@ -0,0 +1,58 @@
1
+ <% require 'arkana/helpers/string' %>
2
+ <% require 'arkana/helpers/swift_template_helper' %>
3
+ <% # TODO: Sort these import statements alphabetically %>
4
+ // DO NOT MODIFY
5
+ // Automatically generated by Arkana (https://github.com/rogerluan/arkana)
6
+
7
+ import Foundation
8
+ import <%= @import_name %>Interfaces
9
+
10
+ public enum <%= @namespace %> {
11
+ @inline(__always)
12
+ fileprivate static let salt: [UInt8] = [
13
+ <%= @salt.formatted %>
14
+
15
+ ]
16
+
17
+ static func decode(encoded: [UInt8], cipher: [UInt8]) -> String {
18
+ return String(decoding: encoded.enumerated().map { offset, element in
19
+ element ^ cipher[offset % cipher.count]
20
+ }, as: UTF8.self)
21
+ }
22
+ }
23
+
24
+ public extension <%= @namespace %> {
25
+ struct Global: <%= @namespace %>GlobalProtocol {
26
+ public init() {}
27
+ <% for secret in @global_secrets %>
28
+
29
+ @inline(__always)
30
+ public <%= @swift_declaration_strategy %> <%= secret.key.camel_case %>: <%= SwiftTemplateHelper.swift_type(secret.type) %> = {
31
+ let encoded: [UInt8] = [
32
+ <%= secret.encoded_value %>
33
+
34
+ ]
35
+ return <%= @namespace %>.decode(encoded: encoded, cipher: <%= @namespace %>.salt)
36
+ }()
37
+ <% end %>
38
+ }
39
+ }
40
+
41
+ <% for environment in @environments %>
42
+ public extension <%= @namespace %> {
43
+ struct <%= environment %>: <%= @namespace %>EnvironmentProtocol {
44
+ public init() {}
45
+ <% for secret in environment_protocol_secrets(environment) %>
46
+
47
+ @inline(__always)
48
+ public <%= @swift_declaration_strategy %> <%= secret.protocol_key.camel_case %>: <%= SwiftTemplateHelper.swift_type(secret.type) %> = {
49
+ let encoded: [UInt8] = [
50
+ <%= secret.encoded_value %>
51
+
52
+ ]
53
+ return <%= @namespace %>.decode(encoded: encoded, cipher: <%= @namespace %>.salt)
54
+ }()
55
+ <% end %>
56
+ }
57
+ }
58
+ <% end %>
@@ -0,0 +1,18 @@
1
+ <% require 'arkana/helpers/string' %>
2
+ <% require 'arkana/helpers/swift_template_helper' %>
3
+ // DO NOT MODIFY
4
+ // Automatically generated by Arkana (https://github.com/rogerluan/arkana)
5
+
6
+ import Foundation
7
+
8
+ public protocol <%= @namespace %>GlobalProtocol {
9
+ <% for secret in @global_secrets %>
10
+ var <%= secret.protocol_key.camel_case %>: <%= SwiftTemplateHelper.swift_type(secret.type) %> { <%= SwiftTemplateHelper.protocol_getter(@swift_declaration_strategy) %> }
11
+ <% end %>
12
+ }
13
+
14
+ public protocol <%= @namespace %>EnvironmentProtocol {
15
+ <% for secret in @environment_secrets.uniq(&:protocol_key) %>
16
+ var <%= secret.protocol_key.camel_case %>: <%= SwiftTemplateHelper.swift_type(secret.type) %> { <%= SwiftTemplateHelper.protocol_getter(@swift_declaration_strategy) %> }
17
+ <% end %>
18
+ }
@@ -0,0 +1,44 @@
1
+ // DO NOT MODIFY
2
+ // Automatically generated by Arkana (https://github.com/rogerluan/arkana)
3
+
4
+ import Foundation
5
+ import XCTest
6
+ @testable import <%= @import_name %>
7
+
8
+
9
+ final class <%= @namespace %>Tests: XCTestCase {
10
+ private var salt: [UInt8] = [
11
+ <%= @salt.formatted %>
12
+
13
+ ]
14
+
15
+ func test_decodeRandomHexKey_shouldDecode() {
16
+ <% hex_key = SecureRandom.hex(64) %>
17
+ <% secret = generate_test_secret(key: hex_key) %>
18
+ let encoded: [UInt8] = [
19
+ <%= secret.encoded_value %>
20
+
21
+ ]
22
+ XCTAssertEqual(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "<%= hex_key %>")
23
+ }
24
+
25
+ func test_decodeRandomBase64Key_shouldDecode() {
26
+ <% base64_key = SecureRandom.base64(64) %>
27
+ <% secret = generate_test_secret(key: base64_key) %>
28
+ let encoded: [UInt8] = [
29
+ <%= secret.encoded_value %>
30
+
31
+ ]
32
+ XCTAssertEqual(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "<%= base64_key %>")
33
+ }
34
+
35
+ func test_decodeUUIDKey_shouldDecode() {
36
+ <% uuid_key = SecureRandom.uuid %>
37
+ <% secret = generate_test_secret(key: uuid_key) %>
38
+ let encoded: [UInt8] = [
39
+ <%= secret.encoded_value %>
40
+
41
+ ]
42
+ XCTAssertEqual(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "<%= uuid_key %>")
43
+ }
44
+ }
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DO NOT MODIFY
4
+ # Automatically generated by Arkana (https://github.com/rogerluan/arkana)
5
+
6
+ Pod::Spec.new do |spec|
7
+ spec.name = '<%= @pod_name %>Interfaces'
8
+ spec.summary = 'Contains env vars and secrets. Autogenerated by Arkana.'
9
+ spec.homepage = 'https://github.com/rogerluan/arkana'
10
+ spec.version = '1.0.0'
11
+ spec.authors = { 'Arkana' => 'https://github.com/rogerluan/arkana' }
12
+ spec.source = { path: "./" }
13
+ spec.source_files = 'Sources/**/*.swift'
14
+ spec.ios.deployment_target = '11.0'
15
+ spec.swift_version = '5.0'
16
+ end
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.6
2
+
3
+ // DO NOT MODIFY
4
+ // Automatically generated by Arkana (https://github.com/rogerluan/arkana)
5
+
6
+ import PackageDescription
7
+
8
+ let package = Package(
9
+ name: "<%= @import_name %>Interfaces",
10
+ platforms: [
11
+ .macOS(.v11),
12
+ .iOS(.v11),
13
+ ],
14
+ products: [
15
+ .library(
16
+ name: "<%= @import_name %>Interfaces",
17
+ targets: ["<%= @import_name %>Interfaces"]
18
+ ),
19
+ ],
20
+ dependencies: [],
21
+ targets: [
22
+ .target(
23
+ name: "<%= @import_name %>Interfaces",
24
+ dependencies: [],
25
+ path: "Sources"
26
+ ),
27
+ ]
28
+ )
@@ -0,0 +1,3 @@
1
+ # <%= @namespace %>Interfaces
2
+
3
+ This Package was autogenerated by [Arkana](https://github.com/rogerluan/arkana). Do not attempt to modify it manually, otherwise your changes will be overriden in the next code generation process. Please visit [Arkana](https://github.com/rogerluan/arkana) to read more.
@@ -0,0 +1,37 @@
1
+ // swift-tools-version: 5.6
2
+
3
+ // DO NOT MODIFY
4
+ // Automatically generated by Arkana (https://github.com/rogerluan/arkana)
5
+
6
+ import PackageDescription
7
+
8
+ let package = Package(
9
+ name: "<%= @import_name %>",
10
+ platforms: [
11
+ .macOS(.v11),
12
+ .iOS(.v11),
13
+ ],
14
+ products: [
15
+ .library(
16
+ name: "<%= @import_name %>",
17
+ targets: ["<%= @import_name %>"]
18
+ ),
19
+ ],
20
+ dependencies: [
21
+ .package(name: "<%= @import_name%>Interfaces", path: "../<%= @import_name%>Interfaces"),
22
+ ],
23
+ targets: [
24
+ .target(
25
+ name: "<%= @import_name %>",
26
+ dependencies: ["<%= @import_name %>Interfaces"],
27
+ path: "Sources"
28
+ ),
29
+ <% if @should_generate_unit_tests %>
30
+ .testTarget(
31
+ name: "<%= @import_name %>Tests",
32
+ dependencies: ["<%= @import_name %>"],
33
+ path: "Tests"
34
+ ),
35
+ <% end %>
36
+ ]
37
+ )
@@ -0,0 +1,4 @@
1
+ # <%= @namespace %>
2
+
3
+
4
+ This Package is autogenerated by [Arkana](https://github.com/rogerluan/arkana). Do not attempt to modify it manually, otherwise your changes will be overriden on the next code generation process. Please visit [Arkana](https://github.com/rogerluan/arkana) to read more.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arkana
4
+ VERSION = "1.1.1"
5
+ end
data/lib/arkana.rb ADDED
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "arkana/config_parser"
4
+ require_relative "arkana/encoder"
5
+ require_relative "arkana/helpers/dotenv_helper"
6
+ require_relative "arkana/models/template_arguments"
7
+ require_relative "arkana/salt_generator"
8
+ require_relative "arkana/swift_code_generator"
9
+ require_relative "arkana/version"
10
+
11
+ # Top-level namespace for Arkana's execution entry point. When ran from CLI, `Arkana.run` is what is invoked.
12
+ module Arkana
13
+ def self.run(arguments)
14
+ config = ConfigParser.parse(arguments)
15
+ salt = SaltGenerator.generate
16
+ DotenvHelper.load(config)
17
+
18
+ begin
19
+ environment_secrets = Encoder.encode!(
20
+ keys: config.environment_keys,
21
+ salt: salt,
22
+ current_flavor: config.current_flavor,
23
+ environments: config.environments,
24
+ )
25
+ global_secrets = Encoder.encode!(
26
+ keys: config.global_secrets,
27
+ salt: salt,
28
+ current_flavor: config.current_flavor,
29
+ environments: config.environments,
30
+ )
31
+ rescue StandardError => e
32
+ # TODO: Improve this by creating an Env/Debug helper
33
+ puts("Something went wrong when parsing and encoding your secrets.")
34
+ puts("Current Flavor: #{config.current_flavor}")
35
+ puts("Dotenv Filepath: #{config.dotenv_filepath}")
36
+ puts("Config Filepath: #{arguments.config_filepath}")
37
+ raise e
38
+ end
39
+ template_arguments = TemplateArguments.new(
40
+ environment_secrets: environment_secrets,
41
+ global_secrets: global_secrets,
42
+ config: config,
43
+ salt: salt,
44
+ )
45
+ SwiftCodeGenerator.generate(
46
+ template_arguments: template_arguments,
47
+ config: config,
48
+ )
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: arkana-bitrise
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Erfan Andesta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-08-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yaml
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ description:
56
+ email:
57
+ - andesta.erfan@gmail.com
58
+ executables:
59
+ - arkana
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - bin/arkana
64
+ - bin/console
65
+ - bin/setup
66
+ - lib/arkana.rb
67
+ - lib/arkana/config_parser.rb
68
+ - lib/arkana/encoder.rb
69
+ - lib/arkana/helpers/dotenv_helper.rb
70
+ - lib/arkana/helpers/string.rb
71
+ - lib/arkana/helpers/swift_template_helper.rb
72
+ - lib/arkana/helpers/ui.rb
73
+ - lib/arkana/models/arguments.rb
74
+ - lib/arkana/models/config.rb
75
+ - lib/arkana/models/salt.rb
76
+ - lib/arkana/models/secret.rb
77
+ - lib/arkana/models/template_arguments.rb
78
+ - lib/arkana/models/type.rb
79
+ - lib/arkana/salt_generator.rb
80
+ - lib/arkana/swift_code_generator.rb
81
+ - lib/arkana/templates/arkana.podspec.erb
82
+ - lib/arkana/templates/arkana.swift.erb
83
+ - lib/arkana/templates/arkana_protocol.swift.erb
84
+ - lib/arkana/templates/arkana_tests.swift.erb
85
+ - lib/arkana/templates/interfaces.podspec.erb
86
+ - lib/arkana/templates/interfaces_package.swift.erb
87
+ - lib/arkana/templates/interfaces_readme.erb
88
+ - lib/arkana/templates/package.swift.erb
89
+ - lib/arkana/templates/readme.erb
90
+ - lib/arkana/version.rb
91
+ homepage: https://github.com/rogerluan/arkana
92
+ licenses:
93
+ - BSD-2-Clause
94
+ metadata:
95
+ homepage_uri: https://github.com/rogerluan/arkana
96
+ source_code_uri: https://github.com/rogerluan/arkana
97
+ changelog_uri: https://github.com/rogerluan/arkana/blob/main/CHANGELOG.md
98
+ rubygems_mfa_required: 'true'
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 2.6.9
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.1.6
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Store your keys and secrets away from your source code. Designed for Android
118
+ and iOS projects.
119
+ test_files: []