Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib')
-rw-r--r--scripts/lib/glfm/constants.rb24
-rw-r--r--scripts/lib/glfm/render_static_html.rb16
-rw-r--r--scripts/lib/glfm/update_example_snapshots.rb57
-rw-r--r--scripts/lib/glfm/update_specification.rb140
-rw-r--r--scripts/lib/glfm/verify_all_generated_files_are_up_to_date.rb48
5 files changed, 231 insertions, 54 deletions
diff --git a/scripts/lib/glfm/constants.rb b/scripts/lib/glfm/constants.rb
index e5790bbdd88..d020d2fec5c 100644
--- a/scripts/lib/glfm/constants.rb
+++ b/scripts/lib/glfm/constants.rb
@@ -10,25 +10,29 @@ module Glfm
# GitHub Flavored Markdown specification file
GHFM_SPEC_TXT_URI = 'https://raw.githubusercontent.com/github/cmark-gfm/master/test/spec.txt'
GHFM_SPEC_VERSION = '0.29'
- GHFM_SPEC_TXT_FILENAME = "ghfm_spec_v_#{GHFM_SPEC_VERSION}.txt"
- GHFM_SPEC_TXT_PATH = specification_path.join('input/github_flavored_markdown', GHFM_SPEC_TXT_FILENAME)
+ GHFM_SPEC_MD_FILENAME = "ghfm_spec_v_#{GHFM_SPEC_VERSION}.md"
+ GHFM_SPEC_MD_PATH = specification_path.join('input/github_flavored_markdown', GHFM_SPEC_MD_FILENAME)
# GitLab Flavored Markdown specification files
specification_input_glfm_path = specification_path.join('input/gitlab_flavored_markdown')
- GLFM_INTRO_TXT_PATH = specification_input_glfm_path.join('glfm_intro.txt')
- GLFM_EXAMPLES_TXT_PATH = specification_input_glfm_path.join('glfm_canonical_examples.txt')
+ GLFM_INTRO_MD_PATH = specification_input_glfm_path.join('glfm_intro.md')
+ GLFM_OFFICIAL_SPECIFICATION_EXAMPLES_MD_PATH =
+ specification_input_glfm_path.join('glfm_official_specification_examples.md')
+ GLFM_INTERNAL_EXTENSION_EXAMPLES_MD_PATH = specification_input_glfm_path.join('glfm_internal_extension_examples.md')
GLFM_EXAMPLE_STATUS_YML_PATH = specification_input_glfm_path.join('glfm_example_status.yml')
GLFM_EXAMPLE_METADATA_YML_PATH =
specification_input_glfm_path.join('glfm_example_metadata.yml')
GLFM_EXAMPLE_NORMALIZATIONS_YML_PATH = specification_input_glfm_path.join('glfm_example_normalizations.yml')
- GLFM_SPEC_TXT_PATH = specification_path.join('output/spec.txt')
+ GLFM_SPEC_OUTPUT_PATH = specification_path.join('output')
+ GLFM_SPEC_TXT_PATH = GLFM_SPEC_OUTPUT_PATH.join('spec.txt')
+ GLFM_SPEC_HTML_PATH = GLFM_SPEC_OUTPUT_PATH.join('spec.html')
# Example Snapshot (ES) files
- es_fixtures_path = File.expand_path("../../../glfm_specification/example_snapshots", __dir__)
- ES_EXAMPLES_INDEX_YML_PATH = File.join(es_fixtures_path, 'examples_index.yml')
- ES_MARKDOWN_YML_PATH = File.join(es_fixtures_path, 'markdown.yml')
- ES_HTML_YML_PATH = File.join(es_fixtures_path, 'html.yml')
- ES_PROSEMIRROR_JSON_YML_PATH = File.join(es_fixtures_path, 'prosemirror_json.yml')
+ EXAMPLE_SNAPSHOTS_PATH = File.expand_path("../../../glfm_specification/example_snapshots", __dir__)
+ ES_EXAMPLES_INDEX_YML_PATH = File.join(EXAMPLE_SNAPSHOTS_PATH, 'examples_index.yml')
+ ES_MARKDOWN_YML_PATH = File.join(EXAMPLE_SNAPSHOTS_PATH, 'markdown.yml')
+ ES_HTML_YML_PATH = File.join(EXAMPLE_SNAPSHOTS_PATH, 'html.yml')
+ ES_PROSEMIRROR_JSON_YML_PATH = File.join(EXAMPLE_SNAPSHOTS_PATH, 'prosemirror_json.yml')
# Other constants used for processing files
GLFM_SPEC_TXT_HEADER = <<~MARKDOWN
diff --git a/scripts/lib/glfm/render_static_html.rb b/scripts/lib/glfm/render_static_html.rb
index 8d72aec7c3b..6af73cd845d 100644
--- a/scripts/lib/glfm/render_static_html.rb
+++ b/scripts/lib/glfm/render_static_html.rb
@@ -20,21 +20,26 @@ require_relative 'shared'
# Factorybot factory methods to create persisted model objects with stable
# and consistent data values, to ensure consistent example snapshot HTML
# across various machines and environments. RSpec also makes it easy to invoke
-# the API # and obtain the response.
+# the API and obtain the response.
#
# It is intended to be invoked as a helper subprocess from the `update_example_snapshots.rb`
# script class. It's not intended to be run or used directly. This usage is also reinforced
# by not naming the file with a `_spec.rb` ending.
-RSpec.describe 'Render Static HTML', :api, type: :request do # rubocop:disable RSpec/TopLevelDescribePath
+RSpec.describe 'Render Static HTML', :api, type: :request do
include Glfm::Constants
include Glfm::Shared
# noinspection RailsParamDefResolve (RubyMine can't find the shared context from this file location)
include_context 'with GLFM example snapshot fixtures'
- it 'can create a project dependency graph using factories' do
+ it do
markdown_hash = YAML.safe_load(File.open(ENV.fetch('INPUT_MARKDOWN_YML_PATH')), symbolize_names: true)
- metadata_hash = YAML.safe_load(File.open(ENV.fetch('INPUT_METADATA_YML_PATH')), symbolize_names: true)
+ metadata_hash =
+ if input_metadata_yml_path = ENV['INPUT_METADATA_YML_PATH']
+ YAML.safe_load(File.open(input_metadata_yml_path), symbolize_names: true) || {}
+ else
+ {}
+ end
# NOTE: We cannot parallelize this loop like the Javascript WYSIWYG example generation does,
# because the rspec `post` API cannot be parallized (it is not thread-safe, it can't find
@@ -66,8 +71,7 @@ RSpec.describe 'Render Static HTML', :api, type: :request do # rubocop:disable R
private
def write_output_file(static_html_hash)
- tmpfile = File.open(ENV.fetch('OUTPUT_STATIC_HTML_TEMPFILE_PATH'), 'w')
yaml_string = dump_yaml_with_formatting(static_html_hash)
- write_file(tmpfile, yaml_string)
+ write_file(ENV.fetch('OUTPUT_STATIC_HTML_TEMPFILE_PATH'), yaml_string)
end
end
diff --git a/scripts/lib/glfm/update_example_snapshots.rb b/scripts/lib/glfm/update_example_snapshots.rb
index 7dc0d0f7c4b..9075260e748 100644
--- a/scripts/lib/glfm/update_example_snapshots.rb
+++ b/scripts/lib/glfm/update_example_snapshots.rb
@@ -5,11 +5,12 @@ require 'yaml'
require 'psych'
require 'tempfile'
require 'open3'
+require 'active_support/core_ext/enumerable'
require_relative 'constants'
require_relative 'shared'
require_relative 'parse_examples'
-# IMPORTANT NOTE: See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/
+# IMPORTANT NOTE: See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#update-example-snapshotsrb-script
# for details on the implementation and usage of this script. This developers guide
# contains diagrams and documentation of this script,
# including explanations and examples of all files it reads and writes.
@@ -29,8 +30,6 @@ module Glfm
def process(skip_static_and_wysiwyg: false)
output('Updating example snapshots...')
- output('(Skipping static HTML generation)') if skip_static_and_wysiwyg
-
output("Reading #{GLFM_SPEC_TXT_PATH}...")
glfm_spec_txt_lines = File.open(GLFM_SPEC_TXT_PATH).readlines
@@ -115,11 +114,13 @@ module Glfm
def write_snapshot_example_files(all_examples, skip_static_and_wysiwyg:)
output("Reading #{GLFM_EXAMPLE_STATUS_YML_PATH}...")
- glfm_examples_statuses = YAML.safe_load(File.open(GLFM_EXAMPLE_STATUS_YML_PATH), symbolize_names: true)
+ glfm_examples_statuses = YAML.safe_load(File.open(GLFM_EXAMPLE_STATUS_YML_PATH), symbolize_names: true) || {}
validate_glfm_example_status_yml(glfm_examples_statuses)
write_examples_index_yml(all_examples)
+ validate_glfm_config_file_example_names(all_examples)
+
write_markdown_yml(all_examples)
if skip_static_and_wysiwyg
@@ -151,6 +152,50 @@ module Glfm
end
end
+ def validate_glfm_config_file_example_names(all_examples)
+ valid_example_names = all_examples.pluck(:name).map(&:to_sym) # rubocop:disable CodeReuse/ActiveRecord
+
+ # We are re-reading GLFM_EXAMPLE_STATUS_YML_PATH here, but that's OK, it's a small file, and rereading it
+ # allows us to handle it in the same loop as the other manually-curated config files.
+ [
+ GLFM_EXAMPLE_STATUS_YML_PATH,
+ GLFM_EXAMPLE_METADATA_YML_PATH,
+ GLFM_EXAMPLE_NORMALIZATIONS_YML_PATH
+ ].each do |path|
+ output("Reading #{path}...")
+ io = File.open(path)
+ config_file_examples = YAML.safe_load(io, symbolize_names: true, aliases: true)
+
+ # Skip validation if the config file is empty
+ next unless config_file_examples
+
+ config_file_example_names = config_file_examples.keys
+
+ # Validate that all example names exist in the config file refer to an existing example in `examples_index.yml`,
+ # unless it starts with the special prefix `00_`, which is preserved for usage as YAML anchors.
+ invalid_name = config_file_example_names.detect do |name|
+ !name.start_with?('00_') && valid_example_names.exclude?(name)
+ end
+ next unless invalid_name
+
+ # NOTE: The extra spaces before punctuation in the error message allows for easier copy/pasting of the paths.
+ err_msg =
+ <<~TXT
+
+ Error in input specification config file #{path} :
+
+ Config file entry named #{invalid_name}
+ does not have a corresponding example entry in
+ #{ES_EXAMPLES_INDEX_YML_PATH} .
+
+ Please delete or rename this config file entry.
+
+ If this entry is being used as a YAML anchor, please rename it to start with '00_'.
+ TXT
+ raise err_msg
+ end
+ end
+
def write_examples_index_yml(all_examples)
generate_and_write_for_all_examples(
all_examples, ES_EXAMPLES_INDEX_YML_PATH, literal_scalars: false
@@ -219,7 +264,7 @@ module Glfm
# NOTE 2: We run this as an RSpec process, for the same reasons we run via Jest process below:
# because that's the easiest way to ensure a reliable, fully-configured environment in which
- # to execute the markdown-generation logic. Also, in the static/backend case, Rspec
+ # to execute the markdown-processing logic. Also, in the static/backend case, Rspec
# provides the easiest and most reliable way to generate example data via Factorybot
# creation of stable model records. This ensures consistent snapshot values across
# machines/environments.
@@ -244,7 +289,7 @@ module Glfm
wysiwyg_html_and_json_tempfile_path = Dir::Tmpname.create(WYSIWYG_HTML_AND_JSON_TEMPFILE_BASENAME) {}
ENV['OUTPUT_WYSIWYG_HTML_AND_JSON_TEMPFILE_PATH'] = wysiwyg_html_and_json_tempfile_path
- cmd = %(yarn jest --testMatch '**/render_wysiwyg_html_and_json.js' #{__dir__}/render_wysiwyg_html_and_json.js)
+ cmd = "yarn jest --testMatch '**/render_wysiwyg_html_and_json.js' #{__dir__}/render_wysiwyg_html_and_json.js"
run_external_cmd(cmd)
output("Reading generated WYSIWYG HTML and prosemirror JSON from tempfile " \
diff --git a/scripts/lib/glfm/update_specification.rb b/scripts/lib/glfm/update_specification.rb
index 73c23d40de5..c7264547e44 100644
--- a/scripts/lib/glfm/update_specification.rb
+++ b/scripts/lib/glfm/update_specification.rb
@@ -2,26 +2,45 @@
require 'fileutils'
require 'open-uri'
require 'pathname'
+require 'tempfile'
+require 'yaml'
require_relative 'constants'
require_relative 'shared'
+# IMPORTANT NOTE: See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#update-specificationrb-script
+# for details on the implementation and usage of this script. This developers guide
+# contains diagrams and documentation of this script,
+# including explanations and examples of all files it reads and writes.
+#
+# Also note that this script is intentionally written in a pure-functional (not OO) style,
+# with no dependencies on Rails or the GitLab libraries. These choices are intended to make
+# it faster and easier to test and debug.
module Glfm
class UpdateSpecification
include Constants
include Shared
- def process
+ def process(skip_spec_html_generation: false)
output('Updating specification...')
- ghfm_spec_txt_lines = load_ghfm_spec_txt
- glfm_spec_txt_string = build_glfm_spec_txt(ghfm_spec_txt_lines)
+
+ ghfm_spec_lines = load_ghfm_spec
+ glfm_spec_txt_string = build_glfm_spec_txt(ghfm_spec_lines)
write_glfm_spec_txt(glfm_spec_txt_string)
+
+ if skip_spec_html_generation
+ output("Skipping GLFM spec.html generation...")
+ return
+ end
+
+ glfm_spec_html_string = generate_glfm_spec_html(glfm_spec_txt_string)
+ write_glfm_spec_html(glfm_spec_html_string)
end
private
- def load_ghfm_spec_txt
+ def load_ghfm_spec
# We only re-download the GitHub Flavored Markdown specification if the
- # UPDATE_GHFM_SPEC_TXT environment variable is set to true, which should only
+ # UPDATE_GHFM_SPEC_MD environment variable is set to true, which should only
# ever be done manually and locally, never in CI. This provides some security
# protection against a possible injection attack vector, if the GitHub-hosted
# version of the spec is ever temporarily compromised with an injection attack.
@@ -29,40 +48,44 @@ module Glfm
# This also avoids doing external network access to download the file
# in CI jobs, which can avoid potentially flaky builds if the GitHub-hosted
# version of the file is temporarily unavailable.
- if ENV['UPDATE_GHFM_SPEC_TXT'] == 'true'
- download_and_write_ghfm_spec_txt
+ if ENV['UPDATE_GHFM_SPEC_MD'] == 'true'
+ update_ghfm_spec_md
else
- read_existing_ghfm_spec_txt
+ read_existing_ghfm_spec_md
end
end
- def read_existing_ghfm_spec_txt
- output("Reading existing #{GHFM_SPEC_TXT_PATH}...")
- File.open(GHFM_SPEC_TXT_PATH).readlines
+ def read_existing_ghfm_spec_md
+ output("Reading existing #{GHFM_SPEC_MD_PATH}...")
+ File.open(GHFM_SPEC_MD_PATH).readlines
end
- def download_and_write_ghfm_spec_txt
+ def update_ghfm_spec_md
output("Downloading #{GHFM_SPEC_TXT_URI}...")
- ghfm_spec_txt_uri_io = URI.open(GHFM_SPEC_TXT_URI)
+ # NOTE: We use `URI.parse` to avoid RuboCop warning "Security/Open",
+ # even though we are using a trusted URI from a string literal constant.
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98656#note_1138595002 for details.
+ ghfm_spec_txt_uri_parsed = URI.parse(GHFM_SPEC_TXT_URI)
+ ghfm_spec_txt_uri_io = ghfm_spec_txt_uri_parsed.open
# Read IO stream into an array of lines for easy processing later
- ghfm_spec_txt_lines = ghfm_spec_txt_uri_io.readlines
- raise "Unable to read lines from #{GHFM_SPEC_TXT_URI}" if ghfm_spec_txt_lines.empty?
+ ghfm_spec_lines = ghfm_spec_txt_uri_io.readlines
+ raise "Unable to read lines from #{GHFM_SPEC_TXT_URI}" if ghfm_spec_lines.empty?
# Make sure the GHFM spec version has not changed
- validate_expected_spec_version!(ghfm_spec_txt_lines[2])
+ validate_expected_spec_version!(ghfm_spec_lines[2])
# Reset IO stream and re-read into a single string for easy writing
# noinspection RubyNilAnalysis
ghfm_spec_txt_uri_io.seek(0)
- ghfm_spec_txt_string = ghfm_spec_txt_uri_io.read
- raise "Unable to read string from #{GHFM_SPEC_TXT_URI}" unless ghfm_spec_txt_string
+ ghfm_spec_string = ghfm_spec_txt_uri_io.read
+ raise "Unable to read string from #{GHFM_SPEC_TXT_URI}" unless ghfm_spec_string
- output("Writing #{GHFM_SPEC_TXT_PATH}...")
- GHFM_SPEC_TXT_PATH.dirname.mkpath
- write_file(GHFM_SPEC_TXT_PATH, ghfm_spec_txt_string)
+ output("Writing #{GHFM_SPEC_MD_PATH}...")
+ GHFM_SPEC_MD_PATH.dirname.mkpath
+ write_file(GHFM_SPEC_MD_PATH, ghfm_spec_string)
- ghfm_spec_txt_lines
+ ghfm_spec_lines
end
def validate_expected_spec_version!(version_line)
@@ -76,7 +99,7 @@ module Glfm
glfm_spec_txt_lines = ghfm_spec_txt_lines.dup
replace_header(glfm_spec_txt_lines)
replace_intro_section(glfm_spec_txt_lines)
- insert_examples_txt(glfm_spec_txt_lines)
+ insert_examples(glfm_spec_txt_lines)
glfm_spec_txt_lines.join('')
end
@@ -85,13 +108,13 @@ module Glfm
end
def replace_intro_section(spec_txt_lines)
- glfm_intro_txt_lines = File.open(GLFM_INTRO_TXT_PATH).readlines
- raise "Unable to read lines from #{GLFM_INTRO_TXT_PATH}" if glfm_intro_txt_lines.empty?
+ glfm_intro_md_lines = File.open(GLFM_INTRO_MD_PATH).readlines
+ raise "Unable to read lines from #{GLFM_INTRO_MD_PATH}" if glfm_intro_md_lines.empty?
ghfm_intro_header_begin_index = spec_txt_lines.index do |line|
line =~ INTRODUCTION_HEADER_LINE_TEXT
end
- raise "Unable to locate introduction header line in #{GHFM_SPEC_TXT_PATH}" if ghfm_intro_header_begin_index.nil?
+ raise "Unable to locate introduction header line in #{GHFM_SPEC_MD_PATH}" if ghfm_intro_header_begin_index.nil?
# Find the index of the next header after the introduction header, starting from the index
# of the introduction header this is the length of the intro section
@@ -100,20 +123,29 @@ module Glfm
end
# Replace the intro section with the GitLab flavored Markdown intro section
- spec_txt_lines[ghfm_intro_header_begin_index, ghfm_intro_section_length] = glfm_intro_txt_lines
+ spec_txt_lines[ghfm_intro_header_begin_index, ghfm_intro_section_length] = glfm_intro_md_lines
end
- def insert_examples_txt(spec_txt_lines)
- glfm_examples_txt_lines = File.open(GLFM_EXAMPLES_TXT_PATH).readlines
- raise "Unable to read lines from #{GLFM_EXAMPLES_TXT_PATH}" if glfm_examples_txt_lines.empty?
+ def insert_examples(spec_txt_lines)
+ official_spec_lines = File.open(GLFM_OFFICIAL_SPECIFICATION_EXAMPLES_MD_PATH).readlines
+ raise "Unable to read lines from #{GLFM_OFFICIAL_SPECIFICATION_EXAMPLES_MD_PATH}" if official_spec_lines.empty?
+
+ internal_extension_lines = File.open(GLFM_INTERNAL_EXTENSION_EXAMPLES_MD_PATH).readlines
+ raise "Unable to read lines from #{GLFM_INTERNAL_EXTENSION_EXAMPLES_MD_PATH}" if internal_extension_lines.empty?
ghfm_end_tests_comment_index = spec_txt_lines.index do |line|
line =~ END_TESTS_COMMENT_LINE_TEXT
end
- raise "Unable to locate 'END TESTS' comment line in #{GHFM_SPEC_TXT_PATH}" if ghfm_end_tests_comment_index.nil?
+ raise "Unable to locate 'END TESTS' comment line in #{GHFM_SPEC_MD_PATH}" if ghfm_end_tests_comment_index.nil?
# Insert the GLFM examples before the 'END TESTS' comment line
- spec_txt_lines[ghfm_end_tests_comment_index - 1] = ["\n", glfm_examples_txt_lines, "\n"].flatten
+ spec_txt_lines[ghfm_end_tests_comment_index - 1] = [
+ "\n",
+ official_spec_lines,
+ "\n",
+ internal_extension_lines,
+ "\n"
+ ].flatten
spec_txt_lines
end
@@ -123,5 +155,49 @@ module Glfm
FileUtils.mkdir_p(Pathname.new(GLFM_SPEC_TXT_PATH).dirname)
write_file(GLFM_SPEC_TXT_PATH, glfm_spec_txt_string)
end
+
+ def generate_glfm_spec_html(glfm_spec_txt_string)
+ output("Generating spec.html from spec.txt markdown...")
+
+ input_markdown_yml_string = <<~MARKDOWN
+ ---
+ spec_txt: |
+ #{glfm_spec_txt_string.gsub(/^/, ' ')}
+ MARKDOWN
+
+ # NOTE: We must copy the input YAML file used by the `render_static_html.rb`
+ # to a separate temporary file in order for the script to read them, because it is run in
+ # a separate subprocess, and during unit testing we are unable to substitute the mock
+ # StringIO when reading the input files in the subprocess.
+ ENV['INPUT_MARKDOWN_YML_PATH'] = Dir::Tmpname.create(MARKDOWN_TEMPFILE_BASENAME) do |path|
+ write_file(path, input_markdown_yml_string)
+ end
+
+ # NOTE 1: We shell out to perform the conversion of markdown to static HTML by invoking a
+ # separate subprocess. This allows us to avoid using the Rails API or environment in this
+ # script, which makes developing and running the unit tests for this script much faster,
+ # because they can use 'fast_spec_helper' which does not require the entire Rails environment.
+
+ # NOTE 2: We run this as an RSpec process, for the same reasons we run via Jest process below:
+ # because that's the easiest way to ensure a reliable, fully-configured environment in which
+ # to execute the markdown-processing logic. Also, in the static/backend case.
+
+ # Dir::Tmpname.create requires a block, but we are using the non-block form to get the path
+ # via the return value, so we pass an empty block to avoid an error.
+ static_html_tempfile_path = Dir::Tmpname.create(STATIC_HTML_TEMPFILE_BASENAME) {}
+ ENV['OUTPUT_STATIC_HTML_TEMPFILE_PATH'] = static_html_tempfile_path
+
+ cmd = %(bin/rspec #{__dir__}/render_static_html.rb)
+ run_external_cmd(cmd)
+
+ output("Reading generated spec.html from tempfile #{static_html_tempfile_path}...")
+ YAML.safe_load(File.open(static_html_tempfile_path), symbolize_names: true).fetch(:spec_txt)
+ end
+
+ def write_glfm_spec_html(glfm_spec_html_string)
+ output("Writing #{GLFM_SPEC_TXT_PATH}...")
+ FileUtils.mkdir_p(Pathname.new(GLFM_SPEC_HTML_PATH).dirname)
+ write_file(GLFM_SPEC_HTML_PATH, "#{glfm_spec_html_string}\n")
+ end
end
end
diff --git a/scripts/lib/glfm/verify_all_generated_files_are_up_to_date.rb b/scripts/lib/glfm/verify_all_generated_files_are_up_to_date.rb
new file mode 100644
index 00000000000..0b824fc589d
--- /dev/null
+++ b/scripts/lib/glfm/verify_all_generated_files_are_up_to_date.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+require_relative 'constants'
+require_relative 'shared'
+
+# IMPORTANT NOTE: See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#verify-all-generated-files-are-up-to-daterb-script
+# for details on the implementation and usage of this script. This developers guide
+# contains diagrams and documentation of this script,
+# including explanations and examples of all files it reads and writes.
+module Glfm
+ class VerifyAllGeneratedFilesAreUpToDate
+ include Constants
+ include Shared
+
+ def process
+ verify_cmd = "git status --porcelain #{GLFM_SPEC_OUTPUT_PATH} #{EXAMPLE_SNAPSHOTS_PATH}"
+ verify_cmd_output = run_external_cmd(verify_cmd)
+ unless verify_cmd_output.empty?
+ msg = "ERROR: Cannot run `#{__FILE__}` because `#{verify_cmd}` shows the following uncommitted changes:\n" \
+ "#{verify_cmd_output}"
+ raise(msg)
+ end
+
+ output('Verifying all generated files are up to date after running GLFM scripts...')
+
+ output("Running `yarn install --frozen-lockfile` to ensure `yarn check-dependencies` doesn't fail...")
+ run_external_cmd('yarn install --frozen-lockfile')
+
+ # noinspection RubyMismatchedArgumentType
+ update_specification_script = File.expand_path('../../glfm/update-specification.rb', __dir__)
+ # noinspection RubyMismatchedArgumentType
+ update_example_snapshots_script = File.expand_path('../../glfm/update-example-snapshots.rb', __dir__)
+
+ output("Running `#{update_specification_script}`...")
+ run_external_cmd(update_specification_script)
+
+ output("Running `#{update_example_snapshots_script}`...")
+ run_external_cmd(update_example_snapshots_script)
+
+ output("Running `#{verify_cmd}` to check that no modifications to generated files have occurred...")
+ verify_cmd_output = run_external_cmd(verify_cmd)
+
+ return if verify_cmd_output.empty?
+
+ raise "The following files were modified by running GLFM scripts. Please review, verify, and commit " \
+ "the changes:\n#{verify_cmd_output}"
+ end
+ end
+end