-
-
Notifications
You must be signed in to change notification settings - Fork 23
/
f-dotfiles.py
executable file
·124 lines (101 loc) · 3.65 KB
/
f-dotfiles.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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import re
import sys
import subprocess
import textwrap
from glob import glob
"""Tool to automatically update sub-directories listings of stow packages.
"""
COMMENT = "<!--- Tree block injection -->"
def usage():
print(
"""
SYNOPSIS
f-dotfiles.py
DESCRIPTION
Parse content of README.md files in the repository packages and edit them
in-place.
Search for the sentinel "{}" and if found
inject package tree view just after it.
Files beginning with a comment (like "# this is a comment"), will have the
comment appended next to their filename in the generated listing.
""".format(
COMMENT
)
)
def make_tree(path, full=False):
"""Generate tree for path"""
cmd = 'tree {full} -I "README.md|.stow-local-ignore|.gitignore" {path} -a | tail -n +2'.format(
path=os.path.basename(path), full="-f" if full else ""
)
s = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
tree = s.stdout.read().decode("utf-8")
# Remove files count line at the end
return tree.split("\n\n")[0] + "\n\n"
def format_tree(text):
"""Format tree for inclusion in markdown text"""
return """{}
{}
""".format(
COMMENT, textwrap.indent(text, " " * 4).strip()
)
def extract_descriptions(root):
"""Extract descriptions in scripts headers"""
desc = {}
header_size = 10
for local_root, dirnames, filenames in os.walk(root):
for filename in filenames:
abs_filename = os.path.join(local_root, filename)
try:
with open(abs_filename) as myfile:
try:
head = [next(myfile) for x in range(header_size)]
except StopIteration:
myfile.seek(0)
head = myfile.readlines()
pattern = re.compile(r"# {}: (.+)".format(filename))
hit = pattern.search("".join(head))
if hit:
desc[
os.path.join(
os.path.basename(root), os.path.relpath(abs_filename, root)
)
] = hit.group(1)
except UnicodeDecodeError:
pass # binary file
return desc
def make_tree_doc(root):
"""Append file description for each text file of tree"""
res = []
desc = extract_descriptions(root)
tree = make_tree(root)
tree_full = make_tree(root, full=True)
for (branch_full, branch) in zip(tree_full.split("\n"), tree.split("\n")):
if branch_full:
filename = branch_full.split()[-1].strip()
if filename in desc:
branch += " \t# " + desc[filename]
res.append(branch)
return "\n".join(res)
def make_doc(description=False, sentinel=COMMENT):
"""For each package, insert the directories listing into the README.md
by updating the block introduced by sentinel
"""
for readme_path in [os.path.abspath(x) for x in glob("*/README.md")]:
with open(readme_path, "r+") as readme_file:
readme = readme_file.read()
if sentinel in readme:
root = os.path.dirname(readme_path)
tree = make_tree_doc(root)
pattern = r"{}.*?\n(\w+)?\n".format(sentinel)
text = re.sub(pattern, format_tree(tree), readme, flags=re.DOTALL)
readme_file.seek(0)
readme_file.truncate()
readme_file.write(text)
if __name__ == "__main__":
if "-h" in sys.argv or "--help" in sys.argv:
usage()
exit()
make_doc(description=True)