SkillAgentSearch skills...

Makem.sh

Makefile-like script for linting and testing Emacs Lisp packages

Install / Use

/learn @alphapapa/Makem.sh
About this skill

Quality Score

0/100

Supported Platforms

Universal

Tags

README

#+TITLE: makem.sh

#+PROPERTY: LOGGING nil

Note: This readme works with the org-make-toc https://github.com/alphapapa/org-make-toc package, which automatically updates the table of contents.

=makem=.sh is a script that helps to build, lint, and test Emacs Lisp packages. It aims to make linting and testing as simple as possible without requiring per-package configuration.

It works similarly to a Makefile in that "rules" are called to perform actions such as byte-compiling, linting, testing, etc.

Source and test files are discovered automatically from the project's Git repo, and package dependencies within them are parsed automatically.

Output is simple: by default, there is no output unless errors occur. With increasing verbosity levels, more detail gives positive feedback. Output is colored by default to make reading easy.

The script can run Emacs with the developer's local Emacs configuration, or with a clean, "sandbox" configuration that can be optionally removed afterward. This is especially helpful when upstream dependencies may have released new versions that differ from those installed in the developer's personal configuration.

  • Contents :noexport: :PROPERTIES: :TOC: :include siblings :depth 0 :END: :CONTENTS:
  • [[#installation][Installation]]
  • [[#usage][Usage]]
  • [[#changelog][Changelog]]
  • [[#comparisons][Comparisons]] :END:
  • Screenshots :PROPERTIES: :TOC: :ignore (this) :END:

Some example output. The first shows running the =test= rule with verbosity level 1, which shows which tests are run but omits each test's output unless it fails:

[[images/make-test-v.png]]

Increasing the verbosity shows green output for passing tests:

[[images/make-test-vv.png]]

The =lint-compile= rule treats byte-compiler warnings as errors:

[[images/make-lint-compile.png]]

The =all= rule runs all rules and treats warnings as errors:

[[images/make-all.png]]

Of course, with increased verbosity, it also shows which rules did not signal errors:

[[images/make-all-v.png]]

The included =test.yml= GitHub Actions file can be used to easily set up CI, giving output like:

[[images/github-action.png]]

  • Installation :PROPERTIES: :TOC: 0 :END:

Copy =makem.sh= into your package's root directory. Optionally, also copy =Makefile=, to make calling the script easier.

  • Usage :PROPERTIES: :TOC: :include descendants :END: The =makem.sh= script can be called directly or through a =Makefile=. Use =makem.sh --help= to list available rules. The Emacs library =makem.el= provides a Transient dispatcher (like Magit) to easily run the script from within Emacs with selected options.

:CONTENTS:

  • [[#makemsh-script][makem.sh script]]
  • [[#transient-menu-makemel][Transient menu (makem.el)]]
  • [[#makefile][Makefile]]
  • [[#github-action][GitHub Action]]
  • [[#github-linguist-statistics][GitHub Linguist statistics]]
  • [[#git-pre-push-hook][git pre-push hook]]
  • [[#spell-checking][Spell checking]] :END:

** =makem.sh= script

The script may be called directly to specify additional options.

#+BEGIN_EXAMPLE makem.sh [OPTIONS] RULES...

Linter- and test-specific rules will error when their linters or tests are not found. With -vv, rules that run multiple rules will show a message for unavailable linters or tests.

Rules: all Run all lints and tests. compile Byte-compile source files.

lint           Run all linters, ignoring unavailable ones.
lint-checkdoc  Run checkdoc.
lint-compile   Byte-compile source files with warnings as errors.
lint-declare   Run check-declare.
lint-elsa      Run Elsa (not included in "lint" rule).
lint-indent    Lint indentation.
lint-package   Run package-lint.
lint-regexps   Run relint.

test, tests           Run all tests, ignoring missing test types.
test-buttercup        Run Buttercup tests.
test-ert              Run ERT tests.
test-ert-interactive  Run ERT tests interactively.

batch        Run Emacs in batch mode, loading project source and test files
             automatically, with remaining args (after "--") passed to Emacs.
interactive  Run Emacs interactively, loading project source and test files
             automatically, with remaining args (after "--") passed to Emacs.

Options: -d, --debug Print debug info. -h, --help I need somebody! -v, --verbose Increase verbosity, up to -vvv. --no-color Disable color output.

--debug-load-path  Print load-path from inside Emacs.

-E, --emacs PATH  Run Emacs at PATH.

-e, --exclude FILE  Exclude FILE from linting and testing.
-f, --file FILE     Check FILE in addition to discovered files.

-c, --compile-batch  Batch-compile files (instead of separately; quicker, but
                                          may hide problems).
-C, --no-compile     Don't compile files automatically.

Sandbox options: -s[DIR], --sandbox[=DIR] Run Emacs with an empty config in a sandbox DIR. If DIR does not exist, make it. If DIR is not specified, use a temporary sandbox directory and delete it afterward, implying --install-deps and --install-linters. --install-deps Automatically install package dependencies. --install-linters Automatically install linters. -i, --install PACKAGE Install PACKAGE before running rules.

An Emacs version-specific subdirectory is automatically made inside
the sandbox, allowing testing with multiple Emacs versions.  When
specifying a sandbox directory, use options --install-deps and
--install-linters on first-run and omit them afterward to save time.

Source files are automatically discovered from git, or may be specified with options. Package dependencies are discovered from "Package-Requires" headers in source files, from -pkg.el files, and from a Cask file.

Checkdoc's spell checker may not recognize some words, causing the lint-checkdoc' rule to fail. Custom words can be added in file-local or directory-local variables using the variable ispell-buffer-session-localwords', which should be set to a list of strings. #+END_EXAMPLE

** Transient menu (=makem.el=)

The Elisp file =makem.el= provides a Transient dispatcher (this file should be installed into your Emacs configuration rather than into a project's directory). Use =M-x makem RET= to show it.

[[images/transient.png]]

** Makefile

A default =Makefile= is provided which calls the =makem.sh= script. Call it with the name of a rule and an optional verbosity level, like:

#+BEGIN_SRC sh

Run all rules.

$ make all

Run all lints.

$ make lint

Run all tests.

$ make test

Run ERT tests with verbosity level 1.

$ make v=v test-ert

Run Buttercup tests with verbosity level 2.

$ make v=vv test-buttercup

Run tests with emacs-sandbox.sh in a temporary sandbox.

Implies install-deps=t.

$ make sandbox=t test

Initialize a permanent sandbox directory, DIR (the developer might

choose to recreate it manually when necessary, leaving it in place

to save time otherwise). Then run all linters and tests.

$ make sandbox=DIR install-deps=t install-linters=t $ make sandbox=DIR all #+END_SRC

** GitHub Action

Using Steve Purcell's [[https://github.com/purcell/setup-emacs][setup-emacs]] Action, it's easy to set up CI on GitHub for an Emacs package.

  1. Put =makem.sh= in your package's repo and make it executable.
  2. Add [[file:test.yml][test.yml]] (from the =makem.sh= repo) to your package's repo at =.github/workflows/test.yml=. It should work without modification for most Emacs packages.

** GitHub Linguist statistics

Having =makem.sh= in your repository will affect GitHub's language stats provided by [[Https://github.com/github/linguist][Linguist]], which might cause it to be classified as a Shell project rather than an Emacs Lisp one. The [[https://github.com/github/linguist#my-repository-is-detected-as-the-wrong-language][Linguist documentation]] explains how to avoid this. Probably the most appropriate way is to use a =.gitattributes= file to classify =makem.sh= as vendored, like:

#+BEGIN_EXAMPLE sh makem.sh linguist-vendored #+END_EXAMPLE

** git pre-push hook

It's often helpful to run tests automatically before pushing with git. Here's an example of using =makem.sh= in a =pre-push= hook:

#+BEGIN_SRC sh #!/bin/sh

* Commit parameters

Unused now, but good for future reference. See man 5 githooks.

remote="$1" url="$2"

read local_ref local_sha remote_ref remote_sha

* Run tests

Not using sandbox and auto-install, because "git push" shouldn't

cause remote code to be downloaded and executed (i.e. what would

happen by installing packages). It can be done manually when

needed. However, in a CI system running in a container, where

testing in a clean config against the latest available dependency

versions is desired, one could use:

make sandbox=t install-deps=t test

make test #+END_SRC

** Spell checking

Checkdoc's spell checker may not recognize some words, causing the ~lint-checkdoc~ rule to fail. Custom words can be added in file-local or directory-local variables using the variable ~ispell-buffer-session-localwords~, which should be set to a list of strings.

  • Changelog :PROPERTIES: :TOC: :ignore children :END:

** 0.8-pre

Changes

  • Don't initialize package system when not necessary (as initializing the package system causes some other libraries, like ~url~, to be loaded, which can obscure problems which might occur otherwise). ([[https://github.com/alphapapa/makem.sh/issues/47][#47]]. Thanks to [[https://github.com/josephmturner][Joseph Turner]] for reporting.)

Fixes

  • Escape backticks in ~makem.sh~'s ~usage~ function's Bash here-document. (Th
View on GitHub
GitHub Stars178
CategoryDevelopment
Updated3d ago
Forks13

Languages

Shell

Security Score

95/100

Audited on Mar 31, 2026

No findings