Automatically render code and documentation from org when you
This is handy to generate README.md (and .txt, and .html, …?) from README.org. It can also be used to "build" all of the programs in a project from a single file, as in the case of this project, itself.
Use this to render documentation, program sources, or a mix of the two, from org format to "pure code" files, markdown, and HTML.
Writing your project's README in org is nearly identical to writing (directly) in Markdown. Org includes more expressive syntax to describe more flexible intents. For example, org allows us to conditionally render parts of a document based on the target extort format (e.g. Markdown, HTML, or "raw" text) and to specify which file code in a given source block is for/from.
The ubiquity of the Markdown (MD/md) format makes maintaining a project README in Org format slightly inconvenient. While some forges support rendering web-portal content directly from Org documents, others do not, potentially leading to vendor lock-in.
This project helps to mitigate that concern though automation.
The default configuration (given a quick start), enables automatic
generation of README.md (and README.html and README.txt) from
README.org automatically upon commit. The supplied
script appends the generated files to the (prior) commit, so the
effect is transparent for the purposes of git history, blame, etc.
The programs use a "stock" (disro- package-manager or GNU supplied) emacs in batch mode (a shell interaction/scripting interface); thus, no direct user interaction with Emacs is expressly required/implied.
Any authoritative version (e.g. from GNU or your OS distro vendor) is likely fine. I suspect that using this in conjunction with a derivative distribution (e.g. Spacemacs) would work; not tested.
Control which files rendered (and post-fixed to each commit) with environment variables.
The programs expect these variables to be setup by
.env which is
generally a shell script in the project's root folder. The project
includes a sample
.env file which only sets these variables. You can
append this to any existing .env file in your project, or simply edit
.env file to define and export them.
The table below lists these shell variables while the subsections following describe how to use them.
|`ORG_GIT_EXPORT`||README.org||file including proj rel path, or several space separated|
|`ORG_GIT_FORMAT`||md html txt||one or more of "md", "txt", or "html"|
Control which files will be exported (converted from org to other
formats) by changing the value of
ORG_GIT_EXPORT, usually by editing
The value should be a file or list of files. Each file name should be given including path, relative to the project root. If you list several files, they should separated by spaces and the entire list enclosed in quotes. It is acceptable but not necessary to enclose a single file/path in quotes.
Extra spaces preceding/following/separating shouldn't be a problem.
In most OSes you can use either single (
') or double (
windows users will want to sick to double-quotes.
Tangling is the process of rendering "pure-code files" (programs) from an org document. By default each file exported is also tangled.
This is controlled by
ORG_GIT_TANGLE exported from the
.env script. Simply replace this line:
Edit it to another file or list as for setting ORGGITEXPORT.
We can export in three formats, of which we do all by default. It is not possible to select formats per a given org source file.
Control the formats output by setting
ORG_GIT_FORMATS, usually by
editing the .env file. You may edit to remove some entries.
Valid values are:
Support for other formats would likely welcome back into this project, noting that requirements outside of "stock" emacs should be guarded; the base project should work with only Emacs as provided by our OS distributions' upstream packaging network. Please see Contribuiting.
To make contact, whether for questions, comments, code suggestions, etc., please
Patches are most welcome, with the caveat that I would like things to work nicely for people with only a OSs "stock" Emacs, e.g. no patches that would introduce hard/noisy deps on non-core Emacs packages.
The README.org file in the root of this projects git repository is used to generate the rest of the project. That is to say, all of the source for this project is (given you are reading the README.org vs README.md, etc.) contained in this file.
That can be quite handy, for example searching this file for a variable name, message or phrase, is guaranteed to turn up all related code and documentation expressly related.
Following the CONTRIBUTING section (notwithstanding the License
boilerplate at the end) the rest of this file contains and explains
this projects' source-code. This file contains the complete source
for all programs in this project; however, exported versions such as
README.md omit most top-of-program commentary.
Changing the code means editing this file.
This will involve editing program code contained within blocks of mark-up that delineate the sources from narrative ("written for humans") and sources for separate programs built within the same org document.
The mark-up wires-up both the "code generation" and "formatted document rendering" for each source block.
Header directives (given in the opening phrase for the block) give the programming language, a file to save program content to, and (in some cases) whether to suppress quoting source the passage when exporting.
A org source block might look like:
#+BEGIN_SRC sh :file pre-commit touch .commit #+END_SRC
To make and test your changes
make your edits to
emacs -batch -exec "(progn (require 'org) (find-file \"README.org\") (org-babel-tangle))"
emacs -batch -l build.el
manually review/test the files create by build.el
create a patch, e.g.
git format-patch master
Default shell environment setup file, in case the target project doesn't have one.
This sets up a couple of environment variables:
ORG_GIT_EXPORTa project root relative file path or a quoted, space separated list of these
The programs created by tangling this
README.org each have these
variables defined when running. This program (the
.env file) is
simply a shell script and can contain other code, for example to
calculate the values used to set the above shell/env variables.
When a given project already has a
.env, appended this projects to
that one, or (perhaps more legibly,) manually edit the target
.env file to set and export the variables.
This is a script file for Emacs.
Generate pure-code files from sources (
org-babel-tangle) and export
org-X-export-to-X) in multiple output formats.
We use the functionality built into org (which is a core component
of Emacs), to first to render pure-code files from this
("tangle"), and then to export this
README.org into other formats,
such as HTML, Markdown, and ASCII text.
Following is the complete program source (excluding top-of-file comments, which aren't shown in exported versions such README.md).
(require 'ox-md) ;; maybe something to install ox-md from MELPA? ;; Tangle first in case we are generating the org files to export (dolist (orgfile (split-string (getenv "ORG_GIT_TANGLE"))) (find-file orgfile) (org-babel-tangle)) (let ((expfmtstr (downcase (getenv "ORG_GIT_FORMAT"))) (expmap '(("md" . org-md-export-to-markdown) ("html" . org-html-export-to-html) ("txt" . org-ascii-export-to-ascii)))) (dolist (orgfile (split-string (getenv "ORG_GIT_EXPORT"))) (find-file orgfile) (dolist (expitem expmap) (when (string-match-p (car expitem) expfmtstr) (funcall (cdr expitem)))))) (provide 'build)
This shell script is called before each git commit. It creates a
"semaphore", touching a file. The
post-commit script will
build.el on the existence of that file, removing
it if it acts. The semaphore file is
.build in the project root,
meaning commuters must have write permission to the project root.
In theory, we can add additional logic to this script. For example, we could check if the commit contains any .org files indented for processing prior to creating the semaphore file.
Shell script executed after each git commit.
If the "semaphore" file created by the
pre-commit script exists,
invoke Emacs in batch mode and run
build.el and append each file
created/updated to the commit.
# do nothing when .commit file is missing if [ -e .commit ]; then # "source" the .env file to create our environment variables . ./.env # tangle and export /bin/env emacs -batch -l build.el # clean-up the commit file rm .commit # list of the files to add ORG_GIT_TMP="" for orgfile in $ORG_GIT_EXPORT $ORG_GIT_TANGLE; do # grep our list so we only add a given file once if [[ -r $orgfile ]]; then for ext in $ORG_GIT_FORMAT; do addfile="$(dirname $orgfile)/$(basename $orgfile .org).$ext" if [[ -z "$(echo \"$ORG_GIT_TMP\" | grep \"\\b$addfile\\b\")" ]]; then ORG_GIT_TMP="$ORG_GIT_TMP $addfile" fi done fi done # add then append to the prior commit if [[ -n "$(echo '$ORG_GIT_TMP' | perl -pe 's/\s+//')" ]]; then git add $ORG_GIT_TMP git commit --amend -C HEAD --no-verify; fi fi exit
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU Affero Public License along with this program. If not, see https://www.gnu.org/licenses/.