xapixctl 1.2.0 → 1.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c745d574b6c72e52ff4a74989ac0b4663c6707e9dbd3c14e69facac3e135a978
4
- data.tar.gz: 042e00431dc81a64d68d3a3b61af8b54b4319133144a4ba3916d26ad6a32a423
3
+ metadata.gz: 175a3c54493e14738757b5eb95ef1da9724b961f460d1ac5dfb17ea6eb696507
4
+ data.tar.gz: 0a79951ab81a2b69d0087161191097cf24b01e2282887d03b12e8f32527d3707
5
5
  SHA512:
6
- metadata.gz: 34c8479e26148a0e158f20e9202340dc65dd84a85d624498f9e0b07994b1964257f5ab3d1b8db5fbc7304d4800ccb1672570f1beaffd857f7443c654d986cb63
7
- data.tar.gz: 7071a6a8a53d41b41eec96af9ba05c58446c56ceb3be73148b619e3c33ac44d6da75b45b4192e0f5d1a4d547903ec98fbd2f87431ab5082cb659e456e827d39b
6
+ metadata.gz: 040c017fb340670f8751a45c5b9c0c659cd814bb712cc74fb40e368f714fb1b80759d9c4387cbbb19999cfd512fc389a082168cde33f67cc1fd9fa0fb17b42f3
7
+ data.tar.gz: 1bdbbbb3da472ade7b520cfebc284ebebdeb871ee77c3bafa5fb22789bf4c0057d4dc7ff116ee3c0d611dbd692d99a96ac168ffa8824be006b22b124c7e98bf7
@@ -13,7 +13,5 @@ jobs:
13
13
  with:
14
14
  ruby-version: ${{ matrix.ruby-version }}
15
15
  bundler-cache: true
16
- - name: Install dependencies
17
- run: bundle install
18
16
  - name: Run tests
19
17
  run: bundle exec rspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- xapixctl (1.2.0)
4
+ xapixctl (1.2.1)
5
5
  activesupport (>= 5.2.3, < 6.0.0)
6
6
  rest-client (>= 2.1.0, < 3.0.0)
7
7
  thor (>= 1.0.0, < 1.2.0)
@@ -97,11 +97,11 @@ DEPENDENCIES
97
97
  bundler (~> 2.1.4)
98
98
  rake (~> 13.0)
99
99
  relaxed-rubocop (~> 2.5)
100
- rspec (~> 3.0)
101
- rubocop (~> 1.11)
102
- rubocop-rake
103
- rubocop-rspec
104
- webmock
100
+ rspec (~> 3.10.0)
101
+ rubocop (~> 1.11.0)
102
+ rubocop-rake (~> 0.5.1)
103
+ rubocop-rspec (~> 2.2.0)
104
+ webmock (~> 3.11.0)
105
105
  xapixctl!
106
106
 
107
107
  BUNDLED WITH
@@ -1,15 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
- require 'pathname'
4
+ require 'xapixctl/util'
5
5
 
6
6
  module Xapixctl
7
7
  class BaseCli < Thor
8
8
  def self.exit_on_failure?; true; end
9
9
 
10
+ def self.start(given_args = ARGV, config = {})
11
+ super
12
+ rescue StandardError => err
13
+ config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise err) : config[:shell].error(err.message)
14
+ exit(false) if exit_on_failure?
15
+ end
16
+
10
17
  class_option :org, aliases: "-o", desc: "Organization; Fallback: environment variable XAPIX_ORG"
11
18
  class_option :project, aliases: "-p", desc: "Project, can be ORG/PROJECT; Fallback: environment variable XAPIX_PROJECT"
12
- class_option :debug
19
+ class_option :debug, type: :boolean, desc: "Print details for debugging"
13
20
  class_option :xapix_url, desc: "Fallback: environment variable XAPIX_URL. URL to Xapix. Default: https://cloud.xapix.io/"
14
21
  class_option :xapix_token, desc: "Fallback: environment variable XAPIX_TOKEN. Your access token."
15
22
 
@@ -31,36 +38,6 @@ module Xapixctl
31
38
  end
32
39
  end
33
40
 
34
- DOCUMENT_STRUCTURE = %w[version kind metadata definition].freeze
35
- def resources_from_file(filename, ignore_missing: false)
36
- load_files(filename, ignore_missing) do |yaml_string|
37
- yaml_string.split(/^---\s*\n/).map { |yml| Psych.safe_load(yml) }.compact.each do |doc|
38
- unless (DOCUMENT_STRUCTURE - doc.keys.map(&:to_s)).empty?
39
- warn "does not look like a correct resource definition:"
40
- warn doc.inspect
41
- exit 1
42
- end
43
- yield doc
44
- end
45
- end
46
- end
47
-
48
- def load_files(filename, ignore_missing)
49
- if filename == '-'
50
- yield $stdin.read
51
- else
52
- pn = filename.is_a?(Pathname) ? filename : Pathname.new(filename)
53
- if pn.directory?
54
- pn.glob(["**/*.yaml", "**/*.yml"]).sort.each { |dpn| yield dpn.read }
55
- elsif pn.exist?
56
- yield pn.read
57
- elsif !ignore_missing
58
- warn "file not found: #{filename}"
59
- exit 1
60
- end
61
- end
62
- end
63
-
64
41
  def connection
65
42
  @connection ||= begin
66
43
  url = options[:xapix_url] || ENV['XAPIX_URL'] || 'https://cloud.xapix.io/'
data/lib/xapixctl/cli.rb CHANGED
@@ -7,10 +7,10 @@ require 'xapixctl/sync_cli'
7
7
  module Xapixctl
8
8
  class Cli < BaseCli
9
9
  desc "preview SUBCOMMAND ...ARGS", "Request preview for resources"
10
- subcommand "preview", Preview
10
+ subcommand "preview", PreviewCli
11
11
 
12
12
  desc "sync SUBCOMMAND ...ARGS", "Sync resources"
13
- subcommand "sync", Sync
13
+ subcommand "sync", SyncCli
14
14
 
15
15
  option :format, aliases: "-f", default: 'text', enum: ['text', 'yaml', 'json'], desc: "Output format"
16
16
  desc "get TYPE [ID]", "retrieve either all resources of given TYPE or just the resource of given TYPE and ID"
@@ -80,7 +80,7 @@ module Xapixctl
80
80
  \x5> $ xapixctl get -o xapix-old -p some-project DataSource -f yaml | xapixctl apply -o xapix-new -f -
81
81
  LONGDESC
82
82
  def apply
83
- resources_from_file(options[:file]) do |desc|
83
+ Util.resources_from_file(options[:file]) do |desc|
84
84
  puts "applying #{desc['kind']} #{desc.dig('metadata', 'id')}"
85
85
  org_or_prj_connection.apply(desc)
86
86
  end
@@ -108,7 +108,7 @@ module Xapixctl
108
108
  org_or_prj_connection.delete(resource_type, resource_id)
109
109
  puts "DELETED #{resource_type} #{resource_id}"
110
110
  elsif options[:file]
111
- resources_from_file(options[:file]) do |desc|
111
+ Util.resources_from_file(options[:file]) do |desc|
112
112
  res_type = desc['kind']
113
113
  res_id = desc.dig('metadata', 'id')
114
114
  delete(res_type, res_id)
@@ -3,7 +3,7 @@
3
3
  require 'xapixctl/base_cli'
4
4
 
5
5
  module Xapixctl
6
- class Preview < BaseCli
6
+ class PreviewCli < BaseCli
7
7
  option :format, aliases: "-f", default: 'text', enum: ['text', 'yaml', 'json'], desc: "Output format"
8
8
  desc "pipeline ID", "Preview a pipeline"
9
9
  long_desc <<-LONGDESC
@@ -1,62 +1,73 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'xapixctl/base_cli'
4
+ require 'pathname'
4
5
 
5
6
  module Xapixctl
6
- class Sync < BaseCli
7
- option :org, aliases: "-o", desc: "Organization"
8
- option :project, aliases: "-p", desc: "Project"
7
+ class SyncCli < BaseCli
8
+ class_option :credentials, desc: "Whether to include Credential resources in sync", type: :boolean, default: true
9
+ class_option :exclude_types, desc: "Resource types to exclude from sync", type: :array
10
+
9
11
  desc "to-dir DIRECTORY", "Syncs resources in project to directory"
10
12
  long_desc <<-LONGDESC
11
- `xapixctl sync to-dir project dir` will export all resources of a given project and remove any additional resources from the directory.
13
+ `xapixctl sync to-dir DIRECTORY` will export all resources of a given project and remove any additional resources from the directory.
14
+
15
+ With --no-credentials you can exclude all credentials from getting exported.
16
+
17
+ With --exclude-types you can specify any resource types besides Project you'd like to exclude.
18
+
19
+ When excluding types, the excluded types will be recorded in the sync directory in a file called .excluded_types, so that any future syncs will exclude those types.
12
20
 
13
21
  Examples:
14
- \x5> $ xapixctl sync to-dir xapix/some-project ./project_dir
22
+ \x5> $ xapixctl sync to-dir ./project_dir -p xapix/some-project
23
+ \x5> $ xapixctl sync to-dir ./project_dir -p xapix/some-project --no-credentials
24
+ \x5> $ xapixctl sync to-dir ./project_dir -p xapix/some-project --exclude-types=ApiPublishing ApiPublishingRole Credential
15
25
  LONGDESC
16
26
  def to_dir(dir)
17
- sync_path = Pathname.new(dir)
27
+ sync_path = SyncPath.new(dir, prj_connection.resource_types_for_export, excluded_types)
18
28
 
19
29
  res_details = prj_connection.project_resource
20
- write_resource_to(res_details, sync_path, 'project')
21
- sync_path.join('README.md').write(generate_readme(res_details))
30
+ sync_path.write_file(generate_readme(res_details), 'README.md')
31
+ sync_path.write_resource_yaml(res_details, 'project')
22
32
 
23
- prj_connection.resource_types_for_export.each do |type|
24
- res_path = sync_path.join(type.underscore)
25
- new_files = []
33
+ sync_path.types_to_sync.each do |type|
34
+ res_path = sync_path.resource_path(type)
26
35
  prj_connection.resource_ids(type).each do |res_id|
27
36
  res_details = prj_connection.resource(type, res_id)
28
- new_files << write_resource_to(res_details, res_path, res_id)
29
- end
30
- (res_path.glob('*.yaml') - new_files).each do |outdated_file|
31
- outdated_file.delete
32
- puts "removed #{outdated_file}"
37
+ res_path.write_resource_yaml(res_details, res_id)
33
38
  end
39
+ res_path.remove_outdated_resources
34
40
  end
41
+ sync_path.update_excluded_types_file
35
42
  end
36
43
 
37
- option :org, aliases: "-o", desc: "Organization"
38
- option :project, aliases: "-p", desc: "Project"
39
44
  desc "from-dir DIRECTORY", "Syncs resources in project from directory"
40
45
  long_desc <<-LONGDESC
41
46
  `xapixctl sync from-dir project dir` will import all resources into the given project from the directory and remove any additional resources which are not present in the directory.
42
47
 
48
+ With --no-credentials you can exclude all credentials from getting exported.
49
+
50
+ With --exclude-types you can specify any resource types besides Project you'd like to exclude.
51
+
43
52
  Examples:
44
- \x5> $ xapixctl sync from-dir xapix/some-project ./project_dir
53
+ \x5> $ xapixctl sync from-dir ./project_dir -p xapix/some-project
54
+ \x5> $ xapixctl sync from-dir ./project_dir -p xapix/some-project --no-credentials
55
+ \x5> $ xapixctl sync from-dir ./project_dir -p xapix/some-project --exclude-types=ApiPublishing ApiPublishingRole Credential
45
56
  LONGDESC
46
57
  def from_dir(dir)
47
- sync_path = Pathname.new(dir)
58
+ sync_path = SyncPath.new(dir, prj_connection.resource_types_for_export, excluded_types)
48
59
 
49
- resources_from_file(sync_path.join('project.yaml'), ignore_missing: false) do |desc|
60
+ sync_path.load_resource('project') do |desc|
50
61
  puts "applying #{desc['kind']} #{desc.dig('metadata', 'id')} to #{prj_connection.project}"
51
62
  desc['metadata']['id'] = prj_connection.project
52
63
  prj_connection.organization.apply(desc)
53
64
  end
54
65
 
55
66
  outdated_resources = {}
56
- prj_connection.resource_types_for_export.each do |type|
57
- res_path = sync_path.join(type.underscore)
67
+ sync_path.types_to_sync.each do |type|
68
+ res_path = sync_path.resource_path(type)
58
69
  updated_resource_ids = []
59
- resources_from_file(res_path, ignore_missing: true) do |desc|
70
+ res_path.load_resources do |desc|
60
71
  puts "applying #{desc['kind']} #{desc.dig('metadata', 'id')}"
61
72
  updated_resource_ids += prj_connection.apply(desc)
62
73
  end
@@ -73,16 +84,74 @@ module Xapixctl
73
84
 
74
85
  private
75
86
 
76
- def write_resource_to(res_details, res_path, res_name)
77
- res_path.mkpath
78
- unless res_path.directory? && res_path.writable?
79
- warn "Cannot write to #{dir}, please check directory exists and is writable"
80
- exit 1
87
+ class ResourcePath
88
+ def initialize(path)
89
+ @path = path
90
+ @resource_files = []
81
91
  end
82
- res_file = res_path.join("#{res_name}.yaml")
83
- res_file.write(res_details.to_yaml)
84
- puts "updated #{res_file}..."
85
- res_file
92
+
93
+ def write_file(content, filename)
94
+ @path.mkpath
95
+ unless @path.directory? && @path.writable?
96
+ warn "Cannot write to #{@path}, please check directory exists and is writable"
97
+ exit 1
98
+ end
99
+ file = @path.join(filename)
100
+ file.write(content)
101
+ puts "updated #{file}..."
102
+ file
103
+ end
104
+
105
+ def write_resource_yaml(res_details, res_name)
106
+ file = write_file(res_details.to_yaml, "#{res_name}.yaml")
107
+ @resource_files << file
108
+ file
109
+ end
110
+
111
+ def load_resources(&block)
112
+ Util.resources_from_file(@path, ignore_missing: true, &block)
113
+ end
114
+
115
+ def load_resource(res_name, &block)
116
+ Util.resources_from_file(@path.join("#{res_name}.yaml"), ignore_missing: false, &block)
117
+ end
118
+
119
+ def remove_outdated_resources
120
+ (@path.glob('*.yaml') - @resource_files).each do |outdated_file|
121
+ outdated_file.delete
122
+ puts "removed #{outdated_file}"
123
+ end
124
+ end
125
+ end
126
+
127
+ class SyncPath < ResourcePath
128
+ attr_reader :types_to_sync
129
+
130
+ def initialize(dir, all_types, excluded_types)
131
+ super(Pathname.new(dir))
132
+ @all_types = all_types
133
+ @excluded_types_file = @path.join('.excluded_types')
134
+ @excluded_types = excluded_types || []
135
+ @excluded_types += @excluded_types_file.read.split if @excluded_types_file.exist?
136
+ @excluded_types &= @all_types
137
+ @excluded_types.sort!
138
+ @types_to_sync = @all_types - @excluded_types
139
+ puts "Resource types excluded from sync: #{@excluded_types.join(', ')}" if @excluded_types.any?
140
+ end
141
+
142
+ def resource_path(type)
143
+ ResourcePath.new(@path.join(type.underscore))
144
+ end
145
+
146
+ def update_excluded_types_file
147
+ @excluded_types_file.write(@excluded_types.join(" ") + "\n") if @excluded_types.any?
148
+ end
149
+ end
150
+
151
+ def excluded_types
152
+ excluded = options[:exclude_types]
153
+ excluded += ['Credential'] unless options[:credentials]
154
+ excluded
86
155
  end
87
156
 
88
157
  def generate_readme(res_details)
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Xapixctl
4
- VERSION = "1.2.0"
4
+ VERSION = "1.2.1"
5
5
  end
data/xapixctl.gemspec CHANGED
@@ -35,9 +35,9 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency "bundler", "~> 2.1.4"
36
36
  spec.add_development_dependency "rake", "~> 13.0"
37
37
  spec.add_development_dependency "relaxed-rubocop", "~> 2.5"
38
- spec.add_development_dependency "rspec", "~> 3.0"
39
- spec.add_development_dependency "rubocop", "~> 1.11"
40
- spec.add_development_dependency "rubocop-rake"
41
- spec.add_development_dependency "rubocop-rspec"
42
- spec.add_development_dependency "webmock"
38
+ spec.add_development_dependency "rspec", "~> 3.10.0"
39
+ spec.add_development_dependency "rubocop", "~> 1.11.0"
40
+ spec.add_development_dependency "rubocop-rake", "~> 0.5.1"
41
+ spec.add_development_dependency "rubocop-rspec", "~> 2.2.0"
42
+ spec.add_development_dependency "webmock", "~> 3.11.0"
43
43
  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.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Reinsch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-12 00:00:00.000000000 Z
11
+ date: 2021-03-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -118,70 +118,70 @@ dependencies:
118
118
  requirements:
119
119
  - - "~>"
120
120
  - !ruby/object:Gem::Version
121
- version: '3.0'
121
+ version: 3.10.0
122
122
  type: :development
123
123
  prerelease: false
124
124
  version_requirements: !ruby/object:Gem::Requirement
125
125
  requirements:
126
126
  - - "~>"
127
127
  - !ruby/object:Gem::Version
128
- version: '3.0'
128
+ version: 3.10.0
129
129
  - !ruby/object:Gem::Dependency
130
130
  name: rubocop
131
131
  requirement: !ruby/object:Gem::Requirement
132
132
  requirements:
133
133
  - - "~>"
134
134
  - !ruby/object:Gem::Version
135
- version: '1.11'
135
+ version: 1.11.0
136
136
  type: :development
137
137
  prerelease: false
138
138
  version_requirements: !ruby/object:Gem::Requirement
139
139
  requirements:
140
140
  - - "~>"
141
141
  - !ruby/object:Gem::Version
142
- version: '1.11'
142
+ version: 1.11.0
143
143
  - !ruby/object:Gem::Dependency
144
144
  name: rubocop-rake
145
145
  requirement: !ruby/object:Gem::Requirement
146
146
  requirements:
147
- - - ">="
147
+ - - "~>"
148
148
  - !ruby/object:Gem::Version
149
- version: '0'
149
+ version: 0.5.1
150
150
  type: :development
151
151
  prerelease: false
152
152
  version_requirements: !ruby/object:Gem::Requirement
153
153
  requirements:
154
- - - ">="
154
+ - - "~>"
155
155
  - !ruby/object:Gem::Version
156
- version: '0'
156
+ version: 0.5.1
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: rubocop-rspec
159
159
  requirement: !ruby/object:Gem::Requirement
160
160
  requirements:
161
- - - ">="
161
+ - - "~>"
162
162
  - !ruby/object:Gem::Version
163
- version: '0'
163
+ version: 2.2.0
164
164
  type: :development
165
165
  prerelease: false
166
166
  version_requirements: !ruby/object:Gem::Requirement
167
167
  requirements:
168
- - - ">="
168
+ - - "~>"
169
169
  - !ruby/object:Gem::Version
170
- version: '0'
170
+ version: 2.2.0
171
171
  - !ruby/object:Gem::Dependency
172
172
  name: webmock
173
173
  requirement: !ruby/object:Gem::Requirement
174
174
  requirements:
175
- - - ">="
175
+ - - "~>"
176
176
  - !ruby/object:Gem::Version
177
- version: '0'
177
+ version: 3.11.0
178
178
  type: :development
179
179
  prerelease: false
180
180
  version_requirements: !ruby/object:Gem::Requirement
181
181
  requirements:
182
- - - ">="
182
+ - - "~>"
183
183
  - !ruby/object:Gem::Version
184
- version: '0'
184
+ version: 3.11.0
185
185
  description:
186
186
  email:
187
187
  - michael@xapix.io
@@ -213,6 +213,7 @@ files:
213
213
  - lib/xapixctl/phoenix_client/result_handler.rb
214
214
  - lib/xapixctl/preview_cli.rb
215
215
  - lib/xapixctl/sync_cli.rb
216
+ - lib/xapixctl/util.rb
216
217
  - lib/xapixctl/version.rb
217
218
  - xapixctl.gemspec
218
219
  homepage: https://github.com/xapix-io/xapixctl