(defun get-string-from-file (filePath)
"Return filePath's file content."
(with-temp-buffer
(insert-file-contents filePath)
(buffer-string)))
(let ((data (get-string-from-file "./typescript")))
(replace-regexp-in-string
(rx (seq "$ exit" (zero-or-more (group anything)) eol))
""
(replace-regexp-in-string
(rx (seq bol "bash-" (zero-or-more nonl) "$"))
"$"
(replace-regexp-in-string
(rx (seq (group (*\? (group anything)))
"$"
(zero-or-more (group anything)) eos))
""
(replace-regexp-in-string "
" "\n" data) nil nil 1))))
What you are reading is an early release of this book, which means its contents is not yet complete, and you may find errors or omissions in it. Still, I hope you find it useful. I would really appreciate your feedback! Please let me know what you liked, what you didn’t like, and what additional topics you would like me to cover. Please send me a message through the /Email the Author/ page and let me know what you think.
This second release includes many improvements and new content, including:
- A whole new chapter The workflow, which covers detailed techniques for writing, exporting, previewing and publishing your book.
- A new section Code block execution and output processing, which shows how you can have code within your document which is executed on the fly, and which you can use to generate content within your book.
- Two new appendices, the first one containing the source code for the diagram shown in The process, and the second one containing samples of all the different block types supported by
ox-leanpub
. - Many other changes, including new instructions on configuring Emacs Doom, innumerable wording, structure and clarity improvements, and a lot more.
With this release, the book is no longer free. If you find it valuable, I would really appreciate your support.
Hope you enjoy it!
This is the initial release of the book! I would love to hear your thoughts. There is still much missing but the core of the book is there, and you can probably already use it to get started writing a book and publishing it on Leanpub using Emacs and Org-mode.
Publishing your words has never been easier than it is today. Traditionally, publishing a book was a long and arduous process, in which you had to go through multiple gatekeepers to have a chance to see your words on paper. Today, publishing a book has become considerably easier through self publishing. There are many tools and publishers that allow you to get started for little or no money. Still, getting started can be confusing, and that is what this book is about.
In this book, I will show you the workflow and tools I use to publish my books. This toolset is geared towards technical writing, which is what I do, but can just as well be used for any other style. The three main tools are:
- The GNU Emacs editor together with Org mode and the ox-leanpub exporter for writing, editing and exporting your text;
- GitHub to store your book files.
- Leanpub for typesetting, publishing and selling your work.
The following are optional, but highly recommended for making the workflow more automated and consistent:
- GitHub Actions, CircleCI or some other CI/CD tool, for automating the typesetting, previewing and publication process;
- Hammerspoon (if you are using a Mac) for monitoring book builds.
To illustrate the process and provide you with a starting point, the source repository for this book is available in GitHub. I am populating the repository as I write this book, so you can follow the progress there. In the repository you can see all the “behind the scenes” text and configuration, and follow along as I describe them to you.
There are as many possible variations to this workflow as users out there. If you have any suggestions or tips that you think should be mentioned, please let me know!
The high-level workflow for publishing a book using Emacs, org-mode and Leanpub looks like this:
In this setup, the only files you manipulates directly are your source files. For your text, you use Emacs with org-mode. In many cases, you can also generate figures from within Org mode, if they can be programmatically produced. Emacs exports your Org files into the structure and format required by Leanpub, and you commit the result to a Git repository (Leanpub at the moment supports GitHub and Bitbucket). From there, Leanpub picks up the files and produces a preview of your book, which you can review to make further changes. When you want to publish your work, you instruct Leanpub to do so, and Leanpub takes care of producing the end result and publishing it for the word to see.
Leanpub is a powerful platform for self-publishing. It allows you to create books and courses and to publish, sell and update them directly through the Leanpub portal. It supports a comprehensive API which allows you to automate most operations, tough you can of course do them via the web UI if you prefer.
Books and courses are written in a specialized markup language called Markua, which is a derivative of Markdown but with some additional features that make it easier to publish longer works. Leanpub also supports Leanpub Flavored Markdown, which is an older version of its markup language. Markua is the recommended format, and the one we use in this book.
I like Leanpub not only for the technical aspect of it (self-publishing, text-based markup, the API) but also because of the company behind it. My interactions with them have always been met with helpful and constructive responses, and you can tell they genuinely care about building the best possible publishing platform.
If you are not convinced yet, read Why Leanpub.
Emacs is a powerful open-source editor which runs on any platform, and offers infinite extensibility through its ability to be programmed using the embedded Emacs LISP programming language. If you are not familiar with Emacs, check out the Guided Tour of Emacs to get started.
The setup described in this book is based on Org mode, an incredibly powerful markup language and set of tools that allows keeping notes, tracking tasks, and writing documents. One of its key features is the concept of Exporters: the same Org document can be exported in a multitude of formats using any of the built-in exporters or a wide variety of community-developed exporters.
One of these exporters is =ox-leanpub=, which handles the conversion of Org markup to Leanpub’s Markua or Markdown formats, and also splitting and structuring the files as expected by Leanpub. Ox-leanpub
allows you to write your book or course entirely in Org mode, and completely manages the production of the files needed by Leanpub to render your material. This is the exporter we will use in this book.
Org mode is extensive and powerful, and I invite you to peruse its documentation but also to be patient — Org is best learned gradually and over time. Discover the parts that interest you the most first, and go from there. To get started with writing using Org, check out Getting started with Org-mode in the Org website to get an overview of the most useful markup constructs. For including source code in your documentation, I recommend reading the Working with Source Code section of the Org manual, as well as Howard Abrams’ Introduction to Literate Programming.
If you don’t have a Leanpub account yet, you can create it by visiting https://leanpub.com/ and clicking on the “Sign Up” link on the homepage. Note that you can also automatically create your account when you create your first book, as described in Creating the book on Leanpub.
Note that among Leanpub’s pricing plans, only the Standard and Pro plans support the “Write on your computer, and sync with Dropbox, GitHub or Bitbucket” option, which is what we focus on in this book. If you want to enable webhooks and other automation aspects, you will need a Pro plan, which supports the “Leanpub API” option. You need to choose your pricing plan when you create your first book, but you can change it later at any time.
The centerpiece of your local setup is the Emacs editor with Org-mode and the ox-leanpub
exporter. There are many different ways of configuring Emacs. In this chapter we will look at setting up a basic configuration by hand, and also an example of a more advanced configuration using Doom Emacs, a popular “Emacs distro” which comes with a set of ready-to-use configuration settings.
Emacs configuration is read from ~/.emacs.d/init.el
. Let’s review the minimum configuration you need to follow along this book.
First, we need to set up the Emacs package system, which enables you to easily install packages from various repositories. Add the following lines to your init.el
file to declare the package repositories to use:
(customize-set-variable
'package-archives
'(("marmalade" . "https://marmalade-repo.org/packages/")
("melpa" . "https://melpa.org/packages/")
("elpa" . "https://elpa.gnu.org/packages/")))
Then we initialize the package system and refresh the list of packages.
(package-initialize)
(when (not package-archive-contents)
(package-refresh-contents))
I highly recommend using the use-package library to manage the packages in your config, since it allows easy, self-contained and declarative installation and configuration of packages. Since use-package
is not bundled with Emacs, the first thing we do is install and load it by hand. All other packages are then declaratively installed and configured with use-package
.
(when (not (package-installed-p 'use-package))
(package-install 'use-package))
(require 'use-package)
Using use-package
we can load the org
package. This is included with Emacs.
(use-package org)
Finally, we declare ox-leanpub
. In this case, use-package
installs the package thanks to the :ensure t
declaration, and it loads it only after org
has been loaded.
(use-package ox-leanpub
:ensure t
:after org)
Doom Emacs is one of a few “Emacs Distros” that provide configuration frameworks for more easily utilizing the multiple features of Emacs. The Doom Emacs base configuration takes care of package management, performance tuning and reasonable defaults for a number of settings, allowing you to simply select and configure additional functionality you need.
To enable Org mode and the ox-leanpub
exporter in Doom Emacs, you need to do first enable the org
module by making sure the following line in your ~/.doom.d/init.el
file is uncommented (it already is in the default Doom installation):
org ; organize your plain life in plain text
To install the ox-leanpub
exporter, you need to add the following line to your ~/.doom.d/packages.el
file:
(package! ox-leanpub)
And load the package by adding the following to ~/.doom.d/config.el
:
(use-package! ox-leanpub
:after org)
Once you have made these changes, you need to run doom sync
from your terminal to make sure all the necessary modules are installed, and then restart Emacs.
The first step is to choose a short name or slug for your book. This is the URL identifier for your book in Leanpub, and it should also be the name of your git repository (this is not mandatory, but makes the automation easier). This book’s slug is emacs-org-leanpub
, so its Leanpub URL will be leanpub.com/emacs-org-leanpub.
Once we have a slug, we create a new Git repository for the new book. Leanpub supports both GitHub and Bitbucket repositories. In these descriptions I use GitHub, but similar steps apply if you are using Bitbucket.
$ cd ~/Personal/writing
$ mkdir -p emacs-org-leanpub
$ cd emacs-org-leanpub
$ git init .
Initialized empty Git repository in
/Users/taazadi1/Dropbox/Personal/writing/emacs-org-leanpub/.git/
Next, we create a new GitHub repository and connect it to our local repository:
$ cd ~/Personal/writing/emacs-org-leanpub
$ hub create
Updating origin
https://github.com/zzamboni/emacs-org-leanpub
$ git remote -v
origin https://github.com/zzamboni/emacs-org-leanpub.git (fetch)
origin https://github.com/zzamboni/emacs-org-leanpub.git (push)
Now you can start writing your text inside the new repository. I usually write the main text in a file called book.org
in the root directory of the repository.
To get you started, a basic skeleton for a book is the following:
#+startup: indent
#+tags: noexport sample frontmatter mainmatter backmatter
#+options: toc:nil tags:nil
#+title: Your book title
#+author: Your name
* Introduction
Some text
* Chapter 1
Some more text
Once you have some text, you can simply commit and push the changes to your remote repository:
$ cd ~/Personal/writing/emacs-org-leanpub
$ git add book.org
$ git ci -m "Initial commit of the book"
[master (root-commit) 3e166f4] Initial commit of the book
1 file changed, 230 insertions(+)
create mode 100644 book.org
$ git push -u origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 3.72 KiB | 3.72 MiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/zzamboni/emacs-org-leanpub.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
Now that you have the initial skeleton for your book, it’s time to export it from Org to Leanpub’s Markua format, from which Leanpub can produce a rendered version of your book for you to preview.
For this, we use the ox-leanpub
module which you installed on Emacs. Pressing C-c C-e
will show you Org-mode’s Export screen. Among other options, you should see the following:
[M] Export to Leanpub Markua [M] To temporary buffer [m] To file [o] To file and open [b] Multifile: Whole book [s] Multifile: Subset
Press M b
to export the whole book in “Multifile format”, which exports your book from the Org file and creates the structure and files needed by Leanpub to render the book. For example, for this book, the following files, directories and symlinks are created (the original source file is book.org
, everything else is created from it, note that all images stored under manuscript/resources/images
are omitted from this listing):
. ├── README.org ├── book.org ├── images -> manuscript/resources/images └── manuscript ├── Book.txt ├── Subset.txt ├── automation.markua ├── backmatter.txt ├── colophon.markua ├── frontmatter.txt ├── images -> resources/images ├── introduction.markua ├── mainmatter.txt ├── preface-to-the-early-release.markua ├── resources │ └── images ├── setting-up.markua ├── the-workflow.markua └── tips-and-tricks.markua
In short, this is what the export operation does:
- Creates a
manuscript
folder if needed, under which all other files are stored.- A
resources/images
directory is created insidemanuscript
, as required by the Leanpub Markua exporter. - Symlinks to the
images
directory are created both from the top-level directory, and from themanuscript
directory, to allow referencing the same image files both from the Org file and from the exported Markua files.
- A
- Exports one
.markua
file for each top-level header (chapter) in your book. - Creates the
Book.txt
file with the filenames corresponding to the chapters of your book.- Depending on the exporter settings, the
Subset.txt
andSample.txt
files may also be created.
- Depending on the exporter settings, the
Now that you have the basics of a book, you need to create a new book in Leanpub and link it to your Git repository. Assuming you are signed into your Leanpub account, you can do this by visiting https://leanpub.com/create/book, and following the prompts. In particular, note the following:
- The “On your computer” option under “Where do you want to write” is only available in the paid Leanpub plans. Choose the corresponding git option, and enter the path to your repository as created above.
- I suggest you use the same name for the “Book URL” and for your Git repository. This is not mandatory, but it makes some of the automation easier (particularly for integration with CI/CD systems).
- I usually enable “Send output to Dropbox” to always have the latest rendered version of my books synchronized to my machine, but feel free to leave it disabled if you prefer.
After you create the book, Leanpub shows you a “Getting Started” page which describes some additional steps you need to complete to finish setting up your book and its integration with your git repository.
Make sure you follow these instructions, which include:
- Adding Leanpub as a collaborator to your GitHub or BitBucket repository. This makes it possible for Leanpub to read the files from your repository to render the book.
- Adding a webhook to your repository to trigger an automatic preview of your book whenever you push new changes to your repository. This is an optional step, but one which makes it much easier to generate the book whenever you make changes. Not this this “hardcodes” the type of book generation (Preview or Publish) which happens when you push changes. For a more complex setup, see CI/CD for previewing and publishing.
Once you have created your book on Leanpub and connected it to your Git repository, you are ready to produce your first preview. Follow the same steps shown in Your first book export to generate the Markua files from your Org file, and then commit and push the changes to your repository.
$ cd ~/Personal/writing/emacs-org-leanpub
$ git add .
$ git ci -m 'Commit for first book Preview'
[master edc3c97] Commit for first book Preview
7 files changed, 80 insertions(+), 9 deletions(-)
$ git push
Enumerating objects: 19, done.
Counting objects: 100% (19/19), done.
Writing objects: 100% (12/12), 1.32 MiB | 1.28 MiB/s, done.
To https://github.com/zzamboni/emacs-org-leanpub.git
00a67a1..edc3c97 master -> master
If you configured the webhook in the previous steps, the git push
will automatically trigger a book preview. If you did not, you can visit the Preview page of your book at https://leanpub.com/your-book-id/preview and clicking on the “Create Preview” button.
In any case, after a few minutes you will get an email from Leanpub telling you about the preview creation.
If you enabled Dropbox integration, the generated preview files of your book will be automatically added to your Dropbox folder, and you can also download them from the Preview page of your book.
Leanpub offers you a great degree of control over all aspects of your books aspect and production. All of them are optional, so you can do them as you explore and feel more comfortable. I highly recommend that you explore your book’s management menu in Leanpub.
Here are some of the common things that I like to configure right away:
- Visual settings
- Found in Settings / Theme. You can choose one of the ready-made themes as chosen when you created the book (Business, Technical, Fiction) but I like to choose “Custom” and fine-tune the different parameters. You can customize the page size, fonts, line spacing, line numbering in code blocks, and many other things. I recommend you set at least the page size you want before creating a cover for your book, since it determines the size of the image you have to upload. But otherwise, feel free to experiment, preview your book with different settings and choose the look you like best.
- Cover
- Found in Settings / Book Cover. This is perhaps one of the most important visual aspects you’ll want to configure to make sure your book stands out. You can upload an image to use as your book cover, although the page also notes that if you do not upload an image, you can also set the cover of your book by storing an image called
title_page.png
ortitle_page.jpg
inside yourimages
folder. The size of the image depends on the page size of your book. - Title and subtitle
- Found in the Settings / Details page. You can modify the title you specified during book creation, and optionally add a subtitle.
- Description and teaser text
- Found in Settings / About. You enter here multiple text description for your book, which appear in the book’s webpage in Leanpub.
- Categories
- Found in Settings / Categories. Allows you to define certain categories for your book, to make them easier to find by readers.
These are just some of the highlights - you should explore all the sections!
Now that you have finished the initial setup of your book, let us look into more detail into the overall workflow you will follow as you write, preview and publish your book, throughout its whole lifecycle. You may remember our overall workflow diagram from The process:
You write your book in Org mode, using the whole range of supported Org markup syntax and features. If you are not familiar with Org yet, I suggest you start at the excellent orgmode.org website for an overview of its features, and that you use the Org mode manual for reference into its details. You can use text formatting markup (italics, bold, verbatim
, etc.) tables, code blocks, example blocks, lists of different types, headlines, and more. Most constructs will be correctly exported into Markua format.
There are a few Leanpub-specific features that you can use in your Org document.
Normally, a level-1 Org heading (preceded by a single asterisk):
* This is a chapter
Is exported as a Markua chapter:
# This is a chapter
However, Markua also supports parts, which are used to denote higher-level groupings of chapters. You can denote a level-1 heading that should be exported as a part by tagging it with part
. For example:
* Part one: the beginning :part:
Is exported in Markua as:
# Part one: the beginning #
In Markua, you can use special directives to denote the book’s front-, main- and backmatter sections, which has an impact on how they are numbered and displayed in the table of contents. You can specify these directives by tagging the corresponding level-1 heading (it can be either a chapter or a part heading) with the frontmatter
, mainmatter
and backmatter
tags, respectively. For example:
* Preface :frontmatter:
...
* Chapter one :mainmatter:
...
* Appendix one :backmatter:
Will be exported as:
{frontmatter}
# Preface
...
{mainmatter}
# Chapter one
...
{backmatter}
# Appendix one
Individual sections can be marked for inclusion in your book’s sample by tagging them with sample
. For example:
* Preface to the book sample :sample:nobook:
Is exported as:
{sample: true, book: false}
# Preface to the book sample
Leanpub supports a few different block elements with different semantic meanings. The following are all supported by ox-leanpub
:
Block type | Gets exported as |
---|---|
#+begin/end_aside | {aside} |
#+begin/end_blockquote | {blockquote} |
#+begin/end_blurb | {blurb} |
#+begin/end_center | {blurb, class: "center"} |
#+begin/end_discussion | {blurb, class: "discussion"} |
#+begin/end_error | {blurb, class: "error"} |
#+begin/end_exercise | {blurb, class: "exercise"} |
#+begin/end_information | {blurb, class: "information"} |
#+begin/end_note | {blurb, class: "information"} |
#+begin/end_question | {blurb, class: "question"} |
#+begin/end_quote | {blockquote} |
#+begin/end_tip | {blurb, class: "tip"} |
#+begin/end_warning | {blurb, class: "warning"} |
You can specify a custom icon for any block using the :icon
attribute in an #+ATTR_LEANPUB
line. For example:
#+ATTR_LEANPUB: :icon github
#+begin_tip
Tip with a GitHub icon instead of the default.
#+end_tip
You can change the default icon for a block for the whole document, or you can even define your own block types, by using #+MARKUA_BLOCK
lines. The syntax is:
#+MARKUA_BLOCK: blockname [:class classname] [:icon iconname]
Where blockname
and at least one of :class
or :icon
needs to be specified:
blockname
is the name of the block to define. Can be one of the existing block names (to redefine it) or a new one.classname
(optional) is the name of an existing supported Markua block class (as listed in the table above). It can be omitted to use a plain{blurb}
block.iconname
(optional) is a valid icon name to use for the block.
For example, you could change the default icon of tip
blocks to be a lightbulb instead of the default key icon:
#+MARKUA_BLOCK: tip :class tip :icon lightbulb
#+begin_tip
Tip with a lightbulb!
#+end_tip
We can also define a completely new block type:
#+MARKUA_BLOCK: leanpub :icon leanpub
#+begin_leanpub
Leanpub block!
#+end_leanpub
If a #+CAPTION
is specified for a block, it is exported as a headline at the top of the block. By default, the level of the headline is one below the current level (e.g. if the block is under a level-2 headline, its caption will be produced as a level-3 headline). You can configure this for the whole document by setting the #+MARKUA_BLOCK_CAPTION_LEVEL
option, or for individual blocks by specifying the :caption-level
option in the #+ATTR_LEANPUB
line. Valid values for this option are:
same
: the caption will be produced as a same-level headline;- A number 1-9: the caption will be produced as a headline of the specified level;
below
(or anything else): default behavior, caption will be produced at one level below the current one.
For example:
#+caption: Default caption level
#+begin_information
This is a block with the default caption level.
#+end_information
#+caption: Fixed level-2 caption
#+attr_leanpub: :caption-level 2
#+begin_information
This block's headline will be bigger!
#+end_information
Org has extensive facilities for working with source code. By far, any code blocks you include in your Org document will be properly recognized and typeset by Leanpub. The only difference might be in syntax highlighting support. Leanpub uses Pygments for syntax highlighting, one of the most extensive syntax highlighting programs. However, Emacs does its own, and there are many languages supported by Emacs (including syntax highlighting) that are not included in Pygments. In those cases, you will see syntax highlighting in your code blocks in Emacs, but they will be rendered as plain text by Leanpub.
Both Markua and LFM support specifying attributes for different elements using attribute lines. We have seen already how some Org constructs automatically get converted into attributes (e.g. #+caption
).
Both ox-leanpub-markua
and ox-leanpub-markdown
support specifying attributes as follows:
- An element’s
#+NAME
,ID
orCUSTOM_ID
, if specified, are used for theid
attribute. - An element’s
#+CAPTION
, if specified, is used for thecaption
attribute in Markua and thetitle
attribute in LFM (see Block Captions for details of how captions are produced in block elements). - Other attributes can be specified in an
#+ATTR_LEANPUB
line before the corresponding element. The syntax is the same as for Org header arguments. These are merged with the previous ones if specified. Attributes specified in#+ATTR_LEANPUB
override those specified through other mechanisms.
Example:
#+name: system-diagram
#+caption: Architecture diagram
#+attr_leanpub: :width 30%
[[file:images/diagram.png]]
Gets exported in Markua as:
{width: "30%", id: "system-diagram", caption: "Architecture diagram"}
![Architecture diagram](images/diagram.png)
And in LFM as:
{width="30%", id="system-diagram", title="Architecture diagram"}
![Architecture diagram](images/diagram.png)
If you want to include text that gets passed as-is to the Leanpub backend, you can use #+markua
/ #+markdown
lines, or export
blocks of the corresponding type. For example, Org does not have a construct to specify a page break, but you can specify Leanpub’s {pagebreak}
directive like this:
#+markua: {pagebreak}
Or like this:
#+begin_export markua
{pagebreak}
#+end_export
For including code in LFM you can use similar constructs, using markdown
instead of markua
.
Whenever you have some text that you want to preview or publish, you need to export it using the corresponding backend, Markua or LFM.
The basic export procedure is as described in Your first book export. Normally, you would use the [b] Book: Whole book
option, which creates both the Book.txt
and Subset.txt
files. If you only want to export the subset files, you can use the [s] Book: Subset
option.
However, you can produce different types of exports. Leanpub’s Subset preview can be particularly useful if you want to do a quick preview of some parts of your book in PDF, without producing all the other supported formats. This results in a much shorter rendering time, which allows you to preview and iterate much faster.
The manuscript/Subset.txt
file determines which parts of the book should be included in the subset preview. When using ox-leanpub
, its contents can be determined in several ways:
- By default,
Subset.txt
is not generated. - If you specify the
#+leanpub_book_write_subset
property in your file, you can configure the default subset export for your book, depending on its value:none
(default): not created.tagged
: use all chapters taggedsubset
.all
: use the same chapters asBook.txt
.sample
: use same chapters asSample.txt
.current
: export the current chapter (where the cursor is at the moment of the export) as the contents ofSubset.txt
.
- If you press
C-s
in Org’s export screen to toggle the Export scope parameter to “Subtree”, theSubset.txt
file will contain only the current chapter (the one in which the cursor is when you do the export) regardless of the value of#+leanpub_book_write_subset
. This is useful to do a quick preview as you are working on a single chapter.
Once you have exported your book, you have to commit the changes to your git repository, and push the changes to GitHub. For example, as I type this, I have executed the following commands:
Once you have pushed your changes, you can generate the preview in one of two ways:
- If you have set up a web hook as described in Creating the book on Leanpub, or by using one of the techniques described in Automation, your preview will be triggered automatically when you push to the repository.
- From the Leanpub web UI, you can produce a preview as described in Your first book preview. Note that in the Preview screen you can select whether you want to produce a full or subset preview.
Finally! You have got your book into a state where you think it’s ready to be published. Or you had published already, and have made enough changes that you want to release a new version.
The process is not very different from generating a preview, but you can provide some additional information. From your book’s Admin menu, select “Publish New Version”. If you enable “Notify readers”, you will be able to enter some text for the release notes, which will be sent by email to readers of your book who have signed up for them. Finally, press “Publish Book” and your new release will be published!
It is good practice to tag software when it is released, so that you have a point of reference as you make changes. The same principle applies to your book. Right before you create a new published version, you should create a tag to indicate the point in your repository that contains the release.
You can choose whatever tag name makes sense to you. I recommend using something like publish-<date>
, which will make it easier, later, to remember when you last published your book. To create such a tag, you can use the following command:
git tag publish-20201202
Git also supports annotated tags, which can contain a message associated with the tag. I like storing the release notes together with the tag. We will see later, in CI/CD for previewing and publishing, that tags like this can be used to automatically publish your book, including the generation of release notes. To create an annotated tag, you can use a command like this:
git tag -a publish-20201202
This will open an editor window where you can enter the message to associate with the tag.
That’s it! You now know everything you need to edit, preview and publish your book. In the following chapters, we will look at techniques you can use to make the process easier and more efficient.
One of the powerful aspects of Org is the ability to store code blocks in a document, execute them on the fly, and insert the code, their output, or both into the document. This is an ability that I have used frequently in this book, and I would like to show you a couple of additional examples.
The list of block samples in Appendix B: Block types is in fact generated by the Emacs-LISP code below. This code gets the list of supported blocks at runtime from the ox-leanpub
module, in the variable org-leanpub-markua--block-defs
. This variable stores the definition of the mappings from Org blocks to Markua rendering, so we extract the names of the blocks, using (mapcar #'car ...)
, sort the list, and then build a list of strings containing the necessary Org syntax to generate the block samples.
(mapconcat
(lambda (b)
(format "#+begin_%s
%s block, produced by:\\\\
=#+begin_%s=\\\\
=...=\\\\
=#+end_%s=\\\\
#+end_%s" b (upcase-initials b) b b b))
(sort (mapcar #'car org-leanpub-markua--block-defs)
#'string-lessp)
"\n")
If you look at the source file of this text, you will see that the source block has the following headers:
#+name: block-samples
#+begin_src emacs-lisp :results drawer :exports both
The #+name
line allows us to insert the results of the block somewhere else in the file (instead of right below the source block, as is the default) by inserting the following line where we want the results to go (you will see this line if you look at the source of Appendix B):
#+RESULTS: block-samples
The :results drawer
part of the header tells Org that the results should be enclosed in a drawer - this is a grouping mechanism that allows the results to be enclosed in a group-like construct, but still be considered as part of the Org document, so that they are rendered as part of the book you are reading. There are multiple possible values for the =:results= header, depending on what and how you want its results to be interpreted.
The :exports both
header makes it so that you can see the code above, as well as its results in Appendix B. You can choose both
, code
, results
or none
as values to =:exports=, depending on what you want in the exported document.
Similarly, the table of default blocks in Block elements is generated programmatically by the code below. Note that we are also getting the information from the org-leanpub-markua--block-defs
variable, but in this case we are extracting not only the name but also its definition, which is used to construct the details of each block type using the org-leanpub-markua--attr-str
function. This is the same function used by the org-leanpub-markua
library to generate the blocks, so we can be assured that the information in the table will always be up to date if the library changes.
(let ((blocks (cl-sort
(cl-copy-seq org-leanpub-markua--block-defs)
#'string-lessp :key #'car)))
(concat
;; Table header is fixed
"| Block type | Gets exported as |
|------------+------------------|\n"
;; Table rows are computed
(mapconcat
(lambda (block-def)
(cl-destructuring-bind (src-blk dst-blk dst-attrs) block-def
(format "| =#+begin/end_%s= | =%s= |" src-blk
(org-leanpub-markua--attr-str dst-attrs dst-blk))))
blocks
"\n")))
The overall workflow diagram shown in The process is generated from within Org using Graphviz and the source code shown below. By labeling the source code with a #+name: workflow-diagram
attribute, its result can be inserted anywhere in the book by using a #+results: workflow-diagram
line.
digraph {
// General graph, node and edge properties
bgcolor="transparent";
rankdir=TB;
node [fontname="DejaVu Sans"];
edge [fontname="DejaVu Sans"];
// Define nodes with their colors, shapes and labels
files [label="Source files\n(Org doc, figures, etc.)",
shape=oval];
emacs [label="Emacs + org-mode",
fillcolor=purple, fontcolor=white, style=filled, shape=box];
manuscript [label="Leanpub manuscript\n(Markua + other files)",
fillcolor=yellow, style=filled, shape=oval];
leanpub [label="Leanpub",
fillcolor=blue, fontcolor=white, style=filled, shape=box];
git [label="Git repo",
fillcolor=red, style=filled, shape=box];
preview [label="Book preview",
fillcolor=grey, style=filled, shape=oval];
book [label="Published book\n(PDF, ePub,\nmobi, online)",
shape=oval, fillcolor=green, style=filled];
// Now we define the clusters to group the nodes by workflow stages
subgraph cluster_writing {
label="Write";
rankdir=TB;
graph [style=dotted];
files -> emacs [label="Edit", dir="both", weight=10];
};
subgraph cluster_export_preview {
label="Export and Preview";
labelloc="b"; rankdir=TB;
graph [style=dotted];
emacs -> manuscript [label="Export", weight=10];
manuscript -> git [label="Commit", weight=10];
git -> leanpub [label="Typeset", weight=10];
leanpub -> preview [label="Preview"];
preview -> emacs [label="Update"];
};
subgraph cluster_publish {
label="Publish";
labelloc="b";
graph [style=dotted];
leanpub -> book [label="Publish"];
book -> emacs [label="Update"];
};
}
The following are samples of all the blocks supported by default by ox-leanpub
, and their default rendering by Leanpub.
Center block, produced by:
#+begin_center
\
...
\
#+end_center
\
Quote block, produced by:
#+begin_quote
\...
\#+end_quote
\
This book was written in Emacs {{{emacs-version}}} using Org mode version {{{org-version}}}, running on macOS {{{macos-version}}}. The text is exported to Leanpub Markua format and structure using the =ox-leanpub= package, and published using Leanpub.
Cover photo by Sharon McCutcheon from Pexels.