Website Preview and Deploy
Introduction
These functions complement Org Mode publishing features by adding a
local preview, using elnode
or webrick
, and a deploy function,
using rsync
.
The functions can be invoked with:
website-server-start
website-deploy-project
These functions are useful if you define Org Mode projects in which files are published locally and deployed with an external tool.
For the local preview to work, you also need to ensure links are relative, so that they work independent of the URL base.
Installation
Use:
M-x org-babel-load-file FILE
or, if you prefer:
- Tangle this file with
M-x org-babel-tangle
. This generates an Emacs Lisp file with the required code. - Load this file, with either
(require 'avm-org-publish-support)
or(load "avm-org-publish-support.el")
which is equivalent to the command above.
Usage
Add the following properties to the specification of an Org Mode project
(those stored in org-publish-project-alist
):
:port
port used to serve a local copy of the website (default to 5000):local-dir
local directory where the website is published1:deploy-dir
directory where the website is deployed:no-sources
whether Org mode files have to be excluded from the deployment. (This is useful when you publish projects in the same directory of your sources and you do not wat to publish the sources.)
Setters and Getters
These function get the information required for previewing, building, and deploying. (They isolate setters and getters in their functions.)
(defun website-get-local-dir (project) "Where do we find the HTML files for <project>?" (plist-get (cdr (assoc project org-publish-project-alist)) :local-dir)) (defun website-get-preview-dir (project) "Where do we find the preview dir for <project>?" (plist-get (cdr (assoc project org-publish-project-alist)) :preview-dir)) (defun website-get-deploy-dir (project) "Where do we push the HTML files for <project>?" (plist-get (cdr (assoc project org-publish-project-alist)) :deploy-dir)) (defun website-get-port (project) "Port used for local preview? (Answer: 5000 if not set explicitly)" (or (plist-get (cdr (assoc project org-publish-project-alist)) :port) 5000)) (defun website-exclude-sources? (project) "Whether we want to exclude org sources from the deploy. This is useful when the project is published in the same directory of your Org mode files and you do not want to publish them in the remote directory." (plist-get (cdr (assoc project org-publish-project-alist)) :no-sources))
Building
This is syntactic sugar, since org-mode
already provides this
function.
(defun website-build-project (project-name &optional force async) "Build a website (invoke org-publish)" (interactive (list (completing-read "Publish project: " org-publish-project-alist nil t) current-prefix-arg)) (org-publish project-name force async))
Previewing
Define two functions which start and stop a webserver serving the files of a project.
Customize the variable website-server
to use the webserver of your choice,
include elnode, if you want. (For the developers, the elnode tutorial
provides the example code for starting and stopping the server.)
These functions can be safely moved to the Emacs initialization file, especially when there is more than one project:
; (require 'elnode nil t) ;; required is you want to use elnode ;;https://gist.github.com/willurd/5720255 (defvar website-server "ruby -run -ehttpd {dir} -p{port}" "*Webserver to use for previewing. It can either be the symbol 'elnode, in which case the Emacs Lisp elnode server will be used, or a command specification, which will be instantiated with the required port and the directory and started with start-process. In order to use the directory and the port specified in the project, use the special symbols {dir} and {port}. Examples of commands include: cd {dir}; python -m http.server {port} ruby -run -ehttpd {dir} -p{port}") (defun website-start-server (project-name) "Ask for a project name and start previewing it" (interactive (list (completing-read "Start local server for project: " org-publish-project-alist nil t))) (let* ( (dir (website-get-preview-dir project-name)) (port (website-get-port project-name)) ) (if (not dir) (message "Property :local-dir is not set for this project. Aborting") (if (equal website-server 'elnode) (elnode-start (elnode-webserver-handler-maker dir) :port port :host "localhost") (let ( (command (website-instantiate-command website-server port dir)) ) (start-process-shell-command "webserver" (format "*webserver %s*" project-name) command))) (message "Started serving directory %s on port %s" dir port)))) (defun website-instantiate-command (command port dir) "Generate the command to launch a webser, based on the value of website-server" (replace-regexp-in-string "{dir}" dir (replace-regexp-in-string "{port}" (format "%s" port) command))) (defalias 'website-preview-project 'website-start-server) (defalias 'website-run-server 'website-start-server) (defun website-stop-server (project-name) "Stop previewing a project, given its name" (interactive (list (completing-read "Stop local server for project: " org-publish-project-alist nil t))) (let ( (port (website-get-port project-name)) ) (progn (if (equal website-server 'elnode) (elnode-stop port) (kill-buffer (concat (format "*webserver %s*" project-name)))) (message "Stopped serving project %s" project-name))))
Deploying
Define a function which invokes the deploy command for the project.
All project specifications are stored in website-alist
.
The code for deploying has been taken from:
https://stackoverflow.com/questions/1453956/which-shell-command-in-emacs-lisp.
A more structured solution probably uses call-process
, but I did not
want to have to manage shell expansions.
This function can be safely moved to the Emacs initialization file, especially when there is more than one project:
(defun website-deploy-project (project-name) (interactive (list (completing-read "Deploy the following project: " org-publish-project-alist nil t))) (let* ( (local-dir (website-get-local-dir project-name)) (deploy-dir (website-get-deploy-dir project-name)) (exclude-sources (website-exclude-sources? project-name)) (buffer (get-buffer-create (concat "*rsync-buffer for " project-name "*"))) ) (if (and deploy-dir local-dir) (progn (display-buffer buffer) (start-process "process-name" buffer "/usr/bin/rsync" "-crvz" "--exclude=*~" "--exclude=.git" "--exclude=_*" (if exclude-sources "--exclude=*.org" "--include=*.org") "--delete" "--delete-excluded" (file-name-as-directory local-dir) ; add a final slash (otherwise local-dir might be created on the server instead) deploy-dir)) (message "No deployment command or no local dir specified for %s" project-name))))
What do we provide?
(provide 'website-management)
[fn:1 Notice that this partially overlaps with the :publishing-directory
property you need to set for Org Mode publishing to work. However, it
simplifies quite a bit the specification of where the HTML files can be found
for local previewing the website.]
Footnotes:
1