uffizzi-cli 0.7.3 → 0.10.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -0
  3. data/config/uffizzi.rb +1 -1
  4. data/lib/uffizzi/auth_helper.rb +5 -0
  5. data/lib/uffizzi/cli/config.rb +1 -1
  6. data/lib/uffizzi/cli/connect.rb +54 -31
  7. data/lib/uffizzi/cli/disconnect.rb +1 -1
  8. data/lib/uffizzi/cli/login.rb +76 -2
  9. data/lib/uffizzi/cli/logout.rb +1 -1
  10. data/lib/uffizzi/cli/preview.rb +43 -16
  11. data/lib/uffizzi/cli/project.rb +82 -2
  12. data/lib/uffizzi/cli.rb +3 -1
  13. data/lib/uffizzi/clients/api/api_client.rb +15 -1
  14. data/lib/uffizzi/config_file.rb +14 -20
  15. data/lib/uffizzi/helpers/project_helper.rb +22 -0
  16. data/lib/uffizzi/promt.rb +21 -0
  17. data/lib/uffizzi/services/preview_service.rb +13 -25
  18. data/lib/uffizzi/services/project_service.rb +40 -0
  19. data/lib/uffizzi/version.rb +1 -1
  20. data/lib/uffizzi.rb +5 -0
  21. data/man/uffizzi +2 -2
  22. data/man/uffizzi-config +2 -2
  23. data/man/uffizzi-config.ronn +1 -1
  24. data/man/uffizzi-connect +2 -2
  25. data/man/uffizzi-connect-acr +2 -2
  26. data/man/uffizzi-connect-acr.ronn +1 -1
  27. data/man/uffizzi-connect-docker-hub +2 -2
  28. data/man/uffizzi-connect-docker-hub.ronn +1 -1
  29. data/man/uffizzi-connect-ecr +2 -2
  30. data/man/uffizzi-connect-ecr.ronn +1 -1
  31. data/man/uffizzi-connect-gcr +2 -2
  32. data/man/uffizzi-connect-gcr.ronn +1 -1
  33. data/man/uffizzi-connect-ghcr +2 -2
  34. data/man/uffizzi-connect-ghcr.ronn +1 -1
  35. data/man/uffizzi-connect.ronn +1 -1
  36. data/man/uffizzi-disconnect +2 -2
  37. data/man/uffizzi-disconnect.ronn +1 -1
  38. data/man/uffizzi-login +2 -2
  39. data/man/uffizzi-login.ronn +1 -1
  40. data/man/uffizzi-logout +2 -2
  41. data/man/uffizzi-logout.ronn +1 -1
  42. data/man/uffizzi-preview +2 -2
  43. data/man/uffizzi-preview-create +2 -2
  44. data/man/uffizzi-preview-create.ronn +1 -1
  45. data/man/uffizzi-preview-delete +2 -2
  46. data/man/uffizzi-preview-delete.ronn +1 -1
  47. data/man/uffizzi-preview-describe +2 -2
  48. data/man/uffizzi-preview-describe.ronn +1 -1
  49. data/man/uffizzi-preview-events +2 -2
  50. data/man/uffizzi-preview-events.ronn +1 -1
  51. data/man/uffizzi-preview-list +2 -2
  52. data/man/uffizzi-preview-list.ronn +1 -1
  53. data/man/uffizzi-preview-service-list +2 -2
  54. data/man/uffizzi-preview-service-list.ronn +1 -1
  55. data/man/uffizzi-preview-service-logs +2 -2
  56. data/man/uffizzi-preview-service-logs.ronn +1 -1
  57. data/man/uffizzi-preview-update +20 -12
  58. data/man/uffizzi-preview-update.ronn +6 -6
  59. data/man/uffizzi-preview.ronn +1 -1
  60. data/man/uffizzi-preview_service_logs +2 -2
  61. data/man/uffizzi-preview_service_logs.ronn +1 -1
  62. data/man/uffizzi-project +2 -2
  63. data/man/uffizzi-project-compose +2 -2
  64. data/man/uffizzi-project-compose-describe +2 -2
  65. data/man/uffizzi-project-compose-describe.ronn +1 -1
  66. data/man/uffizzi-project-compose-set +2 -2
  67. data/man/uffizzi-project-compose-set.ronn +1 -1
  68. data/man/uffizzi-project-compose-unset +2 -2
  69. data/man/uffizzi-project-compose-unset.ronn +1 -1
  70. data/man/uffizzi-project-compose.ronn +1 -1
  71. data/man/uffizzi-project-create +50 -0
  72. data/man/uffizzi-project-create.ronn +41 -0
  73. data/man/uffizzi-project-delete +32 -0
  74. data/man/uffizzi-project-delete.ronn +24 -0
  75. data/man/uffizzi-project-describe +43 -0
  76. data/man/uffizzi-project-describe.ronn +34 -0
  77. data/man/uffizzi-project-secret +2 -2
  78. data/man/uffizzi-project-secret-create +2 -2
  79. data/man/uffizzi-project-secret-create.ronn +1 -1
  80. data/man/uffizzi-project-secret-delete +2 -2
  81. data/man/uffizzi-project-secret-delete.ronn +1 -1
  82. data/man/uffizzi-project-secret-list +2 -2
  83. data/man/uffizzi-project-secret-list.ronn +1 -1
  84. data/man/uffizzi-project-secret.ronn +1 -1
  85. data/man/uffizzi-project-set-default +2 -2
  86. data/man/uffizzi-project-set-default.ronn +1 -1
  87. data/man/uffizzi-project.ronn +1 -1
  88. data/man/uffizzi.html +1 -1
  89. data/man/uffizzi.ronn +1 -1
  90. metadata +25 -22
  91. data/man/uffizzi-config.html +0 -144
  92. data/man/uffizzi-login.html +0 -113
  93. data/man/uffizzi-logout.html +0 -102
  94. data/man/uffizzi-preview-create.html +0 -128
  95. data/man/uffizzi-preview-delete.html +0 -115
  96. data/man/uffizzi-preview-describe.html +0 -116
  97. data/man/uffizzi-preview-events.html +0 -110
  98. data/man/uffizzi-preview-list.html +0 -110
  99. data/man/uffizzi-preview.html +0 -120
  100. data/man/uffizzi-preview_service_logs.html +0 -142
  101. data/man/uffizzi-project-compose-describe.html +0 -118
  102. data/man/uffizzi-project-compose-set.html +0 -149
  103. data/man/uffizzi-project-compose-unset.html +0 -116
  104. data/man/uffizzi-project-compose.html +0 -123
  105. data/man/uffizzi-project-secret-create.html +0 -110
  106. data/man/uffizzi-project-secret-delete.html +0 -110
  107. data/man/uffizzi-project-secret-list.html +0 -110
  108. data/man/uffizzi-project-secret.html +0 -119
  109. data/man/uffizzi-project-set-default.html +0 -111
  110. data/man/uffizzi-project.html +0 -128
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 989c225e4940286c4f59590bb5c7141a8dc3f599f3f1a42db6bc90142a0bc68e
4
- data.tar.gz: 3fb2ef986ac257598cd09f5353fe9361a9d06ad8c0b8f665e4c911c40fb63ddc
3
+ metadata.gz: e1bc2ece2185acfc991e128222d85edd8f23c3dba70797090a4e0c26cbad3104
4
+ data.tar.gz: f0c2497de38303117159dfc170b9a9c447a61f2ee12b6f9aecef09e1565a1212
5
5
  SHA512:
6
- metadata.gz: 95b249607598f13d887b701516fcd83990a7aeec69ffcf0c04a80a59e61ee5496a9b56e0768123b81fa521e12424d71eb0b240d83cca2add2039aa7741f97b2b
7
- data.tar.gz: 367ca2486d780e9d1c864f730e66f024cfef2f0c3ccb0886d6e9572a4aa39c6c9677e4a6e6049c48ada72e5ecccea42ea23d86f2d93d810b80240b5c1e4caaa2
6
+ metadata.gz: 7b50904992dbd671ad3b69d7f056f7d18cd9a7d9912f4a31ffa8ab9b55a687265b501b5f63d35205d6dced804a67ad5389ebdb9020523c79bf163a0932a6662b
7
+ data.tar.gz: 3345ab6d4dbb824c0811771247e76a46710efe77e9b2939903366e7c24bb13ade2e5f00458bc9e67db07610f2d4c5aebfb6d0a20371282792bc30b7b5379f7b1
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
@@ -9,6 +9,11 @@ module Uffizzi
9
9
  ConfigFile.option_has_value?(:cookie) &&
10
10
  ConfigFile.option_has_value?(:server)
11
11
  end
12
+
13
+ def sign_out
14
+ Uffizzi::ConfigFile.unset_option(:cookie)
15
+ Uffizzi::ConfigFile.unset_option(:account_id)
16
+ end
12
17
  end
13
18
  end
14
19
  end
@@ -58,7 +58,7 @@ module Uffizzi
58
58
  \nUffizzi API service and manage previews.\n")
59
59
  server = Uffizzi.ui.ask('Server: ', default: Uffizzi.configuration.default_server.to_s)
60
60
  username = Uffizzi.ui.ask('Username: ')
61
- project = Uffizzi.ui.ask('Project: ')
61
+ project = Uffizzi.ui.ask('Project: ', default: 'default')
62
62
  ConfigFile.delete
63
63
  ConfigFile.write_option(:server, server)
64
64
  ConfigFile.write_option(:username, username)
@@ -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
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'uffizzi'
4
4
  require 'uffizzi/response_helper'
5
+ require 'uffizzi/helpers/project_helper'
5
6
  require 'uffizzi/clients/api/api_client'
7
+ require 'tty-prompt'
6
8
 
7
9
  module Uffizzi
8
10
  class Cli::Login
@@ -15,6 +17,7 @@ module Uffizzi
15
17
  def run
16
18
  Uffizzi.ui.say('Login to Uffizzi to view and manage your previews.')
17
19
  server = set_server
20
+
18
21
  username = set_username
19
22
  password = set_password
20
23
  params = prepare_request_params(username, password)
@@ -30,12 +33,12 @@ module Uffizzi
30
33
  private
31
34
 
32
35
  def set_server
33
- config_server = ConfigFile.exists? && ConfigFile.option_has_value?(:server) ? ConfigFile.read_option(:server) : nil
36
+ config_server = ConfigFile.exists? ? read_option_from_config(:server) : nil
34
37
  @options[:server] || config_server || Uffizzi.ui.ask('Server: ')
35
38
  end
36
39
 
37
40
  def set_username
38
- config_username = ConfigFile.exists? && ConfigFile.option_has_value?(:username) ? ConfigFile.read_option(:username) : nil
41
+ config_username = ConfigFile.exists? ? read_option_from_config(:username) : nil
39
42
  @options[:username] || config_username || Uffizzi.ui.ask('Username: ')
40
43
  end
41
44
 
@@ -43,6 +46,10 @@ module Uffizzi
43
46
  ENV['UFFIZZI_PASSWORD'] || Uffizzi.ui.ask('Password: ', echo: false)
44
47
  end
45
48
 
49
+ def read_option_from_config(option)
50
+ ConfigFile.option_has_value?(option) ? ConfigFile.read_option(option) : nil
51
+ end
52
+
46
53
  def prepare_request_params(username, password)
47
54
  {
48
55
  user: {
@@ -60,10 +67,77 @@ module Uffizzi
60
67
  ConfigFile.write_option(:username, username)
61
68
  ConfigFile.write_option(:cookie, response[:headers])
62
69
  ConfigFile.write_option(:account_id, account[:id])
70
+
71
+ default_project = ConfigFile.read_option(:project)
72
+ return unless default_project
73
+
74
+ check_default_project(default_project, server)
63
75
  end
64
76
 
65
77
  def account_valid?(account)
66
78
  account[:state] == 'active'
67
79
  end
80
+
81
+ def check_default_project(default_project, server)
82
+ check_project_response = fetch_projects(server)
83
+ return ResponseHelper.handle_failed_response(check_project_response) unless ResponseHelper.ok?(check_project_response)
84
+
85
+ projects = check_project_response[:body][:projects]
86
+ slugs = projects.map { |project| project[:slug] }
87
+ return if slugs.include?(default_project)
88
+
89
+ question = "Project '#{default_project}' does not exist. Select one of the following projects or create a new project:"
90
+ choices = projects.map do |project|
91
+ { name: project[:name], value: project[:slug] }
92
+ end
93
+ all_choices = choices + [{ name: 'Create a new project', value: nil }]
94
+ answer = Uffizzi.prompt.select(question, all_choices)
95
+ return ConfigFile.write_option(:project, answer) if answer
96
+
97
+ create_new_project(server)
98
+ end
99
+
100
+ def create_new_project(server)
101
+ project_name = Uffizzi.prompt.ask('Project name: ', required: true)
102
+ generated_slug = Uffizzi::ProjectHelper.generate_slug(project_name)
103
+ project_slug = Uffizzi.prompt.ask('Project slug: ', default: generated_slug)
104
+ raise Uffizzi::Error.new('Slug must not content spaces or special characters') unless project_slug.match?(/^[a-zA-Z0-9\-_]+\Z/i)
105
+
106
+ project_description = Uffizzi.prompt.ask('Project desciption: ')
107
+
108
+ params = {
109
+ project: {
110
+ name: project_name.strip,
111
+ slug: project_slug,
112
+ description: project_description,
113
+ },
114
+ }
115
+
116
+ response = create_project(server, params)
117
+
118
+ if ResponseHelper.created?(response)
119
+ handle_create_project_succeess(response)
120
+ else
121
+ handle_create_project_failed(response)
122
+ end
123
+ end
124
+
125
+ def handle_create_project_failed(response)
126
+ name_error = response[:body][:errors][:name].first
127
+ name_already_exists = name_error && name_error.first == 'Name already exists'
128
+ message = "Project with name #{project_name} already exists. " \
129
+ 'Please run $ uffizzi config to set it as a default project'
130
+ raise Uffizzi::Error.new(message) if name_already_exists
131
+
132
+ ResponseHelper.handle_failed_response(response)
133
+ end
134
+
135
+ def handle_create_project_succeess(response)
136
+ project = response[:body][:project]
137
+
138
+ ConfigFile.write_option(:project, project[:slug])
139
+
140
+ Uffizzi.ui.say("Project #{project[:name]} was successfully created")
141
+ end
68
142
  end
69
143
  end
@@ -17,7 +17,7 @@ module Uffizzi
17
17
  server = ConfigFile.read_option(:server)
18
18
  destroy_session(server)
19
19
 
20
- ConfigFile.delete
20
+ AuthHelper.sign_out
21
21
  Uffizzi.ui.say('You have been successfully logged out')
22
22
  end
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,42 @@ 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)
122
+ rescue SystemExit, Interrupt, SocketError
123
+ deployment_id = response[:body][:deployment][:id]
124
+ handle_preview_interruption(deployment_id, ConfigFile.read_option(:server), project_slug)
116
125
  end
117
126
 
118
127
  def handle_events_command(deployment_name, project_slug)
@@ -198,7 +207,7 @@ module Uffizzi
198
207
 
199
208
  def prepare_params(file_path)
200
209
  begin
201
- compose_file_data = File.read(file_path)
210
+ compose_file_data = EnvVariablesService.substitute_env_variables(File.read(file_path))
202
211
  rescue Errno::ENOENT => e
203
212
  raise Uffizzi::Error.new(e.message)
204
213
  end
@@ -218,8 +227,8 @@ module Uffizzi
218
227
  }
219
228
  end
220
229
 
221
- def handle_preview_interruption(deployment_id, hostname, project_slug)
222
- deletion_response = delete_deployment(hostname, project_slug, deployment_id)
230
+ def handle_preview_interruption(deployment_id, server, project_slug)
231
+ deletion_response = delete_deployment(server, project_slug, deployment_id)
223
232
  deployment_name = "deployment-#{deployment_id}"
224
233
  preview_deletion_message = if ResponseHelper.no_content?(deletion_response)
225
234
  "The preview #{deployment_name} has been disabled."
@@ -229,5 +238,23 @@ module Uffizzi
229
238
 
230
239
  raise Uffizzi::Error.new("The preview creation was interrupted. #{preview_deletion_message}")
231
240
  end
241
+
242
+ def display_deployment_data(deployment, success)
243
+ if Uffizzi.ui.output_format.nil?
244
+ Uffizzi.ui.say('Done')
245
+ preview_url = "https://#{deployment[:preview_url]}"
246
+ Uffizzi.ui.say(preview_url) if success
247
+ else
248
+ deployment_data = build_deployment_data(deployment)
249
+ Uffizzi.ui.output(deployment_data)
250
+ end
251
+ end
252
+
253
+ def build_deployment_data(deployment)
254
+ {
255
+ id: "deployment-#{deployment[:id]}",
256
+ url: "https://#{deployment[:preview_url]}",
257
+ }
258
+ end
232
259
  end
233
260
  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
@@ -49,6 +49,20 @@ module ApiClient
49
49
  build_response(response)
50
50
  end
51
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)
62
+
63
+ build_response(response)
64
+ end
65
+
52
66
  def create_credential(server, params)
53
67
  uri = credentials_uri(server)
54
68
  response = http_client.make_post_request(uri, params)
@@ -221,7 +235,7 @@ module ApiClient
221
235
 
222
236
  cookie_content = cookies.first
223
237
  cookie = cookie_content.split(';').first
224
- Uffizzi::ConfigFile.rewrite_cookie(cookie) if Uffizzi::ConfigFile.exists?
238
+ Uffizzi::ConfigFile.rewrite_cookie(cookie) if Uffizzi::ConfigFile.exists? && Uffizzi::ConfigFile.option_has_value?(:cookie)
225
239
  http_client.auth_cookie = cookie
226
240
 
227
241
  cookie