~mplscorwin/org-git-hooks

export and tangle on commit
various doc fixes apply patch from gxt:
noop commit for testing
give explicit link descriptions so they render nicely in HTML

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~mplscorwin/org-git-hooks
read/write
git@git.sr.ht:~mplscorwin/org-git-hooks

You can also use your local clone with git send-email.

#Table of Contents

  1. Quick Start
  2. Overview
    1. ABSTRACT
    2. Building with Emacs vs Coding with Emacs
  3. Requirements
  4. Configuration
    1. Selecting Files to Export
    2. Selecting Files to Tangle
    3. Selecting Export Formats
  5. CONTRIBUTING
    1. Some Patches Are More Welcome Than Others
    2. WANTED (TODO, maybe)
    3. Literate Programming
    4. How to Edit
  6. SOURCE
    1. .env
    2. build.el
    3. pre-commit
    4. post-commit
  7. LICENSE

Automatically render code and documentation from org when you git commit.

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.

#Quick Start

  • put build.el in your project root
  • put pre-commit and post-commit in .git/hooks
  • setup a .env file

#Overview

Use this to render documentation, program sources, or a mix of the two, from org format to "pure code" files, markdown, and HTML.

Jump to

#ABSTRACT

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 post-commit script appends the generated files to the (prior) commit, so the effect is transparent for the purposes of git history, blame, etc.

#Building with Emacs vs Coding with Emacs

Using Emacs to edit org files is convent, but not required. Org files are plan text files. Various (other) editors can format, highlight ("font-lock"), and otherwise integrate support for Org.

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.

#Requirements

  • GNU Emacs

    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.

#Configuration

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 your existing .env file to define and export them.

The table below lists these shell variables while the subsections following describe how to use them.

Variable Default Valid Value(s)
`ORG_GIT_EXPORT` README.org file including proj rel path, or several space separated
`ORG_GIT_TANGLE` README.org As above.
`ORG_GIT_FORMAT` md html txt one or more of "md", "txt", or "html"

#Selecting Files to Export

Control which files will be exported (converted from org to other formats) by changing the value of ORG_GIT_EXPORT, usually by editing .env. script.

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 (") quotes: windows users will want to sick to double-quotes.

#Selecting Files to Tangle

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:

ORG_GIT_TANGLE=ORG_GIT_EXPORT

Edit it to another file or list as for setting ORGGITEXPORT.

ORG_GIT_TANGLE="docs/README.org examples/README.org"

#Selecting Export Formats

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:

  • md
  • html
  • txt

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.

#CONTRIBUTING

To make contact, whether for questions, comments, code suggestions, etc., please

  • use the sr.ht tracker for this project, or
  • make a PR at the mirror on GitHub, or
  • email me! See L3 of README.org (this file)

#Some Patches Are More Welcome Than Others

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.

Thank you.

#WANTED (TODO, maybe)

  • implement tangle on commit
  • test
  • more export targets? (feature detection for PDF?)
  • configuration file?
  • different export targets per org source-file?

#Literate Programming

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

#How to Edit

To make and test your changes

  1. make your edits to README.org

  2. manually tangle README.org

    emacs -batch -exec "(progn (require 'org) (find-file \"README.org\") (org-babel-tangle))"
    
  3. manually execute build.el

    emacs -batch -l build.el
    
  4. manually review/test the files create by build.el

  5. create a patch, e.g.

    git format-patch master
    

#SOURCE

#.env

Default shell environment setup file, in case the target project doesn't have one.

This sets up a couple of environment variables:

  • ORG_GIT_EXPORT a project root relative file path or a quoted, space separated list of these
  • ORG_GIT_TANGLE same
  • ORGGITFORMAT one or more (quoted, space separated) among md, html, and txt.

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 project's .env file to set and export the variables.

#build.el

This is a script file for Emacs.

Generate pure-code files from sources (org-babel-tangle) and export documentation (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 README.org ("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)

#pre-commit

This shell script is called before each git commit. It creates a "semaphore", touching a file. The post-commit script will predicate running 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.

touch .commit

#post-commit

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

#LICENSE

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/.