uffizzi-cli 0.7.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9ab52c8638dc3d5a8f6245ba337da95fcf986e39040458add0c1128857014f8
4
- data.tar.gz: b897ce235bc887bd161a7262fecddc28228b523b167338b599051ba370b704d3
3
+ metadata.gz: 135c59012b429afe86c2ba348e8d2e5e73562b7a10531d8e2ed329c7eac09929
4
+ data.tar.gz: 8865727d98b597fc622324e7b9d3afdf410bedb00e0b2203448e4023e8896da3
5
5
  SHA512:
6
- metadata.gz: a2976d6389a9a1d5c9d53ab66e34de98c391b250e2b4e5ebf636189b12f7e7a366e5140ef1c8583ce952d68fd8734600945402b6e38b10dea28700b71eb2bcf6
7
- data.tar.gz: 78aa15c2dd0f21febc64c73c2a77e86de98ccda9a90278069ced243f3264f74240d5530aa67828515a4e03f858fb3e2d1d5d1713eca63127d027cbd4b3ea89ac
6
+ metadata.gz: 6cd3dd4875c77bf331f4ab5934f5f6f347288b2c9d0a144e79a943bfca8b54bee4a7ccfb1ddab2e782e83555e085f685547cffedeb03df7221bfc9505cbbe01d
7
+ data.tar.gz: e48f80757a18e246145b4d08dd4d33b8736db90228d9206026f61dc00a46bdf0c0899fb35d7301f0fff09ec391f7f6611f5a6cf7feae42e28a1baafff81c7b85
data/README.md CHANGED
@@ -177,3 +177,9 @@ Supported credential types - `docker-hub`, `acr`, `ecr`, `gcr`
177
177
  ## Contributing
178
178
 
179
179
  Bug reports and pull requests are welcome on GitHub at https://github.com/UffizziCloud/uffizzi_cli. See `CONTRIBUTING.md` in this repository.
180
+
181
+ ## License
182
+
183
+ This project is licensed under the terms of the Apache-2.0 license.
184
+
185
+ See this license at [`LICENSE`](LICENSE).
data/config/uffizzi.rb CHANGED
@@ -18,7 +18,7 @@ module Uffizzi
18
18
  azure: 'UffizziCore::Credential::Azure',
19
19
  google: 'UffizziCore::Credential::Google',
20
20
  amazon: 'UffizziCore::Credential::Amazon',
21
- github_container_registry: 'UffizziCore::Credential::GithubContainerRegistry',
21
+ github_registry: 'UffizziCore::Credential::GithubContainerRegistry',
22
22
  }
23
23
  config.default_server = 'app.uffizzi.com'
24
24
  end
@@ -18,12 +18,14 @@ module Uffizzi
18
18
  end
19
19
 
20
20
  desc 'docker-hub', 'Connect to Docker Hub (hub.docker.com)'
21
+ method_option :skip_raise_existence_error, type: :boolean, default: false,
22
+ desc: 'Skip raising an error within check the credential'
21
23
  def docker_hub
22
24
  type = Uffizzi.configuration.credential_types[:dockerhub]
23
- check_credential_existance(type, 'docker-hub')
25
+ check_credential_existence(type, 'docker-hub')
24
26
 
25
- username = Uffizzi.ui.ask('Username: ')
26
- password = Uffizzi.ui.ask('Password: ', echo: false)
27
+ username = ENV['DOCKERHUB_USERNAME'] || Uffizzi.ui.ask('Username: ')
28
+ password = ENV['DOCKERHUB_PASSWORD'] || Uffizzi.ui.ask('Password: ', echo: false)
27
29
 
28
30
  params = {
29
31
  username: username,
@@ -42,18 +44,20 @@ module Uffizzi
42
44
  end
43
45
 
44
46
  desc 'acr', 'Connect to Azure Container Registry (azurecr.io)'
47
+ method_option :skip_raise_existence_error, type: :boolean, default: false,
48
+ desc: 'Skip raising an error within check the credential'
45
49
  def acr
46
50
  type = Uffizzi.configuration.credential_types[:azure]
47
- check_credential_existance(type, 'acr')
51
+ check_credential_existence(type, 'acr')
48
52
 
49
- registry_url = prepare_registry_url(Uffizzi.ui.ask('Registry Domain: '))
50
- username = Uffizzi.ui.ask('Docker ID: ')
51
- password = Uffizzi.ui.ask('Password/Access Token: ', echo: false)
53
+ registry_url = ENV['ACR_REGISTRY_URL'] || Uffizzi.ui.ask('Registry Domain: ')
54
+ username = ENV['ACR_USERNAME'] || Uffizzi.ui.ask('Docker ID: ')
55
+ password = ENV['ACR_PASSWORD'] || Uffizzi.ui.ask('Password/Access Token: ', echo: false)
52
56
 
53
57
  params = {
54
58
  username: username,
55
59
  password: password,
56
- registry_url: registry_url,
60
+ registry_url: prepare_registry_url(registry_url),
57
61
  type: type,
58
62
  }
59
63
 
@@ -68,18 +72,20 @@ module Uffizzi
68
72
  end
69
73
 
70
74
  desc 'ecr', 'Connect to Amazon Elastic Container Registry'
75
+ method_option :skip_raise_existence_error, type: :boolean, default: false,
76
+ desc: 'Skip raising an error within check the credential'
71
77
  def ecr
72
78
  type = Uffizzi.configuration.credential_types[:amazon]
73
- check_credential_existance(type, 'ecr')
79
+ check_credential_existence(type, 'ecr')
74
80
 
75
- registry_url = prepare_registry_url(Uffizzi.ui.ask('Registry Domain: '))
76
- username = Uffizzi.ui.ask('Access key ID: ')
77
- password = Uffizzi.ui.ask('Secret access key: ', echo: false)
81
+ registry_url = ENV['AWS_REGISTRY_URL'] || Uffizzi.ui.ask('Registry Domain: ')
82
+ access_key = ENV['AWS_ACCESS_KEY_ID'] || Uffizzi.ui.ask('Access key ID: ')
83
+ secret_access_key = ENV['AWS_SECRET_ACCESS_KEY'] || Uffizzi.ui.ask('Secret access key: ', echo: false)
78
84
 
79
85
  params = {
80
- username: username,
81
- password: password,
82
- registry_url: registry_url,
86
+ username: access_key,
87
+ password: secret_access_key,
88
+ registry_url: prepare_registry_url(registry_url),
83
89
  type: type,
84
90
  }
85
91
 
@@ -94,17 +100,13 @@ module Uffizzi
94
100
  end
95
101
 
96
102
  desc 'gcr', 'Connect to Google Container Registry (gcr.io)'
103
+ method_option :skip_raise_existence_error, type: :boolean, default: false,
104
+ desc: 'Skip raising an error within check the credential'
97
105
  def gcr(credential_file_path = nil)
98
106
  type = Uffizzi.configuration.credential_types[:google]
99
- check_credential_existance(type, 'gcr')
107
+ check_credential_existence(type, 'gcr')
100
108
 
101
- return Uffizzi.ui.say('Path to google service account key file wasn\'t specified.') if credential_file_path.nil?
102
-
103
- begin
104
- credential_content = File.read(credential_file_path)
105
- rescue Errno::ENOENT => e
106
- return Uffizzi.ui.say(e)
107
- end
109
+ credential_content = google_service_account_content(credential_file_path)
108
110
 
109
111
  params = {
110
112
  password: credential_content,
@@ -122,12 +124,14 @@ module Uffizzi
122
124
  end
123
125
 
124
126
  desc 'ghcr', 'Connect to GitHub Container Registry (ghcr.io)'
127
+ method_option :skip_raise_existence_error, type: :boolean, default: false,
128
+ desc: 'Skip raising an error within check the credential'
125
129
  def ghcr
126
- type = Uffizzi.configuration.credential_types[:github_container_registry]
127
- check_credential_existance(type, 'gchr')
130
+ type = Uffizzi.configuration.credential_types[:github_registry]
131
+ check_credential_existence(type, 'ghcr')
128
132
 
129
- username = Uffizzi.ui.ask('Github Username: ')
130
- password = Uffizzi.ui.ask('Access Token: ', echo: false)
133
+ username = ENV['GITHUB_USERNAME'] || Uffizzi.ui.ask('Github Username: ')
134
+ password = ENV['GITHUB_ACCESS_TOKEN'] || Uffizzi.ui.ask('Access Token: ', echo: false)
131
135
 
132
136
  params = {
133
137
  username: username,
@@ -160,14 +164,19 @@ module Uffizzi
160
164
  Uffizzi.ui.say("Successfully connected to #{connection_name}")
161
165
  end
162
166
 
163
- def check_credential_existance(type, connection_name)
167
+ def check_credential_existence(type, connection_name)
164
168
  server = ConfigFile.read_option(:server)
165
169
  response = check_credential(server, type)
166
170
  return if ResponseHelper.ok?(response)
167
171
 
168
- message = "Credentials of type #{connection_name} already exist for this account. " \
169
- "To remove them, run $ uffizzi disconnect #{connection_name}."
170
- raise Uffizzi::Error.new(message)
172
+ if options.skip_raise_existence_error?
173
+ Uffizzi.ui.say("Credentials of type #{connection_name} already exist for this account.")
174
+ exit(true)
175
+ else
176
+ message = "Credentials of type #{connection_name} already exist for this account. " \
177
+ "To remove them, run $ uffizzi disconnect #{connection_name}."
178
+ raise Uffizzi::Error.new(message)
179
+ end
171
180
  end
172
181
 
173
182
  def handle_list_credentials_success(response)
@@ -189,5 +198,19 @@ module Uffizzi
189
198
 
190
199
  map[credential]
191
200
  end
201
+
202
+ def google_service_account_content(credential_file_path = nil)
203
+ return ENV['GCLOUD_SERVICE_KEY'] if ENV['GCLOUD_SERVICE_KEY']
204
+
205
+ return Uffizzi.ui.say('Path to google service account key file wasn\'t specified.') if credential_file_path.nil?
206
+
207
+ begin
208
+ credential_content = File.read(credential_file_path)
209
+ rescue Errno::ENOENT => e
210
+ raise Uffizzi::Error.new(e.message)
211
+ end
212
+
213
+ credential_content
214
+ end
192
215
  end
193
216
  end
@@ -17,7 +17,7 @@ module Uffizzi
17
17
  when 'gcr'
18
18
  Uffizzi.configuration.credential_types[:google]
19
19
  when 'ghcr'
20
- Uffizzi.configuration.credential_types[:github_container_registry]
20
+ Uffizzi.configuration.credential_types[:github_registry]
21
21
  else
22
22
  raise Uffizzi::Error.new('Unsupported credential type.')
23
23
  end
@@ -23,7 +23,7 @@ module Uffizzi
23
23
  run('create', file_path: file_path)
24
24
  end
25
25
 
26
- desc 'uffizzi preview update [DEPLOYMENT_ID] [COMPOSE_FILE]', 'create'
26
+ desc 'uffizzi preview update [DEPLOYMENT_ID] [COMPOSE_FILE]', 'Update a preview'
27
27
  method_option :output, required: false, type: :string, aliases: '-o', enum: ['json', 'github-action']
28
28
  def update(deployment_name, file_path)
29
29
  run('update', deployment_name: deployment_name, file_path: file_path)
@@ -86,33 +86,39 @@ module Uffizzi
86
86
  params = file_path.nil? ? {} : prepare_params(file_path)
87
87
  response = create_deployment(ConfigFile.read_option(:server), project_slug, params)
88
88
 
89
- if ResponseHelper.created?(response)
90
- deployment = response[:body][:deployment]
91
- success_message = "Preview created with name deployment-#{deployment[:id]}"
92
- PreviewService.start_deploy_containers(project_slug, deployment, success_message)
93
- else
89
+ if !ResponseHelper.created?(response)
94
90
  ResponseHelper.handle_failed_response(response)
95
91
  end
92
+
93
+ deployment = response[:body][:deployment]
94
+ Uffizzi.ui.say("Preview created with name deployment-#{deployment[:id]}")
95
+
96
+ success = PreviewService.run_containers_deploy(project_slug, deployment)
97
+
98
+ display_deployment_data(deployment, success)
96
99
  rescue SystemExit, Interrupt, SocketError
97
100
  deployment_id = response[:body][:deployment][:id]
98
- handle_preview_interruption(deployment_id, hostname, project_slug)
101
+ handle_preview_interruption(deployment_id, ConfigFile.read_option(:server), project_slug)
99
102
  end
100
103
 
101
104
  def handle_update_command(deployment_name, file_path, project_slug)
102
105
  deployment_id = PreviewService.read_deployment_id(deployment_name)
103
106
 
104
- return Uffizzi.ui.say("Preview should be specified in 'deployment-PREVIEW_ID' format") if deployment_id.nil?
107
+ raise Uffizzi::Error.new("Preview should be specified in 'deployment-PREVIEW_ID' format") if deployment_id.nil?
105
108
 
106
109
  params = prepare_params(file_path)
107
110
  response = update_deployment(ConfigFile.read_option(:server), project_slug, deployment_id, params)
108
111
 
109
- if ResponseHelper.ok?(response)
110
- deployment = response[:body][:deployment]
111
- success_message = "Preview with ID deployment-#{deployment_id} was successfully updated."
112
- PreviewService.start_deploy_containers(project_slug, deployment, success_message)
113
- else
112
+ if !ResponseHelper.ok?(response)
114
113
  ResponseHelper.handle_failed_response(response)
115
114
  end
115
+
116
+ deployment = response[:body][:deployment]
117
+ Uffizzi.ui.say("Preview with ID deployment-#{deployment_id} was successfully updated.")
118
+
119
+ success = PreviewService.run_containers_deploy(project_slug, deployment)
120
+
121
+ display_deployment_data(deployment, success)
116
122
  end
117
123
 
118
124
  def handle_events_command(deployment_name, project_slug)
@@ -218,8 +224,8 @@ module Uffizzi
218
224
  }
219
225
  end
220
226
 
221
- def handle_preview_interruption(deployment_id, hostname, project_slug)
222
- deletion_response = delete_deployment(hostname, project_slug, deployment_id)
227
+ def handle_preview_interruption(deployment_id, server, project_slug)
228
+ deletion_response = delete_deployment(server, project_slug, deployment_id)
223
229
  deployment_name = "deployment-#{deployment_id}"
224
230
  preview_deletion_message = if ResponseHelper.no_content?(deletion_response)
225
231
  "The preview #{deployment_name} has been disabled."
@@ -229,5 +235,23 @@ module Uffizzi
229
235
 
230
236
  raise Uffizzi::Error.new("The preview creation was interrupted. #{preview_deletion_message}")
231
237
  end
238
+
239
+ def display_deployment_data(deployment, success)
240
+ if Uffizzi.ui.output_format.nil?
241
+ Uffizzi.ui.say('Done')
242
+ preview_url = "https://#{deployment[:preview_url]}"
243
+ Uffizzi.ui.say(preview_url) if success
244
+ else
245
+ deployment_data = build_deployment_data(deployment)
246
+ Uffizzi.ui.output(deployment_data)
247
+ end
248
+ end
249
+
250
+ def build_deployment_data(deployment)
251
+ {
252
+ id: "deployment-#{deployment[:id]}",
253
+ url: "https://#{deployment[:preview_url]}",
254
+ }
255
+ end
232
256
  end
233
257
  end
@@ -3,6 +3,8 @@
3
3
  require 'uffizzi'
4
4
  require 'uffizzi/auth_helper'
5
5
  require 'uffizzi/response_helper'
6
+ require 'uffizzi/helpers/project_helper'
7
+ require 'uffizzi/services/project_service'
6
8
 
7
9
  module Uffizzi
8
10
  class Cli::Project < Thor
@@ -27,8 +29,27 @@ module Uffizzi
27
29
  run('set-default', project_slug: project_slug)
28
30
  end
29
31
 
32
+ desc 'describe [PROJECT_SLUG]', 'describe'
33
+ method_option :output, type: :string, aliases: '-o', enum: ['json', 'pretty'], default: 'json'
34
+ def describe(project_slug)
35
+ run('describe', project_slug: project_slug)
36
+ end
37
+
30
38
  map('set-default' => :set_default)
31
39
 
40
+ method_option :name, required: true
41
+ method_option :slug, default: ''
42
+ method_option :description, required: false
43
+ desc 'create', 'Create a project'
44
+ def create
45
+ run('create')
46
+ end
47
+
48
+ desc 'delete [PROJECT_SLUG]', 'Delete a project'
49
+ def delete(project_slug)
50
+ run('delete', project_slug: project_slug)
51
+ end
52
+
32
53
  private
33
54
 
34
55
  def run(command, project_slug: nil)
@@ -39,21 +60,74 @@ module Uffizzi
39
60
  handle_list_command
40
61
  when 'set-default'
41
62
  handle_set_default_command(project_slug)
63
+ when 'create'
64
+ handle_create_command
65
+ when 'delete'
66
+ handle_delete_command(project_slug)
67
+ when 'describe'
68
+ handle_describe_command(project_slug)
42
69
  end
43
70
  end
44
71
 
72
+ def handle_describe_command(project_slug)
73
+ response = describe_project(ConfigFile.read_option(:server), project_slug)
74
+
75
+ if ResponseHelper.ok?(response)
76
+ handle_succeed_describe_response(response)
77
+ else
78
+ ResponseHelper.handle_failed_response(response)
79
+ end
80
+ end
81
+
82
+ def handle_succeed_describe_response(response)
83
+ project = response[:body][:project]
84
+ project[:deployments] = ProjectService.select_active_deployments(project)
85
+ ProjectService.describe_project(project, options[:output])
86
+ end
87
+
45
88
  def handle_list_command
46
89
  server = ConfigFile.read_option(:server)
47
90
  response = fetch_projects(server)
48
91
 
49
92
  if ResponseHelper.ok?(response)
50
- handle_succeed_response(response)
93
+ handle_list_success_response(response)
51
94
  else
52
95
  ResponseHelper.handle_failed_response(response)
53
96
  end
54
97
  end
55
98
 
56
- def handle_succeed_response(response)
99
+ def handle_create_command
100
+ name = options[:name]
101
+ slug = options[:slug].empty? ? Uffizzi::ProjectHelper.generate_slug(name) : options[:slug]
102
+ raise Uffizzi::Error.new('Slug must not content spaces or special characters') unless slug.match?(/^[a-zA-Z0-9\-_]+\Z/i)
103
+
104
+ server = ConfigFile.read_option(:server)
105
+ params = {
106
+ name: name,
107
+ description: options[:description],
108
+ slug: slug,
109
+ }
110
+ response = create_project(server, params)
111
+
112
+ if ResponseHelper.created?(response)
113
+ handle_create_success_response(response)
114
+ else
115
+ ResponseHelper.handle_failed_response(response)
116
+ end
117
+ end
118
+
119
+ def handle_delete_command(project_slug)
120
+ server = ConfigFile.read_option(:server)
121
+ response = delete_project(server, project_slug)
122
+
123
+ if ResponseHelper.no_content?(response)
124
+ Uffizzi.ui.say("Project with slug #{project_slug} was deleted successfully")
125
+ else
126
+ ResponseHelper.handle_failed_response(response)
127
+ end
128
+ end
129
+
130
+ def handle_list_success_response(response)
57
131
  projects = response[:body][:projects]
58
132
  return Uffizzi.ui.say('No projects related to this email') if projects.empty?
59
133
 
@@ -76,6 +150,12 @@ module Uffizzi
76
150
  Uffizzi.ui.say('Default project has been updated.')
77
151
  end
78
152
 
153
+ def handle_create_success_response(response)
154
+ project_name = response[:body][:project][:name]
155
+
156
+ Uffizzi.ui.say("Project #{project_name} was successfully created")
157
+ end
158
+
79
159
  def print_projects(projects)
80
160
  projects_list = projects.reduce('') do |acc, project|
81
161
  "#{acc}#{project[:slug]}\n"
data/lib/uffizzi/cli.rb CHANGED
@@ -62,8 +62,10 @@ module Uffizzi
62
62
  return Common.show_manual(filename(args)) if show_help?(args, opts)
63
63
 
64
64
  super
65
- rescue SystemExit, Interrupt, SocketError
65
+ rescue Interrupt
66
66
  raise Uffizzi::Error.new('The command was interrupted')
67
+ rescue SocketError
68
+ raise Uffizzi::Error.new('A request was not sent to Uffizzi app')
67
69
  end
68
70
 
69
71
  private
@@ -44,7 +44,21 @@ module ApiClient
44
44
 
45
45
  def describe_project(server, project_slug)
46
46
  uri = project_uri(server, project_slug)
47
- response = Uffizzi::HttpClient.make_get_request(uri)
47
+ response = http_client.make_get_request(uri)
48
+
49
+ build_response(response)
50
+ end
51
+
52
+ def create_project(server, params)
53
+ uri = projects_uri(server)
54
+ response = http_client.make_post_request(uri, params)
55
+
56
+ build_response(response)
57
+ end
58
+
59
+ def delete_project(server, project_slug)
60
+ uri = project_uri(server, project_slug)
61
+ response = http_client.make_delete_request(uri)
48
62
 
49
63
  build_response(response)
50
64
  end
@@ -8,17 +8,21 @@ module Uffizzi
8
8
  CONFIG_PATH = "#{Dir.home}/.config/uffizzi/config_default.json"
9
9
 
10
10
  class << self
11
+ def config_path
12
+ CONFIG_PATH
13
+ end
14
+
11
15
  def create(account_id, cookie, server)
12
16
  data = prepare_config_data(account_id, cookie, server)
13
17
  data.each_pair { |key, value| write_option(key, value) }
14
18
  end
15
19
 
16
20
  def delete
17
- File.delete(CONFIG_PATH) if exists?
21
+ File.delete(config_path) if exists?
18
22
  end
19
23
 
20
24
  def exists?
21
- File.exist?(CONFIG_PATH)
25
+ File.exist?(config_path)
22
26
  end
23
27
 
24
28
  def read_option(option)
@@ -79,7 +83,7 @@ module Uffizzi
79
83
  end
80
84
 
81
85
  def read
82
- data = File.read(CONFIG_PATH)
86
+ data = File.read(config_path)
83
87
  options = data.split("\n")
84
88
  options.reduce({}) do |acc, option|
85
89
  key, value = option.split('=', 2)
@@ -112,11 +116,11 @@ module Uffizzi
112
116
  end
113
117
 
114
118
  def create_file
115
- dir = File.dirname(CONFIG_PATH)
119
+ dir = File.dirname(config_path)
116
120
 
117
121
  FileUtils.mkdir_p(dir) unless File.directory?(dir)
118
122
 
119
- File.new(CONFIG_PATH, 'w')
123
+ File.new(config_path, 'w')
120
124
  end
121
125
  end
122
126
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uffizzi
4
+ module ProjectHelper
5
+ SLUG_ENDING_LENGTH = 6
6
+ class << self
7
+ def generate_slug(name)
8
+ formatted_name = name.downcase.gsub(/ /, '-').gsub(/[^\w-]+/, '')
9
+ slug_ending = generate_random_string(SLUG_ENDING_LENGTH)
10
+
11
+ "#{formatted_name}-#{slug_ending}"
12
+ end
13
+
14
+ private
15
+
16
+ def generate_random_string(length)
17
+ hexatridecimal_base = 36
18
+ rand(hexatridecimal_base**length).to_s(hexatridecimal_base)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -5,6 +5,7 @@ require 'uffizzi/clients/api/api_client'
5
5
  class PreviewService
6
6
  class << self
7
7
  include ApiClient
8
+
8
9
  def read_deployment_id(deployment_name)
9
10
  return nil unless deployment_name.start_with?('deployment-')
10
11
  return nil unless deployment_name.split('-').size == 2
@@ -15,23 +16,23 @@ class PreviewService
15
16
  deployment_id
16
17
  end
17
18
 
18
- def start_deploy_containers(project_slug, deployment, success_message)
19
+ def run_containers_deploy(project_slug, deployment)
19
20
  deployment_id = deployment[:id]
20
21
  params = { id: deployment_id }
21
22
 
22
23
  response = deploy_containers(Uffizzi::ConfigFile.read_option(:server), project_slug, deployment_id, params)
23
24
 
24
- if Uffizzi::ResponseHelper.no_content?(response)
25
- Uffizzi.ui.say(success_message)
26
- create_deployment(deployment, project_slug)
27
- else
25
+ if !Uffizzi::ResponseHelper.no_content?(response)
28
26
  Uffizzi::ResponseHelper.handle_failed_response(response)
29
27
  end
28
+
29
+ activity_items = wait_containers_creation(deployment, project_slug)
30
+ wait_containers_deploy(deployment, project_slug, activity_items)
30
31
  end
31
32
 
32
33
  private
33
34
 
34
- def create_deployment(deployment, project_slug)
35
+ def wait_containers_creation(deployment, project_slug)
35
36
  spinner = TTY::Spinner.new('[:spinner] Creating containers...', format: :dots)
36
37
  spinner.auto_spin
37
38
 
@@ -49,10 +50,10 @@ class PreviewService
49
50
 
50
51
  Uffizzi.ui.say('Done')
51
52
 
52
- display_containers_deploying_status(deployment, project_slug, activity_items)
53
+ activity_items
53
54
  end
54
55
 
55
- def display_containers_deploying_status(deployment, project_slug, activity_items)
56
+ def wait_containers_deploy(deployment, project_slug, activity_items)
56
57
  spinner = TTY::Spinner::Multi.new('[:spinner] Deploying preview...', format: :dots, style: {
57
58
  middle: ' ',
58
59
  bottom: ' ',
@@ -64,20 +65,13 @@ class PreviewService
64
65
  response = get_activity_items(Uffizzi::ConfigFile.read_option(:server), project_slug, deployment[:id])
65
66
  handle_activity_items_response(response, spinner)
66
67
  activity_items = response[:body][:activity_items]
67
- check_activity_items_state(activity_items, containers_spinners)
68
+ update_containers_spinners!(activity_items, containers_spinners)
68
69
  break if activity_items.all? { |activity_item| activity_item[:state] == 'deployed' || activity_item[:state] == 'failed' }
69
70
 
70
71
  sleep(5)
71
72
  end
72
73
 
73
- if Uffizzi.ui.output_format.nil?
74
- Uffizzi.ui.say('Done')
75
- preview_url = "https://#{deployment[:preview_url]}"
76
- Uffizzi.ui.say(preview_url) if spinner.success?
77
- else
78
- output_data = build_output_data(deployment)
79
- Uffizzi.ui.output(output_data)
80
- end
74
+ spinner.success?
81
75
  end
82
76
 
83
77
  def create_containers_spinners(activity_items, spinner)
@@ -91,10 +85,11 @@ class PreviewService
91
85
  end
92
86
  end
93
87
 
94
- def check_activity_items_state(activity_items, containers_spinners)
88
+ def update_containers_spinners!(activity_items, containers_spinners)
95
89
  finished_activity_items = activity_items.filter do |activity_item|
96
90
  activity_item[:state] == 'deployed' || activity_item[:state] == 'failed'
97
91
  end
92
+
98
93
  finished_activity_items.each do |activity_item|
99
94
  container_spinner = containers_spinners.detect { |spinner| spinner[:name] == activity_item[:name] }
100
95
  spinner = container_spinner[:spinner]
@@ -113,12 +108,5 @@ class PreviewService
113
108
  Uffizzi::ResponseHelper.handle_failed_response(response)
114
109
  end
115
110
  end
116
-
117
- def build_output_data(output_data)
118
- {
119
- id: "deployment-#{output_data[:id]}",
120
- url: "https://#{output_data[:preview_url]}",
121
- }
122
- end
123
111
  end
124
112
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ProjectService
4
+ class << self
5
+ def describe_project(project, output_format)
6
+ json_format?(output_format) ? output_in_json(project) : output_in_pretty_format(project)
7
+ end
8
+
9
+ def select_active_deployments(project)
10
+ project[:deployments].select { |deployment| deployment[:state] == 'active' }
11
+ end
12
+
13
+ private
14
+
15
+ def json_format?(output_format)
16
+ output_format == 'json'
17
+ end
18
+
19
+ def output_in_json(data)
20
+ Uffizzi.ui.say(data.to_json)
21
+ end
22
+
23
+ def output_in_pretty_format(project)
24
+ Uffizzi.ui.say("Project name: #{project[:name]}")
25
+ Uffizzi.ui.say("Project slug: #{project[:slug]}")
26
+ Uffizzi.ui.say("Description: #{project[:description]}".strip)
27
+ Uffizzi.ui.say("Created: #{project[:created_at]}")
28
+ default_compose = project[:default_compose].nil? ? nil : project[:default_compose][:source]
29
+ Uffizzi.ui.say("Default compose: #{default_compose}".strip)
30
+ Uffizzi.ui.say('Previews:')
31
+ project[:deployments].each do |deployment|
32
+ Uffizzi.ui.say(" - deployment-#{deployment[:id]} (https://#{deployment[:preview_url]})")
33
+ end
34
+ Uffizzi.ui.say('Secrets:')
35
+ project[:secrets].each do |secret|
36
+ Uffizzi.ui.say(" - #{secret}")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uffizzi
4
- VERSION = '0.7.2'
4
+ VERSION = '0.9.0'
5
5
  end
data/man/uffizzi-config CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "CONFIG" "" "April 2022" ""
3
+ .TH "CONFIG" "" "May 2022" ""
4
4
  .SH "NAME"
5
5
  \fBconfig\fR \- configure the Uffizzi command\-line interface (CLI)
6
6
  .SH "SYNOPSIS"
data/man/uffizzi-login CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-LOGIN" "" "April 2022" ""
3
+ .TH "UFFIZZI\-LOGIN" "" "May 2022" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-login\fR \- login to Uffizzi to view and manage your previews\.
6
6
  .SH "SYNOPSIS"
data/man/uffizzi-logout CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-LOGOUT" "" "March 2022" ""
3
+ .TH "UFFIZZI\-LOGOUT" "" "May 2022" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-logout\fR \- log out of a Uffizzi user account
6
6
  .SH "SYNOPSIS"
@@ -0,0 +1,50 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "UFFIZZI\-PROJECT\-CREATE" "" "May 2022" ""
4
+ .SH "NAME"
5
+ \fBuffizzi\-project\-create\fR \- create a new project
6
+ .SH "SYNOPSIS"
7
+ .nf
8
+ uffizzi project create [\-\-name=NAME] [\-\-slug=SLUG]
9
+ [\-\-description=DESCRIPTION] [UFFIZZI_WIDE_FLAG \|\.\|\.\|\.]
10
+ .fi
11
+ .SH "DESCRIPTION"
12
+ .nf
13
+ Create new project\.
14
+
15
+ This command can fail for the following reasons:
16
+ \- The project \-\-description is too long\.
17
+ \- The project \-\-name flag is not specified\.
18
+ \- The project \-\-slug is malformed or used by another project\.
19
+
20
+ For more information on the uffizzi project command, see:
21
+ https:/github\.com/UffizziCloud/uffizzi_cli
22
+ .fi
23
+ .SH "FLAGS"
24
+ .nf
25
+ \-\-description=DESCRIPTION
26
+ Description for the project you want to create\. Max of 256
27
+ characters\.
28
+
29
+ \-\-name=NAME
30
+ Name for the project you want to create\.
31
+
32
+ \-\-slug=SLUG
33
+ A URL\-compatible name\. Do not include whitespaces or special characters\.
34
+ Project slugs must be globally unique across all Uffizzi projects\.
35
+ If a slug is not provided, Uffizzi will automatically generate one\.
36
+ .fi
37
+ .SH "UFFIZZI WIDE FLAGS"
38
+ .nf
39
+ These flags are available to all commands: \-\-project\. Run $ uffizzi
40
+ help for details\.
41
+ .fi
42
+ .SH "EXAMPLES"
43
+ .nf
44
+ To create a new project with name My App, project slug my\-app, and
45
+ description "My first project", run:
46
+
47
+ $ uffizzi project create \-\-name="My App" \-\-slug="my\-app" \e
48
+ \-\-description="My first project"
49
+ .fi
50
+
@@ -0,0 +1,41 @@
1
+ uffizzi-project-create - create a new project
2
+ ================================================================
3
+
4
+ ## SYNOPSIS
5
+ uffizzi project create [--name=NAME] [--slug=SLUG]
6
+ [--description=DESCRIPTION] [UFFIZZI_WIDE_FLAG ...]
7
+
8
+ ## DESCRIPTION
9
+ Create new project.
10
+
11
+ This command can fail for the following reasons:
12
+ - The project --description is too long.
13
+ - The project --name flag is not specified.
14
+ - The project --slug is malformed or used by another project.
15
+
16
+ For more information on the uffizzi project command, see:
17
+ https:/github.com/UffizziCloud/uffizzi_cli
18
+
19
+ ## FLAGS
20
+ --description=DESCRIPTION
21
+ Description for the project you want to create. Max of 256
22
+ characters.
23
+
24
+ --name=NAME
25
+ Name for the project you want to create.
26
+
27
+ --slug=SLUG
28
+ A URL-compatible name. Do not include whitespaces or special characters.
29
+ Project slugs must be globally unique across all Uffizzi projects.
30
+ If a slug is not provided, Uffizzi will automatically generate one.
31
+
32
+ ## UFFIZZI WIDE FLAGS
33
+ These flags are available to all commands: --project. Run $ uffizzi
34
+ help for details.
35
+
36
+ ## EXAMPLES
37
+ To create a new project with name My App, project slug my-app, and
38
+ description "My first project", run:
39
+
40
+ $ uffizzi project create --name="My App" --slug="my-app" \
41
+ --description="My first project"
@@ -0,0 +1,32 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "UFFIZZI\-PROJECT\-DELETE" "" "May 2022" ""
4
+ .SH "NAME"
5
+ \fBuffizzi\-project\-delete\fR \- delete a project
6
+ .SH "SYNOPSIS"
7
+ .nf
8
+ uffizzi project delete PROJECT_SLUG [UFFIZZI_WIDE_FLAG \|\.\|\.\|\.]
9
+ .fi
10
+ .SH "DESCRIPTION"
11
+ .nf
12
+ Deletes a project with the given project slug\.
13
+
14
+ This command can fail for the following reasons:
15
+ \- There is no project with the given project slug
16
+ \- The active account does not have permission to delete the project
17
+
18
+ For more information on Uffizzi projects, see:
19
+ https:/github\.com/UffizziCloud/uffizzi_cli
20
+ .fi
21
+ .SH "UFFIZZI WIDE FLAGS"
22
+ .nf
23
+ These flags are available to all commands: \-\-project\. Run $ uffizzi
24
+ help for details\.
25
+ .fi
26
+ .SH "EXAMPLES"
27
+ .nf
28
+ To delete a project with project slug my\-app\-xc8fw, run:
29
+
30
+ $ uffizzi project delete my\-app\-xc8fw
31
+ .fi
32
+
@@ -0,0 +1,24 @@
1
+ uffizzi-project-delete - delete a project
2
+ ================================================================
3
+
4
+ ## SYNOPSIS
5
+ uffizzi project delete PROJECT_SLUG [UFFIZZI_WIDE_FLAG ...]
6
+
7
+ ## DESCRIPTION
8
+ Deletes a project with the given project slug.
9
+
10
+ This command can fail for the following reasons:
11
+ - There is no project with the given project slug
12
+ - The active account does not have permission to delete the project
13
+
14
+ For more information on Uffizzi projects, see:
15
+ https:/github.com/UffizziCloud/uffizzi_cli
16
+
17
+ ## UFFIZZI WIDE FLAGS
18
+ These flags are available to all commands: --project. Run $ uffizzi
19
+ help for details.
20
+
21
+ ## EXAMPLES
22
+ To delete a project with project slug my-app-xc8fw, run:
23
+
24
+ $ uffizzi project delete my-app-xc8fw
@@ -0,0 +1,43 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "DESCRIBE" "" "May 2022" ""
4
+ .SH "NAME"
5
+ \fBuffizzi project describe\fR \- show metadata for a project
6
+ .SH "SYNOPSIS"
7
+ .nf
8
+ uffizzi project describe PROJECT_SLUG [UFFIZZI_WIDE_FLAG \|\.\|\.\|\.]
9
+ .fi
10
+ .SH "DESCRIPTION"
11
+ .nf
12
+ Shows metadata for a project given a valid project slug\.
13
+
14
+ This command can fail for the following reasons:
15
+ \- There is no project with the given project slug
16
+
17
+ For more information on Uffizzi projects, see:
18
+ https://github\.com/UffizziCloud/uffizzi_cli
19
+ .fi
20
+ .SH "UFFIZZI WIDE FLAGS"
21
+ .nf
22
+ \-\-output FORMAT
23
+ Output format\. FORMAT is one of the following: json, pretty
24
+ Default is json\.
25
+ .fi
26
+ .SH "UFFIZZI WIDE FLAGS"
27
+ .nf
28
+ These flags are available to all commands: \-\-project\. Run $ uffizzi
29
+ help for details\.
30
+ .fi
31
+ .SH "EXAMPLES"
32
+ .nf
33
+ The following command prints metadata in json format for a project with the
34
+ project slug my\-app\-xc8fw:
35
+
36
+ $ uffizzi project describe my\-app\-xc8fw
37
+
38
+ The following command pretty prints metadata for a project with the project
39
+ slug my\-app\-xc8fw:
40
+
41
+ $ uffizzi project describe my\-app\-xc8fw \-\-output pretty
42
+ .fi
43
+
@@ -0,0 +1,34 @@
1
+ uffizzi project describe - show metadata for a project
2
+ ================================================================
3
+
4
+ ## SYNOPSIS
5
+ uffizzi project describe PROJECT_SLUG [UFFIZZI_WIDE_FLAG ...]
6
+
7
+ ## DESCRIPTION
8
+ Shows metadata for a project given a valid project slug.
9
+
10
+ This command can fail for the following reasons:
11
+ - There is no project with the given project slug
12
+
13
+ For more information on Uffizzi projects, see:
14
+ https://github.com/UffizziCloud/uffizzi_cli
15
+
16
+ ## UFFIZZI WIDE FLAGS
17
+ --output FORMAT
18
+ Output format. FORMAT is one of the following: json, pretty
19
+ Default is json.
20
+
21
+ ## UFFIZZI WIDE FLAGS
22
+ These flags are available to all commands: --project. Run $ uffizzi
23
+ help for details.
24
+
25
+ ## EXAMPLES
26
+ The following command prints metadata in json format for a project with the
27
+ project slug my-app-xc8fw:
28
+
29
+ $ uffizzi project describe my-app-xc8fw
30
+
31
+ The following command pretty prints metadata for a project with the project
32
+ slug my-app-xc8fw:
33
+
34
+ $ uffizzi project describe my-app-xc8fw --output pretty
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uffizzi-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Thurman
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-05-13 00:00:00.000000000 Z
12
+ date: 2022-05-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: awesome_print
@@ -323,11 +323,13 @@ files:
323
323
  - lib/uffizzi/config_file.rb
324
324
  - lib/uffizzi/date_helper.rb
325
325
  - lib/uffizzi/error.rb
326
+ - lib/uffizzi/helpers/project_helper.rb
326
327
  - lib/uffizzi/response_helper.rb
327
328
  - lib/uffizzi/services/command_service.rb
328
329
  - lib/uffizzi/services/compose_file_service.rb
329
330
  - lib/uffizzi/services/env_variables_service.rb
330
331
  - lib/uffizzi/services/preview_service.rb
332
+ - lib/uffizzi/services/project_service.rb
331
333
  - lib/uffizzi/shell.rb
332
334
  - lib/uffizzi/version.rb
333
335
  - man/uffizzi
@@ -394,6 +396,12 @@ files:
394
396
  - man/uffizzi-project-compose-unset.ronn
395
397
  - man/uffizzi-project-compose.html
396
398
  - man/uffizzi-project-compose.ronn
399
+ - man/uffizzi-project-create
400
+ - man/uffizzi-project-create.ronn
401
+ - man/uffizzi-project-delete
402
+ - man/uffizzi-project-delete.ronn
403
+ - man/uffizzi-project-describe
404
+ - man/uffizzi-project-describe.ronn
397
405
  - man/uffizzi-project-secret
398
406
  - man/uffizzi-project-secret-create
399
407
  - man/uffizzi-project-secret-create.html