uffizzi-cli 0.7.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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