Skip to content

Commit d4156e4

Browse files
committed
Add release_archiver.py
1 parent 18ac281 commit d4156e4

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

build/release_archiver.py

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

0 commit comments

Comments
 (0)