uffizzi-cli 0.8.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/config/uffizzi.rb +1 -1
  3. data/lib/uffizzi/auth_helper.rb +5 -0
  4. data/lib/uffizzi/cli/config.rb +1 -1
  5. data/lib/uffizzi/cli/connect.rb +54 -31
  6. data/lib/uffizzi/cli/disconnect.rb +1 -1
  7. data/lib/uffizzi/cli/login.rb +76 -2
  8. data/lib/uffizzi/cli/logout.rb +1 -1
  9. data/lib/uffizzi/cli/preview.rb +4 -1
  10. data/lib/uffizzi/cli/project.rb +82 -2
  11. data/lib/uffizzi/cli.rb +3 -1
  12. data/lib/uffizzi/clients/api/api_client.rb +15 -1
  13. data/lib/uffizzi/config_file.rb +8 -18
  14. data/lib/uffizzi/helpers/project_helper.rb +22 -0
  15. data/lib/uffizzi/promt.rb +21 -0
  16. data/lib/uffizzi/services/project_service.rb +40 -0
  17. data/lib/uffizzi/version.rb +1 -1
  18. data/lib/uffizzi.rb +5 -0
  19. data/man/uffizzi +2 -2
  20. data/man/uffizzi-config +2 -2
  21. data/man/uffizzi-config.ronn +1 -1
  22. data/man/uffizzi-connect +2 -2
  23. data/man/uffizzi-connect-acr +2 -2
  24. data/man/uffizzi-connect-acr.ronn +1 -1
  25. data/man/uffizzi-connect-docker-hub +2 -2
  26. data/man/uffizzi-connect-docker-hub.ronn +1 -1
  27. data/man/uffizzi-connect-ecr +2 -2
  28. data/man/uffizzi-connect-ecr.ronn +1 -1
  29. data/man/uffizzi-connect-gcr +2 -2
  30. data/man/uffizzi-connect-gcr.ronn +1 -1
  31. data/man/uffizzi-connect-ghcr +2 -2
  32. data/man/uffizzi-connect-ghcr.ronn +1 -1
  33. data/man/uffizzi-connect.ronn +1 -1
  34. data/man/uffizzi-disconnect +2 -2
  35. data/man/uffizzi-disconnect.ronn +1 -1
  36. data/man/uffizzi-login +2 -2
  37. data/man/uffizzi-login.ronn +1 -1
  38. data/man/uffizzi-logout +2 -2
  39. data/man/uffizzi-logout.ronn +1 -1
  40. data/man/uffizzi-preview +2 -2
  41. data/man/uffizzi-preview-create +2 -2
  42. data/man/uffizzi-preview-create.ronn +1 -1
  43. data/man/uffizzi-preview-delete +2 -2
  44. data/man/uffizzi-preview-delete.ronn +1 -1
  45. data/man/uffizzi-preview-describe +2 -2
  46. data/man/uffizzi-preview-describe.ronn +1 -1
  47. data/man/uffizzi-preview-events +2 -2
  48. data/man/uffizzi-preview-events.ronn +1 -1
  49. data/man/uffizzi-preview-list +2 -2
  50. data/man/uffizzi-preview-list.ronn +1 -1
  51. data/man/uffizzi-preview-service-list +2 -2
  52. data/man/uffizzi-preview-service-list.ronn +1 -1
  53. data/man/uffizzi-preview-service-logs +2 -2
  54. data/man/uffizzi-preview-service-logs.ronn +1 -1
  55. data/man/uffizzi-preview-update +20 -12
  56. data/man/uffizzi-preview-update.ronn +6 -6
  57. data/man/uffizzi-preview.ronn +1 -1
  58. data/man/uffizzi-preview_service_logs +2 -2
  59. data/man/uffizzi-preview_service_logs.ronn +1 -1
  60. data/man/uffizzi-project +2 -2
  61. data/man/uffizzi-project-compose +2 -2
  62. data/man/uffizzi-project-compose-describe +2 -2
  63. data/man/uffizzi-project-compose-describe.ronn +1 -1
  64. data/man/uffizzi-project-compose-set +2 -2
  65. data/man/uffizzi-project-compose-set.ronn +1 -1
  66. data/man/uffizzi-project-compose-unset +2 -2
  67. data/man/uffizzi-project-compose-unset.ronn +1 -1
  68. data/man/uffizzi-project-compose.ronn +1 -1
  69. data/man/uffizzi-project-create +50 -0
  70. data/man/uffizzi-project-create.ronn +41 -0
  71. data/man/uffizzi-project-delete +32 -0
  72. data/man/uffizzi-project-delete.ronn +24 -0
  73. data/man/uffizzi-project-describe +43 -0
  74. data/man/uffizzi-project-describe.ronn +34 -0
  75. data/man/uffizzi-project-secret +2 -2
  76. data/man/uffizzi-project-secret-create +2 -2
  77. data/man/uffizzi-project-secret-create.ronn +1 -1
  78. data/man/uffizzi-project-secret-delete +2 -2
  79. data/man/uffizzi-project-secret-delete.ronn +1 -1
  80. data/man/uffizzi-project-secret-list +2 -2
  81. data/man/uffizzi-project-secret-list.ronn +1 -1
  82. data/man/uffizzi-project-secret.ronn +1 -1
  83. data/man/uffizzi-project-set-default +2 -2
  84. data/man/uffizzi-project-set-default.ronn +1 -1
  85. data/man/uffizzi-project.ronn +1 -1
  86. data/man/uffizzi.html +1 -1
  87. data/man/uffizzi.ronn +1 -1
  88. metadata +25 -22
  89. data/man/uffizzi-config.html +0 -144
  90. data/man/uffizzi-login.html +0 -113
  91. data/man/uffizzi-logout.html +0 -102
  92. data/man/uffizzi-preview-create.html +0 -128
  93. data/man/uffizzi-preview-delete.html +0 -115
  94. data/man/uffizzi-preview-describe.html +0 -116
  95. data/man/uffizzi-preview-events.html +0 -110
  96. data/man/uffizzi-preview-list.html +0 -110
  97. data/man/uffizzi-preview.html +0 -120
  98. data/man/uffizzi-preview_service_logs.html +0 -142
  99. data/man/uffizzi-project-compose-describe.html +0 -118
  100. data/man/uffizzi-project-compose-set.html +0 -149
  101. data/man/uffizzi-project-compose-unset.html +0 -116
  102. data/man/uffizzi-project-compose.html +0 -123
  103. data/man/uffizzi-project-secret-create.html +0 -110
  104. data/man/uffizzi-project-secret-delete.html +0 -110
  105. data/man/uffizzi-project-secret-list.html +0 -110
  106. data/man/uffizzi-project-secret.html +0 -119
  107. data/man/uffizzi-project-set-default.html +0 -111
  108. data/man/uffizzi-project.html +0 -128
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17ac6475e11de6c24b044f64e19541587683aaad4bd3b72cafbc5160b92364a8
4
- data.tar.gz: 228b5a14b359af42f9698eba6bd98c94155dfd05791b55378dfc99cf6ffe365d
3
+ metadata.gz: aad4e300e1250ee113d65b436b58045b368c707c5840bc26375918a33c894a5a
4
+ data.tar.gz: 711dc99c47c9d073405da14ce9409e2bfe99eaca6028143da0eccb7961ba8426
5
5
  SHA512:
6
- metadata.gz: 4d9e8d2f99a297d2fac8452bc03e180c71f7b1e05d71ac333704b2e1649fffdfd2bb4f6b86ffb0d186e441629cb809b2d5c450984b2a36af49080b9b08fab700
7
- data.tar.gz: ce3cdb192caa7ab39a7edec3b5d01788505cae7a4a2d5f440b99ef203f86f18d67c22ec5c07ef67603dc6ebb35871857f3f536ac71720b4634c46579f8e771d6
6
+ metadata.gz: aebda9657557027599278995e7a97bef373b118cdf4a8055d17706f418ffbd7b6a126703dfa5901833ab2c7198f91d91c5f07da5c836b0b602382a6fe860c235
7
+ data.tar.gz: 951c78b92cbee8f70a5b41231fa56cd9ea0cf3ec278c174b703a065567eb1d4ddfca18484396c5db208ea85d6fd1ad8f59c8c9a8a8c29ebbbc5fbe8b308e0ff8
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
@@ -119,6 +119,9 @@ module Uffizzi
119
119
  success = PreviewService.run_containers_deploy(project_slug, deployment)
120
120
 
121
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)
122
125
  end
123
126
 
124
127
  def handle_events_command(deployment_name, project_slug)
@@ -204,7 +207,7 @@ module Uffizzi
204
207
 
205
208
  def prepare_params(file_path)
206
209
  begin
207
- compose_file_data = File.read(file_path)
210
+ compose_file_data = EnvVariablesService.substitute_env_variables(File.read(file_path))
208
211
  rescue Errno::ENOENT => e
209
212
  raise Uffizzi::Error.new(e.message)
210
213
  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
@@ -5,20 +5,15 @@ require 'fileutils'
5
5
 
6
6
  module Uffizzi
7
7
  class ConfigFile
8
- CONFIG_PATH = "#{Dir.home}/.config/uffizzi/config_default.json"
8
+ CONFIG_PATH = "#{Dir.home}/.config/uffizzi/config_default"
9
9
 
10
10
  class << self
11
11
  def config_path
12
12
  CONFIG_PATH
13
13
  end
14
14
 
15
- def create(account_id, cookie, server)
16
- data = prepare_config_data(account_id, cookie, server)
17
- data.each_pair { |key, value| write_option(key, value) }
18
- end
19
-
20
15
  def delete
21
- File.delete(config_path) if exists?
16
+ File.truncate(config_path, 0) if exists?
22
17
  end
23
18
 
24
19
  def exists?
@@ -73,8 +68,6 @@ module Uffizzi
73
68
  data
74
69
  end
75
70
 
76
- private
77
-
78
71
  def option_exists?(option)
79
72
  data = read
80
73
  return false unless data.is_a?(Hash)
@@ -82,6 +75,8 @@ module Uffizzi
82
75
  data.key?(option)
83
76
  end
84
77
 
78
+ private
79
+
85
80
  def read
86
81
  data = File.read(config_path)
87
82
  options = data.split("\n")
@@ -90,7 +85,10 @@ module Uffizzi
90
85
  acc.merge({ key.strip.to_sym => value.strip })
91
86
  end
92
87
  rescue Errno::ENOENT => e
93
- Uffizzi.ui.say(e)
88
+ file_path = e.message.split(' ').last
89
+ message = "Configuration file not found: #{file_path}\n" \
90
+ 'To configure the uffizzi CLI interactively, run $ uffizzi config'
91
+ raise Uffizzi::Error.new(message)
94
92
  end
95
93
 
96
94
  def write(data)
@@ -107,14 +105,6 @@ module Uffizzi
107
105
  end
108
106
  end
109
107
 
110
- def prepare_config_data(account_id, cookie, server)
111
- {
112
- account_id: account_id,
113
- server: server,
114
- cookie: cookie,
115
- }
116
- end
117
-
118
108
  def create_file
119
109
  dir = File.dirname(config_path)
120
110
 
@@ -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
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-prompt'
4
+
5
+ module Uffizzi
6
+ module UI
7
+ class Prompt
8
+ def initialize
9
+ @prompt = TTY::Prompt.new
10
+ end
11
+
12
+ def select(question, choices)
13
+ @prompt.select(question, choices)
14
+ end
15
+
16
+ def ask(message, **args)
17
+ @prompt.ask(message, **args)
18
+ end
19
+ end
20
+ end
21
+ end