import sys import os import re from time import sleep from shutil import rmtree from subprocess import run, Popen import psutil HELP = ''' python3 build.py command command: -h --help help : print help and exit docs : serve docs locally clean : clean local develop build l : local develop build g : push to github, code and docs r : build for shipping, leave version as is r1 : build for shipping, version becomes r1+1.0.0 r2 : build for shipping, version becomes r1.r2+1.0 r3 : build for shipping, version becomes r1.r2.r3+1 For g and the r-commands you need to pass a commit message as well. ''' DIST = 'dist' VERSION_CONFIG = dict( setup=dict( file='setup.py', re=re.compile( r'''version\s*=\s*['"]([^'"]*)['"]''' ), mask="version='{}'", ), fabric=dict( file='tf/fabric.py', re=re.compile( r'''VERSION\s*=\s*['"]([^'"]*)['"]''' ), mask="VERSION = '{}'", ), ) PACKAGE = 'text-fabric' SCRIPT = '/Library/Frameworks/Python.framework/Versions/3.7/bin/text-fabric' currentVersion = None newVersion = None def readArgs(): args = sys.argv[1:] if not len(args) or args[0] in {'-h', '--help', 'help'}: print(HELP) return (False, None) arg = args[0] if arg not in {'docs', 'clean', 'l', 'g', 'r', 'r1', 'r2', 'r3'}: print(HELP) return (False, None) if arg in {'g', 'r', 'r1', 'r2', 'r3'}: if len(args) < 2: print('Provide a commit message') return (False, None) return (arg, args[1]) return (arg, None) def incVersion(version, task): comps = [int(c) for c in version.split('.')] (major, minor, update) = comps if task == 'r1': major += 1 minor = 0 update = 0 elif task == 'r2': minor += 1 update = 0 elif task == 'r3': update += 1 return '.'.join(str(c) for c in (major, minor, update)) def replaceVersion(task, mask): def subVersion(match): global currentVersion global newVersion currentVersion = match.group(1) newVersion = incVersion(currentVersion, task) return mask.format(newVersion) return subVersion def adjustVersion(task): for (key, c) in VERSION_CONFIG.items(): print(f'Adjusting version in {c["file"]}') with open(c['file']) as fh: text = fh.read() text = c['re'].sub( replaceVersion(task, c['mask']), text, ) with open(c['file'], 'w') as fh: fh.write(text) if currentVersion == newVersion: print(f'Rebuilding version {newVersion}') else: print(f'Replacing version {currentVersion} by {newVersion}') def makeDist(): distFile = "{}-{}".format(PACKAGE, newVersion) distFileCompressed = f'{distFile}.tar.gz' distPath = f'{DIST}/{distFileCompressed}' rmtree(DIST) os.makedirs(DIST, exist_ok=True) run(['python3', 'setup.py', 'sdist']) run(['twine', 'upload', distPath]) def commit(task, msg): run(['git', 'add', '--all', '.']) run(['git', 'commit', '-m', msg]) run(['git', 'push', 'origin', 'master']) if task in {'r1', 'r2', 'r3'}: tagVersion = f'v{newVersion}' commitMessage = f'Release {newVersion}: {msg}' run(['git', 'tag', '-a', tagVersion, '-m', commitMessage]) run(['git', 'push', 'origin', '--tags']) def shipDocs(): codestats() run(['mkdocs', 'gh-deploy']) def serveDocs(): codestats() killProcesses() proc = Popen(['mkdocs', 'serve']) sleep(3) run('open http://127.0.0.1:8000', shell=True) try: proc.wait() except KeyboardInterrupt: pass proc.terminate() def killProcesses(): myself = os.getpid() for proc in psutil.process_iter(attrs=['pid', 'name']): pid = proc.info['pid'] if pid == myself: continue if filterProcess(proc): try: proc.terminate() print(f'mkdocs [{pid}] terminated') except psutil.NoSuchProcess: print(f'mkdocs [{pid}] already terminated') def filterProcess(proc): procName = proc.info['name'] commandName = '' if procName is None else procName.lower() found = False if commandName.endswith('python'): parts = proc.cmdline() if len(parts) >= 3: if parts[1].endswith('mkdocs') and parts[2] == 'serve': found = True if parts[1] == 'build.py' and parts[2] == 'docs': found = True return found def codestats(): xd = ( '__pycache__,node_modules,.tmp,.git,_temp,' '.ipynb_checkpoints,images,fonts,favicons,compiled' ) xdtf = xd + ',search,server,extra' rfFmt = 'docs/Code/Stats{}.md' cmdLine = ( 'cloc' ' --no-autogen' ' --exclude_dir={}' f' --exclude-list-file=cloc_exclude.lst' f' --report-file={rfFmt}' ' --md' ' {}' ) run(cmdLine.format(xd, '', '.'), shell=True) run(cmdLine.format(xdtf, 'Base', 'tf'), shell=True) run(cmdLine.format(xd, 'Search', 'tf/search'), shell=True) run(cmdLine.format(xd, 'Server', 'tf/server'), shell=True) run(cmdLine.format(xd, 'Apps', 'tf/extra'), shell=True) def main(): (task, msg) = readArgs() if not task: return elif task == 'docs': serveDocs() elif task == 'clean': run(['python3', 'setup.py', 'develop', '-u']) os.unlink(SCRIPT) elif task == 'l': run(['python3', 'setup.py', 'develop']) elif task == 'g': shipDocs() commit(task, msg) elif task in {'r', 'r1', 'r2', 'r3'}: adjustVersion(task) shipDocs() makeDist() commit(task, msg) main()