Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue_58 - Add support for conditions #59

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 114 additions & 29 deletions PantheonCMD/pcbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ def build_content(content_files, lang, output_format, repo_location, yaml_file_l
for item, members in main_yaml_file.items():
if item == 'variants':
attributes_file_location = repo_location + members[0]["path"]
with open(attributes_file_location,'r') as attributes_file:
attributes = parse_attributes(attributes_file.readlines())
attributes, lines = coalesce_document(attributes_file_location)
break
try:
pool = concurrent.futures.ThreadPoolExecutor()
Expand All @@ -52,20 +51,6 @@ def build_content(content_files, lang, output_format, repo_location, yaml_file_l
return True


def parse_attributes(attributes):
"""Read an attributes file and parse values into a key:value dictionary."""

final_attributes = {}

for line in attributes:
if re.match(r'^:\S+:.*', line):
attribute_name = line.split(":")[1].strip()
attribute_value = line.split(":")[2].strip()
final_attributes[attribute_name] = attribute_value

return final_attributes


def copy_resources(resources):
"""Copy resources such as images and files to the build directory."""
script_dir = os.path.dirname(os.path.realpath(__file__))
Expand Down Expand Up @@ -116,6 +101,8 @@ def coalesce_document(main_file, attributes=None, depth=0, top_level=True):
"""Combines the content from includes into a single, contiguous source file."""
attributes = attributes or {}
comment_block = False
condition_block = False
conditions_set = ''
lines = []

# Create a copy of global attributes
Expand All @@ -124,40 +111,137 @@ def coalesce_document(main_file, attributes=None, depth=0, top_level=True):

# Open the file, iterate over lines
if os.path.exists(main_file):

with open(main_file) as input_file:

# Iterate over content
for line in input_file:
# Process comment block start and finish
if line.strip().startswith('////'):

# CONDITIONS

# Line matches the end of a condition_block
matches = re.match(r'^endif::(.*?)\[\]', line)

if matches:
if matches.group(1) == conditions_set:
conditions_set = ''
condition_block = False
continue

# Line matches the middle of a condition block
if condition_block:
continue

# Line matches the start of an ifdef condition
matches = re.match(r'^ifdef::(\S+)\[(.*?)\]', line)

if matches:
conditions_present = False
conditions = matches.group(1)
conditions_set = matches.group(1)

# Multiple conditions - single match is enough
if conditions.__contains__(','):
conditions_list = conditions.split(',')
for condition in conditions_list:
if condition in attributes.keys():
conditions_present = True
break

# Multiple conditions - all must match
elif conditions.__contains__('+'):
conditions_present = True
conditions_list = conditions.split('+')
for condition in conditions_list:
if not condition in attributes.keys():
conditions_present = False

# Single condition
elif conditions in attributes.keys():
conditions_present = True

if conditions_present:
if matches.group(2).strip() == '':
condition_block = True
else:
lines.append(matches.group(2).strip() + '\n')
continue

# Line matches the start of an ifndef condition
matches = re.match(r'^ifndef::(\S+)\[(.*?)\]', line)

if matches:
conditions_missing = False
conditions = matches.group(1)
conditions_set = matches.group(1)

# Multiple conditions - one condition must be missing
if conditions.__contains__(','):
conditions_list = conditions.split(',')
for condition in conditions_list:
if not condition in attributes.keys():
conditions_missing = True
break

# Multiple conditions - all conditions must be missing
elif conditions.__contains__('+'):
conditions_missing = True
conditions_list = conditions.split('+')
for condition in conditions_list:
if condition in attributes.keys():
conditions_missing = False

# Single condition
elif conditions in attributes.keys():
conditions_missing = True

if conditions_missing:
if matches.group(2).strip() == '':
condition_block = True
else:
lines.append(matches.group(2).strip() + '\n')
continue

# COMMENTS

# Line matches comment block start or finish
if re.match(r'^////.*', line.strip()):
comment_block = True if not comment_block else False
continue
# Process comment block continuation

# Line matches the middle of a comment block
elif comment_block:
continue
# Process inline comments
elif line.strip().startswith('//'):

# Line matches an inline comment
elif re.match(r'^//.*', line.strip()):
continue
# Process section depth

# OTHER CONDITIONS

# Line matches a section header; adjust depth
elif re.match(r'^=+ \S+', line.strip()):
if depth > 0:
lines.append(('=' * depth) + line.strip())
lines.append(('=' * depth) + line.strip() + '\n')
else:
lines.append(line.strip())
# Process includes - recusrive
elif line.strip().startswith("include::"):
lines.append(line.strip() + '\n')

# Line matches an include; process recusrively
elif re.match(r'^include::.*', line.strip()):
include_depth = 0
include_file = line.replace("include::", "").split("[")[0]
include_options = line.split("[")[1].split("]")[0].split(',')
for include_option in include_options:
if include_option.__contains__("leveloffset=+"):
include_depth += int(include_option.split("+")[1])
# Replace attributes in includes, if their values are defined
if re.match(r'^\{\S+\}.*', include_file):
if re.match(r'.*\{\S+\}.*', include_file):
include_file = resolve_attribute_tree(include_file, attributes)
include_filepath = os.path.join(os.path.dirname(main_file), include_file)
attributes, include_lines = coalesce_document(include_filepath, attributes, include_depth, False)
lines.extend(include_lines)
# Build dictionary of found attributes

# Line matches an attribute declaration
elif re.match(r'^:\S+:.*', line):
attribute_name = line.split(":")[1].strip()
attribute_value = line.split(":")[2].strip()
Expand All @@ -166,6 +250,7 @@ def coalesce_document(main_file, attributes=None, depth=0, top_level=True):
lines.append(line)
else:
lines.append(line)

# Add global attribute definitions if main file
if top_level:
lines.insert(0,'\n\n')
Expand Down Expand Up @@ -214,7 +299,7 @@ def process_file(file_name, attributes, lang, output_format, content_count):
if output_format == 'pdf':
cmd = ('asciidoctor-pdf ' + language_code + ' -a pdf-themesdir=' + script_dir + '/templates/ -a pdf-theme=' + script_dir + '/templates/red-hat.yml -a pdf-fontsdir=' + script_dir + '/fonts' + ' ' + file_name + '.tmp').split()
else:
cmd = ('asciidoctor -a toc! ' + language_code + ' -a imagesdir=images -T ' + script_dir + '/haml/ -E haml ' + file_name + '.tmp').split()
cmd = ('asciidoctor -a toc! -a icons! ' + language_code + ' -a imagesdir=images -T ' + script_dir + '/haml/ -E haml ' + file_name + '.tmp').split()

# Build the content using AsciiDoctor
output = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Expand Down
2 changes: 1 addition & 1 deletion PantheonCMD/pcchecks.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Regex:
HUMAN_READABLE_LABEL_LINK = re.compile(r'\b(?:https?|file|ftp|irc):\/\/[^\s\[\]<]*\[\]')
NESTED_ASSEMBLY = re.compile(r'include.*assembly([a-z|0-9|A-Z|\-|_]+)\.adoc(\[.*\])')
NESTED_MODULES = re.compile(r'include.*(proc|con|ref)([a-z|0-9|A-Z|\-|_]+)\.adoc(\[.*\])')
RELATED_INFO = re.compile(r'= Related information|.Related information', re.IGNORECASE)
RELATED_INFO = re.compile(r'= Related information|\.Related information', re.IGNORECASE)
ADDITIONAL_RES = re.compile(r'= Additional resources|\.Additional resources', re.IGNORECASE)
ADD_RES_TAG = re.compile(r'\[role="_additional-resources"]')
ADD_RES_ASSEMBLY = re.compile(r'== Additional resources', re.IGNORECASE)
Expand Down
4 changes: 4 additions & 0 deletions PantheonCMD/pcmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ def parse_args():
# Else parse actions
# Action - preview
if args.command == 'preview':

# Validate the pantheon2.yml file
yaml_validation(pantheon_repo.yaml_file_location)

# Set the output format
if args.format == 'pdf':
output_format = 'pdf'
else:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Pantheon CMD is a Python-based command-line tool that allows you to generate a r

Installing Pantheon CMD using RPM allows you to perform actions using the predefined aliases such as:
* `pcmd validate`
* `pcmd build`
* `pcmd generate`
* `pcmd preview`

Alternatively, you can clone this repository and add the following `pcmd` scripts on $PATH, but the ability to use predefined aliases will not be possible.
Expand Down