xapixctl 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9f36513c3a71e1c789c1438f4aca2aa84b5c21a15fc5a415e0d048ec24342a35
4
+ data.tar.gz: '08fe973118fec5ac856092f1e6588d7b0ff3fefbe48843e6b739e33dec8e3d67'
5
+ SHA512:
6
+ metadata.gz: a305bf7036a9f2a562750cd6bcb0ca4aa8e75017d4b21dbbacbd82580857890438af8162e0779bc4d3d35e0c7e131a0e0616bf1c9cdbf6a56afa819bbfa87f1d
7
+ data.tar.gz: dda3172f38b678db380907b828c4813091219b264e5d4f38b084691eb1efb88f05d755216234a6a12e97cdb432fbd5983df62f64f842d226864ffcd95c06ef49
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ # rvm and other tools
14
+ .ruby-gemset
15
+ .envrc
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,27 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5
3
+ DisplayCopNames: true
4
+ Exclude:
5
+ - bin/*
6
+
7
+ inherit_gem:
8
+ relaxed-rubocop: .rubocop.yml
9
+
10
+ Documentation:
11
+ Enabled: false
12
+
13
+ Metrics/LineLength:
14
+ Max: 120
15
+
16
+ Layout/EmptyLineAfterGuardClause:
17
+ Enabled: false
18
+
19
+ Layout/MultilineOperationIndentation:
20
+ EnforcedStyle: indented
21
+
22
+ Style/OptionHash:
23
+ Enabled: true
24
+
25
+ Metrics/BlockLength:
26
+ Exclude:
27
+ - spec/**/*
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in xapixctl.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,86 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xapixctl (1.0.0)
5
+ activesupport (~> 5.2.3)
6
+ rest-client (~> 2.1.0)
7
+ thor (~> 0.20.3)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (5.2.4.1)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ ast (2.4.0)
18
+ concurrent-ruby (1.1.5)
19
+ diff-lcs (1.3)
20
+ domain_name (0.5.20190701)
21
+ unf (>= 0.0.5, < 1.0.0)
22
+ http-accept (1.7.0)
23
+ http-cookie (1.0.3)
24
+ domain_name (~> 0.5)
25
+ i18n (1.8.2)
26
+ concurrent-ruby (~> 1.0)
27
+ jaro_winkler (1.5.4)
28
+ mime-types (3.3.1)
29
+ mime-types-data (~> 3.2015)
30
+ mime-types-data (3.2019.1009)
31
+ minitest (5.14.0)
32
+ netrc (0.11.0)
33
+ parallel (1.19.1)
34
+ parser (2.7.0.2)
35
+ ast (~> 2.4.0)
36
+ rainbow (3.0.0)
37
+ rake (10.5.0)
38
+ relaxed-rubocop (2.5)
39
+ rest-client (2.1.0)
40
+ http-accept (>= 1.7.0, < 2.0)
41
+ http-cookie (>= 1.0.2, < 2.0)
42
+ mime-types (>= 1.16, < 4.0)
43
+ netrc (~> 0.8)
44
+ rspec (3.9.0)
45
+ rspec-core (~> 3.9.0)
46
+ rspec-expectations (~> 3.9.0)
47
+ rspec-mocks (~> 3.9.0)
48
+ rspec-core (3.9.1)
49
+ rspec-support (~> 3.9.1)
50
+ rspec-expectations (3.9.0)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.9.0)
53
+ rspec-mocks (3.9.1)
54
+ diff-lcs (>= 1.2.0, < 2.0)
55
+ rspec-support (~> 3.9.0)
56
+ rspec-support (3.9.2)
57
+ rubocop (0.79.0)
58
+ jaro_winkler (~> 1.5.1)
59
+ parallel (~> 1.10)
60
+ parser (>= 2.7.0.1)
61
+ rainbow (>= 2.2.2, < 4.0)
62
+ ruby-progressbar (~> 1.7)
63
+ unicode-display_width (>= 1.4.0, < 1.7)
64
+ ruby-progressbar (1.10.1)
65
+ thor (0.20.3)
66
+ thread_safe (0.3.6)
67
+ tzinfo (1.2.6)
68
+ thread_safe (~> 0.1)
69
+ unf (0.1.4)
70
+ unf_ext
71
+ unf_ext (0.0.7.6)
72
+ unicode-display_width (1.6.1)
73
+
74
+ PLATFORMS
75
+ ruby
76
+
77
+ DEPENDENCIES
78
+ bundler (~> 1.17.3)
79
+ rake (~> 10.0)
80
+ relaxed-rubocop (~> 2.5)
81
+ rspec (~> 3.0)
82
+ rubocop (~> 0.79.0)
83
+ xapixctl!
84
+
85
+ BUNDLED WITH
86
+ 1.17.3
data/LICENSE ADDED
@@ -0,0 +1,277 @@
1
+ Eclipse Public License - v 2.0
2
+
3
+ THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
4
+ PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
5
+ OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
6
+
7
+ 1. DEFINITIONS
8
+
9
+ "Contribution" means:
10
+
11
+ a) in the case of the initial Contributor, the initial content
12
+ Distributed under this Agreement, and
13
+
14
+ b) in the case of each subsequent Contributor:
15
+ i) changes to the Program, and
16
+ ii) additions to the Program;
17
+ where such changes and/or additions to the Program originate from
18
+ and are Distributed by that particular Contributor. A Contribution
19
+ "originates" from a Contributor if it was added to the Program by
20
+ such Contributor itself or anyone acting on such Contributor's behalf.
21
+ Contributions do not include changes or additions to the Program that
22
+ are not Modified Works.
23
+
24
+ "Contributor" means any person or entity that Distributes the Program.
25
+
26
+ "Licensed Patents" mean patent claims licensable by a Contributor which
27
+ are necessarily infringed by the use or sale of its Contribution alone
28
+ or when combined with the Program.
29
+
30
+ "Program" means the Contributions Distributed in accordance with this
31
+ Agreement.
32
+
33
+ "Recipient" means anyone who receives the Program under this Agreement
34
+ or any Secondary License (as applicable), including Contributors.
35
+
36
+ "Derivative Works" shall mean any work, whether in Source Code or other
37
+ form, that is based on (or derived from) the Program and for which the
38
+ editorial revisions, annotations, elaborations, or other modifications
39
+ represent, as a whole, an original work of authorship.
40
+
41
+ "Modified Works" shall mean any work in Source Code or other form that
42
+ results from an addition to, deletion from, or modification of the
43
+ contents of the Program, including, for purposes of clarity any new file
44
+ in Source Code form that contains any contents of the Program. Modified
45
+ Works shall not include works that contain only declarations,
46
+ interfaces, types, classes, structures, or files of the Program solely
47
+ in each case in order to link to, bind by name, or subclass the Program
48
+ or Modified Works thereof.
49
+
50
+ "Distribute" means the acts of a) distributing or b) making available
51
+ in any manner that enables the transfer of a copy.
52
+
53
+ "Source Code" means the form of a Program preferred for making
54
+ modifications, including but not limited to software source code,
55
+ documentation source, and configuration files.
56
+
57
+ "Secondary License" means either the GNU General Public License,
58
+ Version 2.0, or any later versions of that license, including any
59
+ exceptions or additional permissions as identified by the initial
60
+ Contributor.
61
+
62
+ 2. GRANT OF RIGHTS
63
+
64
+ a) Subject to the terms of this Agreement, each Contributor hereby
65
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
66
+ license to reproduce, prepare Derivative Works of, publicly display,
67
+ publicly perform, Distribute and sublicense the Contribution of such
68
+ Contributor, if any, and such Derivative Works.
69
+
70
+ b) Subject to the terms of this Agreement, each Contributor hereby
71
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
72
+ license under Licensed Patents to make, use, sell, offer to sell,
73
+ import and otherwise transfer the Contribution of such Contributor,
74
+ if any, in Source Code or other form. This patent license shall
75
+ apply to the combination of the Contribution and the Program if, at
76
+ the time the Contribution is added by the Contributor, such addition
77
+ of the Contribution causes such combination to be covered by the
78
+ Licensed Patents. The patent license shall not apply to any other
79
+ combinations which include the Contribution. No hardware per se is
80
+ licensed hereunder.
81
+
82
+ c) Recipient understands that although each Contributor grants the
83
+ licenses to its Contributions set forth herein, no assurances are
84
+ provided by any Contributor that the Program does not infringe the
85
+ patent or other intellectual property rights of any other entity.
86
+ Each Contributor disclaims any liability to Recipient for claims
87
+ brought by any other entity based on infringement of intellectual
88
+ property rights or otherwise. As a condition to exercising the
89
+ rights and licenses granted hereunder, each Recipient hereby
90
+ assumes sole responsibility to secure any other intellectual
91
+ property rights needed, if any. For example, if a third party
92
+ patent license is required to allow Recipient to Distribute the
93
+ Program, it is Recipient's responsibility to acquire that license
94
+ before distributing the Program.
95
+
96
+ d) Each Contributor represents that to its knowledge it has
97
+ sufficient copyright rights in its Contribution, if any, to grant
98
+ the copyright license set forth in this Agreement.
99
+
100
+ e) Notwithstanding the terms of any Secondary License, no
101
+ Contributor makes additional grants to any Recipient (other than
102
+ those set forth in this Agreement) as a result of such Recipient's
103
+ receipt of the Program under the terms of a Secondary License
104
+ (if permitted under the terms of Section 3).
105
+
106
+ 3. REQUIREMENTS
107
+
108
+ 3.1 If a Contributor Distributes the Program in any form, then:
109
+
110
+ a) the Program must also be made available as Source Code, in
111
+ accordance with section 3.2, and the Contributor must accompany
112
+ the Program with a statement that the Source Code for the Program
113
+ is available under this Agreement, and informs Recipients how to
114
+ obtain it in a reasonable manner on or through a medium customarily
115
+ used for software exchange; and
116
+
117
+ b) the Contributor may Distribute the Program under a license
118
+ different than this Agreement, provided that such license:
119
+ i) effectively disclaims on behalf of all other Contributors all
120
+ warranties and conditions, express and implied, including
121
+ warranties or conditions of title and non-infringement, and
122
+ implied warranties or conditions of merchantability and fitness
123
+ for a particular purpose;
124
+
125
+ ii) effectively excludes on behalf of all other Contributors all
126
+ liability for damages, including direct, indirect, special,
127
+ incidental and consequential damages, such as lost profits;
128
+
129
+ iii) does not attempt to limit or alter the recipients' rights
130
+ in the Source Code under section 3.2; and
131
+
132
+ iv) requires any subsequent distribution of the Program by any
133
+ party to be under a license that satisfies the requirements
134
+ of this section 3.
135
+
136
+ 3.2 When the Program is Distributed as Source Code:
137
+
138
+ a) it must be made available under this Agreement, or if the
139
+ Program (i) is combined with other material in a separate file or
140
+ files made available under a Secondary License, and (ii) the initial
141
+ Contributor attached to the Source Code the notice described in
142
+ Exhibit A of this Agreement, then the Program may be made available
143
+ under the terms of such Secondary Licenses, and
144
+
145
+ b) a copy of this Agreement must be included with each copy of
146
+ the Program.
147
+
148
+ 3.3 Contributors may not remove or alter any copyright, patent,
149
+ trademark, attribution notices, disclaimers of warranty, or limitations
150
+ of liability ("notices") contained within the Program from any copy of
151
+ the Program which they Distribute, provided that Contributors may add
152
+ their own appropriate notices.
153
+
154
+ 4. COMMERCIAL DISTRIBUTION
155
+
156
+ Commercial distributors of software may accept certain responsibilities
157
+ with respect to end users, business partners and the like. While this
158
+ license is intended to facilitate the commercial use of the Program,
159
+ the Contributor who includes the Program in a commercial product
160
+ offering should do so in a manner which does not create potential
161
+ liability for other Contributors. Therefore, if a Contributor includes
162
+ the Program in a commercial product offering, such Contributor
163
+ ("Commercial Contributor") hereby agrees to defend and indemnify every
164
+ other Contributor ("Indemnified Contributor") against any losses,
165
+ damages and costs (collectively "Losses") arising from claims, lawsuits
166
+ and other legal actions brought by a third party against the Indemnified
167
+ Contributor to the extent caused by the acts or omissions of such
168
+ Commercial Contributor in connection with its distribution of the Program
169
+ in a commercial product offering. The obligations in this section do not
170
+ apply to any claims or Losses relating to any actual or alleged
171
+ intellectual property infringement. In order to qualify, an Indemnified
172
+ Contributor must: a) promptly notify the Commercial Contributor in
173
+ writing of such claim, and b) allow the Commercial Contributor to control,
174
+ and cooperate with the Commercial Contributor in, the defense and any
175
+ related settlement negotiations. The Indemnified Contributor may
176
+ participate in any such claim at its own expense.
177
+
178
+ For example, a Contributor might include the Program in a commercial
179
+ product offering, Product X. That Contributor is then a Commercial
180
+ Contributor. If that Commercial Contributor then makes performance
181
+ claims, or offers warranties related to Product X, those performance
182
+ claims and warranties are such Commercial Contributor's responsibility
183
+ alone. Under this section, the Commercial Contributor would have to
184
+ defend claims against the other Contributors related to those performance
185
+ claims and warranties, and if a court requires any other Contributor to
186
+ pay any damages as a result, the Commercial Contributor must pay
187
+ those damages.
188
+
189
+ 5. NO WARRANTY
190
+
191
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
192
+ PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
193
+ BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
194
+ IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
195
+ TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
196
+ PURPOSE. Each Recipient is solely responsible for determining the
197
+ appropriateness of using and distributing the Program and assumes all
198
+ risks associated with its exercise of rights under this Agreement,
199
+ including but not limited to the risks and costs of program errors,
200
+ compliance with applicable laws, damage to or loss of data, programs
201
+ or equipment, and unavailability or interruption of operations.
202
+
203
+ 6. DISCLAIMER OF LIABILITY
204
+
205
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
206
+ PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
207
+ SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
208
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
209
+ PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
210
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
211
+ ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
212
+ EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
213
+ POSSIBILITY OF SUCH DAMAGES.
214
+
215
+ 7. GENERAL
216
+
217
+ If any provision of this Agreement is invalid or unenforceable under
218
+ applicable law, it shall not affect the validity or enforceability of
219
+ the remainder of the terms of this Agreement, and without further
220
+ action by the parties hereto, such provision shall be reformed to the
221
+ minimum extent necessary to make such provision valid and enforceable.
222
+
223
+ If Recipient institutes patent litigation against any entity
224
+ (including a cross-claim or counterclaim in a lawsuit) alleging that the
225
+ Program itself (excluding combinations of the Program with other software
226
+ or hardware) infringes such Recipient's patent(s), then such Recipient's
227
+ rights granted under Section 2(b) shall terminate as of the date such
228
+ litigation is filed.
229
+
230
+ All Recipient's rights under this Agreement shall terminate if it
231
+ fails to comply with any of the material terms or conditions of this
232
+ Agreement and does not cure such failure in a reasonable period of
233
+ time after becoming aware of such noncompliance. If all Recipient's
234
+ rights under this Agreement terminate, Recipient agrees to cease use
235
+ and distribution of the Program as soon as reasonably practicable.
236
+ However, Recipient's obligations under this Agreement and any licenses
237
+ granted by Recipient relating to the Program shall continue and survive.
238
+
239
+ Everyone is permitted to copy and distribute copies of this Agreement,
240
+ but in order to avoid inconsistency the Agreement is copyrighted and
241
+ may only be modified in the following manner. The Agreement Steward
242
+ reserves the right to publish new versions (including revisions) of
243
+ this Agreement from time to time. No one other than the Agreement
244
+ Steward has the right to modify this Agreement. The Eclipse Foundation
245
+ is the initial Agreement Steward. The Eclipse Foundation may assign the
246
+ responsibility to serve as the Agreement Steward to a suitable separate
247
+ entity. Each new version of the Agreement will be given a distinguishing
248
+ version number. The Program (including Contributions) may always be
249
+ Distributed subject to the version of the Agreement under which it was
250
+ received. In addition, after a new version of the Agreement is published,
251
+ Contributor may elect to Distribute the Program (including its
252
+ Contributions) under the new version.
253
+
254
+ Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
255
+ receives no rights or licenses to the intellectual property of any
256
+ Contributor under this Agreement, whether expressly, by implication,
257
+ estoppel or otherwise. All rights in the Program not expressly granted
258
+ under this Agreement are reserved. Nothing in this Agreement is intended
259
+ to be enforceable by any entity that is not a Contributor or Recipient.
260
+ No third-party beneficiary rights are created under this Agreement.
261
+
262
+ Exhibit A - Form of Secondary Licenses Notice
263
+
264
+ "This Source Code may also be made available under the following
265
+ Secondary Licenses when the conditions for such availability set forth
266
+ in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
267
+ version(s), and exceptions or additional permissions here}."
268
+
269
+ Simply including a copy of this Agreement, including this Exhibit A
270
+ is not sufficient to license the Source Code under Secondary Licenses.
271
+
272
+ If it is not possible or desirable to put the notice in a particular
273
+ file, then You may include the notice in a location (such as a LICENSE
274
+ file in a relevant directory) where a recipient would be likely to
275
+ look for such a notice.
276
+
277
+ You may add additional accurate notices of copyright ownership.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # xapixctl
2
+
3
+ Xapix client library and command line tool
4
+
5
+ ## Installation
6
+
7
+ Install it via:
8
+
9
+ $ gem install xapixctl
10
+
11
+ ## Usage
12
+
13
+ To see more details on how to run xapixctl, use:
14
+
15
+ ```
16
+ $ xapixctl help
17
+
18
+ Commands:
19
+ xapixctl api-resources # retrieves a list of all available resource types
20
+ xapixctl apply -f, --file=FILE -o, --org=ORG # Create or update a resource from a file
21
+ xapixctl delete [TYPE ID] [-f FILE] -o, --org=ORG # delete the resources in the file
22
+ xapixctl export -o, --org=ORG -p, --project=PROJECT # retrieves all resources within a project
23
+ xapixctl get TYPE [ID] -o, --org=ORG # retrieve either all resources of given TYPE or just the resource of given TYPE and ID
24
+ xapixctl help [COMMAND] # Describe available commands or one specific command
25
+ xapixctl logs CORRELATION_ID -o, --org=ORG -p, --project=PROJECT # Retrieves the execution logs for the given correlation ID
26
+ xapixctl publish -o, --org=ORG -p, --project=PROJECT # Publishes the current version of the given project
27
+
28
+ Options:
29
+ -v, [--verbose], [--no-verbose]
30
+ [--xapix-url=XAPIX_URL] # Fallback: environment variable XAPIX_URL. URL to Xapix. Default: https://cloud.xapix.io/
31
+ [--xapix-token=XAPIX_TOKEN] # Fallback: environment variable XAPIX_TOKEN. Your access token.
32
+ ```
33
+
34
+ The main commands to interact with Xapix are:
35
+ * `xapixctl get TYPE` to list all the resources of a specific type. You can get the supported types with `xapixctl api-resources`. To see the complete resource definition instead of an overview use the `-f yaml` switch. Use `xapixctl help get` for more details.
36
+ * `xapixctl apply` to create a new resource or update existing ones. Resources are matched using their type and id. If a resource with the same type and id already exists it is updated, otherwise a new one is created.
37
+ * `xapixctl publish` to publish the current status of a project.
38
+
39
+
40
+ ## Development
41
+
42
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
43
+
44
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
45
+
46
+ ## Contributing
47
+
48
+ Bug reports and pull requests are welcome on GitHub at https://github.com/xapix-io/xapixctl.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "xapixctl"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/xapixctl ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "xapixctl"
5
+ require "xapixctl/cli"
6
+
7
+ Xapixctl::Cli.start(ARGV)
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Xapixctl
6
+ class Cli < Thor
7
+ def self.exit_on_failure?; true; end
8
+
9
+ class_option :verbose, type: :boolean, aliases: "-v"
10
+ class_option :xapix_url, desc: "Fallback: environment variable XAPIX_URL. URL to Xapix. Default: https://cloud.xapix.io/"
11
+ class_option :xapix_token, desc: "Fallback: environment variable XAPIX_TOKEN. Your access token."
12
+
13
+ option :org, aliases: "-o", desc: "Organization", required: true
14
+ option :project, aliases: "-p", desc: "Project"
15
+ option :format, aliases: "-f", default: 'text', enum: ['text', 'yaml', 'json'], desc: "Output format"
16
+ desc "get TYPE [ID]", "retrieve either all resources of given TYPE or just the resource of given TYPE and ID"
17
+ long_desc <<-LONGDESC
18
+ `xapctl get TYPE` will retrieve the list of all resources of given type.
19
+
20
+ If requested on an organization (i.e. no project given), the following types are available:
21
+ \x5 Project
22
+
23
+ If requested on an a project (i.e. organization and project are given), the following types are available:
24
+ \x5 Ambassador, AuthScheme, CacheConnection, Credential, DataSource, Endpoint, EndpointGroup, Proxy, Schema, Stream, StreamGroup
25
+
26
+ Use the format to switch between the different output formats.
27
+
28
+ Examples:
29
+ \x5> $ xapctl get -o xapix Project
30
+ \x5> $ xapctl get -o xapix Project some-project
31
+ \x5> $ xapctl get -o xapix -p some-project DataSource
32
+ \x5> $ xapctl get -o xapix -p some-project DataSource get-a-list
33
+ LONGDESC
34
+ def get(resource_type, resource_id = nil)
35
+ if resource_id
36
+ connection.resource(resource_type, resource_id, org: options[:org], project: options[:project], format: options[:format].to_sym) do |res|
37
+ res.on_success { |resource| puts resource }
38
+ res.on_error { |err, result| warn_api_error("could not get", err, result) }
39
+ end
40
+ else
41
+ connection.resource_ids(resource_type, org: options[:org], project: options[:project]) do |res|
42
+ res.on_success do |resource_ids|
43
+ resource_ids.each do |resource_id|
44
+ connection.resource(resource_type, resource_id, org: options[:org], project: options[:project], format: options[:format].to_sym) do |res|
45
+ res.on_success { |resource| puts resource }
46
+ res.on_error { |err, result| warn_api_error("could not get", err, result) }
47
+ end
48
+ end
49
+ end
50
+ res.on_error { |err, result| warn_api_error("could not get", err, result) }
51
+ end
52
+ end
53
+ end
54
+
55
+ option :org, aliases: "-o", desc: "Organization", required: true
56
+ option :project, aliases: "-p", desc: "Project", required: true
57
+ option :format, aliases: "-f", default: 'text', enum: ['text', 'yaml', 'json'], desc: "Output format"
58
+ desc "export", "retrieves all resources within a project"
59
+ long_desc <<-LONGDESC
60
+ `xapctl export` will retrieve the list of all resources of given type.
61
+
62
+ Use the format to switch between the different output formats.
63
+
64
+ Examples:
65
+ \x5> $ xapctl export -o xapix -p some-project
66
+ \x5> $ xapctl export -o xapix -p some-project -f yaml > some_project.yaml
67
+ LONGDESC
68
+ def export
69
+ connection.resource('Project', options[:project], org: options[:org], format: options[:format].to_sym) do |res|
70
+ res.on_success { |resource| puts resource }
71
+ res.on_error { |err, result| warn_api_error("could not get", err, result) }
72
+ end
73
+ (connection.resource_types_for_export - ['Project']).each { |type| get(type) }
74
+ end
75
+
76
+ option :org, aliases: "-o", desc: "Organization", required: true
77
+ option :project, aliases: "-p", desc: "Project"
78
+ option :file, aliases: "-f", required: true
79
+ desc "apply", "Create or update a resource from a file"
80
+ long_desc <<-LONGDESC
81
+ `xapctl apply -f FILE` will apply the given resource description.
82
+
83
+ If applied on an organization (i.e. no project given), the project is taken from the resource description.
84
+
85
+ If applied on a project (i.e. organization and project are given), the given project is used.
86
+
87
+ The given file should be in YAML format and can contain multiple resource definitions, each as it's own YAML document.
88
+ You can also read from stdin by using '-'.
89
+
90
+ Examples:
91
+ \x5> $ xapctl apply -o xapix -f get_a_list.yaml
92
+ \x5> $ xapctl apply -o xapix -p some-project -f get_a_list.yaml
93
+
94
+ To copy over all data sources from one project to another:
95
+ \x5> $ xapctl get -o xapix-old -p some-project DataSource -f yaml | xapctl apply -o xapix-new -f -
96
+ LONGDESC
97
+ def apply
98
+ resources_from_file(options[:file]) do |desc|
99
+ puts "applying #{desc['kind']} #{desc.dig('metadata', 'id')}"
100
+ connection.apply(desc, org: options[:org], project: options[:project]) do |res|
101
+ res.on_success { puts 'OK' }
102
+ res.on_error { |err, result| warn_api_error("could not apply changes", err, result); break }
103
+ end
104
+ end
105
+ end
106
+
107
+ option :org, aliases: "-o", desc: "Organization", required: true
108
+ option :project, aliases: "-p", desc: "Project"
109
+ option :file, aliases: "-f"
110
+ desc "delete [TYPE ID] [-f FILE]", "delete the resources in the file"
111
+ long_desc <<-LONGDESC
112
+ `xapctl delete -f FILE` will delete all the resources listed in the file.
113
+ \x5`xapctl delete TYPE ID` will delete the resource by given TYPE and ID.
114
+
115
+ The given file should be in YAML format and can contain multiple resource definitions, each as it's own YAML document.
116
+ You can also read from stdin by using '-'.
117
+
118
+ Examples:
119
+ \x5> $ xapctl delete -o xapix -p some-project -f get_a_list.yaml
120
+ \x5> $ xapctl delete -o xapix -p some-project DataSource get-a-list
121
+ \x5> $ xapctl delete -o xapix Project some-project
122
+ LONGDESC
123
+ def delete(resource_type = nil, resource_id = nil)
124
+ if resource_type && resource_id
125
+ connection.delete(resource_type, resource_id, org: options[:org], project: options[:project]) do |res|
126
+ res.on_success { puts 'DELETED' }
127
+ res.on_error { |err, result| warn_api_error("could not delete", err, result) }
128
+ end
129
+ elsif options[:file]
130
+ resources_from_file(options[:file]) do |desc|
131
+ type = desc.dig('kind')
132
+ id = desc.dig('metadata', 'id')
133
+ puts "deleting #{type} #{id}"
134
+ connection.delete(type, id, org: options[:org], project: options[:project]) do |res|
135
+ res.on_success { puts "DELETED #{type} #{id}" }
136
+ res.on_error { |err, result| warn_api_error("could not delete", err, result); break }
137
+ end
138
+ end
139
+ else
140
+ warn "need TYPE and ID or --file option"
141
+ end
142
+ end
143
+
144
+ option :org, aliases: "-o", desc: "Organization", required: true
145
+ option :project, aliases: "-p", desc: "Project", required: true
146
+ desc "publish", "Publishes the current version of the given project"
147
+ long_desc <<-LONGDESC
148
+ `xapctl publish` will publish the given project.
149
+
150
+ Examples:
151
+ \x5> $ xapctl publish -o xapix -p some-project
152
+ LONGDESC
153
+ def publish
154
+ connection.publish(org: options[:org], project: options[:project]) do |res|
155
+ res.on_success { |result| show_deployment_status(result) }
156
+ res.on_error { |err, result| show_deployment_status(result); warn_api_error('errors', err, result) }
157
+ end
158
+ end
159
+
160
+ option :org, aliases: "-o", desc: "Organization", required: true
161
+ option :project, aliases: "-p", desc: "Project", required: true
162
+ desc "logs CORRELATION_ID", "Retrieves the execution logs for the given correlation ID"
163
+ long_desc <<-LONGDESC
164
+ `xapctl logs CORRELATION_ID` will retrieve execution logs for the given correlation ID.
165
+
166
+ The correlation ID is included as X-Correlation-Id header in the response of each request.
167
+
168
+ Examples:
169
+ \x5> $ xapctl logs be9c8608-e291-460d-bc20-5a394c4079d4 -o xapix -p some-project
170
+ LONGDESC
171
+ def logs(correlation_id)
172
+ connection.logs(correlation_id, org: options[:org], project: options[:project]) do |res|
173
+ res.on_success { |result| puts result['logs'].to_yaml }
174
+ res.on_error { |err, result| warn_api_error('could not get logs', err, result) }
175
+ end
176
+ end
177
+
178
+ SUPPORTED_CONTEXTS = ['Project', 'Organization'].freeze
179
+ desc "api-resources", "retrieves a list of all available resource types"
180
+ def api_resources
181
+ connection.available_resource_types do |res|
182
+ res.on_success do |available_types|
183
+ format_str = "%20.20s %20.20s"
184
+ puts format_str % ['Type', 'Required Context']
185
+ available_types.sort_by { |desc| desc['type'] }.each do |desc|
186
+ next unless SUPPORTED_CONTEXTS.include?(desc['context'])
187
+ puts format_str % [desc['type'], desc['context']]
188
+ end
189
+ end
190
+ res.on_error { |err, result| warn_api_error("could not get", err, result) }
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ def warn_api_error(text, err, result)
197
+ details = "\n " + result['errors'].map { |k| k['detail'] }.join("\n ") rescue err.to_s
198
+ warn "#{text}: #{details}"
199
+ exit 1
200
+ end
201
+
202
+ def show_deployment_status(result)
203
+ return unless result && result['project_publication']
204
+ puts "deployment: #{result.dig('project_publication', 'deployment')}"
205
+ puts " data api: #{result.dig('project_publication', 'data_api')} (version: #{result.dig('project_publication', 'data_api_version').presence || 'n/a'})"
206
+ puts " user management: #{result.dig('project_publication', 'user_management')}"
207
+ if result.dig('project_publication', 'deployment') == 'success'
208
+ puts " base URL: #{result.dig('project_publication', 'base_url')}"
209
+ end
210
+ end
211
+
212
+ DOCUMENT_STRUCTURE = %w[version kind metadata definition].freeze
213
+ def resources_from_file(filename)
214
+ yaml_string = filename == '-' ? $stdin.read : IO.read(filename)
215
+ yaml_string.split(/^---\s*\n/).map { |yml| Psych.safe_load(yml) }.compact.each do |doc|
216
+ unless (DOCUMENT_STRUCTURE - doc.keys.map(&:to_s)).empty?
217
+ warn "does not look like a correct resource definition:"
218
+ warn doc.inspect
219
+ exit 1
220
+ end
221
+ yield doc
222
+ end
223
+ end
224
+
225
+ def connection
226
+ url = options[:xapix_url] || ENV['XAPIX_URL'] || 'https://cloud.xapix.io/'
227
+ token = options[:xapix_token] || ENV['XAPIX_TOKEN']
228
+ raise Thor::RequiredArgumentMissingError, "no XAPIX_TOKEN given. Either use --xapix_token [TOKEN] or set environment variable XAPIX_TOKEN (recommended)" if !token
229
+ PhoenixClient::Connection.new(url, token)
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xapixctl
4
+ module PhoenixClient
5
+
6
+ class ResultHandler
7
+ def initialize(default_success_handler:, default_error_handler:)
8
+ @success_handler = default_success_handler
9
+ @error_handler = default_error_handler
10
+ @result_handler = nil
11
+ yield self if block_given?
12
+ end
13
+
14
+ def on_success(&block); @success_handler = block; self; end
15
+
16
+ def on_error(&block); @error_handler = block; self; end
17
+
18
+ def prepare_data(proc); @result_handler = proc; self; end
19
+
20
+ def formatter(proc); @formatter = proc; self; end
21
+
22
+ def run
23
+ res = yield
24
+ res = res.present? ? JSON.parse(res) : res
25
+ res = @result_handler ? @result_handler.call(res) : res
26
+ res = @formatter ? @formatter.call(res) : res
27
+ @success_handler.call(res)
28
+ rescue RestClient::Exception => err
29
+ response = JSON.parse(err.response) rescue {}
30
+ @error_handler.call(err, response)
31
+ rescue SocketError, Errno::ECONNREFUSED => err
32
+ @error_handler.call(err, nil)
33
+ end
34
+ end
35
+
36
+ TEXT_FORMATTERS = {
37
+ all: ->(data) { "id : %{id}\nkind: %{kind}\nname: %{name}\n\n" % { id: data.dig('metadata', 'id'), kind: data.dig('kind'), name: data.dig('definition', 'name') } }
38
+ }.freeze
39
+
40
+ FORMATTERS = {
41
+ json: ->(data) { JSON.pretty_generate(data) },
42
+ yaml: ->(data) { Psych.dump(data.deep_transform_keys! { |k| k.to_s.camelize(:lower) }) },
43
+ text: ->(data) { (TEXT_FORMATTERS[data.dig('metadata', 'type')] || TEXT_FORMATTERS[:all]).call(data) }
44
+ }.freeze
45
+
46
+ class Connection
47
+ DEFAULT_SUCCESS_HANDLER = ->(result) { result }
48
+ DEFAULT_ERROR_HANDLER = ->(err, _response) { warn "Could not get data: #{err}" }
49
+
50
+ # sorting is intentional to reflect dependencies when exporting
51
+ SUPPORTED_RESOURCE_TYPES = %w[
52
+ Project
53
+ Ambassador
54
+ AuthScheme
55
+ Credential
56
+ Proxy
57
+ CacheConnection
58
+ Schema
59
+ DataSource
60
+ Pipeline
61
+ EndpointGroup
62
+ Endpoint
63
+ StreamGroup
64
+ Stream
65
+ ApiPublishing
66
+ ApiPublishingRole
67
+ ].freeze
68
+
69
+ def initialize(url, token)
70
+ @client = RestClient::Resource.new(File.join(url, 'api/v1'), verify_ssl: false, accept: :json, content_type: :json, headers: { Authorization: "Bearer #{token}" })
71
+ @default_success_handler = DEFAULT_SUCCESS_HANDLER
72
+ @default_error_handler = DEFAULT_ERROR_HANDLER
73
+ end
74
+
75
+ def on_success(&block); @default_success_handler = block; self; end
76
+
77
+ def on_error(&block); @default_error_handler = block; self; end
78
+
79
+ def resource(resource_type, resource_id, org:, project: nil, format: :hash, &block)
80
+ result_handler(block).
81
+ formatter(FORMATTERS[format]).
82
+ run { @client[resource_path(org, project, resource_type, resource_id)].get }
83
+ end
84
+
85
+ def resource_ids(resource_type, org:, project: nil, &block)
86
+ result_handler(block).
87
+ prepare_data(->(data) { data['resource_ids'] }).
88
+ run { @client[resources_path(org, project, resource_type)].get }
89
+ end
90
+
91
+ def apply(resource_description, org:, project: nil, &block)
92
+ result_handler(block).
93
+ run { @client[generic_resource_path(org, project)].put(resource_description.to_json) }
94
+ end
95
+
96
+ def delete(resource_type, resource_id, org:, project: nil, &block)
97
+ result_handler(block).
98
+ run { @client[resource_path(org, project, resource_type, resource_id)].delete }
99
+ end
100
+
101
+ def publish(org:, project:, &block)
102
+ result_handler(block).
103
+ run { @client[project_publications_path(org, project)].post('') }
104
+ end
105
+
106
+ def logs(correlation_id, org:, project:, &block)
107
+ result_handler(block).
108
+ run { @client[project_logss_path(org, project, correlation_id)].get }
109
+ end
110
+
111
+ def available_resource_types(&block)
112
+ result_handler(block).
113
+ prepare_data(->(data) { data['resource_types'] }).
114
+ run { @client[resource_types_path].get }
115
+ end
116
+
117
+ def resource_types_for_export
118
+ @resource_types_for_export ||=
119
+ available_resource_types do |res|
120
+ res.on_success { |available_types| SUPPORTED_RESOURCE_TYPES & available_types.map { |desc| desc['type'] } }
121
+ res.on_error { |err, _response| raise err }
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def result_handler(block)
128
+ ResultHandler.new(default_success_handler: @default_success_handler, default_error_handler: @default_error_handler, &block)
129
+ end
130
+
131
+ def resource_path(org, project, type, id)
132
+ type = translate_type(type)
133
+ project ? "/projects/#{org}/#{project}/#{type}/#{id}" : "/orgs/#{org}/#{type}/#{id}"
134
+ end
135
+
136
+ def resources_path(org, project, type)
137
+ type = translate_type(type)
138
+ project ? "/projects/#{org}/#{project}/#{type}" : "/orgs/#{org}/#{type}"
139
+ end
140
+
141
+ def generic_resource_path(org, project)
142
+ project ? "projects/#{org}/#{project}/resource" : "orgs/#{org}/resource"
143
+ end
144
+
145
+ def project_publications_path(org, project)
146
+ "/projects/#{org}/#{project}/publications"
147
+ end
148
+
149
+ def project_logss_path(org, project, correlation_id)
150
+ "/projects/#{org}/#{project}/logs/#{correlation_id}"
151
+ end
152
+
153
+ def resource_types_path
154
+ "/resource_types"
155
+ end
156
+
157
+ def translate_type(resource_type)
158
+ return 'ApiPublishingRole' if resource_type == 'ApiPublishing/Role'
159
+ resource_type.sub(%r[/.*], '') # cut off everything after first slash
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,3 @@
1
+ module Xapixctl
2
+ VERSION = "1.0.0"
3
+ end
data/lib/xapixctl.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/hash/keys'
5
+ require 'active_support/core_ext/object/blank'
6
+ require 'active_support/core_ext/string/inflections'
7
+ require 'rest_client'
8
+ require 'json'
9
+ require 'psych'
10
+ require 'xapixctl/version'
11
+ require 'xapixctl/phoenix_client'
12
+
13
+ module Xapixctl
14
+ end
data/xapixctl.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "xapixctl/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "xapixctl"
7
+ spec.version = Xapixctl::VERSION
8
+ spec.authors = ["Michael Reinsch"]
9
+ spec.email = ["michael@xapix.io"]
10
+
11
+ spec.summary = %q{xapix client library and command line tool}
12
+ spec.homepage = "https://github.com/xapix-io/xapixctl"
13
+ spec.license = "EPL-2.0"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = spec.homepage
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ end
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_dependency "activesupport", "~> 5.2.3"
28
+ spec.add_dependency "rest-client", "~> 2.1.0"
29
+ spec.add_dependency "thor", "~> 0.20.3"
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.17.3"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "relaxed-rubocop", "~> 2.5"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
35
+ spec.add_development_dependency "rubocop", "~> 0.79.0"
36
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xapixctl
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Reinsch
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-02-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.20.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.20.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.17.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.17.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: relaxed-rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.79.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.79.0
125
+ description:
126
+ email:
127
+ - michael@xapix.io
128
+ executables:
129
+ - xapixctl
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".rubocop.yml"
136
+ - ".ruby-version"
137
+ - Gemfile
138
+ - Gemfile.lock
139
+ - LICENSE
140
+ - README.md
141
+ - Rakefile
142
+ - bin/console
143
+ - bin/setup
144
+ - exe/xapixctl
145
+ - lib/xapixctl.rb
146
+ - lib/xapixctl/cli.rb
147
+ - lib/xapixctl/phoenix_client.rb
148
+ - lib/xapixctl/version.rb
149
+ - xapixctl.gemspec
150
+ homepage: https://github.com/xapix-io/xapixctl
151
+ licenses:
152
+ - EPL-2.0
153
+ metadata:
154
+ homepage_uri: https://github.com/xapix-io/xapixctl
155
+ source_code_uri: https://github.com/xapix-io/xapixctl
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubygems_version: 3.0.6
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: xapix client library and command line tool
175
+ test_files: []