#!/usr/bin/env python # encoding: utf-8 """static Create and/or upload a static copy of the repository. The main goal is sharing Mercurial on servers with only FTP access and statically served files, while providing the same information as hg serve and full solutions like bitbucket and gitorious (naturally without the interactivity). """ __plan__ = """ * Create the static-dir in the repo: - Overview: Readme + commits + template - Changes: Commit-Log + each commit as changeset/<hex> - source: a filetree, shown as sourcecode: src/<path> and raw/<path> - if b is used: a bugtracker: issue/<id>/<name> - fork-/clone-info for each entry in [paths] with its incoming data (if it has some): clone/<pathname>/ → commit log + possibly an associated issue in b. * Usage: - hg static [--name] [-r] [folder] → parse the static folder for the current revision. Mimic pull and clone wherever possible: This is a clone to <repo>/static - hg static --upload <FTP-path> [folder] → update and upload the folder == clone/push * Idea: hg clone/push ftp://host.tld/path/to/repo → hg static --upload * Setup a new static repo or update an existing one: hg static --upload ftp://host.tld/path/to/repo """ import os from os.path import join, isdir, isfile, basename import shutil import mercurial.cmdutil from mercurial import commands _staticidentifier = ".statichgrepo" templates = { "head": """<!DOCTYPE html> <html><head> <meta charset="utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!--duplicate for older browsers--> <link rel="stylesheet" href="style.css" type="text/css" media="screen" /> <link rel="stylesheet" href="print.css" type="text/css" media="print" /> <title>REPO_NAME</title> </head> <body> <h1>REPO_NAME</h1> """, "foot": "</body></html>", "screenstyle": """ """, "printstyle": """ """ } def parsereadme(filepath): """Parse the readme file""" with open(filepath) as r: return r.read() def parsesite(ui, repo, target, **opts): """Create the static folder.""" idfile = join(target, _staticidentifier) if not isdir(target): # make sure the target exists os.makedirs(target) else: # make sure it is a staticrepo if not isfile(idfile): if not ui.prompt("The target folder exists is no static repo. Really use it?", default="n").lower() in ["y", "yes"]: return with open(idfile, "w") as i: i.write("") if opts["name"]: name = opts["name"] elif target: name = target else: name = basename(repo.root) # first the stylesheets screenstyle = opts["screenstyle"] if screenstyle: shutil.copyfile(screenstyle, join(target, "style.css")) else: with open(join(target, "style.css"), "w") as f: f.write(templates["screenstyle"]) printstyle = opts["printstyle"] if printstyle: shutil.copyfile(printstyle, join(target, "print.css")) else: with open(join(target, "print.css"), "w") as f: f.write(templates["printstyle"]) # then the overview overview = open(join(target, "index.html"), "w") overview.write(templates["head"].replace("REPO_NAME", name)) # add a readme, if it exists # TODO: Parse different types of readme files for f in os.listdir(repo.root): if f.lower().startswith("readme"): overview.write(parsereadme(f)) # now add the 5 most recent log entries # divert all following ui output to a string, so we can just use standard functions ui.pushbuffer() commands.log(ui, repo, template="""<div style='float: right; padding-left: 0.5em'><em>({author|person})</em></div> <p><strong> {date|shortdate}: <a href='commits/{node}.html'>{desc|strip|fill68|firstline}</a> <span style='font-size: xx-small'>{branches} {tags}</span></p><p>{desc|escape}</p>""", date="", rev="0:0", user=ui.username()) overview.write(ui.popbuffer()) # finish the overview overview.write(templates["foot"]) def static(ui, repo, target=None, **opts): """Create a static copy of the repository and/or upload it to an FTP server.""" if not target: target = "static" parsesite(ui, repo, target, **opts) cmdtable = { # "command-name": (function-call, options-list, help-string) "static": (static, [('r', 'rev', None, 'parse the given revision'), ('a', 'all', None, 'parse all revisions (requires much space)'), ('n', 'name', None, 'the repo name. Default: folder or last segment of the repo-path.'), ('u', 'upload', None, 'upload the repo'), ('s', 'screenstyle', None, 'use a custom stylesheet for display on screen'), ('p', 'printstyle', None, 'use a custom stylesheet for printing')], "[options] [folder]") }