pantograph 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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