Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: sitescripts/docs/bin/generate_docs.py

Issue 29335805: Issue 1299 - Generate docs outside the devbuild build process (Closed)
Left Patch Set: Created Feb. 5, 2016, 5:09 p.m.
Right Patch Set: Separate pull and update, specify revision to include Created Feb. 9, 2016, 12:39 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « sitescripts/docs/bin/__init__.py ('k') | sitescripts/extensions/bin/createNightlies.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # coding: utf-8
Sebastian Noack 2016/02/05 18:02:32 Not that a coding declaration is necessary here. M
Felix Dahlke 2016/02/05 20:47:58 Wow true, either that part changed or we misunders
3 2
4 # This file is part of Adblock Plus <https://adblockplus.org/>, 3 # This file is part of Adblock Plus <https://adblockplus.org/>,
5 # Copyright (C) 2006-2016 Eyeo GmbH 4 # Copyright (C) 2006-2016 Eyeo GmbH
6 # 5 #
7 # Adblock Plus is free software: you can redistribute it and/or modify 6 # Adblock Plus is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License version 3 as 7 # it under the terms of the GNU General Public License version 3 as
9 # published by the Free Software Foundation. 8 # published by the Free Software Foundation.
10 # 9 #
11 # Adblock Plus is distributed in the hope that it will be useful, 10 # Adblock Plus is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details. 13 # GNU General Public License for more details.
15 # 14 #
16 # You should have received a copy of the GNU General Public License 15 # You should have received a copy of the GNU General Public License
17 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 16 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
18 17
19 import os 18 import os
20 import shutil 19 import shutil
21 import subprocess 20 import subprocess
22 21
23 from sitescripts.utils import get_config 22 from sitescripts.utils import get_config
24 23
25 config = dict(get_config().items("docs")) 24 def read_projects(config):
Sebastian Noack 2016/02/05 18:02:31 I'd prefer if you use the ConfigParser API consist
Felix Dahlke 2016/02/05 20:47:57 Done.
26
27 def read_projects():
28 projects = {} 25 projects = {}
29 for key, value in config.iteritems(): 26 for key, value in config.items("docs"):
30 key_parts = key.split("_", 1) 27 key_parts = key.split("_", 1)
31 if len(key_parts) < 2: 28 if len(key_parts) < 2:
32 continue 29 continue
33 project_name, field_name = key_parts 30 project_name, field_name = key_parts
34 if field_name not in ["repository", "target_directory", "command"]: 31 if field_name not in {"repository", "target_directory", "command"}:
Sebastian Noack 2016/02/05 18:02:32 Nit: A set seems more appropriate here. In case yo
Felix Dahlke 2016/02/05 20:47:58 No I didn't, nice. Done.
35 continue 32 continue
36 projects.setdefault(project_name, {}) 33 projects.setdefault(project_name, {})[field_name] = value
Sebastian Noack 2016/02/05 18:02:31 There is a reason that setdefault() returns the re
Felix Dahlke 2016/02/05 20:47:58 Done.
37 projects[project_name][field_name] = value
38 return projects 34 return projects
39 35
40 def sync_sources(sources_dir, repository_url): 36 def sync_sources(sources_dir, repository_url):
41 remote_id = subprocess.check_output(["hg", "id", "-i", repository_url])
Sebastian Noack 2016/02/05 18:02:32 Nit: Please use long option names in scripts, that
Felix Dahlke 2016/02/05 20:47:58 Done.
42 id_path = sources_dir.rstrip("/") + ".id"
Sebastian Noack 2016/02/05 18:02:31 Please don't hard-code file separators, use.ospath
Felix Dahlke 2016/02/05 20:47:58 Done.
43 if os.path.exists(id_path):
Sebastian Noack 2016/02/05 18:02:31 It's better to handle the corresponding IOError, r
Felix Dahlke 2016/02/05 20:47:57 Fair enough, I've gotten rid of the os.path.exists
44 with open(id_path, "r") as id_file:
Sebastian Noack 2016/02/05 18:02:32 Please always add "b" to the file mode, for consis
Felix Dahlke 2016/02/05 20:47:58 Done.
45 local_id = id_file.read()
46 if local_id == remote_id:
47 return
48 if os.path.exists(sources_dir): 37 if os.path.exists(sources_dir):
49 shutil.rmtree(sources_dir) 38 subprocess.check_call(["hg", "pull", "--quiet",
50 subprocess.check_call(["hg", "archive", "-R", repository_url, "-r", "master", 39 "--rev", "master",
51 sources_dir]) 40 "--repository", sources_dir])
52 41 subprocess.check_call(["hg", "update", "--quiet",
53 # In theory, it is possible that some changesets are pushed after we fetch 42 "--rev", "master"])
54 # the ID above, but before we run `hg archive`, which will lead to an 43 else:
55 # unnecessary `hg archive` operation the next time this runs. 44 subprocess.check_call(["hg", "clone", "--quiet",
56 with open(id_path, "w") as id_file: 45 "--updaterev", "master",
57 id_file.write(remote_id) 46 repository_url, sources_dir])
58 47
59 def replace_dir(source_dir, target_dir): 48 def replace_dir(source_dir, target_dir):
60 if not os.path.exists(target_dir): 49 if not os.path.exists(target_dir):
61 parent_dir = os.path.dirname(target_dir) 50 parent_dir = os.path.dirname(target_dir)
62 if not os.path.exists(parent_dir): 51 try:
63 os.makedirs(parent_dir) 52 os.makedirs(parent_dir)
53 except OSError:
54 pass
64 os.rename(source_dir, target_dir) 55 os.rename(source_dir, target_dir)
65 else: 56 else:
66 old_target_dir = target_dir.rstrip("/") + ".old" 57 old_target_dir = target_dir.rstrip(os.path.sep) + ".old"
67 if os.path.exists(old_target_dir): 58 shutil.rmtree(old_target_dir, ignore_errors=True)
68 shutil.rmtree(old_target_dir)
69 os.rename(target_dir, old_target_dir) 59 os.rename(target_dir, old_target_dir)
70 os.rename(source_dir, target_dir) 60 os.rename(source_dir, target_dir)
71 shutil.rmtree(old_target_dir) 61 shutil.rmtree(old_target_dir)
72 62
73 def run_generation_command(command, sources_dir, output_dir): 63 def run_generation_command(command, sources_dir, output_dir):
74 if os.path.exists(output_dir): 64 shutil.rmtree(output_dir, ignore_errors=True)
75 shutil.rmtree(output_dir)
76 command = command.format(output_dir=output_dir) 65 command = command.format(output_dir=output_dir)
77 subprocess.check_call(command, shell=True, cwd=sources_dir) 66 subprocess.check_call(command, shell=True, cwd=sources_dir)
78 67
79 def generate_docs(projects): 68 def generate_docs(projects, config):
80 temp_directory = config["temp_directory"] 69 temp_directory = config.get("docs", "temp_directory")
81 if not os.path.exists(temp_directory): 70 try:
82 os.makedirs(temp_directory) 71 os.makedirs(temp_directory)
72 except OSError:
73 pass
83 74
84 for name, data in projects.iteritems(): 75 for name, data in projects.iteritems():
85 sources_dir = os.path.join(temp_directory, name) 76 sources_dir = os.path.join(temp_directory, name)
86 sync_sources(sources_dir, data["repository"]) 77 sync_sources(sources_dir, data["repository"])
87 output_dir = sources_dir.rstrip("/") + ".docs" 78 output_dir = sources_dir.rstrip(os.path.sep) + ".docs"
88 run_generation_command(data["command"], sources_dir, output_dir) 79 run_generation_command(data["command"], sources_dir, output_dir)
89 replace_dir(output_dir, data["target_directory"]) 80 replace_dir(output_dir, data["target_directory"])
90 81
91 if __name__ == "__main__": 82 if __name__ == "__main__":
92 projects = read_projects() 83 config = get_config()
93 generate_docs(projects) 84 projects = read_projects(config)
85 generate_docs(projects, config)
LEFTRIGHT

Powered by Google App Engine
This is Rietveld