pantograph 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +197 -0
  5. data/bin/bin-proxy +19 -0
  6. data/bin/pantograph +23 -0
  7. data/pantograph/README.md +11 -0
  8. data/pantograph/lib/assets/ActionDetails.md.erb +106 -0
  9. data/pantograph/lib/assets/Actions.md.erb +43 -0
  10. data/pantograph/lib/assets/DefaultPantfileTemplate +20 -0
  11. data/pantograph/lib/assets/completions/completion.bash +23 -0
  12. data/pantograph/lib/assets/completions/completion.fish +39 -0
  13. data/pantograph/lib/assets/completions/completion.sh +12 -0
  14. data/pantograph/lib/assets/completions/completion.zsh +23 -0
  15. data/pantograph/lib/assets/custom_action_template.rb +80 -0
  16. data/pantograph/lib/assets/report_template.xml.erb +15 -0
  17. data/pantograph/lib/pantograph/action.rb +194 -0
  18. data/pantograph/lib/pantograph/action_collector.rb +35 -0
  19. data/pantograph/lib/pantograph/actions/README.md +3 -0
  20. data/pantograph/lib/pantograph/actions/actions_helper.rb +166 -0
  21. data/pantograph/lib/pantograph/actions/add_extra_platforms.rb +45 -0
  22. data/pantograph/lib/pantograph/actions/artifactory.rb +157 -0
  23. data/pantograph/lib/pantograph/actions/bundle_install.rb +156 -0
  24. data/pantograph/lib/pantograph/actions/changelog_from_git_commits.rb +197 -0
  25. data/pantograph/lib/pantograph/actions/clipboard.rb +52 -0
  26. data/pantograph/lib/pantograph/actions/cloc.rb +89 -0
  27. data/pantograph/lib/pantograph/actions/create_pull_request.rb +190 -0
  28. data/pantograph/lib/pantograph/actions/danger.rb +131 -0
  29. data/pantograph/lib/pantograph/actions/debug.rb +32 -0
  30. data/pantograph/lib/pantograph/actions/default_platform.rb +47 -0
  31. data/pantograph/lib/pantograph/actions/download.rb +76 -0
  32. data/pantograph/lib/pantograph/actions/echo.rb +14 -0
  33. data/pantograph/lib/pantograph/actions/ensure_bundle_exec.rb +59 -0
  34. data/pantograph/lib/pantograph/actions/ensure_env_vars.rb +58 -0
  35. data/pantograph/lib/pantograph/actions/ensure_git_branch.rb +69 -0
  36. data/pantograph/lib/pantograph/actions/ensure_git_status_clean.rb +81 -0
  37. data/pantograph/lib/pantograph/actions/erb.rb +88 -0
  38. data/pantograph/lib/pantograph/actions/get_build_number_repository.rb +120 -0
  39. data/pantograph/lib/pantograph/actions/get_github_release.rb +163 -0
  40. data/pantograph/lib/pantograph/actions/git_add.rb +93 -0
  41. data/pantograph/lib/pantograph/actions/git_branch.rb +58 -0
  42. data/pantograph/lib/pantograph/actions/git_commit.rb +80 -0
  43. data/pantograph/lib/pantograph/actions/git_pull.rb +53 -0
  44. data/pantograph/lib/pantograph/actions/git_submodule_update.rb +52 -0
  45. data/pantograph/lib/pantograph/actions/git_tag_exists.rb +74 -0
  46. data/pantograph/lib/pantograph/actions/github_api.rb +262 -0
  47. data/pantograph/lib/pantograph/actions/gradle.rb +278 -0
  48. data/pantograph/lib/pantograph/actions/import.rb +49 -0
  49. data/pantograph/lib/pantograph/actions/import_from_git.rb +71 -0
  50. data/pantograph/lib/pantograph/actions/is_ci.rb +51 -0
  51. data/pantograph/lib/pantograph/actions/jira.rb +115 -0
  52. data/pantograph/lib/pantograph/actions/lane_context.rb +60 -0
  53. data/pantograph/lib/pantograph/actions/last_git_commit.rb +58 -0
  54. data/pantograph/lib/pantograph/actions/last_git_tag.rb +51 -0
  55. data/pantograph/lib/pantograph/actions/make_changelog_from_jenkins.rb +81 -0
  56. data/pantograph/lib/pantograph/actions/min_pantograph_version.rb +57 -0
  57. data/pantograph/lib/pantograph/actions/nexus_upload.rb +230 -0
  58. data/pantograph/lib/pantograph/actions/notification.rb +75 -0
  59. data/pantograph/lib/pantograph/actions/number_of_commits.rb +75 -0
  60. data/pantograph/lib/pantograph/actions/opt_out_usage.rb +40 -0
  61. data/pantograph/lib/pantograph/actions/pantograph_version.rb +15 -0
  62. data/pantograph/lib/pantograph/actions/println.rb +14 -0
  63. data/pantograph/lib/pantograph/actions/prompt.rb +119 -0
  64. data/pantograph/lib/pantograph/actions/push_git_tags.rb +76 -0
  65. data/pantograph/lib/pantograph/actions/push_to_git_remote.rb +127 -0
  66. data/pantograph/lib/pantograph/actions/puts.rb +68 -0
  67. data/pantograph/lib/pantograph/actions/reset_git_repo.rb +121 -0
  68. data/pantograph/lib/pantograph/actions/rocket.rb +83 -0
  69. data/pantograph/lib/pantograph/actions/rsync.rb +74 -0
  70. data/pantograph/lib/pantograph/actions/ruby_version.rb +56 -0
  71. data/pantograph/lib/pantograph/actions/say.rb +56 -0
  72. data/pantograph/lib/pantograph/actions/scp.rb +114 -0
  73. data/pantograph/lib/pantograph/actions/set_github_release.rb +274 -0
  74. data/pantograph/lib/pantograph/actions/sh.rb +71 -0
  75. data/pantograph/lib/pantograph/actions/skip_docs.rb +52 -0
  76. data/pantograph/lib/pantograph/actions/slack.rb +288 -0
  77. data/pantograph/lib/pantograph/actions/sonar.rb +156 -0
  78. data/pantograph/lib/pantograph/actions/ssh.rb +162 -0
  79. data/pantograph/lib/pantograph/actions/twitter.rb +89 -0
  80. data/pantograph/lib/pantograph/actions/update_pantograph.rb +177 -0
  81. data/pantograph/lib/pantograph/actions/zip.rb +120 -0
  82. data/pantograph/lib/pantograph/auto_complete.rb +82 -0
  83. data/pantograph/lib/pantograph/boolean.rb +5 -0
  84. data/pantograph/lib/pantograph/cli_tools_distributor.rb +183 -0
  85. data/pantograph/lib/pantograph/command_line_handler.rb +43 -0
  86. data/pantograph/lib/pantograph/commands_generator.rb +344 -0
  87. data/pantograph/lib/pantograph/configuration_helper.rb +26 -0
  88. data/pantograph/lib/pantograph/core_ext/bundler_monkey_patch.rb +14 -0
  89. data/pantograph/lib/pantograph/documentation/actions_list.rb +214 -0
  90. data/pantograph/lib/pantograph/documentation/docs_generator.rb +95 -0
  91. data/pantograph/lib/pantograph/documentation/markdown_docs_generator.rb +221 -0
  92. data/pantograph/lib/pantograph/environment_printer.rb +282 -0
  93. data/pantograph/lib/pantograph/erb_template_helper.rb +30 -0
  94. data/pantograph/lib/pantograph/features.rb +4 -0
  95. data/pantograph/lib/pantograph/helper/README.md +29 -0
  96. data/pantograph/lib/pantograph/helper/dotenv_helper.rb +50 -0
  97. data/pantograph/lib/pantograph/helper/gem_helper.rb +26 -0
  98. data/pantograph/lib/pantograph/helper/git_helper.rb +135 -0
  99. data/pantograph/lib/pantograph/helper/gradle_helper.rb +62 -0
  100. data/pantograph/lib/pantograph/helper/sh_helper.rb +134 -0
  101. data/pantograph/lib/pantograph/junit_generator.rb +27 -0
  102. data/pantograph/lib/pantograph/lane.rb +97 -0
  103. data/pantograph/lib/pantograph/lane_list.rb +77 -0
  104. data/pantograph/lib/pantograph/lane_manager.rb +140 -0
  105. data/pantograph/lib/pantograph/lane_manager_base.rb +92 -0
  106. data/pantograph/lib/pantograph/markdown_table_formatter.rb +62 -0
  107. data/pantograph/lib/pantograph/new_action.rb +47 -0
  108. data/pantograph/lib/pantograph/one_off.rb +45 -0
  109. data/pantograph/lib/pantograph/other_action.rb +29 -0
  110. data/pantograph/lib/pantograph/pant_file.rb +377 -0
  111. data/pantograph/lib/pantograph/pantograph_require.rb +75 -0
  112. data/pantograph/lib/pantograph/plugins/plugin_fetcher.rb +55 -0
  113. data/pantograph/lib/pantograph/plugins/plugin_generator.rb +86 -0
  114. data/pantograph/lib/pantograph/plugins/plugin_generator_ui.rb +19 -0
  115. data/pantograph/lib/pantograph/plugins/plugin_info.rb +49 -0
  116. data/pantograph/lib/pantograph/plugins/plugin_info_collector.rb +159 -0
  117. data/pantograph/lib/pantograph/plugins/plugin_manager.rb +387 -0
  118. data/pantograph/lib/pantograph/plugins/plugin_search.rb +46 -0
  119. data/pantograph/lib/pantograph/plugins/plugin_update_manager.rb +70 -0
  120. data/pantograph/lib/pantograph/plugins/plugins.rb +12 -0
  121. data/pantograph/lib/pantograph/plugins/template/%gem_name%.gemspec.erb +35 -0
  122. data/pantograph/lib/pantograph/plugins/template/.circleci/config.yml +43 -0
  123. data/pantograph/lib/pantograph/plugins/template/.gitignore +12 -0
  124. data/pantograph/lib/pantograph/plugins/template/.rspec +5 -0
  125. data/pantograph/lib/pantograph/plugins/template/.rubocop.yml +179 -0
  126. data/pantograph/lib/pantograph/plugins/template/.travis.yml +4 -0
  127. data/pantograph/lib/pantograph/plugins/template/Gemfile +6 -0
  128. data/pantograph/lib/pantograph/plugins/template/LICENSE.erb +21 -0
  129. data/pantograph/lib/pantograph/plugins/template/README.md.erb +52 -0
  130. data/pantograph/lib/pantograph/plugins/template/Rakefile +9 -0
  131. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%/actions/%plugin_name%_action.rb.erb +47 -0
  132. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%/helper/%plugin_name%_helper.rb.erb +16 -0
  133. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%/version.rb.erb +5 -0
  134. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%.rb.erb +16 -0
  135. data/pantograph/lib/pantograph/plugins/template/pantograph/Pantfile.erb +3 -0
  136. data/pantograph/lib/pantograph/plugins/template/pantograph/Pluginfile.erb +1 -0
  137. data/pantograph/lib/pantograph/plugins/template/spec/%plugin_name%_action_spec.rb.erb +9 -0
  138. data/pantograph/lib/pantograph/plugins/template/spec/spec_helper.rb.erb +15 -0
  139. data/pantograph/lib/pantograph/runner.rb +371 -0
  140. data/pantograph/lib/pantograph/server/action_command.rb +61 -0
  141. data/pantograph/lib/pantograph/server/action_command_return.rb +14 -0
  142. data/pantograph/lib/pantograph/server/command_executor.rb +7 -0
  143. data/pantograph/lib/pantograph/server/command_parser.rb +36 -0
  144. data/pantograph/lib/pantograph/server/control_command.rb +23 -0
  145. data/pantograph/lib/pantograph/server/json_return_value_processor.rb +72 -0
  146. data/pantograph/lib/pantograph/server/socket_server.rb +232 -0
  147. data/pantograph/lib/pantograph/server/socket_server_action_command_executor.rb +101 -0
  148. data/pantograph/lib/pantograph/setup/setup.rb +290 -0
  149. data/pantograph/lib/pantograph/setup/setup_android.rb +64 -0
  150. data/pantograph/lib/pantograph/setup/setup_ios.rb +412 -0
  151. data/pantograph/lib/pantograph/shells.rb +6 -0
  152. data/pantograph/lib/pantograph/supported_platforms.rb +28 -0
  153. data/pantograph/lib/pantograph/tools.rb +10 -0
  154. data/pantograph/lib/pantograph/version.rb +5 -0
  155. data/pantograph/lib/pantograph.rb +51 -0
  156. data/pantograph_core/README.md +79 -0
  157. data/pantograph_core/lib/assets/XMLTemplate.xml.erb +12 -0
  158. data/pantograph_core/lib/pantograph_core/analytics/action_completion_context.rb +34 -0
  159. data/pantograph_core/lib/pantograph_core/analytics/action_launch_context.rb +38 -0
  160. data/pantograph_core/lib/pantograph_core/analytics/analytics_event_builder.rb +23 -0
  161. data/pantograph_core/lib/pantograph_core/analytics/analytics_ingester_client.rb +54 -0
  162. data/pantograph_core/lib/pantograph_core/analytics/analytics_session.rb +71 -0
  163. data/pantograph_core/lib/pantograph_core/cert_checker.rb +116 -0
  164. data/pantograph_core/lib/pantograph_core/command_executor.rb +99 -0
  165. data/pantograph_core/lib/pantograph_core/configuration/commander_generator.rb +103 -0
  166. data/pantograph_core/lib/pantograph_core/configuration/config_item.rb +314 -0
  167. data/pantograph_core/lib/pantograph_core/configuration/configuration.rb +332 -0
  168. data/pantograph_core/lib/pantograph_core/configuration/configuration_file.rb +182 -0
  169. data/pantograph_core/lib/pantograph_core/core_ext/shellwords.rb +63 -0
  170. data/pantograph_core/lib/pantograph_core/core_ext/string.rb +17 -0
  171. data/pantograph_core/lib/pantograph_core/env.rb +9 -0
  172. data/pantograph_core/lib/pantograph_core/feature/feature.rb +51 -0
  173. data/pantograph_core/lib/pantograph_core/features.rb +4 -0
  174. data/pantograph_core/lib/pantograph_core/globals.rb +27 -0
  175. data/pantograph_core/lib/pantograph_core/helper.rb +409 -0
  176. data/pantograph_core/lib/pantograph_core/keychain_importer.rb +74 -0
  177. data/pantograph_core/lib/pantograph_core/languages.rb +14 -0
  178. data/pantograph_core/lib/pantograph_core/module.rb +29 -0
  179. data/pantograph_core/lib/pantograph_core/pantograph_folder.rb +39 -0
  180. data/pantograph_core/lib/pantograph_core/pantograph_pty.rb +57 -0
  181. data/pantograph_core/lib/pantograph_core/pkg_file_analyser.rb +44 -0
  182. data/pantograph_core/lib/pantograph_core/print_table.rb +131 -0
  183. data/pantograph_core/lib/pantograph_core/string_filters.rb +51 -0
  184. data/pantograph_core/lib/pantograph_core/swag.rb +85 -0
  185. data/pantograph_core/lib/pantograph_core/tag_version.rb +31 -0
  186. data/pantograph_core/lib/pantograph_core/test_parser.rb +107 -0
  187. data/pantograph_core/lib/pantograph_core/ui/disable_colors.rb +17 -0
  188. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_common_error.rb +19 -0
  189. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_crash.rb +11 -0
  190. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_error.rb +25 -0
  191. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_exception.rb +19 -0
  192. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_shell_error.rb +11 -0
  193. data/pantograph_core/lib/pantograph_core/ui/errors.rb +1 -0
  194. data/pantograph_core/lib/pantograph_core/ui/github_issue_inspector_reporter.rb +62 -0
  195. data/pantograph_core/lib/pantograph_core/ui/implementations/shell.rb +159 -0
  196. data/pantograph_core/lib/pantograph_core/ui/interface.rb +205 -0
  197. data/pantograph_core/lib/pantograph_core/ui/pantograph_runner.rb +276 -0
  198. data/pantograph_core/lib/pantograph_core/ui/ui.rb +26 -0
  199. data/pantograph_core/lib/pantograph_core/update_checker/changelog.rb +37 -0
  200. data/pantograph_core/lib/pantograph_core/update_checker/update_checker.rb +107 -0
  201. data/pantograph_core/lib/pantograph_core.rb +45 -0
  202. metadata +987 -0
@@ -0,0 +1,314 @@
1
+ require_relative '../ui/ui'
2
+ require_relative '../ui/errors/pantograph_error'
3
+ require_relative '../helper'
4
+ require_relative '../module'
5
+
6
+ module PantographCore
7
+ class ConfigItem
8
+ # [Symbol] the key which is used as command parameters or key in the pantograph tools
9
+ attr_accessor :key
10
+
11
+ # [String] the name of the environment variable, which is only used if no other values were found
12
+ attr_accessor :env_name
13
+
14
+ # [String] A description shown to the user
15
+ attr_accessor :description
16
+
17
+ # [String] A string of length 1 which is used for the command parameters (e.g. -f)
18
+ attr_accessor :short_option
19
+
20
+ # the value which is used if there was no given values and no environment values
21
+ attr_accessor :default_value
22
+
23
+ # [Boolean] Set if the default value is generated dynamically
24
+ attr_accessor :default_value_dynamic
25
+
26
+ # the value which is used during Swift code generation
27
+ # if the default_value reads from ENV or a file, or from local credentials, we need
28
+ # to provide a different default or it might be included in our autogenerated Swift
29
+ # as a built-in default for the pantograph gem. This is because when we generate the
30
+ # Swift API at deployment time, it fetches the default_value from the config_items
31
+ attr_accessor :code_gen_default_value
32
+
33
+ # An optional block which is called when a new value is set.
34
+ # Check value is valid. This could be type checks or if a folder/file exists
35
+ # You have to raise a specific exception if something goes wrong. Use `user_error!` for the message: UI.user_error!("your message")
36
+ attr_accessor :verify_block
37
+
38
+ # [Boolean] is false by default. If set to true, also string values will not be asked to the user
39
+ attr_accessor :optional
40
+
41
+ # [Boolean] is false by default. If set to true, type of the parameter will not be validated.
42
+ attr_accessor :skip_type_validation
43
+
44
+ # [Array] array of conflicting option keys(@param key). This allows to resolve conflicts intelligently
45
+ attr_accessor :conflicting_options
46
+
47
+ # An optional block which is called when options conflict happens
48
+ attr_accessor :conflict_block
49
+
50
+ # [String] Set if the option is deprecated. A deprecated option should be optional and is made optional if the parameter isn't set, and fails otherwise
51
+ attr_accessor :deprecated
52
+
53
+ # [Boolean] Set if the variable is sensitive, such as a password or API token, to prevent echoing when prompted for the parameter
54
+ # If a default value exists, it won't be used during code generation as default values can read from environment variables.
55
+ attr_accessor :sensitive
56
+
57
+ # [Boolean] Set if the default value should never be used during code generation for Swift
58
+ # We generate the Swift API at deployment time, and if there is a value that should never be
59
+ # included in the Pantograph.swift or other autogenerated classes, we need to strip it out.
60
+ # This includes things like API keys that could be read from ENV[]
61
+ attr_accessor :code_gen_sensitive
62
+
63
+ # [Boolean] Set if the variable is to be converted to a shell-escaped String when provided as a Hash or Array
64
+ # Allows items expected to be strings used in shell arguments to be alternatively provided as a Hash or Array for better readability and auto-escaped for us.
65
+ attr_accessor :allow_shell_conversion
66
+
67
+ # [Boolean] Set if the variable can be used from shell
68
+ attr_accessor :display_in_shell
69
+
70
+ # Creates a new option
71
+ # @param key (Symbol) the key which is used as command parameters or key in the pantograph tools
72
+ # @param env_name (String) the name of the environment variable, which is only used if no other values were found
73
+ # @param description (String) A description shown to the user
74
+ # @param short_option (String) A string of length 1 which is used for the command parameters (e.g. -f)
75
+ # @param default_value the value which is used if there was no given values and no environment values
76
+ # @param default_value_dynamic (Boolean) Set if the default value is generated dynamically
77
+ # @param verify_block an optional block which is called when a new value is set.
78
+ # Check value is valid. This could be type checks or if a folder/file exists
79
+ # You have to raise a specific exception if something goes wrong. Append .red after the string
80
+ # @param is_string *DEPRECATED: Use `type` instead* (Boolean) is that parameter a string? Defaults to true. If it's true, the type string will be verified.
81
+ # @param type (Class) the data type of this config item. Takes precedence over `is_string`. Use `:shell_string` to allow types `String`, `Hash` and `Array` that will be converted to shell-escaped strings
82
+ # @param skip_type_validation (Boolean) is false by default. If set to true, type of the parameter will not be validated.
83
+ # @param optional (Boolean) is false by default. If set to true, also string values will not be asked to the user
84
+ # @param conflicting_options ([]) array of conflicting option keys(@param key). This allows to resolve conflicts intelligently
85
+ # @param conflict_block an optional block which is called when options conflict happens
86
+ # @param deprecated (Boolean|String) Set if the option is deprecated. A deprecated option should be optional and is made optional if the parameter isn't set, and fails otherwise
87
+ # @param sensitive (Boolean) Set if the variable is sensitive, such as a password or API token, to prevent echoing when prompted for the parameter
88
+ # @param display_in_shell (Boolean) Set if the variable can be used from shell
89
+ # rubocop:disable Metrics/ParameterLists
90
+ def initialize(key: nil,
91
+ env_name: nil,
92
+ description: nil,
93
+ short_option: nil,
94
+ default_value: nil,
95
+ default_value_dynamic: false,
96
+ verify_block: nil,
97
+ is_string: true,
98
+ type: nil,
99
+ skip_type_validation: false,
100
+ optional: nil,
101
+ conflicting_options: nil,
102
+ conflict_block: nil,
103
+ deprecated: nil,
104
+ sensitive: nil,
105
+ code_gen_sensitive: false,
106
+ code_gen_default_value: nil,
107
+ display_in_shell: true)
108
+ UI.user_error!("key must be a symbol") unless key.kind_of?(Symbol)
109
+ UI.user_error!("env_name must be a String") unless (env_name || '').kind_of?(String)
110
+
111
+ if short_option
112
+ UI.user_error!("short_option for key :#{key} must of type String") unless short_option.kind_of?(String)
113
+ UI.user_error!("short_option for key :#{key} must be a string of length 1") unless short_option.delete('-').length == 1
114
+ end
115
+
116
+ if description
117
+ UI.user_error!("Do not let descriptions end with a '.', since it's used for user inputs as well for key :#{key}") if description[-1] == '.'
118
+ end
119
+
120
+ if conflicting_options
121
+ conflicting_options.each do |conflicting_option_key|
122
+ UI.user_error!("Conflicting option key must be a symbol") unless conflicting_option_key.kind_of?(Symbol)
123
+ end
124
+ end
125
+
126
+ if deprecated
127
+ # deprecated options are automatically optional
128
+ optional = true if optional.nil?
129
+ UI.crash!("Deprecated option must be optional") unless optional
130
+
131
+ # deprecated options are marked deprecated in their description
132
+ description = deprecated_description(description, deprecated)
133
+ end
134
+
135
+ optional = false if optional.nil?
136
+ sensitive = false if sensitive.nil?
137
+
138
+ @key = key
139
+ @env_name = env_name
140
+ @description = description
141
+ @short_option = short_option
142
+ @default_value = default_value
143
+ @default_value_dynamic = default_value_dynamic
144
+ @verify_block = verify_block
145
+ @is_string = is_string
146
+ @data_type = type
147
+ @data_type = String if type == :shell_string
148
+ @optional = optional
149
+ @conflicting_options = conflicting_options
150
+ @conflict_block = conflict_block
151
+ @deprecated = deprecated
152
+ @sensitive = sensitive
153
+ @code_gen_sensitive = code_gen_sensitive || sensitive
154
+ @allow_shell_conversion = (type == :shell_string)
155
+ @display_in_shell = display_in_shell
156
+ @skip_type_validation = skip_type_validation # sometimes we allow multiple types which causes type validation failures, e.g.: export_options in gym
157
+
158
+ @code_gen_default_value = code_gen_default_value
159
+
160
+ update_code_gen_default_value_if_able!
161
+ end
162
+ # rubocop:enable Metrics/ParameterLists
163
+
164
+ # if code_gen_default_value is nil, use the default value if it isn't a `code_gen_sensitive` value
165
+ def update_code_gen_default_value_if_able!
166
+ # we don't support default values for procs
167
+ if @data_type == :string_callback
168
+ @code_gen_default_value = nil
169
+ return
170
+ end
171
+
172
+ if @code_gen_default_value.nil?
173
+ unless @code_gen_sensitive
174
+
175
+ @code_gen_default_value = @default_value
176
+ end
177
+ end
178
+ end
179
+
180
+ def verify!(value)
181
+ valid?(value)
182
+ end
183
+
184
+ def ensure_generic_type_passes_validation(value)
185
+ if @skip_type_validation
186
+ return
187
+ end
188
+
189
+ if data_type != :string_callback && data_type && !value.kind_of?(data_type)
190
+ UI.user_error!("'#{self.key}' value must be a #{data_type}! Found #{value.class} instead.")
191
+ end
192
+ end
193
+
194
+ def ensure_boolean_type_passes_validation(value)
195
+ if @skip_type_validation
196
+ return
197
+ end
198
+
199
+ # We need to explicitly test against Pantograph::Boolean, TrueClass/FalseClass
200
+ if value.class != FalseClass && value.class != TrueClass
201
+ UI.user_error!("'#{self.key}' value must be either `true` or `false`! Found #{value.class} instead.")
202
+ end
203
+ end
204
+
205
+ # Make sure, the value is valid (based on the verify block)
206
+ # Raises an exception if the value is invalid
207
+ def valid?(value)
208
+ # we also allow nil values, which do not have to be verified.
209
+ return true if value.nil?
210
+
211
+ # Verify that value is the type that we're expecting, if we are expecting a type
212
+ if data_type == Pantograph::Boolean
213
+ ensure_boolean_type_passes_validation(value)
214
+ else
215
+ ensure_generic_type_passes_validation(value)
216
+ end
217
+
218
+ if @verify_block
219
+ begin
220
+ @verify_block.call(value)
221
+ rescue => ex
222
+ UI.error("Error setting value '#{value}' for option '#{@key}'")
223
+ raise Interface::PantographError.new, ex.to_s
224
+ end
225
+ end
226
+
227
+ true
228
+ end
229
+
230
+ # Returns an updated value type (if necessary)
231
+ def auto_convert_value(value)
232
+ return nil if value.nil?
233
+
234
+ if data_type == Array
235
+ return value.split(',') if value.kind_of?(String)
236
+ elsif data_type == Integer
237
+ return value.to_i if value.to_i.to_s == value.to_s
238
+ elsif data_type == Float
239
+ return value.to_f if value.to_f.to_s == value.to_s
240
+ elsif allow_shell_conversion
241
+ return value.shelljoin if value.kind_of?(Array)
242
+ return value.map { |k, v| "#{k.to_s.shellescape}=#{v.shellescape}" }.join(' ') if value.kind_of?(Hash)
243
+ elsif data_type != String
244
+ # Special treatment if the user specified true, false or YES, NO
245
+ # There is no boolean type, so we just do it here
246
+ if %w(YES yes true TRUE).include?(value)
247
+ return true
248
+ elsif %w(NO no false FALSE).include?(value)
249
+ return false
250
+ end
251
+ end
252
+
253
+ return value # fallback to not doing anything
254
+ end
255
+
256
+ # Determines the defined data type of this ConfigItem
257
+ def data_type
258
+ if @data_type.kind_of?(Symbol)
259
+ nil
260
+ elsif @data_type
261
+ @data_type
262
+ else
263
+ (@is_string ? String : nil)
264
+ end
265
+ end
266
+
267
+ # Replaces the attr_accessor, but maintains the same interface
268
+ def string?
269
+ data_type == String
270
+ end
271
+
272
+ # it's preferred to use self.string? In most cases, except in commander_generator.rb, cause... reasons
273
+ def is_string
274
+ return @is_string
275
+ end
276
+
277
+ def to_s
278
+ [@key, @description].join(": ")
279
+ end
280
+
281
+ def deprecated_description(initial_description, deprecated)
282
+ has_description = !initial_description.to_s.empty?
283
+
284
+ description = "**DEPRECATED!**"
285
+
286
+ if deprecated.kind_of?(String)
287
+ description << " #{deprecated}"
288
+ description << " -" if has_description
289
+ end
290
+
291
+ description << " #{initial_description}" if has_description
292
+
293
+ description
294
+ end
295
+
296
+ def doc_default_value
297
+ return "[*](#parameters-legend-dynamic)" if self.default_value_dynamic
298
+ return "" if self.default_value.nil?
299
+ return "`''`" if self.default_value.instance_of?(String) && self.default_value.empty?
300
+ return "`:#{self.default_value}`" if self.default_value.instance_of?(Symbol)
301
+
302
+ "`#{self.default_value}`"
303
+ end
304
+
305
+ def help_default_value
306
+ return "#{self.default_value} *".strip if self.default_value_dynamic
307
+ return "" if self.default_value.nil?
308
+ return "''" if self.default_value.instance_of?(String) && self.default_value.empty?
309
+ return ":#{self.default_value}" if self.default_value.instance_of?(Symbol)
310
+
311
+ self.default_value
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,332 @@
1
+ require_relative '../helper'
2
+ require_relative '../globals'
3
+ require_relative 'config_item'
4
+ require_relative 'commander_generator'
5
+ require_relative 'configuration_file'
6
+
7
+ module PantographCore
8
+ class Configuration
9
+ attr_accessor :available_options
10
+
11
+ attr_accessor :values
12
+
13
+ # @return [Array] An array of symbols which are all available keys
14
+ attr_reader :all_keys
15
+
16
+ # @return [String] The name of the configuration file (not the path). Optional!
17
+ attr_accessor :config_file_name
18
+
19
+ # @return [Hash] Options that were set from a config file using load_configuration_file. Optional!
20
+ attr_accessor :config_file_options
21
+
22
+ def self.create(available_options, values)
23
+ UI.user_error!("values parameter must be a hash") unless values.kind_of?(Hash)
24
+ v = values.dup
25
+ v.each do |key, val|
26
+ v[key] = val.dup if val.kind_of?(String) # this is necessary when fetching a value from an environment variable
27
+ end
28
+
29
+ if v.kind_of?(Hash) && available_options.kind_of?(Array) # we only want to deal with the new configuration system
30
+ # Now see if --verbose would be a valid input
31
+ # If not, it might be because it's an action and not a tool
32
+ unless available_options.find { |a| a.kind_of?(ConfigItem) && a.key == :verbose }
33
+ v.delete(:verbose) # as this is being processed by commander
34
+ end
35
+ end
36
+ Configuration.new(available_options, v)
37
+ end
38
+
39
+ #####################################################
40
+ # @!group Setting up the configuration
41
+ #####################################################
42
+
43
+ # collect sensitive strings
44
+ def self.sensitive_strings
45
+ @sensitive_strings ||= []
46
+ end
47
+
48
+ def initialize(available_options, values)
49
+ self.available_options = available_options || []
50
+ self.values = values || {}
51
+ self.config_file_options = {}
52
+
53
+ # used for pushing and popping values to provide nesting configuration contexts
54
+ @values_stack = []
55
+
56
+ # if we are in captured output mode - keep a array of sensitive option values
57
+ # those will be later - replaced by ####
58
+ if PantographCore::Globals.capture_output?
59
+ available_options.each do |element|
60
+ next unless element.sensitive
61
+ self.class.sensitive_strings << values[element.key]
62
+ end
63
+ end
64
+
65
+ verify_input_types
66
+ verify_value_exists
67
+ verify_no_duplicates
68
+ verify_conflicts
69
+ verify_default_value_matches_verify_block
70
+ end
71
+
72
+ def verify_input_types
73
+ UI.user_error!("available_options parameter must be an array of ConfigItems but is #{@available_options.class}") unless @available_options.kind_of?(Array)
74
+ @available_options.each do |item|
75
+ UI.user_error!("available_options parameter must be an array of ConfigItems. Found #{item.class}.") unless item.kind_of?(ConfigItem)
76
+ end
77
+ UI.user_error!("values parameter must be a hash") unless @values.kind_of?(Hash)
78
+ end
79
+
80
+ def verify_value_exists
81
+ # Make sure the given value keys exist
82
+ @values.each do |key, value|
83
+ next if key == :trace # special treatment
84
+ option = self.verify_options_key!(key)
85
+ @values[key] = option.auto_convert_value(value)
86
+ UI.deprecated("Using deprecated option: '--#{key}' (#{option.deprecated})") if option.deprecated
87
+ option.verify!(@values[key]) # Call the verify block for it too
88
+ end
89
+ end
90
+
91
+ def verify_no_duplicates
92
+ # Make sure a key was not used multiple times
93
+ @available_options.each do |current|
94
+ count = @available_options.count { |option| option.key == current.key }
95
+ UI.user_error!("Multiple entries for configuration key '#{current.key}' found!") if count > 1
96
+
97
+ unless current.short_option.to_s.empty?
98
+ count = @available_options.count { |option| option.short_option == current.short_option }
99
+ UI.user_error!("Multiple entries for short_option '#{current.short_option}' found!") if count > 1
100
+ end
101
+ end
102
+ end
103
+
104
+ def verify_conflicts
105
+ option_keys = @values.keys
106
+
107
+ option_keys.each do |current|
108
+ index = @available_options.find_index { |item| item.key == current }
109
+ current = @available_options[index]
110
+
111
+ # ignore conflicts because option value is nil
112
+ next if @values[current.key].nil?
113
+
114
+ next if current.conflicting_options.nil?
115
+
116
+ conflicts = current.conflicting_options & option_keys
117
+ next if conflicts.nil?
118
+
119
+ conflicts.each do |conflicting_option_key|
120
+ index = @available_options.find_index { |item| item.key == conflicting_option_key }
121
+ conflicting_option = @available_options[index]
122
+
123
+ # ignore conflicts because because value of conflict option is nil
124
+ next if @values[conflicting_option.key].nil?
125
+
126
+ if current.conflict_block
127
+ begin
128
+ current.conflict_block.call(conflicting_option)
129
+ rescue => ex
130
+ UI.error("Error resolving conflict between options: '#{current.key}' and '#{conflicting_option.key}'")
131
+ raise ex
132
+ end
133
+ else
134
+ UI.user_error!("Unresolved conflict between options: '#{current.key}' and '#{conflicting_option.key}'")
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ # Verifies the default value is also valid
141
+ def verify_default_value_matches_verify_block
142
+ @available_options.each do |item|
143
+ next unless item.verify_block && item.default_value
144
+
145
+ begin
146
+ unless @values[item.key] # this is important to not verify if there already is a value there
147
+ item.verify_block.call(item.default_value)
148
+ end
149
+ rescue => ex
150
+ UI.error(ex)
151
+ UI.user_error!("Invalid default value for #{item.key}, doesn't match verify_block")
152
+ end
153
+ end
154
+ end
155
+
156
+ # This method takes care of parsing and using the configuration file as values
157
+ # Call this once you know where the config file might be located
158
+ # Take a look at how `gym` uses this method
159
+ #
160
+ # @param config_file_name [String] The name of the configuration file to use (optional)
161
+ # @param block_for_missing [Block] A ruby block that is called when there is an unknown method
162
+ # in the configuration file
163
+ def load_configuration_file(config_file_name = nil, block_for_missing = nil, skip_printing_values = false)
164
+ return unless config_file_name
165
+
166
+ self.config_file_name = config_file_name
167
+
168
+ path = PantographCore::Configuration.find_configuration_file_path(config_file_name: config_file_name)
169
+ return if path.nil?
170
+
171
+ begin
172
+ configuration_file = ConfigurationFile.new(self, path, block_for_missing, skip_printing_values)
173
+ options = configuration_file.options
174
+ rescue PantographCore::ConfigurationFile::ExceptionWhileParsingError => e
175
+ options = e.recovered_options
176
+ wrapped_exception = e.wrapped_exception
177
+ end
178
+
179
+ # Make sure all the values set in the config file pass verification
180
+ options.each do |key, val|
181
+ option = self.verify_options_key!(key)
182
+ option.verify!(val)
183
+ end
184
+
185
+ # Merge the new options into the old ones, keeping all previously set keys
186
+ self.config_file_options = options.merge(self.config_file_options)
187
+
188
+ verify_conflicts # important, since user can set conflicting options in configuration file
189
+
190
+ # Now that everything is verified, re-raise an exception that was raised in the config file
191
+ raise wrapped_exception unless wrapped_exception.nil?
192
+
193
+ configuration_file
194
+ end
195
+
196
+ def self.find_configuration_file_path(config_file_name: nil)
197
+ paths = []
198
+ paths += Dir["./pantograph/#{config_file_name}"]
199
+ paths += Dir["./.pantograph/#{config_file_name}"]
200
+ paths += Dir["./#{config_file_name}"]
201
+ paths += Dir["./pantograph_core/spec/fixtures/#{config_file_name}"] if Helper.test?
202
+ return nil if paths.count == 0
203
+ return paths.first
204
+ end
205
+
206
+ #####################################################
207
+ # @!group Actually using the class
208
+ #####################################################
209
+
210
+ # Returns the value for a certain key. pantograph_core tries to fetch the value from different sources
211
+ # if 'ask' is true and the value is not present, the user will be prompted to provide a value
212
+ # rubocop:disable Metrics/PerceivedComplexity
213
+ def fetch(key, ask: true)
214
+ UI.crash!("Key '#{key}' must be a symbol. Example :app_id.") unless key.kind_of?(Symbol)
215
+
216
+ option = verify_options_key!(key)
217
+
218
+ # Same order as https://docs.pantograph.tools/advanced/#priorities-of-parameters-and-options
219
+ value = if @values.key?(key) && !@values[key].nil?
220
+ @values[key]
221
+ elsif option.env_name && !ENV[option.env_name].nil?
222
+ # verify! before using (see https://github.com/pantograph/pantograph/issues/14449)
223
+ ENV[option.env_name].dup if option.verify!(option.auto_convert_value(ENV[option.env_name]))
224
+ elsif self.config_file_options.key?(key)
225
+ self.config_file_options[key]
226
+ else
227
+ option.default_value
228
+ end
229
+
230
+ value = option.auto_convert_value(value)
231
+ value = nil if value.nil? && !option.string? # by default boolean flags are false
232
+ return value unless value.nil? && !option.optional && ask
233
+
234
+ # fallback to asking
235
+ if Helper.test? || !UI.interactive?
236
+ # Since we don't want to be asked on tests, we'll just call the verify block with no value
237
+ # to raise the exception that is shown when the user passes an invalid value
238
+ set(key, '')
239
+ # If this didn't raise an exception, just raise a default one
240
+ UI.user_error!("No value found for '#{key}'")
241
+ end
242
+
243
+ while value.nil?
244
+ UI.important("To not be asked about this value, you can specify it using '#{option.key}'") if ENV["PANTOGRAPH_ONBOARDING_IN_PROCESS"].to_s.length == 0
245
+ value = option.sensitive ? UI.password("#{option.description}: ") : UI.input("#{option.description}: ")
246
+ # Also store this value to use it from now on
247
+ begin
248
+ set(key, value)
249
+ rescue => ex
250
+ puts(ex)
251
+ value = nil
252
+ end
253
+ end
254
+
255
+ # It's very, very important to use the self[:my_key] notation
256
+ # as this will make sure to use the `fetch` method
257
+ # that is responsible for auto converting the values into the right
258
+ # data type
259
+ # Found out via https://github.com/pantograph/pantograph/issues/11243
260
+ return self[key]
261
+ end
262
+ # rubocop:enable Metrics/PerceivedComplexity
263
+
264
+ # Overwrites or sets a new value for a given key
265
+ # @param key [Symbol] Must be a symbol
266
+ def set(key, value)
267
+ UI.crash!("Key '#{key}' must be a symbol. Example :#{key}.") unless key.kind_of?(Symbol)
268
+ option = option_for_key(key)
269
+
270
+ unless option
271
+ UI.user_error!("Could not find option '#{key}' in the list of available options: #{@available_options.collect(&:key).join(', ')}")
272
+ end
273
+
274
+ option.verify!(value)
275
+
276
+ @values[key] = value
277
+ true
278
+ end
279
+
280
+ # see fetch
281
+ def values(ask: true)
282
+ # As the user accesses all values, we need to iterate through them to receive all the values
283
+ @available_options.each do |option|
284
+ @values[option.key] = fetch(option.key, ask: ask) unless @values[option.key]
285
+ end
286
+ @values
287
+ end
288
+
289
+ # Direct access to the values, without iterating through all items
290
+ def _values
291
+ @values
292
+ end
293
+
294
+ # Clears away any current configuration values by pushing them onto a stack.
295
+ # Values set after calling push_values! will be merged with the previous
296
+ # values after calling pop_values!
297
+ #
298
+ # see: pop_values!
299
+ def push_values!
300
+ @values_stack.push(@values)
301
+ @values = {}
302
+ end
303
+
304
+ # Restores a previous set of configuration values by merging any current
305
+ # values on top of them
306
+ #
307
+ # see: push_values!
308
+ def pop_values!
309
+ return if @values_stack.empty?
310
+ @values = @values_stack.pop.merge(@values)
311
+ end
312
+
313
+ def all_keys
314
+ @available_options.collect(&:key)
315
+ end
316
+
317
+ # Returns the config_item object for a given key
318
+ def option_for_key(key)
319
+ @available_options.find { |o| o.key == key }
320
+ end
321
+
322
+ # Aliases `[key]` to `fetch(key)` because Ruby can do it.
323
+ alias [] fetch
324
+ alias []= set
325
+
326
+ def verify_options_key!(key)
327
+ option = option_for_key(key)
328
+ UI.user_error!("Could not find option '#{key}' in the list of available options: #{@available_options.collect(&:key).join(', ')}") unless option
329
+ option
330
+ end
331
+ end
332
+ end