Skip to content

Commit 9989a45

Browse files
authored
Add version_archiver.py (#1366)
* Add release_archiver.py
1 parent 4e1787d commit 9989a45

File tree

2 files changed

+219
-28
lines changed

2 files changed

+219
-28
lines changed

build/version_archiver.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import os, shutil, re, argparse
2+
3+
4+
class VersionArchiver:
5+
def __init__(self, product, version):
6+
self.product = product
7+
self.new_version = version
8+
9+
if self.product in ("kubernetes", "rs"):
10+
self.prefix = "operate"
11+
else:
12+
self.prefix = "integrate"
13+
self.new_directory = os.path.join("content",self.prefix,self.product,self.new_version)
14+
self.latest = os.path.join("content",self.prefix,self.product)
15+
16+
def archive_version(self):
17+
"""Copy all files from latest in new versioned directory, excluding release-notes"""
18+
19+
# Walk through the latest directory
20+
for root, dirs, files in os.walk(self.latest):
21+
# Exclude directories with numbers in their names (other releases) and 'release-notes' directory
22+
dirs[:] = [
23+
d
24+
for d in dirs
25+
if not any(char.isdigit() for char in d) and d != "release-notes"
26+
]
27+
28+
# Create the corresponding destination directory if it does not exist
29+
relative_path = os.path.relpath(root, self.latest)
30+
dest_dir = os.path.join(self.new_directory, relative_path)
31+
if not os.path.exists(dest_dir):
32+
os.makedirs(dest_dir)
33+
34+
for file in files:
35+
src_file = os.path.join(root, file)
36+
dest_file = os.path.join(dest_dir, file)
37+
shutil.copy2(src_file, dest_file)
38+
39+
def update_relrefs(self, file_path, version, product):
40+
"""Helper function for updating relrefs"""
41+
42+
# Define a pattern to match relref links dynamically using product
43+
pattern = (
44+
r'(\(\{\{< ?relref "/'
45+
+ self.prefix
46+
+ "/"
47+
+ re.escape(product)
48+
+ r'/([^"]+)" ?>\}\})'
49+
)
50+
with open(file_path, "r") as file:
51+
lines = file.readlines()
52+
53+
modified = False
54+
55+
# Process each line in the file
56+
for i in range(len(lines)):
57+
# Search for relref links
58+
def replace_link(match):
59+
full_match = match.group(0) # The entire match
60+
link = match.group(1) # The actual relref link
61+
62+
# Check if the link contains 'release-notes' and whether the replacement has already happened
63+
if (
64+
"release-notes" not in link
65+
and f"/{self.prefix}/{product}/{version}" not in link
66+
):
67+
# Otherwise, replace it
68+
new_link = link.replace(
69+
f"/{self.prefix}/{product}/",
70+
f"/{self.prefix}/{product}/{version}/",
71+
)
72+
return f"{new_link}"
73+
return full_match
74+
75+
# Replace all relref links in the line
76+
modified_line = re.sub(pattern, replace_link, lines[i])
77+
78+
# If the line was modified, update the lines list
79+
if modified_line != lines[i]:
80+
lines[i] = modified_line
81+
modified = True
82+
83+
# If any modification was made, write the updated content back to the file
84+
if modified:
85+
with open(file_path, "w") as file:
86+
file.writelines(lines)
87+
88+
def version_relrefs(self):
89+
"""Make all relrefs in a versioned directory versioned"""
90+
91+
# Walk through the new directory and process all .md files
92+
for root, _, files in os.walk(self.new_directory):
93+
for file_name in files:
94+
if file_name.endswith(".md"):
95+
file_path = os.path.join(root, file_name)
96+
self.update_relrefs(file_path, self.new_version, self.product)
97+
98+
def inject_url_frontmatter(self):
99+
"""Inject url frontmatter property"""
100+
101+
base_url = self.new_directory.strip("content").strip(f"/{self.new_version}")
102+
103+
# Walk through the new directory
104+
for root, dirs, files in os.walk(self.new_directory):
105+
for file in files:
106+
file_path = os.path.join(root, file)
107+
108+
# Read the file line by line
109+
with open(file_path, "r", encoding="utf-8") as f:
110+
lines = f.readlines()
111+
112+
# Find the positions of the first and second '---' markers
113+
first_emdash_index = -1
114+
second_emdash_index = -1
115+
116+
# Search for the frontmatter markers
117+
for i, line in enumerate(lines):
118+
if line.strip() == "---":
119+
if first_emdash_index == -1:
120+
first_emdash_index = i
121+
elif second_emdash_index == -1:
122+
second_emdash_index = i
123+
break
124+
125+
if first_emdash_index != -1 and second_emdash_index != -1:
126+
# Extract the frontmatter lines
127+
frontmatter_lines = lines[
128+
first_emdash_index + 1 : second_emdash_index
129+
]
130+
131+
# If the url property already exists, skip
132+
if any("url" in f for f in frontmatter_lines):
133+
break
134+
135+
if file_path == os.path.join(self.new_directory,"_index.md"):
136+
for idx, item in enumerate(frontmatter_lines):
137+
if "linkTitle" in item:
138+
frontmatter_lines[idx] = (
139+
f"linkTitle: {self.new_version}\n"
140+
)
141+
frontmatter_lines.append(
142+
f"bannerText: This documentation applies to version {self.new_version}.\n"
143+
)
144+
frontmatter_lines.append(f"bannerChildren: true\n")
145+
146+
# Add the 'url' field to the frontmatter
147+
relative_path = os.path.relpath(root, self.new_directory)
148+
if file == "_index.md":
149+
150+
if relative_path == ".":
151+
url = f"url: '/{base_url}/{self.new_version}/'"
152+
else:
153+
url = f"url: '/{base_url}/{self.new_version}/{relative_path}/'"
154+
155+
else:
156+
f = file.strip(".md")
157+
if relative_path == ".":
158+
url = f"url: '/{base_url}/{self.new_version}/{f}/'"
159+
else:
160+
url = f"url: '/{base_url}/{self.new_version}/{relative_path}/{f}/'"
161+
162+
# Add url at the end of the frontmatter
163+
frontmatter_lines.append(url + "\n")
164+
165+
# Rebuild the content with the updated frontmatter
166+
updated_lines = (
167+
lines[: first_emdash_index + 1]
168+
+ frontmatter_lines
169+
+ lines[second_emdash_index:]
170+
)
171+
172+
# Write the updated content back to the file
173+
with open(file_path, "w", encoding="utf-8") as f:
174+
f.writelines(updated_lines)
175+
else:
176+
# skip files without frontmatter
177+
pass
178+
179+
180+
def validate_product(value):
181+
"""Custom validator for product argument to allow only 'rs' or 'kubernetes'"""
182+
if value not in ["rs", "kubernetes", "redis-data-integration"]:
183+
raise argparse.ArgumentTypeError(
184+
"Product must be either 'rs' or 'kubernetes' or 'redis-data-integration'."
185+
)
186+
return value
187+
188+
189+
def validate_version(value):
190+
"""Custom validator for version argument to ensure it follows the version format (e.g., 7, 7.1, 7.1.11)"""
191+
version_pattern = r"^\d+(\.\d+){0,2}(\.\d+)?$"
192+
if not re.match(version_pattern, value):
193+
raise argparse.ArgumentTypeError(
194+
"Version must be in the format 'x', 'x.x', or 'x.x.x' (e.g., 7, 7.1, 7.1.11)."
195+
)
196+
return value
197+
198+
199+
if __name__ == "__main__":
200+
201+
parser = argparse.ArgumentParser(
202+
description="Archive documentation for a specific version of a Redis product."
203+
)
204+
parser.add_argument(
205+
"product",
206+
type=validate_product,
207+
help="The name of the product (e.g., rs, kubernetes, redis-data-integration)",
208+
)
209+
parser.add_argument(
210+
"version",
211+
type=validate_version,
212+
help="The release version (e.g., 7, 7.1, 7.1.11)",
213+
)
214+
args = parser.parse_args()
215+
216+
r = VersionArchiver(args.product, args.version)
217+
r.archive_version()
218+
r.version_relrefs()
219+
r.inject_url_frontmatter()

version_frontmatter_helper.bash

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)