Skip to content

Commit cda9024

Browse files
authored
Merge pull request #65701 from mramendi/cicd-book-build
OSDOCS 8020 modify the build_for_portal.py and makeBuild.py scripts to support second-level books and improve intra-book links
2 parents 74c98b7 + aeb5e85 commit cda9024

File tree

3 files changed

+148
-106
lines changed

3 files changed

+148
-106
lines changed

build_for_portal.py

Lines changed: 98 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
)
4747
CMP_IGNORE_FILES = [".git", ".gitignore", "README.md", "build.cfg"]
4848
DEVNULL = open(os.devnull, "wb")
49-
LIST_OF_HUGE_BOOKS: list = ["Installing", "API reference"]
5049

5150
MASTER_FILE_BASE = "= {title}\n\
5251
:product-author: {product-author}\n\
@@ -269,19 +268,53 @@ def ensure_directory(directory):
269268
Creates DIRECTORY if it does not exist.
270269
"""
271270
if not os.path.exists(directory):
272-
os.mkdir(directory)
271+
os.makedirs(directory)
272+
273+
def expand_huge_books(info):
274+
"""
275+
Finds nodes for huge books, creates new nodes for books from their top-level topics,
276+
and then removes the nodes for huge books
277+
"""
278+
279+
# find all the huge books, sa defined by nodes
280+
huge_book_nodes = [book for book in info["book_nodes"]
281+
if os.path.exists(os.path.join(info["src_dir"],book["Dir"],"hugeBook.flag")) ]
282+
283+
284+
for book in huge_book_nodes:
285+
# save the directory in info
286+
huge_book_dir = book["Dir"]
287+
info["huge_book_dirs"].append(huge_book_dir)
288+
# create the flag file in the book destination directory
289+
book_dest_dir = os.path.join(info["dest_dir"], book["Dir"])
290+
ensure_directory(book_dest_dir)
291+
with open(os.path.join(book_dest_dir,"hugeBook.flag"),"w") as fi:
292+
fi.write("hugebook")
293+
# make new book nodes for the second-level headings
294+
for topic in book["Topics"]:
295+
if "Dir" in topic.keys():
296+
info["book_nodes"].append(topic)
297+
topic["Dir"] = huge_book_dir + "/" + topic["Dir"]
298+
299+
# remove book nodes for huge books
300+
for node_to_remove in huge_book_nodes:
301+
info["book_nodes"].remove(node_to_remove)
273302

274303

275304
def build_master_files(info):
276305
"""
277306
Builds the master.adoc and docinfo.xml files for each guide specified in the config.
278307
"""
279308

309+
# change the huge books into sub-books
310+
expand_huge_books(info)
311+
280312
# TODO: Refactor. This does too much.
281313

282314
dest_dir = info["dest_dir"]
283315
all_in_one = info["all_in_one"]
284316
all_in_one_text = ""
317+
285318
for book in info["book_nodes"]:
286319

287320
book_dest_dir = os.path.join(dest_dir, book["Dir"])
@@ -328,40 +361,6 @@ def build_master_files(info):
328361
info["preface-title"] = ":preface-title: " + preface_title
329362
all_in_one_text += master
330363

331-
if book["Name"] in LIST_OF_HUGE_BOOKS:
332-
huge_book_topics = book["Topics"]
333-
334-
for topic in huge_book_topics:
335-
if "Dir" in topic.keys():
336-
topic_master_file = os.path.join(
337-
book_dest_dir, topic["Dir"], "master.adoc"
338-
)
339-
topic_docinfo_file = os.path.join(
340-
book_dest_dir, topic["Dir"], "docinfo.xml"
341-
)
342-
343-
# TODO: Make less hacky.
344-
book_info["title"] = topic["Name"]
345-
info["title"] = topic["Name"]
346-
347-
master_base = MASTER_FILE_BASE.format(**book_info)
348-
docinfo_node = topic["Name"]
349-
350-
ensure_directory(os.path.join(book_dest_dir, topic["Dir"]))
351-
sub_master = generate_master_entry(
352-
topic,
353-
topic["Dir"],
354-
info["distro"],
355-
all_in_one,
356-
all_in_one=all_in_one,
357-
)
358-
359-
log.debug("Writing " + topic_master_file)
360-
with open(topic_master_file, "w") as f:
361-
f.write(master_base + sub_master)
362-
log.debug("Writing " + topic_docinfo_file)
363-
with open(topic_docinfo_file, "w") as f:
364-
f.write(DOCINFO_BASE.format(**info))
365364
# TODO: And is this ever used?
366365
if all_in_one:
367366
master_file = os.path.join(dest_dir, "master.adoc")
@@ -515,6 +514,7 @@ def copy_file(
515514
Copies a source file to destination, making sure to scrub the content, add id's where the content is referenced elsewhere and fix any
516515
links that should be cross references. Also copies any includes that are referenced, since they aren't included in _build_cfg.yml.
517516
"""
517+
518518
# It's possible that the file might have been created by another include, if so then just return
519519
if os.path.isfile(dest_file):
520520
return
@@ -544,15 +544,17 @@ def copy_file(
544544
key, value = re.split("\s*=\s*", meta, 2)
545545
include_vars[key] = value
546546

547+
547548
# Determine the include src/dest paths
548549
include_file = os.path.join(os.path.dirname(book_src_dir), include_path)
549550
relative_path = os.path.relpath(include_file, os.path.dirname(src_file))
550551

551552
# If the path is in another book, copy it into this one
552553
relative_book_path = os.path.relpath(include_file, book_src_dir)
554+
553555
if relative_book_path.startswith("../"):
554-
path, src_book_name = os.path.split(book_src_dir)
555-
dest_include_dir = os.path.join(dest_dir, src_book_name, "includes")
556+
src_book_relative_dir = os.path.relpath(book_src_dir,info["src_dir"])
557+
dest_include_dir = os.path.join(dest_dir, src_book_relative_dir, "includes")
556558
relative_path = os.path.join(
557559
os.path.relpath(dest_include_dir, parent_dir),
558560
os.path.basename(include_file),
@@ -720,7 +722,7 @@ def fix_links(content, info, book_src_dir, src_file, tag=None, cwd=None):
720722
Fix any links that were done incorrectly and reference the output instead of the source content.
721723
"""
722724
if info["all_in_one"]:
723-
content = fix_links(content, info["src_dir"], src_file, info)
725+
content = _fix_links(content, info["src_dir"], src_file, info)
724726
else:
725727
# Determine if the tag should be passed when fixing the links. If it's in the same book, then process the entire file. If it's
726728
# outside the book then don't process it.
@@ -733,11 +735,27 @@ def fix_links(content, info, book_src_dir, src_file, tag=None, cwd=None):
733735

734736
return content
735737

738+
def dir_to_book_name(dir,src_file,info):
739+
# find a book name by the directory
740+
for book in info["book_nodes"]:
741+
if book["Dir"] == dir:
742+
return(book["Name"])
743+
break
744+
745+
has_errors = True
746+
log.error(
747+
'ERROR (%s): book not found for the directory %s',
748+
src_file,
749+
dir)
750+
return(dir)
751+
736752

737753
def _fix_links(content, book_dir, src_file, info, tag=None, cwd=None):
738754
"""
739755
Fix any links that were done incorrectly and reference the output instead of the source content.
740756
"""
757+
current_book_name = dir_to_book_name(os.path.relpath(book_dir,info["src_dir"]),src_file,info)
758+
741759
# TODO Deal with xref so that they keep the proper path. Atm it'll just strip the path and leave only the id
742760
file_to_id_map = info["file_to_id_map"]
743761
current_dir = cwd or os.path.dirname(src_file)
@@ -750,39 +768,56 @@ def _fix_links(content, book_dir, src_file, info, tag=None, cwd=None):
750768
link_anchor = link.group(2)
751769
link_title = link.group(3)
752770

771+
# sanity check - is this a link to an external site?
772+
# apparently the link macro CAN be used for internal links too, so just testing for http
773+
# NOTE: a docs.openshift.com link would not process here corectly, anyway, so let it pass through
774+
if ("http:" in link_text) or ("https:" in link_text):
775+
continue
776+
777+
fixed_link = "" # setting the scope of fixed_link outside the if statements
778+
753779
if link_file is not None:
754780
fixed_link_file = link_file.replace(".html", ".adoc")
755781
fixed_link_file_abs = os.path.abspath(
756782
os.path.join(current_dir, fixed_link_file)
757783
)
758784
if fixed_link_file_abs in file_to_id_map:
759785

760-
# We are dealing with a cross reference to another book here
761-
external_link = EXTERNAL_LINK_RE.search(link_file)
762-
book_dir_name = external_link.group(1)
786+
# We are dealing with a cross reference to a book here
787+
full_relative_path = os.path.relpath(fixed_link_file_abs,info["src_dir"])
788+
789+
if full_relative_path[:2]=="..":
790+
log.error(
791+
'ERROR (%s): link pointing outside source directory? %s',
792+
src_file,
793+
link_file)
794+
continue
795+
split_relative_path = full_relative_path.split("/")
796+
book_dir_name = split_relative_path[0]
797+
if book_dir_name in info["huge_book_dirs"]:
798+
book_dir_name = split_relative_path[0]+"/"+split_relative_path[1]
763799

764800
# Find the book name
765-
book_name = book_dir_name
766-
for book in info["data"]:
767-
if (
768-
check_node_distro_matches(book, info["distro"])
769-
and book["Dir"] == book_dir_name
770-
):
771-
book_name = book["Name"]
772-
break
773-
774-
fixed_link_file = BASE_PORTAL_URL + build_portal_url(info, book_name)
775-
776-
if link_anchor is None:
777-
fixed_link = (
778-
"link:"
779-
+ fixed_link_file
780-
+ "#"
781-
+ file_to_id_map[fixed_link_file_abs]
782-
+ link_title
783-
)
801+
book_name = dir_to_book_name(book_dir_name,src_file,info)
802+
803+
804+
if book_name==current_book_name:
805+
if link_anchor is None:
806+
fixed_link = "xref:" + file_to_id_map[fixed_link_file_abs] + link_title
807+
else:
808+
fixed_link = "xref:" + link_anchor.replace("#", "") + link_title
784809
else:
785-
fixed_link = "link:" + fixed_link_file + link_anchor + link_title
810+
fixed_link_file = BASE_PORTAL_URL + build_portal_url(info, book_name)
811+
if link_anchor is None:
812+
fixed_link = (
813+
"link:"
814+
+ fixed_link_file
815+
+ "#"
816+
+ file_to_id_map[fixed_link_file_abs]
817+
+ link_title
818+
)
819+
else:
820+
fixed_link = "link:" + fixed_link_file + link_anchor + link_title
786821
else:
787822
# Cross reference or link that isn't in the docs suite
788823
fixed_link = link_text
@@ -1132,6 +1167,7 @@ def main():
11321167
"all_in_one": args.all_in_one,
11331168
"preface-title": "",
11341169
"upstream_branch": args.upstream_branch,
1170+
"huge_book_dirs": []
11351171
}
11361172

11371173
# Build the master files

cicd/hugeBook.flag

Whitespace-only changes.

makeBuild.py

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,46 @@ def _fix_ids_for_html4(tree):
7575
if old_endterm.get("endterm") == id_val:
7676
old_endterm.set('endterm', new_id)
7777

78+
def build_book(book):
79+
validated = True
80+
starting_dir=os.getcwd()
81+
os.chdir(os.path.join("drupal-build", distro, book))
82+
#print(os.getcwd() + "\n")
83+
84+
# Create the transformer instance
85+
transformer = AsciiDocPublicanTransformer()
86+
87+
try:
88+
# Transform the AsciiDoc to DocBook XML
89+
print((">>> Working on " + book + " book in " + distro + " <<<"))
90+
if not transformer._build_docbook_src("master.adoc", "build"):
91+
logging.error(">>> Validation of book " + book + " in " + distro + " failed: master.adoc not found <<<")
92+
return False
93+
94+
# Parse the transformed XML
95+
transformer._before_xml_parse("build/master.xml")
96+
97+
# Parse the XML content
98+
tree = utils.parse_xml("build/master.xml")
99+
100+
# Apply XML updates from aura/ccutil
101+
transformer._fix_uncoverted_xrefs_with_file_paths(tree)
102+
_fix_ids_for_html4(tree)
103+
104+
# Validate the transformed XML
105+
if not transformer._validate_docbook_idrefs(tree):
106+
logging.error(">>> Validation of book " + book + " in " + distro + " failed <<<")
107+
validated = False
108+
except (XMLSyntaxError, XIncludeError, InvalidInputException) as e:
109+
logging.error(e)
110+
validated = False
111+
finally:
112+
print((">>> Finished with " + book + " book in " + distro + " <<<"))
113+
print("---------------------------------------")
114+
os.chdir(starting_dir)
115+
return validated
116+
117+
78118

79119
# all validated?
80120
all_validated = True
@@ -91,53 +131,19 @@ def _fix_ids_for_html4(tree):
91131
for book in os.listdir(os.path.join("drupal-build", distro)):
92132

93133
#print(os.getcwd() + "\n")
94-
#if not os.path.isdir("drupal-build/" + distro + "/" + book):
95-
#print("---------------------------------------")
96-
#print(">>> No Book " + book + " in this repo. Skipping <<<")
97-
#print("---------------------------------------")
98-
99-
#continue
100-
134+
# skip any non-directory entries
135+
if not os.path.isdir("drupal-build/" + distro + "/" + book):
136+
continue
101137
# rest api book is a pain and doesn't convert well
102138
if book == "rest_api":
103-
continue
104-
105-
os.chdir("drupal-build/" + distro + "/" + book)
106-
#print(os.getcwd() + "\n")
107-
108-
# Create the transformer instance
109-
transformer = AsciiDocPublicanTransformer()
110-
111-
try:
112-
# Transform the AsciiDoc to DocBook XML
113-
print((">>> Working on " + book + " book in " + distro + " <<<"))
114-
if not transformer._build_docbook_src("master.adoc", "build"):
115-
print(("Could not transform book " + book))
116-
all_validated = False
117-
continue
118-
119-
# Parse the transformed XML
120-
transformer._before_xml_parse("build/master.xml")
121-
122-
# Parse the XML content
123-
tree = utils.parse_xml("build/master.xml")
124-
125-
# Apply XML updates from aura/ccutil
126-
transformer._fix_uncoverted_xrefs_with_file_paths(tree)
127-
_fix_ids_for_html4(tree)
128-
129-
# Validate the transformed XML
130-
if not transformer._validate_docbook_idrefs(tree):
131-
logging.error(">>> Validation of book " + book + " in " + distro + " failed <<<")
132-
all_validated = False
133-
except (XMLSyntaxError, XIncludeError, InvalidInputException) as e:
134-
logging.error(e)
135-
all_validated = False
136-
finally:
137-
print((">>> Finished with " + book + " book in " + distro + " <<<"))
138-
print("---------------------------------------")
139-
os.chdir("../../../")
139+
continue
140140

141+
if os.path.exists(os.path.join("drupal-build", distro, book,"hugeBook.flag")):
142+
for secondary_book in os.listdir(os.path.join("drupal-build", distro, book)):
143+
if os.path.isdir("drupal-build/" + distro + "/" + book + "/" + secondary_book):
144+
all_validated = all_validated and build_book(book+"/"+secondary_book)
145+
else:
146+
all_validated = all_validated and build_book(book)
141147
if not all_validated:
142148
sys.exit(-1)
143149
else:

0 commit comments

Comments
 (0)