lib/release_tools/helm/version_mapping.rb (65 lines of code) (raw):
# frozen_string_literal: true
module ReleaseTools
module Helm
# Parsing and generating of the Markdown table that maps Helm versions to
# GitLab versions.
class VersionMapping
HEADER = '| Chart version | GitLab version |'
DIVIDER = '|---------------|----------------|'
# An error that may be produced when parsing a Markdown document.
ParseError = Class.new(StandardError)
# A single row in the version mapping table.
Row = Struct.new(:helm_version, :gitlab_version)
attr_reader :lines, :rows, :start, :stop
def self.parse(markdown)
lines = markdown.split("\n")
start = lines.index(HEADER)
if !start || lines[start + 1] != DIVIDER
raise(ParseError, <<~ERROR.strip)
The Markdown table is not formatted correctly.
Markdown tables must start with the following header:
#{HEADER}
#{DIVIDER}
ERROR
end
index = start + 2
rows = []
while index < lines.length
line = lines[index].strip
break if line.empty?
_, raw_helm_version, raw_gitlab_version = line.split(/\s*\|\s*/)
# If Helm version is not specified, `raw_helm_version` will be an
# empty String instead of being nil. If only a Helm version is given,
# `raw_gitlab_version` _will_ be nil. The to_s conversion below allows
# us to handle both cases transparently.
if raw_helm_version.to_s.empty? || raw_gitlab_version.to_s.empty?
raise(
ParseError,
"The row #{line} must contain both a Helm and GitLab version"
)
end
helm_version = Version.new(raw_helm_version)
gitlab_version = Version.new(raw_gitlab_version)
rows << Row.new(helm_version, gitlab_version)
index += 1
end
new(lines: lines, rows: rows, start: start, stop: index - 1)
end
def initialize(lines:, rows:, start:, stop:)
# The original lines are frozen to ensure we don't accidentally modify
# them in place. Doing so could lead to unexpected output being
# produced.
@lines = lines.freeze
@rows = rows
@start = start
@stop = stop
end
def add(helm_version, gitlab_version)
existing = rows.find { |row| row.helm_version == helm_version }
if existing
existing.gitlab_version = gitlab_version
else
rows.push(Row.new(helm_version, gitlab_version))
end
end
def to_s
new_rows = rows.sort { |a, b| b.helm_version <=> a.helm_version }
row_lines = new_rows.map do |row|
"| #{row.helm_version} | #{row.gitlab_version.to_normalized_version} |"
end
table = "#{HEADER}\n#{DIVIDER}\n#{row_lines.join("\n")}"
old_range = start..stop
new_lines = lines.dup
# We want to insert _after_ the last line of the table, so we add 1 to
# the table's end position.
new_lines.insert(old_range.end + 1, table)
# This will remove all lines of the old table, leaving the newly
# inserted table as-is.
new_lines.reject!.with_index { |_, index| old_range.cover?(index) }
# The document has a trailing newline. While (not) adding one doesn't
# really matter, this ensures that any diffs _only_ include the lines
# that change the mapping table.
"#{new_lines.join("\n")}\n"
end
end
end
end