How to Include Remote Sources in AsciiDoc

How to Include Remote Sources in AsciiDoc

AsciiDoc can include code from remote sources, not just local files. In this post, I’m going to show you both how to do it, as well as how doing so can make your content extremely flexible and much less intensive to maintain.


Recently, I’ve been investing time updating my book Zend Expressive Essentials, to reflect the latest version of the framework (rebranded as Mezzio). I self-dateed the book using a toolchain based around the AsciiDoc file format, the Asciidoctor toolchain, and git.

Overall, I’ve been delighted with how the combination of AsciiDoc and Asciidoctor let me create a book with no expensive tooling on any operating system that I choose. All that I need are VIM and a terminal.

However, it’s not been flawless. While updating the book for the latest version, I’ve become increasingly frustrated with how I’ve included code samples, because it feels like too much work.

To put it into context, in one chapter of the book, I create an initial version of an application using the framework, and then improve it across several iterations. The source code for the examples is stored in a single git repository. Within that repository is a directory for each iteration of the application.

For each source code example in the book, I use an include directive inside a source code block, similar to the example below.

[source,php]
----
include::code/version/3.0.0/code/refactor-three/config/routes.php[]
----

This approach lets me keep the PHP source code separate from the book’s AsciiDoc source. By doing so, I can use PHP to ensure that the code is syntactically correct and that the application runs as expected.

Including code from a git repository organised this way, from the perspective of AsciiDoc, isn’t a concern and isn’t wrong. When asciidoctor processes the book’s source files, so long as the included PHP file can be found and read, then the content can be included in the generated book.

However, from the perspective of code organisation, this was becoming a right royal pain the proverbial. I was keeping multiple versions of the code in a way that was reminiscent of the dark days of the past, where people might update a website by manually FTP’ing update files from a development machine to a production server.

There are still places that do that, would you believe?!

The more I looked at this approach, the more I believed that there must be a better way to organise the book’s PHP source code, yet still be able to reference it fairly trivially in the book’s AsciiDoc source files.

After a bit of reading, I remembered that AsciiDoc’s include directive can reference external content from URIs — such as a GitHub repository! Consequently, after a little bit of thinking and planning, I recreated the book’s source code in a new repository.

This time, as I should have done it the first time, I created a branch for each iteration of the application, where each branch contained only the differences for that particular iteration of the code.

After pushing it to GitHub, I updated each of the relevant source code blocks’ include directive to reference the relevant version of the file in the remote GitHub repository. You can see an example of the change in the sample below.

 [source,php,linenos]
 ----
+include::https://raw.githubusercontent.com/settermjd/mezzio-expressive-book-code-examples-manual-build/iteration-one/config/routes.php[]
 ----

GitHub (and I assume GitLab, Gitea, etc.) makes referencing multiple versions quite convenient, because of the URIs that it makes available from a repository. The example above shows how to refer to a file (config/routes.php) in a particular branch (iteration-five). Below is how I could refer to two other versions of the file.

 [source,php,linenos]
 ----
+include::https://raw.githubusercontent.com/settermjd/mezzio-expressive-book-code-examples-manual-build/iteration-two/config/routes.php[]
 ----
 [source,php,linenos]
 ----
+include::https://raw.githubusercontent.com/settermjd/mezzio-expressive-book-code-examples-manual-build/iteration-three/config/routes.php[]
 ----

By taking this approach, the code is much easier to create and maintain, yet just as simple to refer to — if not more so — in the AsciiDoc source. To be clear, this isn’t a problem with AsciiDoc. However, it does show an essential aspect of AsciiDoc’s functionality that makes it extremely flexible and accommodating.

Good luck trying to do that in Markdown!

Enable Support for Remote Content in Asciidoctor

That’s not the end of the story, however. As my PHP source files were now on GitHub, and not available on the local filesystem, when I tried to build the book with asciidoctor, the examples would not be retrieved.

This is because - for security reasons — Asciidoctor disables including remote content. It does this to avoid potentially dangerous macros in source files, such as those brought in using the include::[] directive, being used.

To get around this, as I trust the remote content, when calling asciidoctor, I had to set the allow-uri-read attribute, which enables including remote content. Here is an example of how to do so, when using asciidoctor’s Docker container:

docker run -it -v "$(pwd)/source/":/documents/ \
    asciidoctor/docker-asciidoctor \
    asciidoctor-pdf \
    -a allow-uri-read \
    -d book \
    --base-dir "." \
    --out-file "build/book.pdf" \
    "book.asciidoc"

With that done, the revised version of the book builds correctly, and I have less work to do and fewer things to think about.

## In Summary

I hope that this article’s shown yet another way in which AsciiDoc is an exceptionally well thought out file format, and is an excellent choice for creating sophisticated content, such as technical documentation — one far preferable to Markdown. I’d love to get your thoughts in the comments below about this feature.

Are you using it? Are you keen to use it?

Please share your thoughts and let’s have a lively discussion.


You might also be interested in these tutorials too...

How to Set Custom Attributes in the Asciidoctor.js Live Preview Extension
Tue, Apr 21, 2020

How to Set Custom Attributes in the Asciidoctor.js Live Preview Extension

In this post, I step through how to set custom AsciiDoc attributes in the Asciidoctor.js Live Preview Extension for Firefox (and Chromium/Chrome). By doing so, you can preview your content properly and avoid setting attributes directly in your AsciiDoc content and other hacks.

Antora 101: How to Create Redirects
Thu, May 30, 2019

Antora 101: How to Create Redirects

Whenever you create online documentation, eventually, the structure needs to change; such as a name change, content restructure, or old content is removed. When these times come, it’s important to create redirects to avoid breaking user expectations. In this post, I’m going to step you through how to do so with Antora.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus