fix permissions with some over permissive chmoding

master
vilmibm 2017-10-15 00:25:57 +00:00
parent c8f2c3b72f
commit e87547b5f6
2 changed files with 39 additions and 18 deletions

View File

@ -1,7 +1,7 @@
import os import os
import re import re
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional, Callable
from markdown import markdown from markdown import markdown
@ -10,6 +10,8 @@ HEADER_TITLE_RE = re.compile(r'<h([12])>(.*?)</h\1>')
TITLE_RE = re.compile(r'<title>.*?</title>') TITLE_RE = re.compile(r'<title>.*?</title>')
LINK_RE = re.compile(r'href="\/wiki') LINK_RE = re.compile(r'href="\/wiki')
DEFAULT_ON_CREATE = lambda _: None
def relativize_links(content:str, depth:int) -> str: def relativize_links(content:str, depth:int) -> str:
"""Given compiled html content, change URLs that start in "/wiki" to be """Given compiled html content, change URLs that start in "/wiki" to be
relative instead of absolute. Depth indicates how many pairs of dots we relative instead of absolute. Depth indicates how many pairs of dots we
@ -18,7 +20,9 @@ def relativize_links(content:str, depth:int) -> str:
repl = 'href="{}'.format(os.path.join(dots, 'wiki')) repl = 'href="{}'.format(os.path.join(dots, 'wiki'))
return re.sub(LINK_RE, repl, content) return re.sub(LINK_RE, repl, content)
def compile_wiki(source_path: str, dest_path: str) -> None: def compile_wiki(source_path: str,
dest_path: str,
on_create: Callable[[str], None]=DEFAULT_ON_CREATE) -> None:
"""Given a source path (presumably a git repository) and a destination """Given a source path (presumably a git repository) and a destination
path, compiles the files found in {source_path}/articles and compiles them all path, compiles the files found in {source_path}/articles and compiles them all
to {dest_path}/. to {dest_path}/.
@ -26,6 +30,9 @@ def compile_wiki(source_path: str, dest_path: str) -> None:
THIS FUNCTION CLEARS {dest_path}/! THIS FUNCTION CLEARS {dest_path}/!
Be absolutely sure you know what you are doing when you call this ^_^ Be absolutely sure you know what you are doing when you call this ^_^
If passed, on_create will be called per directory and file created by the
compiler. The default is to take no action.
""" """
last_compiled = '<p><em>last compiled: {}</em></p>'.format(datetime.utcnow()) last_compiled = '<p><em>last compiled: {}</em></p>'.format(datetime.utcnow())
@ -46,7 +53,9 @@ def compile_wiki(source_path: str, dest_path: str) -> None:
dest_root = os.path.join(dest_path, current_suffix) dest_root = os.path.join(dest_path, current_suffix)
for directory in dirs: for directory in dirs:
os.mkdir(os.path.join(dest_root, directory)) dir_path = os.path.join(dest_root, directory)
os.mkdir(dir_path)
on_create(dir_path)
for source_filename in files: for source_filename in files:
if source_filename.startswith('.'): if source_filename.startswith('.'):
@ -61,13 +70,18 @@ def compile_wiki(source_path: str, dest_path: str) -> None:
toc_content += '<li><a href="{}">{}</a></li>\n'.format( toc_content += '<li><a href="{}">{}</a></li>\n'.format(
os.path.join(current_suffix, dest_filename), os.path.join(current_suffix, dest_filename),
os.path.join(current_suffix,dest_filename.split('.')[0])) os.path.join(current_suffix,dest_filename.split('.')[0]))
with open(os.path.join(dest_root, dest_filename), 'w') as f: final_path = os.path.join(dest_root, dest_filename)
with open(final_path, 'w') as f:
f.write(output) f.write(output)
on_create(final_path)
toc_content += '\n</ul>' toc_content += '\n</ul>'
with open(os.path.join(dest_path, 'toc.html'), 'w') as f: toc_path = os.path.join(dest_path, 'toc.html')
with open(toc_path, 'w') as f:
f.write(toc_content) f.write(toc_content)
f.write(footer_content) f.write(footer_content)
on_create(toc_path)
def slurp(file_path:str) -> str: def slurp(file_path:str) -> str:
"""Convenience function for reading a file and returning its contents.""" """Convenience function for reading a file and returning its contents."""
@ -87,7 +101,7 @@ def compile_source_file(source_file_path:str, header_content:str, footer_content
raise ValueError( raise ValueError(
'{} is not an absolute path.'.format(source_file_path)) '{} is not an absolute path.'.format(source_file_path))
compiler = None # pick a compiler
if source_file_path.endswith('.md'): if source_file_path.endswith('.md'):
compiler = compile_markdown compiler = compile_markdown
elif source_file_path.endswith('.txt'): elif source_file_path.endswith('.txt'):
@ -120,17 +134,17 @@ def extract_title(content:str) -> Optional[str]:
return matches.groups()[1] return matches.groups()[1]
return None return None
def compile_markdown(source_file_path:str) -> str: def compile_markdown(file_path:str) -> str:
"""Given a string of markdown, compiles it and returns the result.""" """Given a string of markdown, compiles it and returns the result."""
return markdown( return markdown(
slurp(source_file_path), slurp(file_path),
output_format='html5') output_format='html5')
def compile_plaintext(source_file_path:str) -> str: def compile_plaintext(file_path:str) -> str:
output = '<p>\n' output = '<p>\n'
output += re.sub( output += re.sub(
DOUBLE_NEWLINE_RE, DOUBLE_NEWLINE_RE,
'</p><p>', '</p><p>',
slurp(source_file_path)) slurp(file_path))
output += '\n</p>\n' output += '\n</p>\n'
return output return output

View File

@ -1,7 +1,7 @@
import os import os
import re import re
import stat
from os.path import expanduser from os.path import expanduser
import subprocess
import click import click
from click import ClickException, Abort from click import ClickException, Abort
@ -119,6 +119,19 @@ def preview(config, preview_path, local_repo_path):
clear_directory(preview_path) clear_directory(preview_path)
_preview(preview_path, local_repo_path) _preview(preview_path, local_repo_path)
def _on_create(file_path: str) -> None:
"""This callback takes a path to a file or directory created on disk
during compilation. We want to make sure that everything we create as part
of publish compilation is world-writable so the next user can overwrite
it."""
flags = stat.S_ISGID
flags |= stat.S_IWOTH | stat.S_IROTH
flags |= stat.S_IRUSR | stat.S_IWUSR
flags |= stat.S_IRGRP | stat.S_IWGRP
if os.path.isdir(file_path):
flags |= stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR
os.chmod(file_path, flags)
@main.command() @main.command()
@click.option('--local-repo-path', default=LOCAL_REPOSITORY_PATH, @click.option('--local-repo-path', default=LOCAL_REPOSITORY_PATH,
help='Path to local clone of wiki repository.', type=WikiRepo(**DEFAULT_PATH_KWARGS)) help='Path to local clone of wiki repository.', type=WikiRepo(**DEFAULT_PATH_KWARGS))
@ -144,8 +157,7 @@ def publish(config, local_repo_path):
click.echo('Compiling wiki to {}'.format(config.publish_path)) click.echo('Compiling wiki to {}'.format(config.publish_path))
click.confirm(WIPE_PROMPT.format(config.publish_path), abort=True) click.confirm(WIPE_PROMPT.format(config.publish_path), abort=True)
clear_directory(config.publish_path) clear_directory(config.publish_path)
compile_wiki(config.repo_path, config.publish_path) compile_wiki(config.repo_path, config.publish_path, on_create=_on_create)
world_readablize(config.publish_path)
except ClickException: except ClickException:
raise raise
except Abort: except Abort:
@ -205,11 +217,6 @@ def _preview(preview_path, local_repo_path):
click.echo('Your wiki preview is ready! navigate to ~{}/wiki'.format( click.echo('Your wiki preview is ready! navigate to ~{}/wiki'.format(
os.environ.get('LOGNAME'))) os.environ.get('LOGNAME')))
def world_readablize(path: str) -> None:
"""Given a path to a directory, recursively make it world readable."""
# TODO the correct way to do this is with a wiki group
subprocess.run(['chmod', '-R', 'o+w', path], check=True)
def clear_directory(path:str) -> None: def clear_directory(path:str) -> None:
"""Given a path to a directory, deletes everything in it. Use with """Given a path to a directory, deletes everything in it. Use with
caution.""" caution."""