xapixctl 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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