This manual is a reference guide for Liaison {{{version}}}, first released on {{{release-date}}}.
- Homepage: <https://grtcdr.tn/liaison>
- Repository: <https://github.com/grtcdr/liaison>
Copyright (C) 2022 Aziz Ben Ali.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License”.
Find and navigate to your desired project, and run the following in a shell prompt:
git clone https://github.com/grtcdr/liaison.git
Likewise, you may choose to add it as a submodule:
git submodule add https://github.com/grtcdr/liaison.git
It is assumed that you have a publishing script which this manual has
chosen to name publish.el
; within this file, you should define the
project specification as well as any other configuration variables or
special functions that affect the output of the published website.
It is best that you initiate the publishing process using a
Makefile
.
In your publishing script, add the package to the load-path
and then
require it like so:
(add-to-list 'load-path "liaison")
(require 'liaison)
If you’re interested in a more elaborate but less tedious method for installing this package, you should read my article on Implementing per-project package management for ox-publish.el projects.
This section outlines the different steps needed to integrate this package into your website, it assumes that you have already created a publishing script.
In the previous section you were told to import liaison
, but
you’ll also need to import ox-publish
and any other libraries you
depend on, in your publishing script.
(require 'ox-publish)
This example registers a new blob
macro which will receive the URL
to the blob page of the currently visited document.
(setq org-export-global-macros
'(("blob" . "(eval (liaison-get-resource-url 'blob))")))
We can now call the blob
macro in an Org document and it will expand
to the string we assigned to it.
#+TITLE: Example
* Heading
To view this document in its raw format, follow this link: {{{blob}}}.
In your publishing script, you will want to redefine the
org-html-format-spec
function such as to extend it with your custom
format strings.
If you intend to use any other format strings, such as the preset %a
or %e
, don’t forget to add them, otherwise they won’t be expanded.
(defun org-html-format-spec (info)
"Return format specification for preamble and postamble."
`((?b . ,(liaison-get-resource-url 'blob))
(?m . ,(liaison-get-resource-url 'blame))
(?t . ,(liaison-get-resource-url 'tree))
(?l . ,(liaison-get-resource-url 'log))
(?p . ,(liaison-get-resource-url 'plain))
(?e . ,(liaison-get-resource-url 'edit))))
This next step involves preparing your project structure to support
the use of HTML templates; snippets which will be added onto our Org
documents. These templates can reference at any point any of the
format strings defined in the org-html-format-spec
function.
The following is an example of such a template:
<nav>
<a href="%e"><button>Edit</button></a>
<a href="%m"><button>Blame</button></a>
<a href="%b"><button>Blob</button></a>
<a href="%t"><button>Tree</button></a>
<a href="%l"><button>Log</button></a>
<a href="%p"><button>Plain</button></a>
</nav>
During the export phase, Org will locate the format strings, and interpolate them using the return value of their associated function.
We’ll now need a way to access the contents of these HTML files from our publishing script - to achieve that, you could use something like this:
(defun read-template (filename)
"Read contents of FILENAME from the templates directory."
(with-temp-buffer
(insert-file-contents
(file-name-concat "templates" filename))
(buffer-string)))
We will use read-template
along with the :html-preamble
property
to inject a preamble into the files of a particular project
component.
(setq org-publish-project-alist
(let ((preamble (read-template "preamble.html")))
(list
(list "articles"
:base-extension "org"
:base-directory "articles"
:publishing-directory "public/articles"
:publishing-function 'org-html-publish-to-html
:html-preamble preamble
:html-postamble nil)
(list "all"
:components "articles"))))
Your website is now properly configured to use Liaison.
The publishing script should be loaded before the
org-publish-project
function is called, this translates to the
following command:
emacs --quick --batch \
--load publish.el \
--funcall org-publish-all t t
Liaison by default doesn’t recognize domains beside the presets, so you’ll have to specify the domain of your forge and its associated resource URL builder.
Now, suppose you’re a member of freedesktop.org, your wonderful projects are present in the GitLab instance provided by your organization.
To add support for your infrastructure, you need to customize the
liaison-forge-alist
variable, here’s an example:
(defvar liaison-forge-alist
'(("gitlab.freedesktop.org" . #'liaison--build-gitlab-resource-url)))
In some cases, you may find yourself using Liaison’s functions in a
publishing project with the :auto-sitemap
option set to t
.
Provided you are relying upon a templating system like the one
proposed in this document, you will notice that when you visit the
sitemap (e.g. https://example.com/blog/sitemap.html
) in a web
browser, the document will contain the same preamble/postamble as the
files which it is indexing.
To get around this, you should use set the :sitemap-function
option. Here’s an example in which we hide the postamble from
appearing in the sitemap:
(defun my/blog-sitemap-function (title list)
"Custom site map function for the blog project."
(concat "#+OPTIONS: html-postamble:nil\n"
(org-publish-sitemap-default title list)))
Here’s another example which references a setupfile:
(defun my/blog-sitemap-function (title list)
"Custom site map function for the blog project."
(concat "#+SETUPFILE: setup.org\n"
(org-publish-sitemap-default title list)))
By now you will have noticed that the strings we’re concatenating to
the default sitemap function, i.e. org-publish-sitemap-default
,
follow the same syntax as any Org document.
The result of this endeavor is a persistent configuration of the project’s sitemap with our own custom options.
Git submodules are by default in a detached HEAD
state, meaning that
HEAD
will always point to a specific commit (usually the most
recent), rather than the usual, i.e. a specific branch.
This causes severe problems to the generation of URLs. Instead of receiving a working URL such as the following:
https://github.com/grtcdr/liaison/commits/main/liaison.el
You’ll instead receive a URL like this:
https://github.com/grtcdr/liaison/commits/(HEAD detached at 984d79d)/liaison.el
This URL is not understood by GitHub, or any other web interface for
that matter. To get around this, one must checkout
a specific branch
(usually the default) immediately after pulling the contents of the
submodule.
A workflow such as the following will not suffice on its own:
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: true
actions/checkout@v3
does not, at the time of writing this, offer the
option to check out a specific branch after pulling the submodules of
the repository, so our only option is to do this manually.
If you’re sure that every submodule is using the same name for their
default branches, foreach
is the appropriate and most efficient
function. However, you shouldn’t do this unless you’re intending to
integrate Liaison into every submodule.
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: true
- name: [WORKAROUND] Attach to the default branches of submodules
run:
git submodule foreach 'git checkout <default-branch> && git pull'
A better alternative is to individually checkout the default branch for the specific submodules you’re targeting.
This way, if your submodules are using different names for their
default branch, e.g. “master” vs “main” or “x.y.z”, you won’t receive
any errors about an unknown pathspec
. Additionally, you are
targeting only the necessary submodules.
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: true
- name: [WORKAROUND] Attach to the default branches of submodules
run: |
cd <path/to/submodule>
git checkout <default-branch>
git pull