oxidized 0.30.1 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +2 -2
  3. data/.github/workflows/stale.yml +4 -2
  4. data/.rubocop.yml +18 -2
  5. data/.rubocop_todo.yml +5 -12
  6. data/CHANGELOG.md +61 -1
  7. data/CONTRIBUTING.md +5 -0
  8. data/Dockerfile +82 -21
  9. data/README.md +5 -21
  10. data/Rakefile +3 -2
  11. data/docs/Configuration.md +36 -12
  12. data/docs/Creating-Models.md +45 -4
  13. data/docs/Hooks.md +34 -0
  14. data/docs/Issues.md +91 -0
  15. data/docs/Model-Notes/Cumulus.md +5 -0
  16. data/docs/Model-Notes/FSOS.md +5 -0
  17. data/docs/Model-Notes/FortiOS.md +21 -5
  18. data/docs/Model-Notes/HPEAruba.md +31 -0
  19. data/docs/Model-Notes/OS6.md +10 -0
  20. data/docs/Model-Notes/RouterOS.md +15 -0
  21. data/docs/Model-Notes/SikluMHTG.md +7 -0
  22. data/docs/Outputs.md +2 -0
  23. data/docs/Release.md +18 -15
  24. data/docs/Sources.md +21 -0
  25. data/docs/Supported-OS-Types.md +11 -5
  26. data/docs/Troubleshooting.md +35 -0
  27. data/examples/device-simulation/README.md +173 -0
  28. data/examples/device-simulation/cmdsets/aoscx +9 -0
  29. data/examples/device-simulation/cmdsets/arubainstant +5 -0
  30. data/examples/device-simulation/cmdsets/asa +7 -0
  31. data/examples/device-simulation/cmdsets/ios +7 -0
  32. data/examples/device-simulation/cmdsets/nxos +5 -0
  33. data/examples/device-simulation/cmdsets/routeros +5 -0
  34. data/examples/device-simulation/cmdsets/srosmd +11 -0
  35. data/examples/device-simulation/device2yaml.rb +225 -0
  36. data/examples/device-simulation/yaml/aoscx_R0X25A-6410_FL.10.10.1100.yaml +2281 -0
  37. data/examples/device-simulation/yaml/aoscx_R8N85A-C6000-48G-CL4_PL.10.08.1010.yaml +451 -0
  38. data/examples/device-simulation/yaml/arubainstant_IAP515_8.10.0.6_VWLC.yaml +213 -0
  39. data/examples/device-simulation/yaml/asa_5512_9.12-4-67_single-context.yaml +531 -0
  40. data/examples/device-simulation/yaml/asr920_16.8.1b.yaml +1122 -0
  41. data/examples/device-simulation/yaml/garderos_R7709_003_006_068.yaml +101 -0
  42. data/examples/device-simulation/yaml/iosxe_C9200L-24P-4G_17.09.04a.yaml +514 -0
  43. data/examples/device-simulation/yaml/iosxe_C9800-L-F-K9_17.06.05.yaml +417 -0
  44. data/examples/device-simulation/yaml/riverbed_915.yaml +123 -0
  45. data/examples/device-simulation/yaml/routeros_CHR_7.10.1.yaml +145 -0
  46. data/examples/device-simulation/yaml/routeros_CHR_7.16.yaml +79 -0
  47. data/examples/device-simulation/yaml/routeros_L009UiGS_7.15.2.yaml +353 -0
  48. data/examples/podman-compose/Makefile +60 -17
  49. data/examples/podman-compose/README.md +63 -27
  50. data/examples/podman-compose/docker-compose.yml +11 -2
  51. data/examples/podman-compose/gitserver/.gitignore +1 -0
  52. data/examples/podman-compose/gitserver/Dockerfile +14 -0
  53. data/examples/podman-compose/model-simulation/Dockerfile-model +1 -1
  54. data/examples/podman-compose/model-simulation/asternos.sh +2 -0
  55. data/examples/podman-compose/oxidized-config/.gitignore +2 -0
  56. data/examples/podman-compose/oxidized-config/config +1 -1
  57. data/examples/podman-compose/oxidized-config/config_csv-file +46 -0
  58. data/examples/podman-compose/oxidized-config/config_csv-gitserver +56 -0
  59. data/examples/podman-compose/oxidized-ssh/.gitignore +1 -0
  60. data/lib/oxidized/config.rb +7 -1
  61. data/lib/oxidized/hook/githubrepo.rb +37 -7
  62. data/lib/oxidized/hook/slackdiff.rb +29 -7
  63. data/lib/oxidized/input/http.rb +1 -0
  64. data/lib/oxidized/input/telnet.rb +1 -1
  65. data/lib/oxidized/manager.rb +17 -16
  66. data/lib/oxidized/model/aoscx.rb +16 -2
  67. data/lib/oxidized/model/aosw.rb +7 -1
  68. data/lib/oxidized/model/arubainstant.rb +90 -0
  69. data/lib/oxidized/model/audiocodes.rb +2 -2
  70. data/lib/oxidized/model/cnos.rb +13 -10
  71. data/lib/oxidized/model/cumulus.rb +3 -0
  72. data/lib/oxidized/model/dlink.rb +1 -0
  73. data/lib/oxidized/model/dlinknextgen.rb +3 -0
  74. data/lib/oxidized/model/edgecos.rb +2 -1
  75. data/lib/oxidized/model/eos.rb +2 -0
  76. data/lib/oxidized/model/f5os.rb +17 -0
  77. data/lib/oxidized/model/firewareos.rb +10 -1
  78. data/lib/oxidized/model/fortios.rb +24 -1
  79. data/lib/oxidized/model/garderos.rb +43 -0
  80. data/lib/oxidized/model/h3c.rb +1 -1
  81. data/lib/oxidized/model/ibos.rb +1 -0
  82. data/lib/oxidized/model/ios.rb +20 -12
  83. data/lib/oxidized/model/iosxr.rb +1 -1
  84. data/lib/oxidized/model/lenovonos.rb +2 -0
  85. data/lib/oxidized/model/linuxgeneric.rb +1 -1
  86. data/lib/oxidized/model/netgear.rb +1 -1
  87. data/lib/oxidized/model/nodegrid.rb +1 -1
  88. data/lib/oxidized/model/nsxdfw.rb +30 -0
  89. data/lib/oxidized/model/nxos.rb +2 -1
  90. data/lib/oxidized/model/os6.rb +48 -0
  91. data/lib/oxidized/model/rgos.rb +1 -1
  92. data/lib/oxidized/model/riverbed.rb +104 -0
  93. data/lib/oxidized/model/routeros.rb +2 -2
  94. data/lib/oxidized/model/saos.rb +18 -1
  95. data/lib/oxidized/model/siklumhtg.rb +22 -0
  96. data/lib/oxidized/model/uplinkolt.rb +46 -0
  97. data/lib/oxidized/model/vyatta.rb +2 -2
  98. data/lib/oxidized/model/xos.rb +7 -0
  99. data/lib/oxidized/node.rb +30 -18
  100. data/lib/oxidized/nodes.rb +13 -5
  101. data/lib/oxidized/output/file.rb +45 -42
  102. data/lib/oxidized/output/git.rb +185 -160
  103. data/lib/oxidized/output/gitcrypt.rb +188 -186
  104. data/lib/oxidized/output/http.rb +53 -51
  105. data/lib/oxidized/output/output.rb +6 -4
  106. data/lib/oxidized/source/csv.rb +44 -49
  107. data/lib/oxidized/source/http.rb +63 -81
  108. data/lib/oxidized/source/jsonfile.rb +63 -0
  109. data/lib/oxidized/source/source.rb +43 -18
  110. data/lib/oxidized/source/sql.rb +66 -59
  111. data/lib/oxidized/version.rb +2 -2
  112. data/oxidized.gemspec +22 -16
  113. metadata +111 -15
data/lib/oxidized/node.rb CHANGED
@@ -80,6 +80,8 @@ module Oxidized
80
80
  @err_reason = err.message.to_s
81
81
  false
82
82
  rescue StandardError => e
83
+ # Send a message in debug mode in case we are not able to create a crashfile
84
+ Oxidized.logger.send(:debug, '%s raised %s with msg "%s", creating crashfile' % [ip, e.class, e.message])
83
85
  crashdir = Oxidized.config.crash.directory
84
86
  crashfile = Oxidized.config.crash.hostnames? ? name : ip.to_s
85
87
  FileUtils.mkdir_p(crashdir) unless File.directory?(crashdir)
@@ -198,33 +200,43 @@ module Oxidized
198
200
  end
199
201
 
200
202
  def resolve_key(key, opt, global = nil)
201
- # resolve key, first get global, then get group then get node config
203
+ # resolve key: the priority is as follows: node -> group specific model -> group -> model -> global passed -> global
204
+ # where node has the highest priority (= if defined, overwrites other values)
202
205
  key_sym = key.to_sym
203
206
  key_str = key.to_s
204
- value = global
205
- Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{value}' and node value '#{opt[key_sym]}'"
207
+ model_name = @model.class.name.to_s.downcase
208
+ Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{global}' and node value '#{opt[key_sym]}'"
206
209
 
207
- # global
208
- if (not value) && Oxidized.config.has_key?(key_str)
209
- value = Oxidized.config[key_str]
210
- Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global"
211
- end
210
+ # Node
211
+ if opt[key_sym]
212
+ value = opt[key_sym]
213
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from node"
212
214
 
213
- # group
214
- if Oxidized.config.groups.has_key?(@group) && Oxidized.config.groups[@group].has_key?(key_str)
215
+ # Group specific model
216
+ elsif Oxidized.config.groups.has_key?(@group) && Oxidized.config.groups[@group].models.has_key?(model_name) && Oxidized.config.groups[@group].models[model_name].has_key?(key_str)
217
+ value = Oxidized.config.groups[@group].models[model_name][key_str]
218
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from model in group"
219
+
220
+ # Group
221
+ elsif Oxidized.config.groups.has_key?(@group) && Oxidized.config.groups[@group].has_key?(key_str)
215
222
  value = Oxidized.config.groups[@group][key_str]
216
223
  Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from group"
217
- end
218
224
 
219
- # model
220
- if Oxidized.config.models.has_key?(@model.class.name.to_s.downcase) && Oxidized.config.models[@model.class.name.to_s.downcase].has_key?(key_str)
221
- value = Oxidized.config.models[@model.class.name.to_s.downcase][key_str]
225
+ # Model
226
+ elsif Oxidized.config.models.has_key?(model_name) && Oxidized.config.models[model_name].has_key?(key_str)
227
+ value = Oxidized.config.models[model_name][key_str]
222
228
  Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from model"
223
- end
224
229
 
225
- # node
226
- value = opt[key_sym] || value
227
- Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'"
230
+ # Global passed
231
+ elsif global
232
+ value = global
233
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from passed global value"
234
+
235
+ # Global
236
+ elsif Oxidized.config.has_key?(key_str)
237
+ value = Oxidized.config[key_str]
238
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global"
239
+ end
228
240
  value
229
241
  end
230
242
 
@@ -61,6 +61,9 @@ module Oxidized
61
61
  end
62
62
  end
63
63
 
64
+ # Returns the configuration of group/node_name
65
+ #
66
+ # #fetch is called by oxidzed-web
64
67
  def fetch(node_name, group)
65
68
  yield_node_output(node_name) do |node, output|
66
69
  output.fetch node, group
@@ -69,7 +72,7 @@ module Oxidized
69
72
 
70
73
  # @param node [String] name of the node moved into the head of array
71
74
  def next(node, opt = {})
72
- return unless waiting.find_node_index(node)
75
+ return if running.find_index(node)
73
76
 
74
77
  with_lock do
75
78
  n = del node
@@ -98,6 +101,9 @@ module Oxidized
98
101
  find_index(node) || raise(NodeNotFound, "unable to find '#{node}'")
99
102
  end
100
103
 
104
+ # Returns all stored versions of group/node_name
105
+ #
106
+ # Called by oxidized-web
101
107
  def version(node_name, group)
102
108
  yield_node_output(node_name) do |node, output|
103
109
  output.version node, group
@@ -116,6 +122,10 @@ module Oxidized
116
122
  end
117
123
  end
118
124
 
125
+ def find_index(node)
126
+ index { |e| [e.name, e.ip].include? node }
127
+ end
128
+
119
129
  private
120
130
 
121
131
  def initialize(opts = {})
@@ -133,10 +143,6 @@ module Oxidized
133
143
  @mutex.synchronize(...)
134
144
  end
135
145
 
136
- def find_index(node)
137
- index { |e| [e.name, e.ip].include? node }
138
- end
139
-
140
146
  # @param node node which is removed from nodes list
141
147
  # @return [Node] deleted node
142
148
  def del(node)
@@ -179,6 +185,8 @@ module Oxidized
179
185
  def yield_node_output(node_name)
180
186
  with_lock do
181
187
  node = find { |n| n.name == node_name }
188
+ raise(NodeNotFound, "unable to find '#{node_name}'") if node.nil?
189
+
182
190
  output = node.output.new
183
191
  raise NotSupported unless output.respond_to? :fetch
184
192
 
@@ -1,55 +1,58 @@
1
1
  module Oxidized
2
- class OxidizedFile < Output
3
- require 'fileutils'
2
+ module Output
3
+ # ruby's File class must be accessed with ::File to avoid name conflicts
4
+ class File < Output
5
+ require 'fileutils'
4
6
 
5
- attr_reader :commitref
7
+ attr_reader :commitref
6
8
 
7
- def initialize
8
- super
9
- @cfg = Oxidized.config.output.file
10
- end
9
+ def initialize
10
+ super
11
+ @cfg = Oxidized.config.output.file
12
+ end
11
13
 
12
- def setup
13
- return unless @cfg.empty?
14
+ def setup
15
+ return unless @cfg.empty?
14
16
 
15
- Oxidized.asetus.user.output.file.directory = File.join(Config::ROOT, 'configs')
16
- Oxidized.asetus.save :user
17
- raise NoConfig, 'no output file config, edit ~/.config/oxidized/config'
18
- end
17
+ Oxidized.asetus.user.output.file.directory = ::File.join(Config::ROOT, 'configs')
18
+ Oxidized.asetus.save :user
19
+ raise NoConfig, "no output file config, edit #{Oxidized::Config.configfile}"
20
+ end
19
21
 
20
- def store(node, outputs, opt = {})
21
- file = File.expand_path @cfg.directory
22
- file = File.join File.dirname(file), opt[:group] if opt[:group]
23
- FileUtils.mkdir_p file
24
- file = File.join file, node
25
- File.write(file, outputs.to_cfg)
26
- @commitref = file
27
- end
22
+ def store(node, outputs, opt = {})
23
+ file = ::File.expand_path @cfg.directory
24
+ file = ::File.join ::File.dirname(file), opt[:group] if opt[:group]
25
+ FileUtils.mkdir_p file
26
+ file = ::File.join file, node
27
+ ::File.write(file, outputs.to_cfg)
28
+ @commitref = file
29
+ end
28
30
 
29
- def fetch(node, group)
30
- cfg_dir = File.expand_path @cfg.directory
31
- node_name = node.name
32
-
33
- if group # group is explicitly defined by user
34
- cfg_dir = File.join File.dirname(cfg_dir), group
35
- File.read File.join(cfg_dir, node_name)
36
- elsif File.exist? File.join(cfg_dir, node_name) # node configuration file is stored on base directory
37
- File.read File.join(cfg_dir, node_name)
38
- else
39
- path = Dir.glob(File.join(File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups
40
- File.read path
31
+ def fetch(node, group)
32
+ cfg_dir = ::File.expand_path @cfg.directory
33
+ node_name = node.name
34
+
35
+ if group # group is explicitly defined by user
36
+ cfg_dir = ::File.join ::File.dirname(cfg_dir), group
37
+ ::File.read ::File.join(cfg_dir, node_name)
38
+ elsif ::File.exist? ::File.join(cfg_dir, node_name) # node configuration file is stored on base directory
39
+ ::File.read ::File.join(cfg_dir, node_name)
40
+ else
41
+ path = Dir.glob(::File.join(::File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups
42
+ ::File.read path
43
+ end
44
+ rescue Errno::ENOENT
45
+ nil
41
46
  end
42
- rescue Errno::ENOENT
43
- nil
44
- end
45
47
 
46
- def version(_node, _group)
47
- # not supported
48
- []
49
- end
48
+ def version(_node, _group)
49
+ # not supported
50
+ []
51
+ end
50
52
 
51
- def get_version(_node, _group, _oid)
52
- 'not supported'
53
+ def get_version(_node, _group, _oid)
54
+ 'not supported'
55
+ end
53
56
  end
54
57
  end
55
58
  end
@@ -1,195 +1,220 @@
1
1
  module Oxidized
2
- class Git < Output
3
- using Refinements
4
-
5
- class GitError < OxidizedError; end
6
- begin
7
- require 'rugged'
8
- rescue LoadError
9
- raise OxidizedError, 'rugged not found: sudo gem install rugged'
10
- end
2
+ module Output
3
+ class Git < Output
4
+ using Refinements
11
5
 
12
- attr_reader :commitref
6
+ class GitError < OxidizedError; end
7
+ begin
8
+ require 'rugged'
9
+ rescue LoadError
10
+ raise OxidizedError, 'rugged not found: sudo gem install rugged'
11
+ end
13
12
 
14
- def initialize
15
- super
16
- @cfg = Oxidized.config.output.git
17
- end
13
+ attr_reader :commitref
18
14
 
19
- def setup
20
- if @cfg.empty?
21
- Oxidized.asetus.user.output.git.user = 'Oxidized'
22
- Oxidized.asetus.user.output.git.email = 'o@example.com'
23
- Oxidized.asetus.user.output.git.repo = File.join(Config::ROOT, 'oxidized.git')
24
- Oxidized.asetus.save :user
25
- raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
15
+ def initialize
16
+ super
17
+ @cfg = Oxidized.config.output.git
26
18
  end
27
19
 
28
- if @cfg.repo.respond_to?(:each)
29
- @cfg.repo.each do |group, repo|
30
- @cfg.repo["#{group}="] = File.expand_path repo
20
+ def setup
21
+ if @cfg.empty?
22
+ Oxidized.asetus.user.output.git.user = 'Oxidized'
23
+ Oxidized.asetus.user.output.git.email = 'o@example.com'
24
+ Oxidized.asetus.user.output.git.repo = File.join(Config::ROOT, 'oxidized.git')
25
+ Oxidized.asetus.save :user
26
+ raise NoConfig, "no output git config, edit #{Oxidized::Config.configfile}"
31
27
  end
32
- else
33
- @cfg.repo = File.expand_path @cfg.repo
34
- end
35
- end
36
28
 
37
- def store(file, outputs, opt = {})
38
- @msg = opt[:msg]
39
- @user = opt[:user] || @cfg.user
40
- @email = opt[:email] || @cfg.email
41
- @opt = opt
42
- @commitref = nil
43
- repo = @cfg.repo
44
-
45
- outputs.types.each do |type|
46
- type_cfg = ''
47
- type_repo = File.join(File.dirname(repo), type + '.git')
48
- outputs.type(type).each do |output|
49
- (type_cfg << output; next) unless output.name # rubocop:disable Style/Semicolon
50
- type_file = file + '--' + output.name
51
- if @cfg.type_as_directory?
52
- type_file = type + '/' + type_file
53
- type_repo = repo
29
+ if @cfg.repo.respond_to?(:each)
30
+ @cfg.repo.each do |group, repo|
31
+ @cfg.repo["#{group}="] = File.expand_path repo
54
32
  end
55
- update type_repo, type_file, output
33
+ else
34
+ @cfg.repo = File.expand_path @cfg.repo
56
35
  end
57
- update type_repo, file, type_cfg
58
36
  end
59
37
 
60
- update repo, file, outputs.to_cfg
61
- end
38
+ def store(file, outputs, opt = {})
39
+ @msg = opt[:msg]
40
+ @user = opt[:user] || @cfg.user
41
+ @email = opt[:email] || @cfg.email
42
+ @opt = opt
43
+ @commitref = nil
44
+ repo = @cfg.repo
45
+
46
+ outputs.types.each do |type|
47
+ type_cfg = ''
48
+ type_repo = File.join(File.dirname(repo), type + '.git')
49
+ outputs.type(type).each do |output|
50
+ (type_cfg << output; next) unless output.name # rubocop:disable Style/Semicolon
51
+ type_file = file + '--' + output.name
52
+ if @cfg.type_as_directory?
53
+ type_file = type + '/' + type_file
54
+ type_repo = repo
55
+ end
56
+ update type_repo, type_file, output
57
+ end
58
+ update type_repo, file, type_cfg
59
+ end
62
60
 
63
- def fetch(node, group)
64
- repo, path = yield_repo_and_path(node, group)
65
- repo = Rugged::Repository.new repo
66
- index = repo.index
67
- index.read_tree repo.head.target.tree unless repo.empty?
68
- repo.read(index.get(path)[:oid]).data
69
- rescue StandardError
70
- 'node not found'
71
- end
61
+ update repo, file, outputs.to_cfg
62
+ end
72
63
 
73
- # give a hash of all oid revision for the given node, and the date of the commit
74
- def version(node, group)
75
- repo, path = yield_repo_and_path(node, group)
76
-
77
- repo = Rugged::Repository.new repo
78
- walker = Rugged::Walker.new(repo)
79
- walker.sorting(Rugged::SORT_DATE)
80
- walker.push(repo.head.target.oid)
81
- i = -1
82
- tab = []
83
- walker.each do |commit|
84
- # Diabled rubocop because the suggested .empty? does not work here.
85
- next if commit.diff(paths: [path]).size.zero? # rubocop:disable Style/ZeroLengthPredicate
86
-
87
- hash = {}
88
- hash[:date] = commit.time.to_s
89
- hash[:oid] = commit.oid
90
- hash[:author] = commit.author
91
- hash[:message] = commit.message
92
- tab[i += 1] = hash
64
+ # Returns the configuration of group/node_name
65
+ #
66
+ # #fetch is called by Nodes#fetch
67
+ # Nodes#fetch creates a new Output object each time, so we cannot
68
+ # store the repo index in memory. But as we keep the repo index up
69
+ # to date on disk in #update_repo, we can read it from disk instead of
70
+ # rebuilding it each time.
71
+ def fetch(node, group)
72
+ repo, path = yield_repo_and_path(node, group)
73
+ repo = Rugged::Repository.new repo
74
+ # Read the index from disk
75
+ index = repo.index
76
+
77
+ repo.read(index.get(path)[:oid]).data
78
+ rescue StandardError
79
+ 'node not found'
93
80
  end
94
- walker.reset
95
- tab
96
- rescue StandardError
97
- 'node not found'
98
- end
99
81
 
100
- # give the blob of a specific revision
101
- def get_version(node, group, oid)
102
- repo, path = yield_repo_and_path(node, group)
103
- repo = Rugged::Repository.new repo
104
- repo.blob_at(oid, path).content
105
- rescue StandardError
106
- 'version not found'
107
- end
82
+ # give a hash of all oid revision for the given node, and the date of the commit
83
+ def version(node, group)
84
+ repo, path = yield_repo_and_path(node, group)
108
85
 
109
- # give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines)
110
- def get_diff(node, group, oid1, oid2)
111
- diff_commits = nil
112
- repo, = yield_repo_and_path(node, group)
113
- repo = Rugged::Repository.new repo
114
- commit = repo.lookup(oid1)
115
-
116
- if oid2
117
- commit_old = repo.lookup(oid2)
118
- diff = repo.diff(commit_old, commit)
119
- diff.each do |patch|
120
- if /#{node.name}\s+/ =~ patch.to_s.lines.first
121
- diff_commits = { patch: patch.to_s, stat: patch.stat }
122
- break
123
- end
86
+ repo = Rugged::Repository.new repo
87
+ walker = Rugged::Walker.new(repo)
88
+ walker.sorting(Rugged::SORT_DATE)
89
+ walker.push(repo.head.target.oid)
90
+ i = -1
91
+ tab = []
92
+ walker.each do |commit|
93
+ # Diabled rubocop because the suggested .empty? does not work here.
94
+ next if commit.diff(paths: [path]).size.zero? # rubocop:disable Style/ZeroLengthPredicate
95
+
96
+ hash = {}
97
+ hash[:date] = commit.time.to_s
98
+ hash[:oid] = commit.oid
99
+ hash[:author] = commit.author
100
+ hash[:message] = commit.message
101
+ tab[i += 1] = hash
124
102
  end
125
- else
126
- stat = commit.parents[0].diff(commit).stat
127
- stat = [stat[1], stat[2]]
128
- patch = commit.parents[0].diff(commit).patch
129
- diff_commits = { patch: patch, stat: stat }
103
+ walker.reset
104
+ tab
105
+ rescue StandardError
106
+ 'node not found'
130
107
  end
131
108
 
132
- diff_commits
133
- rescue StandardError
134
- 'no diffs'
135
- end
109
+ # give the blob of a specific revision
110
+ def get_version(node, group, oid)
111
+ repo, path = yield_repo_and_path(node, group)
112
+ repo = Rugged::Repository.new repo
113
+ repo.blob_at(oid, path).content
114
+ rescue StandardError
115
+ 'version not found'
116
+ end
136
117
 
137
- private
118
+ # give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines)
119
+ def get_diff(node, group, oid1, oid2)
120
+ diff_commits = nil
121
+ repo, = yield_repo_and_path(node, group)
122
+ repo = Rugged::Repository.new repo
123
+ commit = repo.lookup(oid1)
124
+
125
+ if oid2
126
+ commit_old = repo.lookup(oid2)
127
+ diff = repo.diff(commit_old, commit)
128
+ diff.each do |patch|
129
+ if /#{node.name}\s+/ =~ patch.to_s.lines.first
130
+ diff_commits = { patch: patch.to_s, stat: patch.stat }
131
+ break
132
+ end
133
+ end
134
+ else
135
+ stat = commit.parents[0].diff(commit).stat
136
+ stat = [stat[1], stat[2]]
137
+ patch = commit.parents[0].diff(commit).patch
138
+ diff_commits = { patch: patch, stat: stat }
139
+ end
138
140
 
139
- def yield_repo_and_path(node, group)
140
- repo, path = node.repo, node.name
141
+ diff_commits
142
+ rescue StandardError
143
+ 'no diffs'
144
+ end
141
145
 
142
- path = "#{group}/#{node.name}" if group && @cfg.single_repo?
146
+ private
143
147
 
144
- [repo, path]
145
- end
148
+ def yield_repo_and_path(node, group)
149
+ repo, path = node.repo, node.name
146
150
 
147
- def update(repo, file, data)
148
- return if data.empty?
151
+ path = "#{group}/#{node.name}" if group && !group.empty? && @cfg.single_repo?
149
152
 
150
- if @opt[:group]
151
- if @cfg.single_repo?
152
- file = File.join @opt[:group], file
153
- else
154
- repo = if repo.is_a?(::String)
155
- File.join File.dirname(repo), @opt[:group] + '.git'
156
- else
157
- repo[@opt[:group]]
158
- end
159
- end
153
+ [repo, path]
160
154
  end
161
155
 
162
- begin
163
- repo = Rugged::Repository.new repo
164
- update_repo repo, file, data
165
- rescue Rugged::OSError, Rugged::RepositoryError => e
156
+ def update(repo, file, data)
157
+ return if data.empty?
158
+
159
+ if @opt[:group]
160
+ if @cfg.single_repo?
161
+ file = File.join @opt[:group], file
162
+ else
163
+ repo = if repo.is_a?(::String)
164
+ File.join File.dirname(repo), @opt[:group] + '.git'
165
+ else
166
+ repo[@opt[:group]]
167
+ end
168
+ end
169
+ end
170
+
166
171
  begin
167
- Rugged::Repository.init_at repo, :bare
168
- rescue StandardError => create_error
169
- raise GitError, "first '#{e.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo"
172
+ repo = Rugged::Repository.new repo
173
+ update_repo repo, file, data
174
+ rescue Rugged::OSError, Rugged::RepositoryError => e
175
+ begin
176
+ Rugged::Repository.init_at repo, :bare
177
+ rescue StandardError => create_error
178
+ raise GitError, "first '#{e.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo"
179
+ end
180
+ retry
170
181
  end
171
- retry
172
182
  end
173
- end
174
-
175
- def update_repo(repo, file, data)
176
- oid_old = repo.blob_at(repo.head.target_id, file) rescue nil
177
- return false if oid_old && (oid_old.content.b == data.b)
178
-
179
- oid = repo.write data, :blob
180
- index = repo.index
181
- index.add path: file, oid: oid, mode: 0o100644
182
183
 
183
- repo.config['user.name'] = @user
184
- repo.config['user.email'] = @email
185
- @commitref = Rugged::Commit.create(repo,
186
- tree: index.write_tree(repo),
187
- message: @msg,
188
- parents: repo.empty? ? [] : [repo.head.target].compact,
189
- update_ref: 'HEAD')
190
-
191
- index.write
192
- true
184
+ # Uploads data into file in the repo
185
+ #
186
+ # @param [String] file: the file to save the configuration to
187
+ # @param [String] data: the configuration to save
188
+ # @param [Rugged::Repository] repo: the git repository to use
189
+ #
190
+ # If Oxidized.config.output.git.single_repo = false (which is the default),
191
+ # there will one repository for each group.
192
+ #
193
+ # update_repo caches the index on disk. An index is usually used in a
194
+ # working directory and not in a bare repository, which confuses users.
195
+ # The alternative would be to rebuild the index each time, which a little
196
+ # time consuming. Caching the index in memory is difficult because a new
197
+ # Output object is created each time #store is called.
198
+ def update_repo(repo, file, data)
199
+ oid_old = repo.blob_at(repo.head.target_id, file) rescue nil
200
+ return false if oid_old && (oid_old.content.b == data.b)
201
+
202
+ oid = repo.write data, :blob
203
+ # Read the index from disk
204
+ index = repo.index
205
+ index.add path: file, oid: oid, mode: 0o100644
206
+
207
+ repo.config['user.name'] = @user
208
+ repo.config['user.email'] = @email
209
+ @commitref = Rugged::Commit.create(repo,
210
+ tree: index.write_tree(repo),
211
+ message: @msg,
212
+ parents: repo.empty? ? [] : [repo.head.target].compact,
213
+ update_ref: 'HEAD')
214
+
215
+ index.write
216
+ true
217
+ end
193
218
  end
194
219
  end
195
220
  end