ghedsh 1.1.40 → 2.3.8
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.
- checksums.yaml +4 -4
- data/.editorconfig +12 -0
- data/.github/ISSUE_TEMPLATE.md +31 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +30 -0
- data/.gitignore +12 -1
- data/.rspec +3 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +5 -0
- data/LICENSE +165 -0
- data/README.md +6 -93
- data/Rakefile +3 -9
- data/bin/ghedsh +2 -1
- data/file_templates/add_members_template.json +12 -0
- data/file_templates/create_teams_template.json +19 -0
- data/file_templates/invite_outside_collabs.json +12 -0
- data/file_templates/remove_members_template.json +12 -0
- data/ghedsh.gemspec +3 -2
- data/lib/actions/orgs.rb +636 -842
- data/lib/actions/system.rb +212 -278
- data/lib/actions/teams.rb +15 -229
- data/lib/actions/user.rb +304 -12
- data/lib/commands.rb +465 -0
- data/lib/common.rb +15 -0
- data/lib/context.rb +42 -0
- data/lib/helpers.rb +147 -0
- data/lib/interface.rb +71 -733
- data/lib/plugin_loader.rb +43 -0
- data/lib/version.rb +1 -1
- data/spec/cli_spec.rb +30 -0
- data/spec/spec_helper.rb +106 -0
- metadata +38 -10
- data/docs/Javier-clemente-MemoriaTFG-ghedsh.pdf +0 -0
- data/lib/actions/help.rb +0 -357
- data/lib/actions/repo.rb +0 -832
- data/spec/spec.rb +0 -1
data/lib/commands.rb
ADDED
@@ -0,0 +1,465 @@
|
|
1
|
+
require 'version'
|
2
|
+
require 'common'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
# Class that registers commands into has COMMANDS located in common.rb
|
7
|
+
# The interface access that hash to look up for the command. If command is defined,
|
8
|
+
# and the class that points @deep has the method defined, the interface calls it with
|
9
|
+
# parameters provided.
|
10
|
+
#
|
11
|
+
# If a command should be available in different contexts, each class (e.g User or Organization)
|
12
|
+
# must implement that command (and perform acrions related to that context)
|
13
|
+
class Commands
|
14
|
+
attr_reader :enviroment
|
15
|
+
|
16
|
+
# when Commands class is instantiated, all methods are added to COMMANDS hash with a name (string)
|
17
|
+
# that identify each one and the caller method within Commands class.
|
18
|
+
def initialize
|
19
|
+
@context_stack = []
|
20
|
+
add_command('clear', method(:clear))
|
21
|
+
# add_command('help', method(:help))
|
22
|
+
add_command('exit', method(:exit))
|
23
|
+
add_command('version', method(:show_version))
|
24
|
+
add_command('new_issue', method(:new_issue))
|
25
|
+
add_command('issues', method(:display_issues))
|
26
|
+
add_command('repos', method(:display_repos))
|
27
|
+
add_command('new_repo', method(:new_repo))
|
28
|
+
add_command('rm_repo', method(:rm_repo))
|
29
|
+
add_command('new_team', method(:new_team))
|
30
|
+
add_command('set_private', method(:set_private))
|
31
|
+
add_command('set_public', method(:set_public))
|
32
|
+
add_command('files', method(:show_files))
|
33
|
+
add_command('rm_team', method(:rm_team))
|
34
|
+
add_command('clone', method(:clone_repo))
|
35
|
+
add_command('rm_cloned', method(:delete_cloned_repos))
|
36
|
+
add_command('commits', method(:display_commits))
|
37
|
+
add_command('orgs', method(:display_orgs))
|
38
|
+
add_command('new_eval', method(:new_eval))
|
39
|
+
add_command('foreach', method(:foreach_eval))
|
40
|
+
add_command('foreach_try', method(:foreach_try))
|
41
|
+
add_command('invite_member', method(:invite_member))
|
42
|
+
add_command('remove_member', method(:remove_member))
|
43
|
+
add_command('invite_member_from_file', method(:invite_member_from_file))
|
44
|
+
add_command('invite_outside_collaborators', method(:invite_outside_collaborators))
|
45
|
+
add_command('people', method(:display_people))
|
46
|
+
add_command('teams', method(:display_teams))
|
47
|
+
add_command('orgsn', method(:orgsn))
|
48
|
+
add_command('cd', method(:change_context))
|
49
|
+
add_command('open', method(:open))
|
50
|
+
add_command('bash', method(:bash))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Fills COMMANDS hash with available commands.
|
54
|
+
#
|
55
|
+
# @param [String] command_name string that identifies a command, when user types it, command
|
56
|
+
# executes depending on context.
|
57
|
+
# @param [Method] command method inside Class Commands that triggers the action depending on where
|
58
|
+
# is pointing @deep
|
59
|
+
def add_command(command_name, command)
|
60
|
+
COMMANDS[command_name] = command
|
61
|
+
end
|
62
|
+
|
63
|
+
# Gets enviroment from class ShellContext. Includes all GitHub authenticated user configuration,
|
64
|
+
# ghedsh configuration etc.
|
65
|
+
# Also it stores the default enviroment to avoid context_stack underflow.
|
66
|
+
#
|
67
|
+
# @param [ShellContext] console_enviroment object containing running enviroment.
|
68
|
+
def load_enviroment(console_enviroment)
|
69
|
+
@enviroment = console_enviroment
|
70
|
+
@default_enviroment = OpenStruct.new
|
71
|
+
default_config = {
|
72
|
+
'User' => @enviroment.client.login.to_s,
|
73
|
+
'user_url' => @enviroment.client.web_endpoint.to_s << @enviroment.client.login.to_s,
|
74
|
+
'Org' => nil,
|
75
|
+
'org_url' => nil,
|
76
|
+
'Repo' => nil,
|
77
|
+
'repo_url' => nil,
|
78
|
+
'Team' => nil,
|
79
|
+
'team_url' => nil,
|
80
|
+
'TeamID' => nil,
|
81
|
+
'Assig' => nil
|
82
|
+
}
|
83
|
+
@default_enviroment.config = default_config
|
84
|
+
@default_enviroment.deep = User
|
85
|
+
@context_stack.push(@default_enviroment)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Display organization depending on context. If method is not defined in current deep, information
|
89
|
+
# is provided.
|
90
|
+
#
|
91
|
+
# @param [Array<String>] params user provided parameters, like Regexp to show matching organizations
|
92
|
+
def display_orgs(params)
|
93
|
+
if @enviroment.deep.method_defined? :show_organizations
|
94
|
+
@enviroment.deep.new.show_organizations(@enviroment.client, params)
|
95
|
+
else
|
96
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
97
|
+
end
|
98
|
+
puts
|
99
|
+
end
|
100
|
+
|
101
|
+
# Creates a 'super repo' containing subrepos of assignments
|
102
|
+
#
|
103
|
+
# @param [Array<String>] params first provide name of eval repository and a Regexp to match
|
104
|
+
# repos to add them as submodules
|
105
|
+
def new_eval(params)
|
106
|
+
if @enviroment.deep.method_defined? :new_eval
|
107
|
+
@enviroment.deep.new.new_eval(@enviroment.client, @enviroment.config, params)
|
108
|
+
else
|
109
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
110
|
+
end
|
111
|
+
puts
|
112
|
+
end
|
113
|
+
|
114
|
+
# Run a bash command over each submodule inside a evaluation repo
|
115
|
+
# Requirements: Current working directory must be the evaluation repo containing all submodules
|
116
|
+
#
|
117
|
+
# @param [Array<String>] params command to run over each submodule
|
118
|
+
def foreach_eval(params)
|
119
|
+
if @enviroment.deep.method_defined? :foreach_eval
|
120
|
+
@enviroment.deep.new.foreach_eval(@enviroment.client, @enviroment.config, params)
|
121
|
+
else
|
122
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
123
|
+
end
|
124
|
+
puts
|
125
|
+
end
|
126
|
+
|
127
|
+
def foreach_try(params)
|
128
|
+
if @enviroment.deep.method_defined? :foreach_try
|
129
|
+
@enviroment.deep.new.foreach_try(@enviroment.client, @enviroment.config, params)
|
130
|
+
else
|
131
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
132
|
+
end
|
133
|
+
puts
|
134
|
+
end
|
135
|
+
|
136
|
+
# Display files from current repo.
|
137
|
+
#
|
138
|
+
# @param [Array<String>] params user provided parameters, like path within a repository
|
139
|
+
def show_files(params)
|
140
|
+
if @enviroment.deep.method_defined? :show_files
|
141
|
+
@enviroment.deep.new.show_files(@enviroment.client, @enviroment.config, params)
|
142
|
+
else
|
143
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
144
|
+
end
|
145
|
+
puts
|
146
|
+
end
|
147
|
+
|
148
|
+
# Invite member to organization
|
149
|
+
# @param [Array<String>] params user provided parameters, like members to be added
|
150
|
+
def invite_member(params)
|
151
|
+
if @enviroment.deep.method_defined? :add_members
|
152
|
+
@enviroment.deep.new.add_members(@enviroment.client, @enviroment.config, params)
|
153
|
+
else
|
154
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
155
|
+
end
|
156
|
+
puts
|
157
|
+
end
|
158
|
+
|
159
|
+
# Invite members from JSON files. file_templates directory has template with file structure for this command.
|
160
|
+
# @param [Array<String>] params path to JSON file containing members
|
161
|
+
def invite_member_from_file(params)
|
162
|
+
if @enviroment.deep.method_defined? :add_members_from_file
|
163
|
+
@enviroment.deep.new.add_members_from_file(@enviroment.client, @enviroment.config, params[0])
|
164
|
+
else
|
165
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
166
|
+
end
|
167
|
+
puts
|
168
|
+
end
|
169
|
+
|
170
|
+
# Remove member from oranization.
|
171
|
+
# @param [Array<String>] params path to JSON file containing members to be removed or Regexp to match members
|
172
|
+
# to be removed. file_templates contains a template for this command.
|
173
|
+
def remove_member(params)
|
174
|
+
if @enviroment.deep.method_defined? :delete_member
|
175
|
+
@enviroment.deep.new.delete_member(@enviroment.client, @enviroment.config, params[0])
|
176
|
+
else
|
177
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
178
|
+
end
|
179
|
+
puts
|
180
|
+
end
|
181
|
+
|
182
|
+
# Invite outside collaborators of an organization to be members of that organization.
|
183
|
+
# @param [Array<String>] params path to file or Regexp to match outside collaborators to be invited.
|
184
|
+
# file_templates contains a JSON template for this command.
|
185
|
+
def invite_outside_collaborators(params)
|
186
|
+
if @enviroment.deep.method_defined? :invite_all_outside_collaborators
|
187
|
+
@enviroment.deep.new.invite_all_outside_collaborators(@enviroment.client, @enviroment.config, params[0])
|
188
|
+
else
|
189
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
190
|
+
end
|
191
|
+
puts
|
192
|
+
end
|
193
|
+
|
194
|
+
# Exit Github Education Shell CLI saving configuration. When user runs the CLI again, configuration
|
195
|
+
# and context are restored from last session. However, previous contexts are not restored, only last one.
|
196
|
+
def exit(_params)
|
197
|
+
@enviroment.sysbh.save_memory(@enviroment.config_path, @enviroment.config)
|
198
|
+
@enviroment.sysbh.save_cache(@enviroment.config_path, @enviroment.config)
|
199
|
+
@enviroment.sysbh.remove_temp("#{ENV['HOME']}/.ghedsh/temp")
|
200
|
+
|
201
|
+
0
|
202
|
+
end
|
203
|
+
|
204
|
+
# Runs a bash command.
|
205
|
+
# @param [Array<String>] params bash command to perform
|
206
|
+
def bash(params)
|
207
|
+
bash_command = params.join(' ')
|
208
|
+
system(bash_command)
|
209
|
+
puts
|
210
|
+
end
|
211
|
+
|
212
|
+
# Open info depending on context. Within organization will open GitHub organization profile, member profile, etc.
|
213
|
+
def open(params)
|
214
|
+
if @enviroment.deep.method_defined? :open_info
|
215
|
+
@enviroment.deep.new.open_info(@enviroment.config, params[0], @enviroment.client)
|
216
|
+
else
|
217
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Create new repository with the provided name. Two modes are available: fast and custom. Fast mode creates a public repo.
|
222
|
+
# Custom mode allows to set several details, like privacy, description, .gitignore template, etc.
|
223
|
+
#
|
224
|
+
# @param [Array<String>] params repository name
|
225
|
+
def new_repo(params)
|
226
|
+
if @enviroment.deep.method_defined? :create_repo
|
227
|
+
begin
|
228
|
+
repo_name = params[0]
|
229
|
+
options = repo_creation_guide
|
230
|
+
if options == 'Default'
|
231
|
+
@enviroment.deep.new.create_repo(@enviroment, repo_name, options = {})
|
232
|
+
else
|
233
|
+
@enviroment.deep.new.create_repo(@enviroment, repo_name, options)
|
234
|
+
end
|
235
|
+
rescue StandardError => exception
|
236
|
+
puts Rainbow(exception.message.to_s).color('#cc0000')
|
237
|
+
end
|
238
|
+
else
|
239
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
240
|
+
end
|
241
|
+
puts
|
242
|
+
end
|
243
|
+
|
244
|
+
# Remove repository
|
245
|
+
#
|
246
|
+
# @param [Array<String>] params repository name to be deleted
|
247
|
+
def rm_repo(params)
|
248
|
+
if @enviroment.deep.method_defined? :remove_repo
|
249
|
+
@enviroment.deep.new.remove_repo(@enviroment, params[0])
|
250
|
+
else
|
251
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
252
|
+
end
|
253
|
+
puts
|
254
|
+
end
|
255
|
+
|
256
|
+
# Change repository to private.
|
257
|
+
#
|
258
|
+
# @param [Array<String>] params Regexp to match repositories and edit its privacy
|
259
|
+
def set_private(params)
|
260
|
+
if @enviroment.deep.method_defined? :change_to_private_repo
|
261
|
+
@enviroment.deep.new.change_to_private_repo(@enviroment.client, @enviroment.config, params[0])
|
262
|
+
else
|
263
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
264
|
+
end
|
265
|
+
puts
|
266
|
+
end
|
267
|
+
|
268
|
+
# Change repository to public
|
269
|
+
#
|
270
|
+
# @param [Array<String>] params Regexp to match repositories and edit its privacy
|
271
|
+
def set_public(params)
|
272
|
+
if @enviroment.deep.method_defined? :change_to_public_repo
|
273
|
+
@enviroment.deep.new.change_to_public_repo(@enviroment.client, @enviroment.config, params[0])
|
274
|
+
else
|
275
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
276
|
+
end
|
277
|
+
puts
|
278
|
+
end
|
279
|
+
|
280
|
+
# Clone repository
|
281
|
+
#
|
282
|
+
# @param [Array<String>] params Regexp to match repositories to be cloned or individual repository to be cloned.
|
283
|
+
# Second parameter is custom path to find cloned repositories. If not provided CWD is the path.
|
284
|
+
def clone_repo(params)
|
285
|
+
if @enviroment.deep.method_defined? :clone_repository
|
286
|
+
@enviroment.deep.new.clone_repository(@enviroment, params[0], params[1])
|
287
|
+
else
|
288
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# Delete cloned repository
|
293
|
+
def delete_cloned_repos(_params)
|
294
|
+
####### FileUtils.remove_entry_secure("", force = true)
|
295
|
+
puts Rainbow("Cloned content deleted.\n").color('#00529B')
|
296
|
+
end
|
297
|
+
|
298
|
+
# Display commits
|
299
|
+
#
|
300
|
+
# @param [Array<String>] params repository name and baranch. If user is already inside a repository, a branch
|
301
|
+
# can be specified, if not 'master' is default branch.
|
302
|
+
def display_commits(params)
|
303
|
+
if @enviroment.deep.method_defined? :show_commits
|
304
|
+
@enviroment.deep.new.show_commits(@enviroment, params)
|
305
|
+
else
|
306
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
307
|
+
end
|
308
|
+
puts
|
309
|
+
end
|
310
|
+
|
311
|
+
# Display a table with GitHub IDs and membership type within an prganization
|
312
|
+
#
|
313
|
+
# @param [Array<String>] params Regexp to show matching people, if empty, shows all people.
|
314
|
+
def display_people(params)
|
315
|
+
if @enviroment.deep.method_defined? :show_people
|
316
|
+
@enviroment.deep.new.show_people(@enviroment.client, @enviroment.config, params[0])
|
317
|
+
else
|
318
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
319
|
+
end
|
320
|
+
puts
|
321
|
+
end
|
322
|
+
|
323
|
+
# Show teams within an organization
|
324
|
+
#
|
325
|
+
# @param [Array<String>] params Regexp to show matching teams
|
326
|
+
def display_teams(params)
|
327
|
+
if @enviroment.deep.method_defined? :show_teams
|
328
|
+
@enviroment.deep.new.show_teams(@enviroment.client, @enviroment.config, params[0])
|
329
|
+
else
|
330
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
331
|
+
end
|
332
|
+
puts
|
333
|
+
end
|
334
|
+
|
335
|
+
# Create teams from file or by name
|
336
|
+
#
|
337
|
+
# @param [Array<String>] params path to JSON template or team name to be created
|
338
|
+
def new_team(params)
|
339
|
+
if @enviroment.deep.method_defined? :create_team
|
340
|
+
@enviroment.deep.new.create_team(@enviroment.client, @enviroment.config, params[0])
|
341
|
+
else
|
342
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
343
|
+
end
|
344
|
+
puts
|
345
|
+
end
|
346
|
+
|
347
|
+
# Remove team
|
348
|
+
def rm_team(_params)
|
349
|
+
if @enviroment.deep.method_defined? :remove_team
|
350
|
+
@enviroment.deep.new.remove_team(@enviroment.config)
|
351
|
+
else
|
352
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
353
|
+
end
|
354
|
+
puts
|
355
|
+
end
|
356
|
+
|
357
|
+
# Open repository issue creation form URL
|
358
|
+
def new_issue(_params)
|
359
|
+
if @enviroment.deep.method_defined? :create_issue
|
360
|
+
@enviroment.deep.new.create_issue(@enviroment.config)
|
361
|
+
else
|
362
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Open browser with active issues URL
|
367
|
+
def display_issues(_params)
|
368
|
+
if @enviroment.deep.method_defined? :show_issues
|
369
|
+
@enviroment.deep.new.show_issues(@enviroment.config)
|
370
|
+
else
|
371
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# Display respositories
|
376
|
+
#
|
377
|
+
# @param [Array<String>] param Regexp to show matchin repository names. If not provided, show all
|
378
|
+
def display_repos(params)
|
379
|
+
if @enviroment.deep.method_defined? :show_repos
|
380
|
+
@enviroment.deep.new.show_repos(@enviroment.client, @enviroment.config, params[0])
|
381
|
+
else
|
382
|
+
puts Rainbow("Command not available in context \"#{@enviroment.deep.name}\"").color(WARNING_CODE)
|
383
|
+
end
|
384
|
+
puts
|
385
|
+
end
|
386
|
+
|
387
|
+
# Show ghedsh current version
|
388
|
+
def show_version(_params)
|
389
|
+
puts "GitHub Education Shell v#{Ghedsh::VERSION}"
|
390
|
+
end
|
391
|
+
|
392
|
+
# Clear screen
|
393
|
+
def clear(_params)
|
394
|
+
system('clear')
|
395
|
+
end
|
396
|
+
|
397
|
+
def orgsn(params)
|
398
|
+
# params[0].prepend("/")
|
399
|
+
# file_path = "#{Dir.home}#{params[0]}"
|
400
|
+
# p file_path[0]
|
401
|
+
# p file_path = file_path.delete('"')
|
402
|
+
# puts File.file?(file_path) ? true : false
|
403
|
+
end
|
404
|
+
|
405
|
+
# Change CLI context and move between repositories, organization, teams
|
406
|
+
# Contexts are stored in a stack
|
407
|
+
# @param [Array<String>] params cd operation
|
408
|
+
# @example change to organization
|
409
|
+
# User > cd org /regexp/
|
410
|
+
# @example change to repository
|
411
|
+
# User > Org > cd repo /regexp/ or String
|
412
|
+
def change_context(params)
|
413
|
+
if params.empty?
|
414
|
+
@enviroment.config['Org'] = nil
|
415
|
+
@enviroment.config['Repo'] = nil
|
416
|
+
@enviroment.config['Team'] = nil
|
417
|
+
@enviroment.config['TeamID'] = nil
|
418
|
+
@enviroment.config['Assig'] = nil
|
419
|
+
|
420
|
+
@context_stack.clear
|
421
|
+
@context_stack.push(@default_enviroment)
|
422
|
+
|
423
|
+
@enviroment.deep = User
|
424
|
+
elsif params[0] == '..'
|
425
|
+
if @context_stack.size > 1
|
426
|
+
@context_stack.pop
|
427
|
+
stack_pointer = @context_stack.last
|
428
|
+
|
429
|
+
@enviroment.config = stack_pointer.config
|
430
|
+
@enviroment.deep = stack_pointer.deep
|
431
|
+
else
|
432
|
+
@enviroment.config = @default_enviroment.config
|
433
|
+
@enviroment.deep = @default_enviroment.deep
|
434
|
+
end
|
435
|
+
else
|
436
|
+
begin
|
437
|
+
name = params[1]
|
438
|
+
name = name.gsub(/\A("|')|("|')\Z/, '')
|
439
|
+
name.insert(0, '\'')
|
440
|
+
name.insert(-1, '\'')
|
441
|
+
action = @enviroment.deep.new.build_cd_syntax(params[0], name)
|
442
|
+
env = OpenStruct.new
|
443
|
+
env.config = Marshal.load(Marshal.dump(@enviroment.config))
|
444
|
+
env.deep = @enviroment.deep
|
445
|
+
client = @enviroment.client
|
446
|
+
changed_enviroment = eval(action)
|
447
|
+
unless changed_enviroment.nil?
|
448
|
+
current_enviroment = OpenStruct.new
|
449
|
+
current_enviroment.config = changed_enviroment.config
|
450
|
+
current_enviroment.deep = changed_enviroment.deep
|
451
|
+
@context_stack.push(current_enviroment)
|
452
|
+
@enviroment.config = changed_enviroment.config
|
453
|
+
@enviroment.deep = changed_enviroment.deep
|
454
|
+
end
|
455
|
+
rescue SyntaxError => err
|
456
|
+
puts Rainbow('Syntax Error typing the command. Tip: cd <type> <Regexp|String>').color('#cc0000')
|
457
|
+
puts Rainbow('Regexp options for Ruby: /i, /m, /x, /o').color('#cc0000')
|
458
|
+
puts
|
459
|
+
rescue StandardError => exception
|
460
|
+
puts Rainbow('Error while running cd command. Usage: cd <type> <Regexp|String>').color('#D8000C')
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
data/lib/common.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'actions/system'
|
2
|
+
require 'actions/orgs'
|
3
|
+
require 'actions/teams'
|
4
|
+
require 'actions/user'
|
5
|
+
|
6
|
+
#color codes
|
7
|
+
ERROR_CODE = '#cc0000'
|
8
|
+
WARNING_CODE = '#9f6000'
|
9
|
+
INFO_CODE = '#00529B'
|
10
|
+
SUCCESS_CODE = '#4f8a10'
|
11
|
+
|
12
|
+
COMMANDS = {}
|
13
|
+
User
|
14
|
+
Organization
|
15
|
+
Team
|
data/lib/context.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'common'
|
2
|
+
require 'version'
|
3
|
+
require 'commands'
|
4
|
+
|
5
|
+
class ShellContext
|
6
|
+
attr_accessor :deep
|
7
|
+
attr_accessor :config
|
8
|
+
attr_accessor :client
|
9
|
+
attr_accessor :sysbh
|
10
|
+
attr_accessor :commands
|
11
|
+
attr_accessor :config_path
|
12
|
+
|
13
|
+
def initialize(user, config_path, argv_token)
|
14
|
+
@commands = {}
|
15
|
+
@config_path = config_path
|
16
|
+
@sysbh = Sys.new
|
17
|
+
@sysbh.write_initial_memory
|
18
|
+
|
19
|
+
if !user.nil?
|
20
|
+
@config = @sysbh.load_config_user(config_path, user)
|
21
|
+
@client = @sysbh.client
|
22
|
+
@deep = User
|
23
|
+
else
|
24
|
+
@config = @sysbh.load_config(config_path, argv_token) # retorna la configuracion ya guardada anteriormente
|
25
|
+
@client = @sysbh.client
|
26
|
+
@deep = @sysbh.return_deep(config_path)
|
27
|
+
end
|
28
|
+
@sysbh.load_memory(config_path, @config)
|
29
|
+
unless @client.nil?
|
30
|
+
@sysbh.add_history_str(2, Organization.new.read_orgs(@client))
|
31
|
+
end
|
32
|
+
|
33
|
+
# let commands class access context variables
|
34
|
+
share_context = Commands.new
|
35
|
+
share_context.load_enviroment(self)
|
36
|
+
@commands = COMMANDS
|
37
|
+
end
|
38
|
+
|
39
|
+
def prompt
|
40
|
+
@deep.shell_prompt(@config)
|
41
|
+
end
|
42
|
+
end
|
data/lib/helpers.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'tty-prompt'
|
2
|
+
require 'tty-spinner'
|
3
|
+
require 'rainbow'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'rbconfig'
|
6
|
+
require 'terminal-table'
|
7
|
+
|
8
|
+
# colors:
|
9
|
+
# error: .color('#cc0000')
|
10
|
+
# command not found: .yellow
|
11
|
+
# warning: .color('#9f6000')
|
12
|
+
# info: .color(#00529B)
|
13
|
+
# success: .color(79, 138, 16)
|
14
|
+
|
15
|
+
def custom_spinner(message)
|
16
|
+
TTY::Spinner.new(Rainbow(message.to_s).color(79, 138, 16), format: :bouncing_ball)
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_item_table(item, pattern)
|
20
|
+
matches = 0
|
21
|
+
rows = []
|
22
|
+
item.each do |i, v|
|
23
|
+
if pattern.match(i)
|
24
|
+
rows << [i, v]
|
25
|
+
matches += 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
table = Terminal::Table.new headings: ['Github ID', 'Role'], rows: rows
|
29
|
+
puts table
|
30
|
+
matches
|
31
|
+
end
|
32
|
+
|
33
|
+
def show_matching_items(item, pattern)
|
34
|
+
occurrences = 0
|
35
|
+
item.each do |i|
|
36
|
+
if pattern.match(i)
|
37
|
+
puts i
|
38
|
+
occurrences += 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
occurrences
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_regexp_from_string(string)
|
45
|
+
str = eval(string) # string.gsub(/\//, '')
|
46
|
+
Regexp.new(str)
|
47
|
+
rescue SyntaxError => e
|
48
|
+
puts Rainbow('Error building Regexp, check syntax.').color('#cc0000')
|
49
|
+
puts
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_file?(path)
|
53
|
+
path = path.delete('"')
|
54
|
+
File.file?("#{Dir.home}#{path}") ? true : false
|
55
|
+
end
|
56
|
+
|
57
|
+
def repo_creation_guide
|
58
|
+
puts Rainbow("Select 'Default' to create a quick public repo.").color('#f18973')
|
59
|
+
puts Rainbow("Select 'Custom' for private/public repo whith specific options.").color('#f18973')
|
60
|
+
puts Rainbow('To skip any option just hit Enter (Default options).').color('#f18973')
|
61
|
+
puts
|
62
|
+
choices = %w[Default Custom]
|
63
|
+
prompt = TTY::Prompt.new
|
64
|
+
answer = prompt.select('Select configuration', choices)
|
65
|
+
if answer == 'Default'
|
66
|
+
return answer
|
67
|
+
else
|
68
|
+
puts Rainbow('Answer questions with yes/true or no/false').color('#f18973')
|
69
|
+
custom_options = prompt.collect do
|
70
|
+
key(:private).ask('Private repo? (Default: false) [yes/true, no/false]', convert: :bool)
|
71
|
+
key(:description).ask('Write description of the repo')
|
72
|
+
key(:has_issues).ask('Has issues? (Default:true) [yes/true, no/false]', convert: :bool)
|
73
|
+
key(:has_wiki).ask('Has wiki? (Default: true) [yes/true, no/false]', convert: :bool)
|
74
|
+
key(:auto_init).ask('Create an initial commit with empty README? (Default: false) (if you want .gitignore template must be yes/true)',
|
75
|
+
convert: :bool)
|
76
|
+
key(:gitignore_template).ask('Desired language or platform for .gitignore template')
|
77
|
+
end
|
78
|
+
return custom_options = custom_options.compact
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def select_member(config, pattern, client)
|
83
|
+
members = []
|
84
|
+
members_url = {}
|
85
|
+
client.organization_members(config['Org'].to_s).each do |member|
|
86
|
+
if pattern.match(member[:login].to_s)
|
87
|
+
members << member[:login]
|
88
|
+
members_url[member[:login]] = member[:html_url]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
if members.empty?
|
92
|
+
puts Rainbow("No member matched with #{pattern.source} inside organization #{config['Org']}").color('#9f6000')
|
93
|
+
puts
|
94
|
+
else
|
95
|
+
prompt = TTY::Prompt.new
|
96
|
+
answer = prompt.select('Select desired organization member', members)
|
97
|
+
end
|
98
|
+
members_url[answer]
|
99
|
+
end
|
100
|
+
|
101
|
+
def perform_git_clone(repos_to_clone, custom_path)
|
102
|
+
begin
|
103
|
+
dir_path = if custom_path.nil?
|
104
|
+
Dir.pwd.to_s
|
105
|
+
else
|
106
|
+
"#{Dir.home}#{custom_path}"
|
107
|
+
end
|
108
|
+
FileUtils.mkdir_p(dir_path)
|
109
|
+
rescue StandardError => exception
|
110
|
+
puts Rainbow(exception.message.to_s).color('#cc0000')
|
111
|
+
end
|
112
|
+
repos_to_clone.each do |repos|
|
113
|
+
FileUtils.cd(dir_path) do
|
114
|
+
if !Dir.exist?("#{dir_path}/#{repos[:name]}") || Dir.empty?("#{dir_path}/#{repos[:name]}")
|
115
|
+
system("git clone --recurse-submodules --progress #{repos[:ssh_url]}")
|
116
|
+
puts
|
117
|
+
else
|
118
|
+
FileUtils.cd("#{dir_path}/#{repos[:name]}") do
|
119
|
+
puts repos[:name]
|
120
|
+
system('git pull --all --recurse-submodules')
|
121
|
+
puts
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
rescue StandardError => exception
|
127
|
+
puts Rainbow(exception.message.to_s).color('#cc0000')
|
128
|
+
puts
|
129
|
+
end
|
130
|
+
|
131
|
+
def split_members(members_list)
|
132
|
+
members = []
|
133
|
+
members_list.each do |i|
|
134
|
+
string = i.split(/[,(\s)?]/)
|
135
|
+
members.push(string)
|
136
|
+
end
|
137
|
+
members = members.flatten
|
138
|
+
end
|
139
|
+
|
140
|
+
def open_url(url)
|
141
|
+
os = RbConfig::CONFIG['host_os']
|
142
|
+
if os.downcase.include?('linux')
|
143
|
+
system("xdg-open #{url}")
|
144
|
+
elsif os.downcase.include?('darwin')
|
145
|
+
system("open #{url}")
|
146
|
+
end
|
147
|
+
end
|