xapixctl 1.1.2 → 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/cd.yaml +17 -0
- data/.ruby-version +1 -1
- data/Gemfile.lock +55 -37
- data/lib/xapixctl.rb +0 -3
- data/lib/xapixctl/base_cli.rb +47 -35
- data/lib/xapixctl/cli.rb +51 -66
- data/lib/xapixctl/connector_cli.rb +49 -0
- data/lib/xapixctl/phoenix_client.rb +37 -259
- data/lib/xapixctl/phoenix_client/connection.rb +50 -0
- data/lib/xapixctl/phoenix_client/organization_connection.rb +61 -0
- data/lib/xapixctl/phoenix_client/project_connection.rb +184 -0
- data/lib/xapixctl/phoenix_client/result_handler.rb +35 -0
- data/lib/xapixctl/preview_cli.rb +7 -19
- data/lib/xapixctl/sync_cli.rb +241 -0
- data/lib/xapixctl/titan_cli.rb +286 -0
- data/lib/xapixctl/util.rb +42 -0
- data/lib/xapixctl/version.rb +1 -1
- data/xapixctl.gemspec +12 -4
- metadata +83 -12
@@ -0,0 +1,286 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'xapixctl/base_cli'
|
4
|
+
|
5
|
+
module Xapixctl
|
6
|
+
class TitanCli < BaseCli
|
7
|
+
DEFAULT_METHODS = {
|
8
|
+
"predict" => :predict,
|
9
|
+
"performance" => :performance,
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
option :schema_import, desc: "Resource id of an existing Schema import"
|
13
|
+
option :data, desc: "JSON encoded data for predict API", default: "[[1,2,3]]"
|
14
|
+
desc "service URL", "build a service for the deployed model"
|
15
|
+
long_desc <<-LONGDESC
|
16
|
+
`xapixctl titan service URL` will build a ML service around a deployed ML model.
|
17
|
+
|
18
|
+
We expect the following of the deployed ML model:
|
19
|
+
|
20
|
+
- There should be a "POST /predict" endpoint. Use --data="JSON" to specify an example dataset the model expects.
|
21
|
+
|
22
|
+
- If there is a "GET /performance" endpoint, it'll be made available. It's expected to return a JSON object with the properties 'accuracy', 'precision', 'recall', and 'min_acc_threshold'.
|
23
|
+
|
24
|
+
Examples:
|
25
|
+
\x5> $ xapixctl titan service https://services.demo.akoios.com/ai-model-name -p xapix/ml-project
|
26
|
+
LONGDESC
|
27
|
+
def service(akoios_url)
|
28
|
+
url = URI.parse(akoios_url)
|
29
|
+
schema = JSON.parse(RestClient.get(File.join(url.to_s, 'spec'), params: { host: url.hostname }, headers: { accept: :json }))
|
30
|
+
patch_schema(schema)
|
31
|
+
connector_refs = import_swagger(File.basename(url.path), schema)
|
32
|
+
say "\n== Onboarding Connectors", :bold
|
33
|
+
connectors = match_connectors_to_action(connector_refs)
|
34
|
+
if connectors.empty?
|
35
|
+
warn "\nNo valid connectors for ML service detected, not building service."
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
say "\n== Building Service", :bold
|
39
|
+
service_doc = build_service(schema.dig('info', 'title'), connectors)
|
40
|
+
res = prj_connection.apply(service_doc)
|
41
|
+
say "\ncreated / updated service #{res.first}"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def patch_schema(schema)
|
47
|
+
predict_schema = schema.dig('paths', '/predict', 'post')
|
48
|
+
if predict_schema
|
49
|
+
predict_data = JSON.parse(options[:data]) rescue {}
|
50
|
+
predict_schema['operationId'] = 'predict'
|
51
|
+
predict_schema['parameters'].each do |param|
|
52
|
+
if param['name'] == 'json' && param['in'] == 'body'
|
53
|
+
param['schema']['properties'] = { "data" => extract_schema(predict_data) }
|
54
|
+
param['schema']['example'] = { "data" => predict_data }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
performane_schema = schema.dig('paths', '/performance', 'get')
|
60
|
+
if performane_schema
|
61
|
+
performane_schema['operationId'] = 'performance'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def import_swagger(filename, schema)
|
66
|
+
Tempfile.create([filename, '.json']) do |f|
|
67
|
+
f.write(schema.to_json)
|
68
|
+
f.rewind
|
69
|
+
|
70
|
+
if options[:schema_import]
|
71
|
+
result = prj_connection.update_schema_import(options[:schema_import], f)
|
72
|
+
say "updated #{result.dig('resource', 'kind')} #{result.dig('resource', 'id')}"
|
73
|
+
else
|
74
|
+
result = prj_connection.add_schema_import(f)
|
75
|
+
say "created #{result.dig('resource', 'kind')} #{result.dig('resource', 'id')}"
|
76
|
+
end
|
77
|
+
result.dig('schema_import', 'updated_resources')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def match_connectors_to_action(connector_refs)
|
82
|
+
connector_refs.map do |connector_ref|
|
83
|
+
connector = prj_connection.resource(connector_ref['kind'], connector_ref['id'])
|
84
|
+
action = DEFAULT_METHODS[connector.dig('definition', 'name')]
|
85
|
+
say "\n#{connector['kind']} #{connector.dig('definition', 'name')} -> "
|
86
|
+
if action
|
87
|
+
say "#{action} action"
|
88
|
+
updated_connector = update_connector_with_preview(connector)
|
89
|
+
[action, updated_connector] if updated_connector
|
90
|
+
else
|
91
|
+
say "no action type detected, ignoring"
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end.compact
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_connector_with_preview(connector)
|
98
|
+
say "fetching preview for #{connector['kind']} #{connector.dig('definition', 'name')}..."
|
99
|
+
preview_details = prj_connection.data_source_preview(connector.dig('metadata', 'id'))
|
100
|
+
preview = preview_details.dig('preview', 'sample')
|
101
|
+
say "got a #{preview['status']} response: #{preview['body']}"
|
102
|
+
if preview['status'] != 200
|
103
|
+
say "unexpected status, please check data or model"
|
104
|
+
elsif yes?("Does this look alright?", :bold)
|
105
|
+
res = prj_connection.accept_data_source_preview(connector.dig('metadata', 'id'))
|
106
|
+
return res.dig('data_source', 'resource_description')
|
107
|
+
end
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
def extract_schema(data_sample)
|
112
|
+
case data_sample
|
113
|
+
when Array
|
114
|
+
{ type: 'array', items: extract_schema(data_sample[0]) }
|
115
|
+
when Hash
|
116
|
+
{ type: 'object', properties: data_sample.transform_values { |v| extract_schema(v) } }
|
117
|
+
when Numeric
|
118
|
+
{ type: 'number' }
|
119
|
+
else
|
120
|
+
{}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_service(title, connectors)
|
125
|
+
{
|
126
|
+
version: 'v1',
|
127
|
+
kind: 'Service',
|
128
|
+
metadata: { id: title.parameterize },
|
129
|
+
definition: {
|
130
|
+
name: title.humanize,
|
131
|
+
actions: connectors.map { |action, connector| build_service_action(action, connector) }
|
132
|
+
}
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def build_service_action(action_type, connector)
|
137
|
+
{
|
138
|
+
name: action_type,
|
139
|
+
parameter_schema: parameter_schema(action_type, connector),
|
140
|
+
result_schema: result_schema(action_type),
|
141
|
+
pipeline: { units: pipeline_units(action_type, connector) }
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
def parameter_schema(action_type, connector)
|
146
|
+
case action_type
|
147
|
+
when :predict
|
148
|
+
{ type: 'object', properties: {
|
149
|
+
data: connector.dig('definition', 'parameter_schema', 'properties', 'body', 'properties', 'data'),
|
150
|
+
} }
|
151
|
+
when :performance
|
152
|
+
{ type: 'object', properties: {} }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def result_schema(action_type)
|
157
|
+
case action_type
|
158
|
+
when :predict
|
159
|
+
{ type: 'object', properties: {
|
160
|
+
prediction: { type: 'object', properties: { percent: { type: 'number' }, raw: { type: 'number' } } },
|
161
|
+
success: { type: 'boolean' },
|
162
|
+
error: { type: 'string' }
|
163
|
+
} }
|
164
|
+
when :performance
|
165
|
+
{ type: 'object', properties: {
|
166
|
+
performance: {
|
167
|
+
type: 'object', properties: {
|
168
|
+
accuracy: { type: 'number' },
|
169
|
+
precision: { type: 'number' },
|
170
|
+
recall: { type: 'number' },
|
171
|
+
min_acc_threshold: { type: 'number' }
|
172
|
+
}
|
173
|
+
}
|
174
|
+
} }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def pipeline_units(action_type, connector)
|
179
|
+
case action_type
|
180
|
+
when :predict
|
181
|
+
[entry_unit, predict_unit(connector), predict_result_unit]
|
182
|
+
when :performance
|
183
|
+
[entry_unit, performance_unit(connector), performance_result_unit]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def entry_unit
|
188
|
+
{ version: 'v2', kind: 'Unit/Entry',
|
189
|
+
metadata: { id: 'entry' },
|
190
|
+
definition: { name: 'Entry' } }
|
191
|
+
end
|
192
|
+
|
193
|
+
def predict_unit(connector)
|
194
|
+
leafs = extract_leafs(connector.dig('definition', 'parameter_schema', 'properties', 'body'))
|
195
|
+
{ version: 'v2', kind: 'Unit/DataSource',
|
196
|
+
metadata: { id: 'predict' },
|
197
|
+
definition: {
|
198
|
+
name: 'Predict',
|
199
|
+
inputs: ['entry'],
|
200
|
+
data_source: connector.dig('metadata', 'id'),
|
201
|
+
formulas: leafs.map { |leaf|
|
202
|
+
{ ref: leaf[:node]['$id'], formula: (['', 'entry'] + leaf[:path]).join('.') }
|
203
|
+
}
|
204
|
+
} }
|
205
|
+
end
|
206
|
+
|
207
|
+
def predict_result_unit
|
208
|
+
{ version: 'v2', kind: 'Unit/Result',
|
209
|
+
metadata: { id: 'result' },
|
210
|
+
definition: {
|
211
|
+
name: 'Result',
|
212
|
+
inputs: ['predict'],
|
213
|
+
formulas: [{
|
214
|
+
ref: "#prediction.percent",
|
215
|
+
formula: "IF(.predict.status = 200, ROUND(100*coerce.to-float(.predict.body), 2))"
|
216
|
+
}, {
|
217
|
+
ref: "#prediction.raw",
|
218
|
+
formula: "IF(.predict.status = 200, coerce.to-float(.predict.body))"
|
219
|
+
}, {
|
220
|
+
ref: "#success",
|
221
|
+
formula: ".predict.status = 200"
|
222
|
+
}, {
|
223
|
+
ref: "#error",
|
224
|
+
formula: "IF(.predict.status <> 200, 'Model not trained!')"
|
225
|
+
}],
|
226
|
+
parameter_sample: {
|
227
|
+
"prediction" => { "percent" => 51.12, "raw" => 0.5112131 },
|
228
|
+
"success" => true,
|
229
|
+
"error" => nil
|
230
|
+
}
|
231
|
+
} }
|
232
|
+
end
|
233
|
+
|
234
|
+
def performance_unit(connector)
|
235
|
+
{ version: 'v2', kind: 'Unit/DataSource',
|
236
|
+
metadata: { id: 'performance' },
|
237
|
+
definition: {
|
238
|
+
name: 'Performance',
|
239
|
+
inputs: ['entry'],
|
240
|
+
data_source: connector.dig('metadata', 'id')
|
241
|
+
} }
|
242
|
+
end
|
243
|
+
|
244
|
+
def performance_result_unit
|
245
|
+
{ version: 'v2', kind: 'Unit/Result',
|
246
|
+
metadata: { id: 'result' },
|
247
|
+
definition: {
|
248
|
+
name: 'Result',
|
249
|
+
inputs: ['performance'],
|
250
|
+
formulas: [{
|
251
|
+
ref: "#performance.recall",
|
252
|
+
formula: "WITH(data, JSON.DECODE(REGEXREPLACE(performance.body, \"'\", \"\\\"\")), .data.recall)"
|
253
|
+
}, {
|
254
|
+
ref: "#performance.accuracy",
|
255
|
+
formula: "WITH(data, JSON.DECODE(REGEXREPLACE(performance.body, \"'\", \"\\\"\")), .data.accuracy)"
|
256
|
+
}, {
|
257
|
+
ref: "#performance.precision",
|
258
|
+
formula: "WITH(data, JSON.DECODE(REGEXREPLACE(performance.body, \"'\", \"\\\"\")), .data.precision)"
|
259
|
+
}, {
|
260
|
+
ref: "#performance.min_acc_threshold",
|
261
|
+
formula: "WITH(data, JSON.DECODE(REGEXREPLACE(performance.body, \"'\", \"\\\"\")), .data.min_acc_threshold)"
|
262
|
+
}],
|
263
|
+
parameter_sample: {
|
264
|
+
"performance" => {
|
265
|
+
"recall" => 0.8184713375796179,
|
266
|
+
"accuracy" => 0.9807847896440129,
|
267
|
+
"precision" => 0.8711864406779661,
|
268
|
+
"min_acc_threshold" => 0.84,
|
269
|
+
}
|
270
|
+
}
|
271
|
+
} }
|
272
|
+
end
|
273
|
+
|
274
|
+
def extract_leafs(schema, current_path = [])
|
275
|
+
return unless schema
|
276
|
+
case schema['type']
|
277
|
+
when 'object'
|
278
|
+
schema['properties'].flat_map { |key, sub_schema| extract_leafs(sub_schema, current_path + [key]) }.compact
|
279
|
+
when 'array'
|
280
|
+
extract_leafs(schema['items'], current_path + [:[]])
|
281
|
+
else
|
282
|
+
{ path: current_path, node: schema }
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Xapixctl
|
6
|
+
module Util
|
7
|
+
extend self
|
8
|
+
|
9
|
+
class InvalidDocumentStructureError < StandardError
|
10
|
+
def initialize(file)
|
11
|
+
super("#{file} has invalid document structure")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
DOCUMENT_STRUCTURE = %w[version kind metadata definition].freeze
|
16
|
+
|
17
|
+
def resources_from_file(filename, ignore_missing: false)
|
18
|
+
load_files(filename, ignore_missing) do |actual_file, yaml_string|
|
19
|
+
yaml_string.split(/^---\s*\n/).map { |yml| Psych.safe_load(yml) }.compact.each do |doc|
|
20
|
+
raise InvalidDocumentStructureError, actual_file unless (DOCUMENT_STRUCTURE - doc.keys.map(&:to_s)).empty?
|
21
|
+
yield doc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_files(filename, ignore_missing)
|
27
|
+
if filename == '-'
|
28
|
+
yield 'STDIN', $stdin.read
|
29
|
+
else
|
30
|
+
pn = filename.is_a?(Pathname) ? filename : Pathname.new(filename)
|
31
|
+
if pn.directory?
|
32
|
+
pn.glob(["**/*.yaml", "**/*.yml"]).sort.each { |dpn| yield dpn.to_s, dpn.read }
|
33
|
+
elsif pn.exist?
|
34
|
+
yield pn.to_s, pn.read
|
35
|
+
elsif !ignore_missing
|
36
|
+
warn "file not found: #{filename}"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/xapixctl/version.rb
CHANGED
data/xapixctl.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
lib = File.expand_path("lib", __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require "xapixctl/version"
|
@@ -24,13 +26,19 @@ Gem::Specification.new do |spec|
|
|
24
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
27
|
spec.require_paths = ["lib"]
|
26
28
|
|
29
|
+
spec.required_ruby_version = '>= 2.6'
|
30
|
+
|
27
31
|
spec.add_dependency "activesupport", ">= 5.2.3", "< 6.0.0"
|
32
|
+
spec.add_dependency "hashdiff", ">= 1.0.1", "< 1.2.0"
|
28
33
|
spec.add_dependency "rest-client", ">= 2.1.0", "< 3.0.0"
|
29
|
-
spec.add_dependency "thor", ">= 1.0.0", "< 1.
|
34
|
+
spec.add_dependency "thor", ">= 1.0.0", "< 1.2.0"
|
30
35
|
|
31
|
-
spec.add_development_dependency "bundler", "~> 1.
|
36
|
+
spec.add_development_dependency "bundler", "~> 2.1.4"
|
32
37
|
spec.add_development_dependency "rake", "~> 13.0"
|
33
38
|
spec.add_development_dependency "relaxed-rubocop", "~> 2.5"
|
34
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
35
|
-
spec.add_development_dependency "rubocop", "~> 0
|
39
|
+
spec.add_development_dependency "rspec", "~> 3.10.0"
|
40
|
+
spec.add_development_dependency "rubocop", "~> 1.11.0"
|
41
|
+
spec.add_development_dependency "rubocop-rake", "~> 0.5.1"
|
42
|
+
spec.add_development_dependency "rubocop-rspec", "~> 2.2.0"
|
43
|
+
spec.add_development_dependency "webmock", "~> 3.11.0"
|
36
44
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xapixctl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Reinsch
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,6 +30,26 @@ dependencies:
|
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 6.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: hashdiff
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.0.1
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.2.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.0.1
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.2.0
|
33
53
|
- !ruby/object:Gem::Dependency
|
34
54
|
name: rest-client
|
35
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,7 +79,7 @@ dependencies:
|
|
59
79
|
version: 1.0.0
|
60
80
|
- - "<"
|
61
81
|
- !ruby/object:Gem::Version
|
62
|
-
version: 1.
|
82
|
+
version: 1.2.0
|
63
83
|
type: :runtime
|
64
84
|
prerelease: false
|
65
85
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -69,21 +89,21 @@ dependencies:
|
|
69
89
|
version: 1.0.0
|
70
90
|
- - "<"
|
71
91
|
- !ruby/object:Gem::Version
|
72
|
-
version: 1.
|
92
|
+
version: 1.2.0
|
73
93
|
- !ruby/object:Gem::Dependency
|
74
94
|
name: bundler
|
75
95
|
requirement: !ruby/object:Gem::Requirement
|
76
96
|
requirements:
|
77
97
|
- - "~>"
|
78
98
|
- !ruby/object:Gem::Version
|
79
|
-
version: 1.
|
99
|
+
version: 2.1.4
|
80
100
|
type: :development
|
81
101
|
prerelease: false
|
82
102
|
version_requirements: !ruby/object:Gem::Requirement
|
83
103
|
requirements:
|
84
104
|
- - "~>"
|
85
105
|
- !ruby/object:Gem::Version
|
86
|
-
version: 1.
|
106
|
+
version: 2.1.4
|
87
107
|
- !ruby/object:Gem::Dependency
|
88
108
|
name: rake
|
89
109
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,28 +138,70 @@ dependencies:
|
|
118
138
|
requirements:
|
119
139
|
- - "~>"
|
120
140
|
- !ruby/object:Gem::Version
|
121
|
-
version:
|
141
|
+
version: 3.10.0
|
122
142
|
type: :development
|
123
143
|
prerelease: false
|
124
144
|
version_requirements: !ruby/object:Gem::Requirement
|
125
145
|
requirements:
|
126
146
|
- - "~>"
|
127
147
|
- !ruby/object:Gem::Version
|
128
|
-
version:
|
148
|
+
version: 3.10.0
|
129
149
|
- !ruby/object:Gem::Dependency
|
130
150
|
name: rubocop
|
131
151
|
requirement: !ruby/object:Gem::Requirement
|
132
152
|
requirements:
|
133
153
|
- - "~>"
|
134
154
|
- !ruby/object:Gem::Version
|
135
|
-
version:
|
155
|
+
version: 1.11.0
|
136
156
|
type: :development
|
137
157
|
prerelease: false
|
138
158
|
version_requirements: !ruby/object:Gem::Requirement
|
139
159
|
requirements:
|
140
160
|
- - "~>"
|
141
161
|
- !ruby/object:Gem::Version
|
142
|
-
version:
|
162
|
+
version: 1.11.0
|
163
|
+
- !ruby/object:Gem::Dependency
|
164
|
+
name: rubocop-rake
|
165
|
+
requirement: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - "~>"
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: 0.5.1
|
170
|
+
type: :development
|
171
|
+
prerelease: false
|
172
|
+
version_requirements: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - "~>"
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: 0.5.1
|
177
|
+
- !ruby/object:Gem::Dependency
|
178
|
+
name: rubocop-rspec
|
179
|
+
requirement: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - "~>"
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: 2.2.0
|
184
|
+
type: :development
|
185
|
+
prerelease: false
|
186
|
+
version_requirements: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - "~>"
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: 2.2.0
|
191
|
+
- !ruby/object:Gem::Dependency
|
192
|
+
name: webmock
|
193
|
+
requirement: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - "~>"
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: 3.11.0
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
requirements:
|
202
|
+
- - "~>"
|
203
|
+
- !ruby/object:Gem::Version
|
204
|
+
version: 3.11.0
|
143
205
|
description:
|
144
206
|
email:
|
145
207
|
- michael@xapix.io
|
@@ -148,6 +210,7 @@ executables:
|
|
148
210
|
extensions: []
|
149
211
|
extra_rdoc_files: []
|
150
212
|
files:
|
213
|
+
- ".github/workflows/cd.yaml"
|
151
214
|
- ".gitignore"
|
152
215
|
- ".rspec"
|
153
216
|
- ".rubocop.yml"
|
@@ -163,8 +226,16 @@ files:
|
|
163
226
|
- lib/xapixctl.rb
|
164
227
|
- lib/xapixctl/base_cli.rb
|
165
228
|
- lib/xapixctl/cli.rb
|
229
|
+
- lib/xapixctl/connector_cli.rb
|
166
230
|
- lib/xapixctl/phoenix_client.rb
|
231
|
+
- lib/xapixctl/phoenix_client/connection.rb
|
232
|
+
- lib/xapixctl/phoenix_client/organization_connection.rb
|
233
|
+
- lib/xapixctl/phoenix_client/project_connection.rb
|
234
|
+
- lib/xapixctl/phoenix_client/result_handler.rb
|
167
235
|
- lib/xapixctl/preview_cli.rb
|
236
|
+
- lib/xapixctl/sync_cli.rb
|
237
|
+
- lib/xapixctl/titan_cli.rb
|
238
|
+
- lib/xapixctl/util.rb
|
168
239
|
- lib/xapixctl/version.rb
|
169
240
|
- xapixctl.gemspec
|
170
241
|
homepage: https://github.com/xapix-io/xapixctl
|
@@ -181,14 +252,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
181
252
|
requirements:
|
182
253
|
- - ">="
|
183
254
|
- !ruby/object:Gem::Version
|
184
|
-
version: '
|
255
|
+
version: '2.6'
|
185
256
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
257
|
requirements:
|
187
258
|
- - ">="
|
188
259
|
- !ruby/object:Gem::Version
|
189
260
|
version: '0'
|
190
261
|
requirements: []
|
191
|
-
rubygems_version: 3.0.
|
262
|
+
rubygems_version: 3.0.9
|
192
263
|
signing_key:
|
193
264
|
specification_version: 4
|
194
265
|
summary: xapix client library and command line tool
|