diff --git a/.gitignore b/.gitignore
index 946caa1c..6ae7730d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,7 @@
output/*
!output/README.md
-webpage/*.ots
+webpage/v
# Manubot cache directory
ci/cache
diff --git a/README.md b/README.md
index 20c938c5..966f1bfc 100644
--- a/README.md
+++ b/README.md
@@ -72,6 +72,8 @@ Manubot automates citations and references, versions manuscripts using git, and
The [Manubot Rootstock repository](https://git.io/vQSvo) is a general purpose template for creating new Manubot instances.
See [`USAGE.md`](USAGE.md) for documentation how to write a manuscript.
+Please open [an issue](https://github.com/greenelab/manubot-rootstock/issues) for questions related to Manubot usage, bug reports, or general inquiries.
+
### Repository directories & files
The directories are as follows:
@@ -101,6 +103,12 @@ sh build/build.sh
# when a change is detected.
sh build/autobuild.sh
+# At this point, the HTML & PDF outputs will have been created. The remaining
+# commands are for serving the webpage to view the HTML manuscript locally.
+
+# Configure the webpage directory
+python build/webpage.py
+
# View the manuscript locally at http://localhost:8000/
cd webpage
python -m http.server
diff --git a/USAGE.md b/USAGE.md
index fcd4bd10..d6ad4deb 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -95,7 +95,7 @@ For example, the following citations all refer to the same study, but will be tr
The system also supports citation tags, which are recommended for the following applications:
-1. A citation's identifier contains forbidden characters, such as `;` or ending with a non-alphanumeric character other than `/`.
+1. A citation's identifier contains forbidden characters, such as `;` or `=`, or ends with a non-alphanumeric character other than `/`.
In these instances, you must use a tag.
2. A single reference is cited many times.
Therefore, it might make sense to define a tag, so if the citation updates (e.g. a newer version becomes available), only a single change is required.
diff --git a/build/assets/redirect-template.html b/build/assets/redirect-template.html
new file mode 100644
index 00000000..9fd83c23
--- /dev/null
+++ b/build/assets/redirect-template.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Page Redirection
+
+
+ If you are not redirected automatically, follow this link.
+
+
diff --git a/build/environment.yml b/build/environment.yml
index ac8bdf43..071bd661 100644
--- a/build/environment.yml
+++ b/build/environment.yml
@@ -7,22 +7,22 @@ dependencies:
- conda-forge::ghp-import=0.5.5
- conda-forge::jinja2=2.10
- conda-forge::pandas=0.22.0
- - conda-forge::pandoc=2.1
+ - conda-forge::pandoc=2.1.1
- conda-forge::python=3.6.4
- conda-forge::pyyaml=3.12
- conda-forge::requests=2.18.4
- conda-forge::watchdog=0.8.3
- pip:
- errorhandler==2.0.1
- - git+https://github.com/greenelab/manubot@7ca386db0610d1e1113abba10c109dcffd3e4c9a
+ - git+https://github.com/greenelab/manubot@27456594c407f3fed35804db4bcf3b5bba88a716
- opentimestamps-client==0.5.1
- opentimestamps==0.2.0.1
- pandoc-eqnos==1.2.0
- pandoc-fignos==1.2.0
- pandoc-tablenos==1.2.0
- - pandoc-xnos==0.15
+ - pandoc-xnos==1.0.1
- pybase62==0.3.2
- pysha3==1.0.2
- python-bitcoinlib==0.9.0
- requests-cache==0.4.13
- - weasyprint==0.42
+ - weasyprint==0.42.2
diff --git a/build/webpage.py b/build/webpage.py
new file mode 100644
index 00000000..bf0b0218
--- /dev/null
+++ b/build/webpage.py
@@ -0,0 +1,155 @@
+import argparse
+import os
+import pathlib
+import shutil
+import subprocess
+
+
+def parse_arguments():
+ """
+ Read and process command line arguments.
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--checkout',
+ nargs='?', const='gh-pages', default=None,
+ help='branch to checkout /v directory contents from. For example, --checkout=upstream/gh-pages. --checkout is equivalent to --checkout=gh-pages. If --checkout is ommitted, no checkout is performed.',
+ )
+ parser.add_argument(
+ '--version',
+ default=os.environ.get('TRAVIS_COMMIT', 'local'),
+ help="Used to create webpage/v/{version} directory. Generally a commit hash, tag, or 'local'",
+ )
+ args = parser.parse_args()
+ return args
+
+
+def configure_directories(args):
+ """
+ Add directories to args and create them if neccessary.
+ Note that versions_directory is the parent of version_directory.
+ """
+ args_dict = vars(args)
+
+ # Directory where Manubot outputs reside
+ args_dict['output_directory'] = pathlib.Path('output')
+
+ # Set webpage directory
+ args_dict['webpage_directory'] = pathlib.Path('webpage')
+
+ # Create webpage/v directory (if it doesn't already exist)
+ args_dict['versions_directory'] = args.webpage_directory.joinpath('v')
+ args.versions_directory.mkdir(exist_ok=True)
+
+ # Checkout existing version directories
+ checkout_existing_versions(args)
+
+ # Create empty webpage/v/version directory
+ version_directory = args.versions_directory.joinpath(args.version)
+ if version_directory.is_dir():
+ print(f'{version_directory} exists: replacing it with an empty directory')
+ shutil.rmtree(version_directory)
+ version_directory.mkdir()
+ args_dict['version_directory'] = version_directory
+
+ # Symlink webpage/v/latest to point to webpage/v/commit
+ latest_directory = args.versions_directory.joinpath('latest')
+ if latest_directory.is_symlink() or latest_directory.is_file():
+ latest_directory.unlink()
+ elif latest_directory.is_dir():
+ shutil.rmtree(latest_directory)
+ latest_directory.symlink_to(args.version, target_is_directory=True)
+ args_dict['latest_directory'] = latest_directory
+
+ # Create freeze directory
+ freeze_directory = args.versions_directory.joinpath('freeze')
+ freeze_directory.mkdir(exist_ok=True)
+ args_dict['freeze_directory'] = freeze_directory
+
+ return args
+
+
+def checkout_existing_versions(args):
+ """
+ Must populate webpage/v from the gh-pages branch to get history
+
+ References:
+ http://clubmate.fi/git-checkout-file-or-directories-from-another-branch/
+ https://stackoverflow.com/a/2668947/4651668
+ https://stackoverflow.com/a/16493707/4651668
+
+ Command modeled after:
+ git --work-tree=webpage checkout upstream/gh-pages -- v
+ """
+ if not args.checkout:
+ return
+ command = [
+ 'git',
+ f'--work-tree={args.webpage_directory}',
+ 'checkout',
+ args.checkout,
+ '--',
+ 'v',
+ ]
+ print('Attempting checkout with the following command:', ' '.join(command), sep='\n')
+ process = subprocess.run(command, stderr=subprocess.PIPE)
+ if process.returncode == 0:
+ # Addresses an odd behavior where git checkout stages v/* files that don't actually exist
+ subprocess.run(['git', 'add', 'v'])
+ else:
+ print(f'Checkout returned a nonzero exit status. See stderr:\n{process.stderr.decode()}')
+
+
+def create_version(args):
+ """
+ Populate the version directory for a new version.
+ """
+
+ # Copy content/images to webpage/v/commit/images
+ shutil.copytree(
+ src=pathlib.Path('content/images'),
+ dst=args.version_directory.joinpath('images'),
+ )
+
+ # Copy output files to to webpage/v/version/
+ renamer = {
+ 'manuscript.html': 'index.html',
+ 'manuscript.pdf': 'manuscript.pdf',
+ }
+ for src, dst in renamer.items():
+ shutil.copy2(
+ src=args.output_directory.joinpath(src),
+ dst=args.version_directory.joinpath(dst),
+ )
+
+ # Copy webpage/github-pandoc.css to to webpage/v/version/github-pandoc.css
+ shutil.copy2(
+ src=args.webpage_directory.joinpath('github-pandoc.css'),
+ dst=args.version_directory.joinpath('github-pandoc.css'),
+ )
+
+ # Create v/freeze to redirect to v/commit
+ path = pathlib.Path('build/assets/redirect-template.html')
+ redirect_html = path.read_text()
+ redirect_html = redirect_html.format(url=f'../{args.version}/')
+ args.freeze_directory.joinpath('index.html').write_text(redirect_html)
+
+
+def get_versions():
+ """
+ Extract versions from the webpage/v directory, which should each contain
+ a manuscript.
+ """
+ versions = {x.name for x in args.versions_directory.iterdir() if x.is_dir()}
+ versions -= {'freeze', 'latest'}
+ versions = sorted(versions)
+ return versions
+
+
+if __name__ == '__main__':
+ args = parse_arguments()
+ configure_directories(args)
+ print(args)
+ create_version(args)
+ versions = get_versions()
+ print(versions)
diff --git a/ci/deploy.sh b/ci/deploy.sh
index 1badbf10..dd87d85e 100644
--- a/ci/deploy.sh
+++ b/ci/deploy.sh
@@ -7,15 +7,10 @@ export REPO_NAME=`basename $TRAVIS_REPO_SLUG`
envsubst < webpage/README.md > webpage/README-complete.md
mv webpage/README-complete.md webpage/README.md
-# Generate OpenTimestamps
-ots stamp \
- webpage/index.html \
- webpage/manuscript.pdf
-
# Configure git
git config --global push.default simple
git config --global user.email `git log --max-count=1 --format='%ae'`
-git config --global user.name `git log --max-count=1 --format='%an'`
+git config --global user.name "`git log --max-count=1 --format='%an'`"
git checkout $TRAVIS_BRANCH
git remote set-url origin git@github.com:$TRAVIS_REPO_SLUG.git
@@ -34,6 +29,16 @@ ssh-add ci/deploy.key
git remote set-branches --add origin gh-pages output
git fetch origin gh-pages:gh-pages output:output
+# Configure versioned webpage
+python build/webpage.py \
+ --checkout=gh-pages \
+ --version=$TRAVIS_COMMIT
+
+# Generate OpenTimestamps
+ots stamp \
+ webpage/v/$TRAVIS_COMMIT/index.html \
+ webpage/v/$TRAVIS_COMMIT/manuscript.pdf
+
# Commit message
MESSAGE="\
`git log --max-count=1 --format='%s'`
diff --git a/content/00.front-matter.md b/content/00.front-matter.md
index 2f9669cb..54c2613f 100644
--- a/content/00.front-matter.md
+++ b/content/00.front-matter.md
@@ -13,6 +13,9 @@ This manuscript was automatically generated
from [{{ci_source.repo_slug}}@{{ci_source.commit | truncate(length=7, end='', leeway=0)}}](https://github.com/{{ci_source.repo_slug}}/tree/{{ci_source.commit}})
{% endif -%}
on {{date}}.
+{% if ci_source is defined -%}
+The permalink for this manuscript version is .
+{% endif -%}
## Authors
diff --git a/webpage/README.md b/webpage/README.md
index 407f3e9f..b347eccc 100644
--- a/webpage/README.md
+++ b/webpage/README.md
@@ -1,6 +1,8 @@
# Output directory containing the formatted manuscript
The [`gh-pages`](https://github.com/$TRAVIS_REPO_SLUG/tree/gh-pages) branch hosts the contents of this directory at https://$OWNER_NAME.github.io/$REPO_NAME/.
+The permalink for this webpage version is https://$OWNER_NAME.github.io/$REPO_NAME/v/$TRAVIS_COMMIT/.
+To redirect to the permalink for the latest manuscript version at anytime, use the link https://$OWNER_NAME.github.io/$REPO_NAME/v/freeze/.
## Files
@@ -9,8 +11,23 @@ This directory contains the following files, which are mostly ignored on the `ma
+ [`index.html`](index.html) is an HTML manuscript.
+ [`github-pandoc.css`](github-pandoc.css) sets the display style for `index.html`.
+ [`manuscript.pdf`](manuscript.pdf) is a PDF manuscript.
-+ `*.ots` files are OpenTimestamps which can be used to verify manuscript existence at or before a given time.
- [OpenTimestamps](opentimestamps.org) uses the Bitcoin blockchain to attest to file hash existence.
+
+The `v` directory contains directories for each manuscript version.
+In general, a version is identified by the commit hash of the source content that created it.
+
+### Timestamps
+
+The `*.ots` files in version directories are OpenTimestamps which can be used to verify manuscript existence at or before a given time.
+[OpenTimestamps](https://opentimestamps.org/) uses the Bitcoin blockchain to attest to file hash existence.
+The `deploy.sh` script run during continuous deployment creates the `.ots` files.
+However, these files are initially dependent on calendar services and must be upgraded at a later time by running the following in the `gh-pages` branch:
+
+```sh
+# Requires a local bitcoin node with JSON-RPC configured
+ots upgrade v/*/*.ots
+rm v/*/*.ots.bak
+git add v/*/*.ots
+```
## Source
diff --git a/webpage/github-pandoc.css b/webpage/github-pandoc.css
index 6445e2bb..3189ab30 100644
--- a/webpage/github-pandoc.css
+++ b/webpage/github-pandoc.css
@@ -87,8 +87,9 @@ body {
margin: 0;
}
-/***Page margins when printing***/
-@page {
+/***Page size and margins when printing***/
+@page {
+ size: Letter;
margin-top: 19mm;
margin-bottom: 16mm;
margin-left: 0mm;
diff --git a/webpage/images b/webpage/images
index a0fc2191..2b55bac8 120000
--- a/webpage/images
+++ b/webpage/images
@@ -1 +1 @@
-../content/images
\ No newline at end of file
+v/latest/images
\ No newline at end of file
diff --git a/webpage/index.html b/webpage/index.html
index 936379e7..2f982aca 120000
--- a/webpage/index.html
+++ b/webpage/index.html
@@ -1 +1 @@
-../output/manuscript.html
\ No newline at end of file
+v/latest/index.html
\ No newline at end of file
diff --git a/webpage/manuscript.pdf b/webpage/manuscript.pdf
index 90c08c69..b8867276 120000
--- a/webpage/manuscript.pdf
+++ b/webpage/manuscript.pdf
@@ -1 +1 @@
-../output/manuscript.pdf
\ No newline at end of file
+v/latest/manuscript.pdf
\ No newline at end of file