uffizzi-cli 0.7.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/config/uffizzi.rb +1 -1
- data/lib/uffizzi/cli/connect.rb +54 -31
- data/lib/uffizzi/cli/disconnect.rb +1 -1
- data/lib/uffizzi/cli/preview.rb +39 -15
- data/lib/uffizzi/cli/project.rb +82 -2
- data/lib/uffizzi/cli.rb +3 -1
- data/lib/uffizzi/clients/api/api_client.rb +15 -1
- data/lib/uffizzi/config_file.rb +9 -5
- data/lib/uffizzi/helpers/project_helper.rb +22 -0
- data/lib/uffizzi/services/preview_service.rb +13 -25
- data/lib/uffizzi/services/project_service.rb +40 -0
- data/lib/uffizzi/version.rb +1 -1
- data/man/uffizzi-config +1 -1
- data/man/uffizzi-login +1 -1
- data/man/uffizzi-logout +1 -1
- data/man/uffizzi-project-create +50 -0
- data/man/uffizzi-project-create.ronn +41 -0
- data/man/uffizzi-project-delete +32 -0
- data/man/uffizzi-project-delete.ronn +24 -0
- data/man/uffizzi-project-describe +43 -0
- data/man/uffizzi-project-describe.ronn +34 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 135c59012b429afe86c2ba348e8d2e5e73562b7a10531d8e2ed329c7eac09929
|
4
|
+
data.tar.gz: 8865727d98b597fc622324e7b9d3afdf410bedb00e0b2203448e4023e8896da3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
21
|
+
github_registry: 'UffizziCore::Credential::GithubContainerRegistry',
|
22
22
|
}
|
23
23
|
config.default_server = 'app.uffizzi.com'
|
24
24
|
end
|
data/lib/uffizzi/cli/connect.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
51
|
+
check_credential_existence(type, 'acr')
|
48
52
|
|
49
|
-
registry_url =
|
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
|
-
|
79
|
+
check_credential_existence(type, 'ecr')
|
74
80
|
|
75
|
-
registry_url =
|
76
|
-
|
77
|
-
|
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:
|
81
|
-
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
|
-
|
107
|
+
check_credential_existence(type, 'gcr')
|
100
108
|
|
101
|
-
|
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[:
|
127
|
-
|
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
|
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
|
-
|
169
|
-
|
170
|
-
|
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[:
|
20
|
+
Uffizzi.configuration.credential_types[:github_registry]
|
21
21
|
else
|
22
22
|
raise Uffizzi::Error.new('Unsupported credential type.')
|
23
23
|
end
|
data/lib/uffizzi/cli/preview.rb
CHANGED
@@ -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]', '
|
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,
|
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
|
-
|
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,
|
222
|
-
deletion_response = delete_deployment(
|
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
|
data/lib/uffizzi/cli/project.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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 =
|
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
|
data/lib/uffizzi/config_file.rb
CHANGED
@@ -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(
|
21
|
+
File.delete(config_path) if exists?
|
18
22
|
end
|
19
23
|
|
20
24
|
def exists?
|
21
|
-
File.exist?(
|
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(
|
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(
|
119
|
+
dir = File.dirname(config_path)
|
116
120
|
|
117
121
|
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
118
122
|
|
119
|
-
File.new(
|
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
|
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
|
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
|
-
|
53
|
+
activity_items
|
53
54
|
end
|
54
55
|
|
55
|
-
def
|
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
|
-
|
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
|
-
|
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
|
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
|
data/lib/uffizzi/version.rb
CHANGED
data/man/uffizzi-config
CHANGED
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" "" "
|
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
@@ -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.
|
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-
|
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
|