diff --git a/.gitignore b/.gitignore index cdf363a..d793de9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ #test result -tests/result* -tests/result* +result/* #vim session files *.vims diff --git a/ASCIIsite.py b/ASCIIsite.py index 65e6275..3277b64 100755 --- a/ASCIIsite.py +++ b/ASCIIsite.py @@ -1,17 +1,16 @@ #! /usr/bin/env python3 #takes directory, converts all .adoc files to html files, copying the resulting html files to an identical directory strucuture, and copies over all non .adoc files unchanged. Optionally outputs as a tar.gz file. -import subprocess, argparse, logging, tempfile, shutil, os, glob +import subprocess, sys, argparse, logging, tempfile, shutil, os, glob from pathlib import Path logging.basicConfig(format='%(asctime)s:%(message)s', level=logging.INFO) #logging.basicConfig(format='%(asctime)s:%(message)s', level=logging.DEBUG) -def parse_arguments()->tuple[Path, Path, Path | None, bool, list[str]]: +def parse_arguments(): parser=argparse.ArgumentParser(description='create a website directory structure by converting .adoc files in a directory strucutre to .html files.') parser.add_argument('inputDir', type=Path, help='The directory of adoc files to be copied and converted.') parser.add_argument('-o', '--output', type=Path, help='What to name the generated directory or tar file') - parser.add_argument('--stylesheet', type=Path, help='A custom CSS file to be applied to the output.') parser.add_argument('--exclude-file', type=Path, help='A text file containing glob patterns to exclude, 1 per line.') parser.add_argument('--exclude', nargs='+', help='A list of glob patterns to ignore. Remember to quote them so your shell doesnt escape them!') parser.add_argument('-z', '--compress', action='store_true', help='whether to compress the resulting directory to a tar.gz file. can be usefull for scripting to transfer the site to a remote server.') @@ -27,23 +26,23 @@ def parse_arguments()->tuple[Path, Path, Path | None, bool, list[str]]: if args.output != None and not args.compress: #detect based on whether outFile has a .tar.gz filename. if args.output.suffixes == ['.tar', '.gz']: - compress:bool=True + compress=True else: - compress:bool=False + compress=False else: - compress:bool=args.compress - + compress=args.compress + #If outfile was not set, set it. if args.output == None: - baseName:str=args.inputDir.with_name(args.inputDir.name+'_compiled').name - outFile:Path=Path(os.getcwd()).joinpath(baseName) + baseName=args.inputDir.with_name(args.inputDir.name+'_compiled').name + outFile=Path(os.getcwd()).joinpath(baseName) else: - outFile:Path=Path(args.output.resolve()) + outFile=args.output.resolve() #add .tar.gz if compress is set and the outfile does not already have it. if compress and outFile.suffixes != ['.tar', '.gz']: logging.info(f'outFile was {outFile}, corrected because compress flag is set.') - outFile:Path=outFile.with_suffix('.tar.gz').resolve() + outFile=outFile.with_suffix('.tar.gz').resolve() if args.inputDir.resolve() == outFile.resolve(): raise FileExistsError('output file cannot have the same path as the input file!') @@ -52,107 +51,78 @@ def parse_arguments()->tuple[Path, Path, Path | None, bool, list[str]]: logging.info(f'outputting to {outFile.resolve()}') logging.debug(f'compress is {compress}') - exclude:list[str]=[] - if args.exclude_file != None: + try: with open(args.exclude_file, 'r') as file: exclude=[glob.strip() for glob in file] - if args.exclude != None: - exclude.extend(args.exclude) + if args.exclude != None: + exclude.extend(args.exclude) + except Exception as e: + print(str(e)) + exit() if not args.inputDir.resolve().exists(): print(f'Inputdir {args.inputDir.resolve()} does not exist!') exit() - stylesheet:Path|None=None - if args.stylesheet != None: - stylesheet=args.stylesheet.resolve() - logging.info(f'using stylesheet {stylesheet}') - - return Path(args.inputDir.resolve()), outFile, stylesheet, compress, exclude + return args.inputDir.resolve(), outFile, compress, exclude #Doing it in a tmpDir first, as some distrubutions put temp files on a ramdisk. this should speed up the operation sigificantly. class TmpDir: - def __init__(self, srcDir:Path, exclude:list[str]): + def __init__(self, srcDir, exclude): self.tmpDir=tempfile.TemporaryDirectory() logging.debug(f'making tmp file from {srcDir} at {self.tmpDir.name}') - self.path:Path=Path(self.tmpDir.name+'/'+Path(srcDir).resolve().name) - self.ignorePatterns:list[str]=['*.adoc', '.gitignore', '.git/*'] + self.path=self.tmpDir.name+'/'+Path(srcDir).resolve().name + self.ignorePatterns=['*.adoc', '.gitignore', '.git/*'] self.ignorePatterns.extend(exclude) self.ignorePattern=shutil.ignore_patterns(*self.ignorePatterns) shutil.copytree(srcDir, self.path, ignore=self.ignorePattern, symlinks=False) #copy out from tmpDir (which may be in RAM, depending on distrubution) to disk - def copy_self_to(self, destPath:Path): + def copy_self_to(self, destPath): logging.debug(f'outputting to {Path(destPath).resolve()}') shutil.copytree(self.path, destPath, symlinks=False) #copy out from tmpDir (which may be in RAM, depending on distrubution) to a compressed file on disk - def compress_and_copy_self_to(self, destPath:Path)->Path: + def compress_and_copy_self_to(self, destPath): #shutil.make_archive wants destPath to be without file extentions for some godforsaken reason. destPath=Path(destPath.with_name(destPath.name.split('.')[0])).resolve() logging.debug(f'compressing to {Path(destPath).resolve()} from {Path(self.path).parent}') - tarFile:Path=Path(shutil.make_archive(str(destPath), 'gztar', Path(self.path).parent)) - return tarFile + tarFile=shutil.make_archive(destPath, 'gztar', Path(self.path).parent) def cleanup(self): self.tmpDir.cleanup() #works on the current working directory -def find_paths_to_convert(fileNameGlob:str)->list[Path]: - pathstrings: list[str] = glob.glob(f'**/{fileNameGlob}', recursive=True) - paths:list[Path]=[Path(i) for i in pathstrings] - return paths - -#finds the depth of a file relative to given directory -def find_relative_file_depth (subfile:Path, parentDir:Path)->int: - subfile=Path(subfile).resolve() - parentDir=Path(parentDir).resolve() - return len(subfile.parts)-len(parentDir.parts)-1 +def find_paths_to_convert(fileNameGlob): + return glob.glob(f'**/{fileNameGlob}', recursive=True) #simple wrapper around the asciidoctor cli. -def convert_file(inDir: Path, outDir: Path, inFile: Path, stylesheet: Path|None): - #in order for the stylesdir and imagesdir to be linked to correctly, we need to know the relative depth between the two directories. - depth:int=find_relative_file_depth(inFile, inDir) - +def convert_file(inDir, outDir, inFile): logging.info(f'converting {Path(inFile).resolve()}') - logging.debug(f'converting {inFile=}, {outDir=}, {inDir=}, {stylesheet=}') - - depthstring= '../'*depth - - arguments=['asciidoctor', - #makes the stylesheet linked, but still includes it in the output. - '--attribute=linkcss', - f'--attribute=stylesdir={depthstring}css', - #set imagesdir - f'--attribute=imagesdir={depthstring}images', + logging.debug(f'converting {inFile} from directory {inDir} to directory {outDir}') + try: + #the destdir can be used instead of destfile in order to preserve the directory structure relative to the base dir. really useful. + subprocess.run(['asciidoctor', #specifies the source directory root. f'--source-dir={inDir}', #Destination dir. It takes the file from the subtree --source-dir and puts it in the equivilant location in the subtree --destination-dir. (talking about filesystem subtrees). f'--destination-dir={outDir}', - inFile] - - if stylesheet != None: - arguments.insert(1, f'--attribute=copycss={stylesheet}') - arguments.insert(1, f'--attribute=stylesheet={stylesheet.name}') - else: - arguments.insert(1, f'--attribute=copycss') - logging.debug(f'{arguments=}') - try: - #the destdir can be used instead of destfile in order to preserve the directory structure relative to the base dir. really useful. - subprocess.run(arguments, check=True) + inFile], + check=True) except Exception as e: logging.error(f'could not convert {inFile}!') - logging.error(f'{e}') + logging.error(f'stdErr was {e.stderr}') + logging.error(f'stdOut was {e.stdout}') if __name__ == '__main__': - inFile, outFile, stylesheet, compress, exclude=parse_arguments() + inFile, outFile, compress, exclude=parse_arguments() os.chdir(inFile) - tmpDir=TmpDir(Path('./'), exclude) - pathsToConvert:list[Path]=find_paths_to_convert('*.adoc') + tmpDir=TmpDir('./', exclude) + pathsToConvert=find_paths_to_convert('*.adoc') for i in pathsToConvert: - convert_file(inDir=Path('./'), outDir=tmpDir.path, inFile=i, stylesheet=stylesheet) + convert_file('./', tmpDir.path, i) if compress: tmpDir.compress_and_copy_self_to(outFile) diff --git a/README.md b/README.md index f4d2562..464838f 100644 --- a/README.md +++ b/README.md @@ -7,22 +7,21 @@ ASCIIsite is a simple, bare bones static site generator. You give it a directory ## Usage -ASCIISite several optional arguments followed by a single mandatory argument telling it what directory to convert. +ASCIISite takes 2 (so far) optional arguments followed by the single mandatory argument telling it what directory to convert. the -o or --output option simply tells ASCIISite what to name the output file. the -z or --compress flag tells ASCIISite to put the final product in a compressed tar.gz file as its output. This is especially useful if you are running ASCIISite on your personal computer, and will be uploading the tar.gz file to your server. -The --sylesheet options allows you to set a custom stylesheet to use instead of the default ASCIIDoctor stylesheet. - -The --exclude flag allows you to specify a list of glob patterns. Any file matching these glob patterns will not be copied to the output. +the --exclude flag allows you to specify a list of glob patterns. Any file matching these glob patterns will not be copied to the output. The --exclude-file flag allows you to specify a file containing one glob to exclude per line. Other than inputting from a file, works exactly the same as --exclude. Note that it cannot parse the full spec of .gitignore files, only traditional globs. Exclusions are helpful for any files that are needed for the compilation of the asciidoc files, but do not need to be in the final site. The main use case I am aware of is files that are put into an asciidoc document via an include statement. + As for how to format the input directory, thats up to you. The directory structure of the input will be mirrored in the structure of the output website. The only real rule you need to follow is that all your links to other pages in the input directory should be relative, so they dont get broken when you move the output directory around. @@ -32,6 +31,7 @@ Say you have a nice asciidoctor directory like this: ``` test ├── dir +│   ├── collatz.py │   └── subdir │   └── linked.adoc ├── images @@ -45,21 +45,20 @@ Where some pages link to others, some pages include others, and some pages have You can run ``` -ASCIISite.py -o output test +ASCIISite.py -o result test ``` to get a file tree like: ``` -output -├── css -│   └── asciidoctor.css +result ├── dir +│   ├── collatz.py │   └── subdir │   └── linked.html -├── include -│   └── include.txt ├── images │   └── test_pattern.svg +├── include +│   └── include.txt └── landing_page.html ``` @@ -71,10 +70,9 @@ ASCIIsite.py --exclude 'include*' -o output test will get you an output like: ``` -output -├── css -│   └── asciidoctor.css +result ├── dir +│   ├── collatz.py │   └── subdir │   └── linked.html ├── images @@ -82,30 +80,9 @@ output └── landing_page.html ``` -and, to use your custom stylesheet named `+myTheme.css+`, you can use: - -``` -ASCIIsite.py --stylesheet myTheme.css -o output test -``` - -``` -output -├── css -│   └── myTheme.css -├── dir -│   └── subdir -│   └── linked.html -├── include -│   └── include.txt -├── images -│   └── test_pattern.svg -└── landing_page.html -``` - Alternatively, you can run - ``` -ASCIISite.py -z -o output test +ASCIISite.py -z -o result test ``` -to get a .tar.gz file containing the output directory. +to get a .tar.gz file containing the result directory. diff --git a/tests/control/dir/subdir/linked.html b/tests/control/dir/subdir/linked.html index b15754c..1a4a433 100644 --- a/tests/control/dir/subdir/linked.html +++ b/tests/control/dir/subdir/linked.html @@ -4,11 +4,439 @@ - + relatively linked doc - +