forked from kubernetes/website
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate-imported-docs.py
executable file
·279 lines (233 loc) · 9.63 KB
/
update-imported-docs.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#!/usr/bin/env python3
##
# This script was tested with Python 3.7.4, Go 1.14.4+, and PyYAML 5.1.2
# installed in a virtual environment.
# This script assumes you have the Python package manager 'pip' installed.
#
# This script updates the generated reference documentation.
# See https://kubernetes.io/docs/contribute/generate-ref-docs/kubernetes-components/
# for further details.
#
# This script checks to make sure Go and PyYAML have been installed.
# The reference docs are generated by a Go command so the Go binary must be
# in your PATH.
#
# A temp "work_dir" is created and is the path where repos will be cloned.
# The work_dir is printed out so you can remove it
# when you no longer need the contents.
# This work_dir will temporarily become the GOPATH.
#
# To execute the script from the website/update-imported-docs directory:
# ./update-imported-docs.py <config_file> <k8s_release>
# Config files:
# reference.yml use this to update the reference docs
# release.yml use this to auto-generate/import release notes
# K8S_RELEASE: provide a valid release tag such as, 1.17.0
##
import argparse
import glob
import os
import re
import shutil
import subprocess
import sys
import tempfile
import platform
error_msgs = []
# pip should be installed when Python is installed, but just in case...
if not (shutil.which('pip') or shutil.which('pip3')):
error_msgs.append(
"Install pip so you can install PyYAML. https://pip.pypa.io/en/stable/installing")
reqs = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'])
installed_packages = [r.decode().split('==')[0] for r in reqs.split()]
if 'PyYAML' not in installed_packages:
error_msgs.append(
"Please ensure the PyYAML package is installed; see https://pypi.org/project/PyYAML")
else:
import yaml
if not shutil.which('go'):
error_msgs.append(
"Go must be installed. See https://golang.org/doc/install")
def process_links(content, remote_prefix, sub_path):
"""Process markdown links found in the docs."""
def analyze(match_obj):
ankor = match_obj.group('ankor')
target = match_obj.group('target')
if not (target.startswith("https://") or
target.startswith("mailto:") or
target.startswith("#")):
if target.startswith("/"):
target_list = remote_prefix, target[1:]
target = "/".join(target_list)
else:
target_list = remote_prefix, sub_path, target
target = "/".join(target_list)
return "[%s](%s)" % (ankor, target)
# Links are in the form '[text](url)'
link_regex = re.compile(r"\[(?P<ankor>.*)\]\((?P<target>.*)\)")
content = re.sub(link_regex, analyze, content)
h1_regex = re.compile("^(# .*)?\n")
content = re.sub(h1_regex, "", content)
return content
def process_kubectl_links(content):
"""Update markdown links found in the SeeAlso section of kubectl page.
Example:[kubectl annotate](/docs/reference/generated/kubectl/kubectl-commands#annotate)
"""
def analyze(match_obj):
ankor = match_obj.group('ankor')
target = match_obj.group('target')
if target.endswith(".md") and target.startswith("kubectl"):
ankor_list = ankor.split("kubectl ")
target = "/docs/reference/generated/kubectl/kubectl-commands" + "#" + \
ankor_list[1]
return "[%s](%s)" % (ankor, target)
# Links are in the form '[text](url)'
link_regex = re.compile(r"\[(?P<ankor>.*)\]\((?P<target>.*?)\)")
content = re.sub(link_regex, analyze, content)
return content
def process_file(src, dst, repo_path, repo_dir, root_dir, gen_absolute_links):
"""Process a file element.
:param src: A string containing the relative path of a source file. The
string may contain wildcard characters such as '*' or '?'.
:param dst: The path for the destination file. The string can be a
directory name or a file name.
:param repo_path:
:param repo_dir:
:param root_dir:
:param gen_absolute_links:
"""
pattern = os.path.join(repo_dir, repo_path, src)
dst_path = os.path.join(root_dir, dst)
for src in glob.glob(pattern):
# we don't dive into subdirectories
if not os.path.isfile(src):
print("[Error] skipping non-regular path {}".format(src))
continue
content = ""
try:
with open(src, "r") as srcFile:
content = srcFile.read()
except Exception as ex:
print("[Error] failed in reading source file: ".format(ex))
continue
dst = dst_path
if dst_path.endswith("/"):
base_name = os.path.basename(src)
dst = os.path.join(dst, base_name)
try:
print("Writing doc: " + dst)
with open(dst, "w") as dstFile:
if gen_absolute_links:
src_dir = os.path.dirname(src)
remote_prefix = repo_path + "/tree/master"
content = process_links(content, remote_prefix, src_dir)
if dst.endswith("kubectl.md"):
print("Processing kubectl links")
content = process_kubectl_links(content)
dstFile.write(content)
except Exception as ex:
print("[Error] failed in writing target file {}: {}".format(dst, ex))
continue
def parse_input_args():
"""
Parse command line argument
'config_file' is the first argument; it should be one of the YAML
files in this same directory
'k8s_release' is the second argument; provide the release version
:return: parsed argument
"""
parser = argparse.ArgumentParser()
parser.add_argument('config_file', type=str,
help="reference.yml to generate reference docs; "
"release.yml to generate release notes")
parser.add_argument('k8s_release', type=str,
help="k8s release version, ex: 1.17.0"
)
return parser.parse_args()
def main():
"""The main entry of the program."""
if len(error_msgs) > 0:
for msg in error_msgs:
print(msg + "\n")
return -2
# first parse input argument
in_args = parse_input_args()
config_file = in_args.config_file
print("config_file is {}".format(config_file))
# second parse input argument
k8s_release = in_args.k8s_release
print("k8s_release is {}".format(k8s_release))
# if release string does not contain patch num, add zero
if len(k8s_release) == 4:
k8s_release = k8s_release + ".0"
print("k8s_release updated to {}".format(k8s_release))
curr_dir = os.path.dirname(os.path.abspath(__file__))
print("curr_dir {}".format(curr_dir))
root_dir = os.path.realpath(os.path.join(curr_dir, '..'))
print("root_dir {}".format(root_dir))
try:
config_data = yaml.full_load(open(config_file, 'r'))
except Exception as ex:
# to catch when a user specifies a file that does not exist
print("[Error] failed in loading config file - {}".format(str(ex)))
return -2
os.chdir(root_dir)
# create the temp work_dir
try:
print("Making temp work_dir")
work_dir = tempfile.mkdtemp(
dir='/tmp' if platform.system() == 'Darwin' else tempfile.gettempdir()
)
except OSError as ose:
print("[Error] Unable to create temp work_dir {}; error: {}"
.format(work_dir, ose))
return -2
print("Working dir {}".format(work_dir))
for repo in config_data["repos"]:
if "name" not in repo:
print("[Error] repo missing name")
continue
repo_name = repo["name"]
if "remote" not in repo:
print("[Error] repo {} missing repo path".format(repo_name))
continue
repo_remote = repo["remote"]
remote_regex = re.compile(r"^https://(?P<prefix>.*)\.git$")
matches = remote_regex.search(repo_remote)
if not matches:
print("[Error] repo path for {} is invalid".format(repo_name))
continue
repo_path = os.path.join("src", matches.group('prefix'))
os.chdir(work_dir)
print("Cloning repo {}".format(repo_name))
cmd = "git clone --depth=1 -b {0} {1} {2}".format(
repo["branch"], repo_remote, repo_path)
res = subprocess.call(cmd, shell=True)
if res != 0:
print("[Error] failed in cloning repo {}".format(repo_name))
continue
os.chdir(repo_path)
if "generate-command" in repo:
gen_cmd = repo["generate-command"]
gen_cmd = "export K8S_RELEASE=" + k8s_release + "\n" + \
"export GOPATH=" + work_dir + "\n" + \
"export K8S_ROOT=" + work_dir + \
"/src/k8s.io/kubernetes" + "\n" + \
"export K8S_WEBROOT=" + root_dir + "\n" + gen_cmd
print("Generating docs for {} with {}".format(repo_name, gen_cmd))
res = subprocess.call(gen_cmd, shell=True)
if res != 0:
print("[Error] failed in generating docs for {}".format(
repo_name))
continue
os.chdir(root_dir)
for f in repo["files"]:
process_file(f['src'], f['dst'], repo_path, work_dir, root_dir,
"gen-absolute-links" in repo)
print("Completed docs update. Now run the following command to commit:\n\n"
" git add .\n"
" git commit -m <comment>\n"
" git push\n"
" delete temp dir {} when done ".format(work_dir))
if __name__ == '__main__':
sys.exit(main())