@@ -250,6 +250,73 @@ def initialize(advisory)
250250 @vulnerabilities = [ ]
251251 end
252252
253+ def self . formatted_yaml ( data )
254+ yaml = data . to_yaml
255+ formatted_yaml = indent_mapping_sequences ( yaml )
256+
257+ return formatted_yaml if yaml_round_trips? ( formatted_yaml , data )
258+
259+ yaml
260+ end
261+
262+ def self . indent_mapping_sequences ( yaml )
263+ active_sequence_indents = [ ]
264+ pending_sequence_indent = nil
265+ block_scalar_indent = nil
266+
267+ yaml . lines . map do |line |
268+ if line . strip . empty? || line . start_with? ( "---" )
269+ next line
270+ end
271+
272+ indent = line [ /\A */ ] . length
273+
274+ if block_scalar_indent
275+ if indent > block_scalar_indent
276+ next "#{ ' ' * active_sequence_indents . length } #{ line } "
277+ end
278+
279+ block_scalar_indent = nil
280+ end
281+
282+ sequence_item = line . match? ( /\A \s *-\s / )
283+
284+ active_sequence_indents . pop while active_sequence_indents . any? &&
285+ indent <= active_sequence_indents . last &&
286+ !( sequence_item && indent == active_sequence_indents . last )
287+
288+ if sequence_item && pending_sequence_indent == indent
289+ active_sequence_indents << indent
290+ end
291+
292+ pending_sequence_indent = sequence_indent_after_mapping_key ( line , indent )
293+ block_scalar_indent = indent if block_scalar_header? ( line )
294+
295+ "#{ ' ' * active_sequence_indents . length } #{ line } "
296+ end . join
297+ end
298+
299+ def self . sequence_indent_after_mapping_key ( line , indent )
300+ if line . match? ( /\A \s *-\s +.*:\s *\z / )
301+ indent + 2
302+ elsif line . match? ( /\A \s *[^#].*:\s *\z / )
303+ indent
304+ end
305+ end
306+
307+ def self . block_scalar_header? ( line )
308+ line . match? ( /\A \s *(?:-\s +)?[^#]+:\s *[>|](?:[+-]?[1-9]|[1-9][+-]?)?\s *\z / )
309+ end
310+
311+ def self . yaml_round_trips? ( yaml , data )
312+ YAML . safe_load ( yaml , permitted_classes : [ Date ] ) == data
313+ rescue Psych ::Exception
314+ false
315+ end
316+
317+ private_class_method :indent_mapping_sequences , :sequence_indent_after_mapping_key ,
318+ :block_scalar_header? , :yaml_round_trips?
319+
253320 def identifier_list
254321 advisory [ "identifiers" ]
255322 end
@@ -338,7 +405,7 @@ def update(package)
338405 return if saved_data == new_data
339406
340407 File . open ( package . filename , 'w' ) do |file |
341- file . write YAML . dump ( new_data )
408+ file . write self . class . formatted_yaml ( new_data )
342409 end
343410
344411 puts "Updated: #{ package . filename } "
@@ -428,7 +495,7 @@ def create(package)
428495 FileUtils . mkdir_p ( File . dirname ( filename_to_write ) )
429496 File . open ( filename_to_write , "w" ) do |file |
430497 # create an automatically generated advisory yaml file
431- file . write new_data . to_yaml
498+ file . write self . class . formatted_yaml ( new_data )
432499
433500 # The data we just wrote is incomplete,
434501 # and therefore should not be committed as is
@@ -448,7 +515,7 @@ def create(package)
448515 # Still it should be removed before the data goes into rubysec
449516 file . write "# GitHub advisory data below - **Remove this data before committing**\n "
450517 file . write "# Use this data to write patched_versions (and potentially unaffected_versions) above\n "
451- file . write advisory . merge ( "vulnerabilities" => vulnerabilities ) . to_yaml
518+ file . write self . class . formatted_yaml ( advisory . merge ( "vulnerabilities" => vulnerabilities ) )
452519 end
453520 puts "Wrote: #{ filename_to_write } "
454521 filename_to_write
0 commit comments