cyclid-client 0.3.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.
- checksums.yaml +7 -0
- data/LICENSE +174 -0
- data/README.md +587 -0
- data/bin/cyclid +20 -0
- data/lib/cyclid/auth_methods.rb +25 -0
- data/lib/cyclid/cli.rb +90 -0
- data/lib/cyclid/cli/admin.rb +32 -0
- data/lib/cyclid/cli/admin/organization.rb +122 -0
- data/lib/cyclid/cli/admin/user.rb +142 -0
- data/lib/cyclid/cli/job.rb +114 -0
- data/lib/cyclid/cli/organization.rb +129 -0
- data/lib/cyclid/cli/organization/config.rb +90 -0
- data/lib/cyclid/cli/organization/member.rb +126 -0
- data/lib/cyclid/cli/secret.rb +42 -0
- data/lib/cyclid/cli/stage.rb +121 -0
- data/lib/cyclid/cli/user.rb +84 -0
- data/lib/cyclid/client.rb +98 -0
- data/lib/cyclid/client/api.rb +114 -0
- data/lib/cyclid/client/api/basic.rb +30 -0
- data/lib/cyclid/client/api/hmac.rb +59 -0
- data/lib/cyclid/client/api/none.rb +29 -0
- data/lib/cyclid/client/api/token.rb +30 -0
- data/lib/cyclid/client/auth.rb +36 -0
- data/lib/cyclid/client/health.rb +34 -0
- data/lib/cyclid/client/job.rb +88 -0
- data/lib/cyclid/client/organization.rb +187 -0
- data/lib/cyclid/client/stage.rb +79 -0
- data/lib/cyclid/client/user.rb +134 -0
- data/lib/cyclid/config.rb +92 -0
- metadata +157 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'colorize'
|
16
|
+
require 'uri'
|
17
|
+
|
18
|
+
require_rel 'organization/*.rb'
|
19
|
+
|
20
|
+
module Cyclid
|
21
|
+
module Cli
|
22
|
+
# 'organization' sub-command
|
23
|
+
class Organization < Thor
|
24
|
+
desc 'show', 'Show details of the organization'
|
25
|
+
def show
|
26
|
+
org = client.org_get(client.config.organization)
|
27
|
+
|
28
|
+
# Convert the public key to PEM
|
29
|
+
der_key = Base64.decode64(org['public_key'])
|
30
|
+
public_key = OpenSSL::PKey::RSA.new(der_key)
|
31
|
+
|
32
|
+
# Pretty print the organization details
|
33
|
+
puts 'Name: '.colorize(:cyan) + org['name']
|
34
|
+
puts 'Owner Email: '.colorize(:cyan) + org['owner_email']
|
35
|
+
puts 'Public Key: '.colorize(:cyan) + public_key.to_pem
|
36
|
+
puts 'Members:'.colorize(:cyan)
|
37
|
+
if org['users'].any?
|
38
|
+
org['users'].each do |user|
|
39
|
+
puts "\t#{user}"
|
40
|
+
end
|
41
|
+
else
|
42
|
+
puts "\tNone"
|
43
|
+
end
|
44
|
+
rescue StandardError => ex
|
45
|
+
abort "Failed to get organization: #{ex}"
|
46
|
+
end
|
47
|
+
|
48
|
+
desc 'modify', 'Modify the organization'
|
49
|
+
long_desc <<-LONGDESC
|
50
|
+
Modify the organization.
|
51
|
+
|
52
|
+
The --email option sets the owners email address.
|
53
|
+
LONGDESC
|
54
|
+
option :email, aliases: '-e'
|
55
|
+
def modify
|
56
|
+
client.org_modify(client.config.organization,
|
57
|
+
owner_email: options[:email])
|
58
|
+
rescue StandardError => ex
|
59
|
+
abort "Failed to modify organization: #{ex}"
|
60
|
+
end
|
61
|
+
|
62
|
+
desc 'list', 'List your available organizations'
|
63
|
+
def list
|
64
|
+
Dir.glob("#{CYCLID_CONFIG_DIR}/*").each do |fname|
|
65
|
+
next if File.symlink?(fname)
|
66
|
+
next unless File.file?(fname)
|
67
|
+
|
68
|
+
begin
|
69
|
+
# Create a Config from this file and display the details
|
70
|
+
config = Cyclid::Client::Config.new(path: fname)
|
71
|
+
|
72
|
+
puts File.basename(fname).colorize(:cyan)
|
73
|
+
uri = URI::HTTP.build(host: config.server, port: config.port)
|
74
|
+
puts "\tServer: ".colorize(:cyan) + uri.to_s
|
75
|
+
puts "\tOrganization: ".colorize(:cyan) + config.organization
|
76
|
+
puts "\tUsername: ".colorize(:cyan) + config.username
|
77
|
+
rescue StandardError => ex
|
78
|
+
$stderr.puts "Failed to load config file #{fname}: #{ex}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
desc 'use NAME', 'Select the organization NAME to use by default'
|
84
|
+
def use(name = nil)
|
85
|
+
# If 'use' was called without an argument, print the name of the
|
86
|
+
# current configuration
|
87
|
+
if name.nil?
|
88
|
+
fname = if File.symlink?(options[:config])
|
89
|
+
File.readlink(options[:config])
|
90
|
+
else
|
91
|
+
options[:config]
|
92
|
+
end
|
93
|
+
puts File.basename(fname)
|
94
|
+
else
|
95
|
+
# List the avialble configurations
|
96
|
+
fname = File.join(CYCLID_CONFIG_DIR, name)
|
97
|
+
|
98
|
+
# Sanity check that the configuration file exists and is valid
|
99
|
+
abort 'No such organization' unless File.exist?(fname)
|
100
|
+
abort 'Not a valid organization' unless File.file?(fname)
|
101
|
+
|
102
|
+
begin
|
103
|
+
config = Cyclid::Client::Config.new(path: fname)
|
104
|
+
|
105
|
+
raise if config.server.nil? or \
|
106
|
+
config.organization.nil? or \
|
107
|
+
config.username.nil? or \
|
108
|
+
config.secret.nil?
|
109
|
+
rescue StandardError
|
110
|
+
abort 'Invalid configuration file'
|
111
|
+
end
|
112
|
+
|
113
|
+
# The configuration file exists and appears to be sane, so switch the
|
114
|
+
# 'config' symlink to point to it.
|
115
|
+
Dir.chdir(CYCLID_CONFIG_DIR) do
|
116
|
+
File.delete('config')
|
117
|
+
File.symlink(name, 'config')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
desc 'member', 'Manage organization members'
|
123
|
+
subcommand 'member', Member
|
124
|
+
|
125
|
+
desc 'config', 'Manage organization configuration'
|
126
|
+
subcommand 'config', Config
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Cyclid
|
16
|
+
module Cli
|
17
|
+
# Commands for managing per. organization configuration
|
18
|
+
class Config < Thor
|
19
|
+
desc 'show TYPE PLUGIN', 'Show a plugin configuration'
|
20
|
+
def show(type, plugin)
|
21
|
+
plugin_data = client.org_config_get(client.config.organization, type, plugin)
|
22
|
+
|
23
|
+
config = plugin_data['config']
|
24
|
+
schema = plugin_data['schema']
|
25
|
+
schema.each do |setting|
|
26
|
+
name = setting['name']
|
27
|
+
type = setting['type']
|
28
|
+
|
29
|
+
case type
|
30
|
+
when 'string', 'integer'
|
31
|
+
data = config[name] || 'Not set'
|
32
|
+
puts "#{setting['description']}: ".colorize(:cyan) + data
|
33
|
+
when 'boolean'
|
34
|
+
data = config[name] || 'Not set'
|
35
|
+
puts "#{setting['description']}: ".colorize(:cyan) + (data ? 'true' : 'false')
|
36
|
+
when 'list'
|
37
|
+
puts setting['description'].colorize(:cyan)
|
38
|
+
data = config[name]
|
39
|
+
if data.empty?
|
40
|
+
puts "\tNone"
|
41
|
+
else
|
42
|
+
data.each do |item|
|
43
|
+
puts "\t#{item}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
when 'hash-list'
|
47
|
+
puts setting['description'].colorize(:cyan)
|
48
|
+
data = config[name]
|
49
|
+
if data.empty?
|
50
|
+
puts "\tNone"
|
51
|
+
else
|
52
|
+
data.each do |item|
|
53
|
+
item.each do |k, v|
|
54
|
+
puts "\t#{k}: #{v}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
else
|
59
|
+
raise "unknown schema type #{type}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
rescue StandardError => ex
|
63
|
+
abort "Failed to get plugin configuration: #{ex}"
|
64
|
+
end
|
65
|
+
|
66
|
+
desc 'edit TYPE PLUGIN', 'Edit a plugin configuration'
|
67
|
+
def edit(type, plugin)
|
68
|
+
plugin_data = client.org_config_get(client.config.organization, type, plugin)
|
69
|
+
|
70
|
+
# Inject the schema description into each config item
|
71
|
+
schema = plugin_data['schema']
|
72
|
+
config = plugin_data['config'].each do |k, v|
|
73
|
+
description = ''
|
74
|
+
schema.each do |item|
|
75
|
+
description = item['description'] if item['name'] == k
|
76
|
+
end
|
77
|
+
{ k => v, 'description' => description }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Open a text editor on the configuration
|
81
|
+
config = invoke_editor(config)
|
82
|
+
|
83
|
+
# Submit it to the server
|
84
|
+
client.org_config_set(client.config.organization, type, plugin, config)
|
85
|
+
rescue StandardError => ex
|
86
|
+
abort "Failed to update plugin configuration: #{ex}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Cyclid
|
16
|
+
module Cli
|
17
|
+
# Commands for managing organization members
|
18
|
+
class Member < Thor
|
19
|
+
desc 'add USERS', 'Add users to the organization'
|
20
|
+
def add(*users)
|
21
|
+
org = client.org_get(client.config.organization)
|
22
|
+
|
23
|
+
# Concat the new list with the existing list and remove any
|
24
|
+
# duplicates.
|
25
|
+
user_list = org['users']
|
26
|
+
user_list.concat users
|
27
|
+
user_list.uniq!
|
28
|
+
|
29
|
+
client.org_modify(client.config.organization,
|
30
|
+
members: user_list)
|
31
|
+
rescue StandardError => ex
|
32
|
+
abort "Failed to add users to the organization: #{ex}"
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'permission USER PERMISSION', 'Modify a members organization permissions'
|
36
|
+
long_desc <<-LONGDESC
|
37
|
+
Modify the organization permissions for USER.
|
38
|
+
|
39
|
+
PERMISSION must be one of:
|
40
|
+
* admin - Give the user organization administrator permissions.
|
41
|
+
* write - Give the user organization create/modify permissions.
|
42
|
+
* read - Give the user organization read permissions.
|
43
|
+
* none - Remove all organization permissions.
|
44
|
+
|
45
|
+
With 'none' the user remains an organization member but can not
|
46
|
+
interact with it. See the 'member remove' command if you want to
|
47
|
+
actually remove a user from your organization.
|
48
|
+
LONGDESC
|
49
|
+
def permission(user, permission)
|
50
|
+
perms = case permission.downcase
|
51
|
+
when 'admin'
|
52
|
+
{ 'admin' => true, 'write' => true, 'read' => true }
|
53
|
+
when 'write'
|
54
|
+
{ 'admin' => false, 'write' => true, 'read' => true }
|
55
|
+
when 'read'
|
56
|
+
{ 'admin' => false, 'write' => false, 'read' => true }
|
57
|
+
when 'none'
|
58
|
+
{ 'admin' => false, 'write' => false, 'read' => false }
|
59
|
+
else
|
60
|
+
raise "invalid permission #{permission}"
|
61
|
+
end
|
62
|
+
|
63
|
+
client.org_user_permissions(client.config.organization, user, perms)
|
64
|
+
rescue StandardError => ex
|
65
|
+
abort "Failed to modify user permissions: #{ex}"
|
66
|
+
end
|
67
|
+
map 'perms' => :permission
|
68
|
+
|
69
|
+
desc 'list', 'List organization members'
|
70
|
+
def list
|
71
|
+
org = client.org_get(client.config.organization)
|
72
|
+
org['users'].each do |user|
|
73
|
+
puts user
|
74
|
+
end
|
75
|
+
rescue StandardError => ex
|
76
|
+
abort "Failed to get organization members: #{ex}"
|
77
|
+
end
|
78
|
+
|
79
|
+
desc 'show USER', 'Show details of an organization member'
|
80
|
+
def show(user)
|
81
|
+
user = client.org_user_get(client.config.organization, user)
|
82
|
+
|
83
|
+
# Pretty print the user details
|
84
|
+
puts 'Username: '.colorize(:cyan) + user['username']
|
85
|
+
puts 'Email: '.colorize(:cyan) + user['email']
|
86
|
+
puts 'Permissions'.colorize(:cyan)
|
87
|
+
user['permissions'].each do |k, v|
|
88
|
+
puts "\t#{k.capitalize}: ".colorize(:cyan) + v.to_s
|
89
|
+
end
|
90
|
+
rescue StandardError => ex
|
91
|
+
abort "Failed to get user: #{ex}"
|
92
|
+
end
|
93
|
+
|
94
|
+
desc 'remove USERS', 'Remove users from the organization'
|
95
|
+
long_desc <<-LONGDESC
|
96
|
+
Remove the list of USERS from the organization.
|
97
|
+
|
98
|
+
The --force option will remove the users without asking for confirmation.
|
99
|
+
LONGDESC
|
100
|
+
option :force, aliases: '-f', type: :boolean
|
101
|
+
def remove(*users)
|
102
|
+
org = client.org_get(client.config.organization)
|
103
|
+
|
104
|
+
# Remove any users that exist as members of this organization;
|
105
|
+
# ask for confirmation on a per-user basis (unless '-f' was passed)
|
106
|
+
user_list = org['users']
|
107
|
+
user_list.delete_if do |user|
|
108
|
+
if users.include? user
|
109
|
+
if options[:force]
|
110
|
+
true
|
111
|
+
else
|
112
|
+
print "Remove user #{user}: are you sure? (Y/n): ".colorize(:red)
|
113
|
+
STDIN.getc.chr.casecmp('y') == 0
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
user_list.uniq!
|
118
|
+
|
119
|
+
client.org_modify(client.config.organization,
|
120
|
+
members: user_list)
|
121
|
+
rescue StandardError => ex
|
122
|
+
abort "Failed to remove users from the organization: #{ex}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'colorize'
|
16
|
+
|
17
|
+
module Cyclid
|
18
|
+
module Cli
|
19
|
+
# 'secret' sub-command
|
20
|
+
class Secret < Thor
|
21
|
+
desc 'encrypt', 'Encrypt a secret with the organizations public key'
|
22
|
+
def encrypt
|
23
|
+
# Get the organizations public key in a form we can use
|
24
|
+
org = client.org_get(client.config.organization)
|
25
|
+
der_key = Base64.decode64(org['public_key'])
|
26
|
+
public_key = OpenSSL::PKey::RSA.new(der_key)
|
27
|
+
|
28
|
+
# Get the secret in a safe manner
|
29
|
+
print 'Secret: '
|
30
|
+
secret = STDIN.noecho(&:gets).chomp
|
31
|
+
print "\r"
|
32
|
+
|
33
|
+
# Encrypt with the public key
|
34
|
+
encrypted = public_key.public_encrypt(secret)
|
35
|
+
|
36
|
+
puts 'Secret: '.colorize(:cyan) + Base64.strict_encode64(encrypted)
|
37
|
+
rescue StandardError => ex
|
38
|
+
abort "Failed to encrypt secret: #{ex}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# Copyright 2016 Liqwyd Ltd.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Cyclid
|
16
|
+
module Cli
|
17
|
+
# 'stage' sub-command
|
18
|
+
class Stage < Thor
|
19
|
+
desc 'list', 'List the defined stages'
|
20
|
+
def list
|
21
|
+
stages = client.stage_list(client.config.organization)
|
22
|
+
stages.each do |stage|
|
23
|
+
puts "#{stage[:name]} v#{stage[:version]}"
|
24
|
+
end
|
25
|
+
rescue StandardError => ex
|
26
|
+
abort "Failed to get stages: #{ex}"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'show NAME', 'Show details of a stage'
|
30
|
+
def show(name)
|
31
|
+
stages = client.stage_get(client.config.organization, name)
|
32
|
+
|
33
|
+
# Pretty print the stage details
|
34
|
+
stages.each do |stage|
|
35
|
+
puts 'Name: '.colorize(:cyan) + stage['name']
|
36
|
+
puts 'Version: '.colorize(:cyan) + stage['version']
|
37
|
+
puts 'Steps'.colorize(:cyan)
|
38
|
+
stage['steps'].each do |step|
|
39
|
+
puts "\t\tAction: ".colorize(:cyan) + step['action']
|
40
|
+
step.delete('action')
|
41
|
+
step.each do |k, v|
|
42
|
+
puts "\t\t#{k.capitalize}: ".colorize(:cyan) + v.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
rescue StandardError => ex
|
47
|
+
abort "Failed to get stage: #{ex}"
|
48
|
+
end
|
49
|
+
|
50
|
+
desc 'create FILENAME', 'Create a new stage from a file'
|
51
|
+
long_desc <<-LONGDESC
|
52
|
+
Create a new stage. FILENAME should be the path to a valid Cyclid stage definition, in
|
53
|
+
either YAML or JSON format.
|
54
|
+
|
55
|
+
You can create multiple versions of a stage with the same name. You can either set the
|
56
|
+
version in the stage definition itself or use the --version option.
|
57
|
+
|
58
|
+
Cyclid will attempt to detect the format of the file automatically. You can force the
|
59
|
+
parsing format using either the --yaml or --json options.
|
60
|
+
|
61
|
+
The --yaml option causes the file to be parsed as YAML.
|
62
|
+
|
63
|
+
The --json option causes the file to be parsed as JSON.
|
64
|
+
LONGDESC
|
65
|
+
option :yaml, aliases: '-y'
|
66
|
+
option :json, aliases: '-j'
|
67
|
+
option :version, aliases: '-v'
|
68
|
+
def create(filename)
|
69
|
+
stage_file = File.expand_path(filename)
|
70
|
+
raise 'Cannot open file' unless File.exist?(stage_file)
|
71
|
+
|
72
|
+
stage_type = if options[:yaml]
|
73
|
+
'yaml'
|
74
|
+
elsif options[:json]
|
75
|
+
'json'
|
76
|
+
else
|
77
|
+
# Detect format
|
78
|
+
match = stage_file.match(/\A.*\.(json|yml|yaml)\z/)
|
79
|
+
match[1]
|
80
|
+
end
|
81
|
+
stage_type = 'yaml' if stage_type == 'yml'
|
82
|
+
|
83
|
+
# Do a client-side sanity check by attempting to parse the file; it
|
84
|
+
# will fail-fast if the file has a syntax error
|
85
|
+
stage = File.read(stage_file)
|
86
|
+
stage_data = if stage_type == 'yaml'
|
87
|
+
YAML.load(stage)
|
88
|
+
elsif stage_type == 'json'
|
89
|
+
JSON.parse(stage)
|
90
|
+
else
|
91
|
+
raise 'Unknown or unsupported file type'
|
92
|
+
end
|
93
|
+
|
94
|
+
# Inject the version if it was passed on the command line
|
95
|
+
stage_data['version'] = options[:version] if options[:version]
|
96
|
+
|
97
|
+
client.stage_create(client.config.organization, stage_data)
|
98
|
+
rescue StandardError => ex
|
99
|
+
abort "Failed to create stage: #{ex}"
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'edit NAME', 'Edit a stage definition'
|
103
|
+
long_desc <<-LONGDESC
|
104
|
+
Edit a stage. Individual stages are immutable, but you may create a new
|
105
|
+
version of an existing stage using this command.
|
106
|
+
LONGDESC
|
107
|
+
def edit(name)
|
108
|
+
stages = client.stage_get(client.config.organization, name)
|
109
|
+
|
110
|
+
# XXX This is a hack. The API returns all stages from this endpoint;
|
111
|
+
# we might need to add or extend the API to return "latest" only.
|
112
|
+
stage = stages.last
|
113
|
+
|
114
|
+
stage = invoke_editor(stage)
|
115
|
+
client.stage_modify(client.config.organization, stage)
|
116
|
+
rescue StandardError => ex
|
117
|
+
abort "Failed to edit stage: #{ex}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|