diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 93229ca..f772c75 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,28 +5,28 @@ Development process & infrastructure guide. # Table of Contents -- [Submitting Pull Requests](#org4355e81) -- [Development](#orgb80f1ca) - - [Running tests](#orgd876a7f) - - [Lint and byte-compile code](#orgbec56ee) - - [Loading and re-loading your package](#org92b2837) - - [Re-generating Documentation](#org1cd6e7f) -- [License](#org18b3bcd) -- [Developer Certificate of Origin (DCO)](#orgc37003a) - - [Sign-off](#orga38e641) - - [GPG signature](#org8bb1158) - - [User setup for submitting changes](#orgbb3bd78) - - [Maintaining versions](#org5b25b0b) - - - +- [Submitting Pull Requests](#orgdb50680) +- [Development](#org68d37af) + - [Running tests](#org0c6d9cc) + - [Lint and byte-compile code](#org267041a) + - [Loading and re-loading your package](#orgde00915) + - [Re-generating Documentation](#org2c72808) +- [License](#orga1f71ba) +- [Developer Certificate of Origin (DCO)](#orge655018) + - [Sign-off](#orgb5c6344) + - [GPG signature](#org468b6f3) + - [User setup for submitting changes](#org982715d) + - [Maintaining versions](#orgbf8e6d4) + + + # Submitting Pull Requests Be sure to abide by instructions in [the pull request template](../.github/pull_request_template.md). - + # Development @@ -34,7 +34,7 @@ This repository was created with [elisp-repo-kit](https://github.com/positron-so development workflows. - + ## Running tests @@ -62,7 +62,7 @@ have. **You do not need Nix to run tests pretty close to what CI does.** CI will use Nix to obtain Emacs & dependencies. - + ## Lint and byte-compile code @@ -86,7 +86,7 @@ You can run the lints manually almost the same as running tests. emacs --script .github/run-shim.el -- lint-tests - + ## Loading and re-loading your package @@ -109,7 +109,7 @@ directly, call `emacs-lisp-byte-compile-and-load` or `emacs-lisp-native-compile-and-load`. - + ## Re-generating Documentation @@ -131,7 +131,7 @@ manually type the path to the `manual.info`. Honestly, just use because they use `#+include:` directives. - + # License @@ -139,7 +139,7 @@ This package is distributed under the terms of the [included license](./COPYING) configuration, documentation, and scripts are MIT licensed. - + # Developer Certificate of Origin (DCO) @@ -152,7 +152,7 @@ A [copy of the DCO](./DCO) is distributed with this project. Read its text to understand the significance of configuring for sign-off. - + ## Sign-off @@ -163,7 +163,7 @@ following: Signed-off-by: Random J Developer - + ## GPG signature @@ -174,7 +174,7 @@ participating in. Corroborating user's signature accross a history of works strengthens that user's attestation provided by DCO sign-off. - + ## User setup for submitting changes @@ -249,7 +249,7 @@ equivalent: git rebase -i - + ## Maintaining versions diff --git a/README.md b/README.md index 4790895..f0b4108 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ answer all the questions. # Install ERK - (use-package erk) ; vanilla, assuming you have MELPA configured ;; using elpaca's with explicit recipe @@ -43,18 +42,6 @@ answer all the questions. ;; or use manual load-path & require, you brave yak shaver -## Manual cloning - -The standalone command, `erk-clone` will clone without renaming. - -If you create via [a template](https://github.com/positron-solutions/erk-basic) or clone manually, it's presumed you know what -you're doing at that point. Call `erk-rename` on its own to rename in these -cases. - -There are some customize options that cause the renaming to be transitively -consistent. - - ## Manually add just CI Copy the .github folder and the contributing guide to your package. Set up @@ -63,26 +50,30 @@ your secrets for Cachix. Read the CI customization section. # Table of Contents -- [Creating Packages](#org8f89e2d) -- [Using ERK for development](#org634d2bc) - - [Find Files](#org4337be9) -- [File contents and structure](#org216d1ea) - - [Setting Up Your Github Repository](#org2d988e4) -- [Customizing CI](#org451ffdb) -- [Licensing, Developer Certificate of Origin](#org1de3655) -- [Publishing to MELPA](#org6ad8b28) - - [Creating the recipe](#orga968d33) - - [Testing package build](#org61a373c) - - [Testing stable package build](#org05aef0d) - - [MELPA Lints](#orga1cf780) -- [Maintaining versions](#orge89d3e9) -- [Package scope and relation to other work](#org8cc4546) - - [Dependency Management](#orgcf9d699) - - [Discovering and Running Tests & Lints](#orgd516806) - - [Comparisons](#org78c421e) -- [Contributing](#org10cc81e) -- [Footnote on FSF and Emacs Core Licensing](#org65e7167) -- [Shout-outs](#org6008479) +- [Creating Packages](#orgdbe452f) +- [Using ERK for development](#orgac601cf) + - [Loading and re-loading your package](#orga0cf377) + - [Jumping to files](#org396eda3) + - [Run tests](#orgcda3976) + - [Duplicating CI Locally](#org3dacc44) + - [Find Files](#org4970f85) +- [File contents and structure](#org8ce5e2c) + - [Setting Up Your Github Repository](#orge370d02) +- [Customizing CI](#orgad90c4f) +- [Licensing, Developer Certificate of Origin](#orgbccb740) +- [Publishing to MELPA](#org89cc5b7) + - [Creating the recipe](#orgc42570c) + - [Testing package build](#org8509443) + - [Testing stable package build](#orgf82d903) + - [MELPA Lints](#org0496004) +- [Maintaining versions](#org2c1191b) +- [Package scope and relation to other work](#orge7e095a) + - [Dependency Management](#org5df7fa9) + - [Discovering and Running Tests & Lints](#orgdcccbf5) + - [Comparisons](#orgbfd286f) +- [Contributing](#orgbaa1077) +- [Footnote on FSF and Emacs Core Licensing](#orgd1e7ded) +- [Shout-outs](#org0f2ed06) # Creating Packages @@ -93,15 +84,15 @@ ask you for: - Choose a template - Root directory you want to clone to - Package title -- Package feature -- Package prefix +- Package feature name +- Package elisp prefix - Author name - Email address - GitHub user or organization `erk-new` also calls `erk-rename-relicense` to rename all of the files, string replace names, and re-license to GPL3. It also changes the author and resets -the git history. Now just follow the steps in [finish setting up](#org2d988e4). Have fun! +the git history. Now just follow the steps in [finish setting up](#orge370d02). Have fun! # Using ERK for development @@ -109,22 +100,48 @@ the git history. Now just follow the steps in [finish setting up](#org2d988e4). Elisp repo kit contains some convenience functions to reload your package and to discover and run ert tests. These shortcuts just make common cases faster. -- Loading and re-loading your package - - `erk-reload-project-package` will unload and recompile your package if - necessary and then reload it. - - `erk-reload-project-tests` is the complementary command for reloading tests. -- Run tests - - `erk-ert-project` will discover, rebuild & reload if necessary, and run - tests. There are a few other commands to augment the [ert](https://www.gnu.org/software/emacs/manual/html_node/ert/) package. +## Loading and re-loading your package + +`erk-reload-project-package` will unload and recompile your package if +necessary and then reload it. + +`erk-reload-project-tests` is the complementary command for reloading tests. + + +## Jumping to files -- Duplicating CI Locally - The CI configuration is all stored in [.github](./.github/). Usually you will want - development instructions in your new repository. The [CONTRIBUTING](./CONTRIBUTING.md) guide - contains instructions to reproduce the CI behavior. +Lots of locations come in pairs or small sets. Jumping functions try to go +between these locations or to a sensible default location. They are very +do-what-I-mean (DWIM). + +- `erk-jump-features` is a command that will jump between the feature and its + corresponding test feature. If you aren't in an elisp file, it will jump to + the root feature file. + +- `erk-jump-defs` will try to go to the test definition and even ask insert an + `ert-deftest` body if you forgot to write the test. It can then jump back to + the function definition. If it fails, it will fall back to jumping to the + feature. + + +## Run tests + +**Warning**! These commands are very likely to completely change. If you want to +try changing them, go ahead. Binding them is not recommended, but they do work. + +- `erk-ert-project` will discover, rebuild & reload if necessary, and run tests. + There are a few other commands to augment the [ert](https://www.gnu.org/software/emacs/manual/html_node/ert/) package. + +- `erk-ert-rerun-this` Is not a very smart function yet, but if you are working on + a test at point, it will run it or re-run it. + + +## Duplicating CI Locally + +The CI configuration is all stored in [.github](./.github/). Usually you will want +development instructions in your new repository. The [CONTRIBUTING](./CONTRIBUTING.md) guide +contains instructions to reproduce the CI behavior. ## Find Files @@ -135,10 +152,10 @@ annoying. ERK provides a helper to find files based on their purpose. `erk-find` will ask you to pick the file based on what it does. It's choices: -- ci-dco +- ci-dco-action - ci-nix-flake - ci-run-shim -- ci-tests +- ci-tests-action - doc-contributing - doc-manual - doc-readme @@ -146,13 +163,13 @@ choices: Generated files or extremely common files are not included. For each one of these choices, there is a corresponding command: -- `erk-find-ci-dco` +- `erk-find-ci-dco-action` - `erk-find-ci-nix-flake` - `erk-find-ci-run-shim` -- `erk-find-ci-test` +- `erk-find-ci-tests-action` - `erk-find-doc-contributing` @@ -165,7 +182,6 @@ these choices, there is a corresponding command: *After cloning and renaming,* you will have a file tree like this: - ├── .gitignore # ignores for byte compiles, autoloads etc │ ├── README.md # this file @@ -196,7 +212,7 @@ these choices, there is a corresponding command: └── erk-test.el # ERT unit tests for the package You can use either a multi-file or flat layout for lisp. Just name test files -`something-test.el` and keep all lisp files in root, `/lisp` or `/test` +`something-tests.el` and keep all lisp files in root, `/lisp` or `/test` directories. @@ -204,27 +220,21 @@ directories. You can copy this checklist to your org agenda files: -- [X] Create a repository (from [install](#orgaf2c287) instructions) +- [X] Create a repository (from [install](#orgb10843e) instructions) - [ ] Create an empty GitHub repository configure it as your git remote - [ ] Set up your git commit signing (and verification so that it's obvious) - **and** [sign-off](#org1de3655) so that it will be hypothetically [straightforward](README.md) for for FSF + **and** [sign-off](#orgbccb740) so that it will be hypothetically [straightforward](README.md) for for FSF to pull in your changes if they later change to DCO instead of copyright assignment. - [ ] Sign up for [cachix](https://app.cachix.org/) and, create a binary cache with API tokens and public read access -\#+cindex nix enabling cachix -\#+cindex github adding secrets - - [ ] Add repository secrets necessary for your GitHub actions `CACHIX_AUTH_TOKEN` and `CACHIX_CACHE_NAME` (settings -> secrets -> new repository secret) -\#+cindex github allowed actions - - [ ] Enable actions and add the following actions to your allowed actions list: - actions/checkout@v3.2.0, cachix/cachix-action@v12, cachix/install-nix-action@v20, @@ -233,7 +243,7 @@ You can copy this checklist to your org agenda files: **Note**, Python is used to run a DCO check script, nothing more. - [ ] Get your package working, pushed, actions run, and CI badges all green -- [ ] [Publish](#org6ad8b28) to MELPA +- [ ] [Publish](#org89cc5b7) to MELPA - [ ] Make a post on [reddit](https://reddit.com/r/emacs/) and [mastodon](https://emacs.ch/) about your new package diff --git a/doc/erk.texi b/doc/erk.texi index 11332aa..4fe1d84 100644 --- a/doc/erk.texi +++ b/doc/erk.texi @@ -36,7 +36,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE@. @finalout @titlepage @title Elisp Repo Kit -@subtitle for version 0.4.0 +@subtitle for version 0.5.0 @author Positron Solutions @page @vskip 0pt plus 1filll @@ -91,6 +91,8 @@ Navigating the project * Documenting Your Package:: * Distributing Your Package:: * Maintaining Your Package:: +* Configuring ERK:: +* Conventions:: * Indices:: @detailmenu @@ -105,12 +107,12 @@ High Level Overview Install ERK -* Manual cloning:: * Manually add just CI:: Using ERK for development * Loading and re-loading your package:: +* Jumping to files:: * Run tests:: * Duplicating CI Locally:: * Find Files:: @@ -173,6 +175,7 @@ Indices * Command and Function index:: * Concept index:: +* Variable index:: @end detailmenu @end menu @@ -202,11 +205,8 @@ Elisp packages). @section File contents and structure @cindex project layout - @emph{After cloning and renaming,} you will have a file tree like this: - @example - ├── .gitignore # ignores for byte compiles, autoloads etc │ ├── README.md # this file @@ -235,11 +235,9 @@ Elisp packages). │ └── test └── erk-test.el # ERT unit tests for the package - @end example - You can use either a multi-file or flat layout for lisp. Just name test files -@code{something-test.el} and keep all lisp files in root, @code{/lisp} or @code{/test} +@code{something-tests.el} and keep all lisp files in root, @code{/lisp} or @code{/test} directories. @node Developing @@ -302,7 +300,6 @@ workflows for doing so. @chapter Install ERK @lisp - (use-package erk) ; vanilla, assuming you have MELPA configured ;; using elpaca's with explicit recipe @@ -314,28 +311,12 @@ workflows for doing so. :straight (erk :type git :host github :repo "positron-solutions/elisp-repo-kit")) ;; or use manual load-path & require, you brave yak shaver - @end lisp @menu -* Manual cloning:: * Manually add just CI:: @end menu -@node Manual cloning -@section Manual cloning - -@findex erk-clone -The standalone command, @code{erk-clone} will clone without renaming. - -@findex erk-rename -If you create via @uref{https://github.com/positron-solutions/erk-basic, a template} or clone manually, it's presumed you know what -you're doing at that point. Call @code{erk-rename} on its own to rename in these -cases. - -There are some customize options that cause the renaming to be transitively -consistent. - @node Manually add just CI @section Manually add just CI @@ -357,9 +338,9 @@ Root directory you want to clone to @item Package title @item -Package feature +Package feature name @item -Package prefix +Package elisp prefix @item Author name @item @@ -381,6 +362,7 @@ to discover and run ert tests. These shortcuts just make common cases faster. @menu * Loading and re-loading your package:: +* Jumping to files:: * Run tests:: * Duplicating CI Locally:: * Find Files:: @@ -396,12 +378,50 @@ necessary and then reload it. @findex erk-reload-project-tests @code{erk-reload-project-tests} is the complementary command for reloading tests. +@node Jumping to files +@section Jumping to files + +@cindex jumping +Lots of locations come in pairs or small sets. Jumping functions try to go +between these locations or to a sensible default location. They are very +do-what-I-mean (DWIM). + +@findex erk-jump-features +@itemize +@item +@code{erk-jump-features} is a command that will jump between the feature and its +corresponding test feature. If you aren't in an elisp file, it will jump to +the root feature file. +@end itemize + +@findex erk-jump-defs +@itemize +@item +@code{erk-jump-defs} will try to go to the test definition and even ask insert an +@code{ert-deftest} body if you forgot to write the test. It can then jump back to +the function definition. If it fails, it will fall back to jumping to the +feature. +@end itemize + @node Run tests @section Run tests -@findex erk-ert-project~ -@code{erk-ert-project} will discover, rebuild & reload if necessary, and run -tests. There are a few other commands to augment the @uref{https://www.gnu.org/software/emacs/manual/html_node/ert/, ert} package. +@strong{Warning}! These commands are very likely to completely change. If you want to +try changing them, go ahead. Binding them is not recommended, but they do work. + +@findex erk-ert-project +@itemize +@item +@code{erk-ert-project} will discover, rebuild & reload if necessary, and run tests. +There are a few other commands to augment the @uref{https://www.gnu.org/software/emacs/manual/html_node/ert/, ert} package. +@end itemize + +@findex erk-ert-rerun-this +@itemize +@item +@code{erk-ert-rerun-this} Is not a very smart function yet, but if you are working on +a test at point, it will run it or re-run it. +@end itemize @node Duplicating CI Locally @section Duplicating CI Locally @@ -422,13 +442,13 @@ choices: @itemize @item -ci-dco +ci-dco-action @item ci-nix-flake @item ci-run-shim @item -ci-tests +ci-tests-action @item doc-contributing @item @@ -440,10 +460,10 @@ doc-readme Generated files or extremely common files are not included. For each one of these choices, there is a corresponding command: -@findex erk-find-ci-dco +@findex erk-find-ci-dco-action @itemize @item -@code{erk-find-ci-dco} +@code{erk-find-ci-dco-action} @end itemize @findex erk-find-ci-nix-flake @itemize @@ -455,10 +475,10 @@ these choices, there is a corresponding command: @item @code{erk-find-ci-run-shim} @end itemize -@findex erk-find-ci-test +@findex erk-find-ci-tests-action @itemize @item -@code{erk-find-ci-test} +@code{erk-find-ci-tests-action} @end itemize @findex erk-find-doc-contributing @itemize @@ -694,7 +714,6 @@ Symbols will \~display\~ `quoted' in the manual. Mentioning a symbol will (let ((truth "Source blocks will render inside the manual like this")) (message true)) @end lisp - You can use @code{org-insert-structure-template} etc to create these easily. @node Links @@ -707,7 +726,6 @@ formats such as running elisp expressions through links may not work. The syntax is @samp{[[http://somewhere.com][label for my link]]} if you are unfamiliar. -** Indices @cindex document indices Texi has a lot of built-in support for indexes. Until we have GPT for your manual, indexes are a good way to provide alternative views into your @@ -740,12 +758,10 @@ data type You can also declare a new index with @@defindex or @@defcodeindex. The only difference is that a code index will render entries in monospace, like code. - @example #+TEXINFO_HEADER: @@defindex foo #+TEXINFO_HEADER: @@defcodeindex foo @end example - Creating entries with a custom index could be tricky. Good luck! @node Adding Entries @@ -779,14 +795,12 @@ listings of related entities. @subsection Render the Index :item: Just use a regular header like so: - @example ** Keystroke index :PROPERTIES: :INDEX: ky :END: @end example - The built-in index keys are @samp{ky}, @samp{fn}, @samp{vr}, @samp{cp}, @samp{pg}, and @samp{tp}. @node Distributing Your Package @@ -818,26 +832,24 @@ assignment. Sign up for @uref{https://app.cachix.org/, cachix} and, create a binary cache with API tokens and public read access @end itemize -#+cindex nix enabling cachix -#+cindex github adding secrets +@cindex nix enabling cachix +@cindex github adding secrets @itemize @item Add repository secrets necessary for your GitHub actions @code{CACHIX_AUTH_TOKEN} and @code{CACHIX_CACHE_NAME} (settings -> secrets -> new repository secret) @end itemize -#+cindex github allowed actions +@cindex github allowed actions @itemize @item Enable actions and add the following actions to your allowed actions list: @example - actions/checkout@@v3.2.0, cachix/cachix-action@@v12, cachix/install-nix-action@@v20, actions/setup-python@@v4, - @end example @strong{Note}, Python is used to run a DCO check script, nothing more. @@ -1149,7 +1161,6 @@ time. You can view which attributes are available by inspecting the current version of the flake in the @code{nix repl}: @example - # is known in your flake registry pkgs = import @{ system = builtins.currentSystem; overlays = [ (builtins.getFlake ("emacs-overlay")).overlay ];@} @@ -1161,14 +1172,12 @@ pkgs.emacsUnstable.version # Have fun inspecting the various versions. Checking their declarations in # emacs-overlay source can be insightful. - @end example To obtain truly specific Emacs versions, specify the Emacs source as a flake input and then override the attributes of an Emacs package: @example - inputs = @{ # declare the exact source you want emacs29-src = @{ @@ -1186,7 +1195,6 @@ emacs29 = pkgs.emacs.overrideAttrs (old: @{ @}); # It's nix expressions. It's programming. # Ask your favorite large langauge model! - @end example @cindex nix binary cache @@ -1226,12 +1234,74 @@ By changing the versions within the flake to match the versions in question that are breaking, the user can try versions to confirm a version mismatch and then give you their flake so that you can reproduce and confirm a fix. +@node Configuring ERK +@chapter Configuring ERK + +@vindex erk-after-new-hook +@code{erk-after-new-hook} will be run after you create a new repository. The +@code{default-directory} is set to the newly cloned directory. By default it runs +@code{magit-status}, where you can check the renaming results. + +@vindex erk-templates +@code{erk-templates} can be set to default to your own favorite template. If you want +to fork @uref{https://github.com/positron-solutions/erk-basic, erk-basic}, you can set this to point to your fork. If you are making a +radically different template, consider a PR to mention your fork or even add it +to the defaults! (Please don't abuse this privilege to execute a supply chain +attack or we will find you). + +@node Conventions +@chapter Conventions + +@cindex conventions +ERK relies on @ref{Packaging,(elisp)standard practices,,elisp,} to discover files. +@itemize +@item +Feature names always match file names +@item +Elisp prefixes are consistent throughout the entire package +@item +The project is either all flat or all nested: +@itemize +@item +tests in @samp{/test} +@item +package lisp in @samp{/lisp} +@item +documenation inputs in @samp{/doc} +@end itemize +@item +Tests for a feature are found in the same feature + @samp{-test}. +@item +Unit tests are always named after the function they test, ending in @samp{-test} +@item +Documentation generation expects: +@itemize +@item +CONTRIBUTING.org +@item +manual.org +@item +README.org +@end itemize +@item +Emacs will only install your manual by default if it exports to +@samp{my-feature.texi} +@end itemize + +Files that can vary based on the hosting platform often must use another +convention, such as @samp{/.github}. Tools expect mostly rigid names such as @samp{flake.nix} +and @samp{/.git}. + +If you create a new template, please respect this very simple standard, which is +also expected by package managers like @code{elpaca} and @code{straight}. + @node Indices @chapter Indices @menu * Command and Function index:: * Concept index:: +* Variable index:: @end menu @node Command and Function index @@ -1244,4 +1314,9 @@ then give you their flake so that you can reproduce and confirm a fix. @printindex cp +@node Variable index +@section Variable index + +@printindex vr + @bye \ No newline at end of file diff --git a/doc/manual.org b/doc/manual.org index e1095fc..3eb4330 100644 --- a/doc/manual.org +++ b/doc/manual.org @@ -47,11 +47,8 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: ** File contents and structure #+cindex: project layout - /After cloning and renaming,/ you will have a file tree like this: - #+begin_src shell - ├── .gitignore # ignores for byte compiles, autoloads etc │ ├── README.md # this file @@ -80,11 +77,9 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: │ └── test └── erk-test.el # ERT unit tests for the package - #+end_src - You can use either a multi-file or flat layout for lisp. Just name test files - ~something-test.el~ and keep all lisp files in root, ~/lisp~ or ~/test~ + ~something-tests.el~ and keep all lisp files in root, ~/lisp~ or ~/test~ directories. ** Developing - The package is stored in /lisp and its tests in /test. @@ -113,9 +108,7 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: package is made available on MELPA partly to maintain the structure and workflows for doing so. * Install ERK - #+begin_src elisp - (use-package erk) ; vanilla, assuming you have MELPA configured ;; using elpaca's with explicit recipe @@ -127,29 +120,11 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: :straight (erk :type git :host github :repo "positron-solutions/elisp-repo-kit")) ;; or use manual load-path & require, you brave yak shaver - #+end_src - -** Manual cloning - - #+findex: erk-clone - The standalone command, ~erk-clone~ will clone without renaming. - - #+findex: erk-rename - If you create via [[https://github.com/positron-solutions/erk-basic][a template]] or clone manually, it's presumed you know what - you're doing at that point. Call ~erk-rename~ on its own to rename in these - cases. - - There are some customize options that cause the renaming to be transitively - consistent. - ** Manually add just CI - - Copy the .github folder and the contributing guide to your package. Set up - your secrets for Cachix. Read the CI customization section. - +Copy the .github folder and the contributing guide to your package. Set up +your secrets for Cachix. Read the CI customization section. * Creating Packages - #+findex: erk-new The simplest and intended way is to call ~erk-new~. It will first ask you for: @@ -157,8 +132,8 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: - Choose a template - Root directory you want to clone to - Package title - - Package feature - - Package prefix + - Package feature name + - Package elisp prefix - Author name - Email address - GitHub user or organization @@ -177,11 +152,33 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: #+findex: erk-reload-project-tests ~erk-reload-project-tests~ is the complementary command for reloading tests. +** Jumping to files +#+cindex: jumping +Lots of locations come in pairs or small sets. Jumping functions try to go +between these locations or to a sensible default location. They are very +do-what-I-mean (DWIM). + +#+findex: erk-jump-features +- ~erk-jump-features~ is a command that will jump between the feature and its + corresponding test feature. If you aren't in an elisp file, it will jump to + the root feature file. + +#+findex: erk-jump-defs +- ~erk-jump-defs~ will try to go to the test definition and even ask insert an + ~ert-deftest~ body if you forgot to write the test. It can then jump back to + the function definition. If it fails, it will fall back to jumping to the + feature. ** Run tests - #+findex: erk-ert-project~ - ~erk-ert-project~ will discover, rebuild & reload if necessary, and run - tests. There are a few other commands to augment the [[https://www.gnu.org/software/emacs/manual/html_node/ert/][ert]] package. +*Warning*! These commands are very likely to completely change. If you want to +try changing them, go ahead. Binding them is not recommended, but they do work. + +#+findex: erk-ert-project +- ~erk-ert-project~ will discover, rebuild & reload if necessary, and run tests. + There are a few other commands to augment the [[https://www.gnu.org/software/emacs/manual/html_node/ert/][ert]] package. +#+findex: erk-ert-rerun-this +- ~erk-ert-rerun-this~ Is not a very smart function yet, but if you are working on + a test at point, it will run it or re-run it. ** Duplicating CI Locally The CI configuration is all stored in [[file:./.github/][.github]]. Usually you will want development instructions in your new repository. The [[file:./CONTRIBUTING.md][CONTRIBUTING]] guide @@ -194,10 +191,10 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: ~erk-find~ will ask you to pick the file based on what it does. It's choices: - - ci-dco + - ci-dco-action - ci-nix-flake - ci-run-shim - - ci-tests + - ci-tests-action - doc-contributing - doc-manual - doc-readme @@ -205,21 +202,20 @@ working with Elisp packages, Elisp Repo Kit provides shortcuts: Generated files or extremely common files are not included. For each one of these choices, there is a corresponding command: - #+findex: erk-find-ci-dco - - ~erk-find-ci-dco~ + #+findex: erk-find-ci-dco-action + - ~erk-find-ci-dco-action~ #+findex: erk-find-ci-nix-flake - ~erk-find-ci-nix-flake~ #+findex: erk-find-ci-run-shim - ~erk-find-ci-run-shim~ - #+findex: erk-find-ci-test - - ~erk-find-ci-test~ + #+findex: erk-find-ci-tests-action + - ~erk-find-ci-tests-action~ #+findex: erk-find-doc-contributing - ~erk-find-doc-contributing~ #+findex: erk-find-doc-manual - ~erk-find-find-doc-manual~ #+findex: erk-find-doc-readme - ~erk-find-doc-readme~ - * Documenting Your Package #+cindex: document re-structuring #+cindex: document export @@ -340,7 +336,6 @@ overview: (let ((truth "Source blocks will render inside the manual like this")) (message true)) #+end_src - You can use ~org-insert-structure-template~ etc to create these easily. *** Links :item: Hyperlinks thankfully can be written like you expect, though many org @@ -350,7 +345,6 @@ overview: The syntax is =[[http://somewhere.com][label for my link]]= if you are unfamiliar. - ** Indices #+cindex: document indices Texi has a lot of built-in support for indexes. Until we have GPT for your manual, indexes are a good way to provide alternative views into your @@ -372,14 +366,11 @@ overview: You can also declare a new index with @defindex or @defcodeindex. The only difference is that a code index will render entries in monospace, like code. - #+begin_src org #+TEXINFO_HEADER: @defindex foo #+TEXINFO_HEADER: @defcodeindex foo #+end_src - Creating entries with a custom index could be tricky. Good luck! - *** Adding Entries :item: Quoting a symbol will not result in an index entry. Which quoted symbol @@ -400,14 +391,12 @@ overview: *** Render the Index :item: Just use a regular header like so: - #+begin_src org ,** Keystroke index :PROPERTIES: :INDEX: ky :END: #+end_src - The built-in index keys are =ky=, =fn=, =vr=, =cp=, =pg=, and =tp=. * Distributing Your Package ** Setting Up Your Github Repository @@ -421,21 +410,19 @@ overview: assignment. - [ ] Sign up for [[https://app.cachix.org/][cachix]] and, create a binary cache with API tokens and public read access - #+cindex nix enabling cachix - #+cindex github adding secrets + #+cindex: nix enabling cachix + #+cindex: github adding secrets - [ ] Add repository secrets necessary for your GitHub actions ~CACHIX_AUTH_TOKEN~ and ~CACHIX_CACHE_NAME~ (settings -> secrets -> new repository secret) - #+cindex github allowed actions + #+cindex: github allowed actions - [ ] Enable actions and add the following actions to your allowed actions list: #+begin_src txt - actions/checkout@v3.2.0, cachix/cachix-action@v12, cachix/install-nix-action@v20, actions/setup-python@v4, - #+end_src *Note*, Python is used to run a DCO check script, nothing more. @@ -590,7 +577,6 @@ overview: If everything works, you are ready to make a pull request to MELPA. Push your changes and check all the boxes in the PR template except the one that requires you to read the instructions. - * Maintaining Your Package Keeping your project fresh. ** Upgrading ERK @@ -634,7 +620,6 @@ overview: By using Nix, your repository can declare a fixed set of dependencies for development and testing. Not just Elisp dependencies, but also 3rd party dependencies. - *** Maintaining versions #+cindex: nix dependency updating @@ -667,7 +652,6 @@ overview: version of the flake in the ~nix repl~: #+begin_src nix - # is known in your flake registry pkgs = import { system = builtins.currentSystem; overlays = [ (builtins.getFlake ("emacs-overlay")).overlay ];} @@ -679,14 +663,12 @@ overview: # Have fun inspecting the various versions. Checking their declarations in # emacs-overlay source can be insightful. - #+end_src To obtain truly specific Emacs versions, specify the Emacs source as a flake input and then override the attributes of an Emacs package: #+begin_src nix - inputs = { # declare the exact source you want emacs29-src = { @@ -704,7 +686,6 @@ overview: }); # It's nix expressions. It's programming. # Ask your favorite large langauge model! - #+end_src #+cindex: nix binary cache @@ -737,17 +718,59 @@ overview: By changing the versions within the flake to match the versions in question that are breaking, the user can try versions to confirm a version mismatch and then give you their flake so that you can reproduce and confirm a fix. +* Configuring ERK +#+vindex: erk-after-new-hook +~erk-after-new-hook~ will be run after you create a new repository. The +~default-directory~ is set to the newly cloned directory. By default it runs +~magit-status~, where you can check the renaming results. + +#+vindex: erk-templates +~erk-templates~ can be set to default to your own favorite template. If you want +to fork [[https://github.com/positron-solutions/erk-basic][erk-basic]], you can set this to point to your fork. If you are making a +radically different template, consider a PR to mention your fork or even add it +to the defaults! (Please don't abuse this privilege to execute a supply chain +attack or we will find you). +* Conventions +#+cindex: conventions +ERK relies on [[info:elisp#Packaging][(elisp)standard practices]] to discover files. +- Feature names always match file names +- Elisp prefixes are consistent throughout the entire package +- The project is either all flat or all nested: + + tests in =/test= + + package lisp in =/lisp= + + documenation inputs in =/doc= +- Tests for a feature are found in the same feature + =-test=. +- Unit tests are always named after the function they test, ending in =-test= +- Documentation generation expects: + + CONTRIBUTING.org + + manual.org + + README.org +- Emacs will only install your manual by default if it exports to + =my-feature.texi= + +Files that can vary based on the hosting platform often must use another +convention, such as =/.github=. Tools expect mostly rigid names such as =flake.nix= +and =/.git=. + +If you create a new template, please respect this very simple standard, which is +also expected by package managers like ~elpaca~ and ~straight~. * Indices ** Command and Function index - :PROPERTIES: - :INDEX: fn - :END: +:PROPERTIES: +:INDEX: fn +:END: ** Concept index - :PROPERTIES: - :INDEX: cp - :END: +:PROPERTIES: +:INDEX: cp +:INDEX: cp +:END: + +** Variable index +:PROPERTIES: +:INDEX: vr +:END: * Licensing :PROPERTIES: diff --git a/lisp/erk.el b/lisp/erk.el index 8632163..de07c84 100644 --- a/lisp/erk.el +++ b/lisp/erk.el @@ -4,7 +4,7 @@ ;; Author: Positron Solutions ;; Keywords: convenience, programming -;; Version: 0.4.0 +;; Version: 0.5.0 ;; Package-Requires: ((emacs "28.1") (auto-compile "1.2.0") (dash "2.18.0") (license-templates "0.1.3")) ;; Homepage: http://github.com/positron-solutions/elisp-repo-kit @@ -27,23 +27,23 @@ ;;; Commentary: -;; Set up Emacs package with GitHub repository configuration, complete with -;; Actions CI, tests, lints, documentation generation, and a licensing scheme -;; all ready to go. Included commands are focused on productivity, appropriate -;; for professional development in elisp. The goal of the package is streamline -;; authoring & distributing new Emacs packages. It provides a well-integrated -;; but rigid scheme, aka opinionated. +;; Set up and develop Emacs packages, complete with Github Actions CI, tests, +;; lints, documentation generation, and a licensing scheme all ready to go. +;; Included commands are focused on productivity, appropriate for professional +;; development in elisp. The goal of the package is streamline authoring & +;; distributing new Emacs packages. It provides a well-integrated but rigid +;; scheme, aka opinionated. ;; -;; The package also uses its own hosted source as a substrate for creating new -;; packages. It will clone its source repository and then perform renaming & -;; re-licensing. Simply call `erk-new' to start a new package. The -;; README documents remaining setup steps on GitHub and in preparation for -;; publishing on MELPA. +;; Simply call `erk-new' to start a new package. ERK will clone a small +;; template project and interactively rename and relicense the project. +;; Instructions on how to host and publish your package are included in the +;; manual. ;; ;; As a development aid, the package is versatile enough to work on some elisp -;; packages that were not descended from its own source. The scope of -;; functionality is primarily to interface with linting and testing frameworks, -;; both in batch and interactive workflows. +;; packages not descended from its templates. The provided functionality +;; focuses on smoothing out typical workflows. Common actions like reloading +;; packages and navigating between source & tests are streamlined. Processes +;; like exporting all documents are automated. ;;; Code: @@ -51,6 +51,7 @@ (require 'auto-compile) (require 'dash) (require 'ert) +(require 'find-func) (require 'finder) (require 'license-templates) (require 'lisp-mnt) @@ -86,6 +87,13 @@ templates with different layouts and other requirements." :group 'erk :type '(alist :key-type symbol :value-type plist)) +(defcustom erk-after-new-hook '(magit-status) + "Action to perform after a new clone has been created. +Called with no arguments. The cloned directory set as +`default-directory'" + :group 'erk + :type '(list function)) + (defun erk--template-github-userorg (template) "Get the github repo from a TEMPLATE." (car (split-string (plist-get template :github-path) "/"))) @@ -148,6 +156,10 @@ you can redistribute it and/or modify (lambda (f) (format f feature)) files)) +;; TODO I mean, it's ideologically good but practically bad. In the lazy case, +;; template authors lose motivation. New package maintainers forget to sign up. +;; Okay, convert this to an optional string replacement and let users decide to +;; be lazy for the greater good. (defconst erk--delete-files '(".github/FUNDING.yml") "Files that would require other accounts to migrate.") @@ -167,6 +179,39 @@ you can redistribute it and/or modify (funcall #'project-root project)) default-directory))) +(defun erk--lisp-directory () + "Heuristic to find where package files are stored." + (let ((root (erk--project-root))) + (if (erk--project-flat-p) + root + (file-name-as-directory (concat root "lisp"))))) + +(defun erk--test-directory () + "Heuristic to find where package files are stored." + (let ((root (erk--project-root))) + (if (erk--project-flat-p) + root + (file-name-as-directory (concat root "test"))))) + +(defun erk--project-flat-p () + "Heuristic to detect flat project structure." + (let ((root (erk--project-root))) + (not (or (file-exists-p (concat root "lisp")) + (file-exists-p (concat root "test")))))) + +(defun erk--project-contains-p (file) + "Is FILE a member of the current project?" + (or (string-prefix-p (erk--project-root) file) + (member file (project-files (project-current))))) + +(defun erk--project-contains-fun-p (fun) + "Is FUN currently defined within the project? +Returns FUN if it's bound and within the project." + (and (symbol-function fun) + (erk--project-contains-p + (cdr (find-function-library fun))) + fun)) + (defun erk--reload (features dir) "Reload FEATURES, found in DIR." (dolist (feature features) @@ -235,6 +280,8 @@ Only understands project root or root/lisp." (root-feature-file (car (sort package-files #'string<)))) (concat project-elisp-dir "/" root-feature-file))) +;; Note, these functions are kind of redundant, but just want to consume +;; consistent interfaces internally. (defun erk-package-author () "Return the author of this project's package." (car (car (lm-authors (erk--project-root-feature-file))))) @@ -290,6 +337,105 @@ for development, and being lenient for degenerate cases is fine." (erk--dir-features project-test-dir)))) (erk--reload package-features project-test-dir))) +(defun erk-jump-features () + "Jump between the corresponding package and test features." + (interactive) + (let ((file (file-name-nondirectory (buffer-file-name (current-buffer))))) + (if (string-match-p ".*\\.el" file) + (find-file (if (string-match "\\(.*\\)-tests?\\.el" file) + (concat (erk--lisp-directory) + (match-string 1 file) ".el") + (concat (erk--test-directory) + (and (string-match "\\(.*\\)\\.el" file) + (match-string 1 file)) + "-test.el"))) + ;; fallback, go to root feature, convenient shortcut + ;; back into elisp files + (find-file (erk--project-root-feature-file))))) + +(defun erk--insert-test (fun buffer) + "Insert test named TEST-SYMBOL for FUN into BUFFER." + (pop-to-buffer buffer) + ;; If the buffer is open and the point is near a defun already, just slam in a + ;; test. If not, put the test in at the end. + (if (erk--last-defname) + (progn + (beginning-of-defun) + (forward-sexp)) + (progn + (goto-char (point-max)) + (backward-sexp) + (backward-sexp) + (forward-sexp))) + (let ((before (format "\n\n(ert-deftest %s-test ()\n (should (%s" fun fun)) + (after ")))")) + (insert before) + (save-excursion + (insert after)))) + +(defun erk--last-defname () + "Return previous definition and name. +Returns nil if we don't know what kind of definition it is or +what to do with it (yet). Returns `(def . name)' form." + (save-excursion + (beginning-of-defun) + (pcase-let* ((`(,def ,name) + (funcall load-read-function (current-buffer)))) + (cons def name)))) + +(defun erk--make-test-symbol (symbol) + "Convert defun SYMBOL into test symbol." + (intern (concat (symbol-name symbol) "-test" ))) + +(defun erk--make-defun-symbol (symbol) + "Convert test SYMBOL into defun symbol." + (let ((name (symbol-name symbol))) + (when (string-match "\\(.*\\)-test\\'" name) + (intern (match-string 1 name))))) + +;; TODO Possibly add some heuristics to find improper names. Missing test +;; suffix is one such irritant. Check the feature. +(defun erk-jump-defs () + "Jump between defuns and their test defs. +For now, only the rigid pairing of an `ert-deftest' and it's +corresponding `defun' are supported." + (interactive) + (pcase-let* ((`(,def . ,name) (erk--last-defname))) + (pcase def + ;; Find the def, try reloading if it's defined from somewhere outside the + ;; project, such as within an installed version of the package. + (`defun + ;; TODO Doesn't check if test is defined inside the project. Rare, + ;; but not if working on two versions of a project. Let's let the + ;; interfaces normalize a bit. + (let* ((test-name (erk--make-test-symbol name)) + (test (or (when (ert-test-boundp test-name) test-name) + (progn (erk-reload-project-tests) + (when (ert-test-boundp test-name) test-name))))) + (if test + (progn (ert-find-test-other-window test) + (forward-sexp)) + (let ((test-buffer (erk-jump-features))) + (when (y-or-n-p (format "%s not found. Create? " + test-name)) + (erk--insert-test name test-buffer)))))) + (`ert-deftest + (let* ((def-name (erk--make-defun-symbol name)) + (def (or (erk--project-contains-fun-p def-name) + (progn (erk-reload-project-package) + (erk--project-contains-fun-p def-name))))) + (if def + (progn + (find-function-do-it def nil 'switch-to-buffer) + (forward-sexp)) + (progn (erk-jump-features) + (user-error "Definition not found: %s" def-name))))) + (_ + (user-error + "No compatible def before point. def: %s name: %s" + def name))))) + + ;;;###autoload (defun erk-ert-rerun-this-no-reload () "Rerun the ert test at point, but don't reload anything. @@ -308,6 +454,7 @@ middle of a command." "Rerun the ert test at point. Will reload all features and test features." (interactive) + ;; TODO integrate ERT and commands within buffer (erk-reload-project-package) (erk-reload-project-tests) (save-excursion @@ -382,7 +529,7 @@ are renaming. Existing files will be clobbered." ;; spdx identifiers. (license-templates-new-file "gpl-3.0" default-directory) (erk--nze - (call-process "cp" nil output nil "--verbose" new-license old-license) + (call-process "mv" nil output nil "--verbose" new-license old-license) (format "Could not update %s contents." old-license)) (erk--nze (call-process git-bin nil output nil "add" old-license) @@ -578,7 +725,7 @@ CLONE-ROOT is where the path for the new clone will be created. REPLACEMENTS is a plist with keys: -- `:full-name': The title of the package, usually found in the +- `:title': The title of the package, usually found in the README, manual, and the first line of Elisp files. - `:feature': By convention, all Elisp file names will include @@ -610,8 +757,16 @@ implementation information and more details about argument usage." "Rev, tag, or branch (empty implies default branch): ")) (template (plist-put template :rev rev)) (clone-root - (directory-file-name - (read-directory-name "Clone root: " default-directory))) + (let ((root)) + (while (not root) + (let ((candidate + (directory-file-name + (read-directory-name "Clone root (must exist): " default-directory)))) + (if (file-directory-p candidate) + (setq root candidate) + (message "Directory must exist!") + (sit-for 1)))) + root)) (title (read-string "Package full name, for documentation: " @@ -649,7 +804,9 @@ implementation information and more details about argument usage." :author author :email email)))) (let ((cloned (erk-clone template clone-root replacements))) - (erk-rename-relicense template cloned replacements))) + (erk-rename-relicense template cloned replacements) + (let ((default-directory cloned)) + (run-hooks 'erk-after-new-hook)))) ;;;###autoload (defun erk-insert-package-keyword (keyword) @@ -666,10 +823,10 @@ This list's name is easy to forget, so here's a shortcut." (insert (format "\"%s\"" keyword))) (defconst erk--find-paths - '((ci-dco . ".github/workflows/dco.yml") + '((ci-dco-action . ".github/workflows/dco.yml") (ci-nix-flake . ".github/flake.nix") (ci-run-shim . ".github/run-shim.el") - (ci-tests . ".github/workflows/ci.yml") + (ci-tests-action . ".github/workflows/ci.yml") (doc-contributing . "doc/CONTRIBUTING.org") (doc-manual . "doc/manual.org") (doc-readme . "doc/README.org")) @@ -682,10 +839,10 @@ This list's name is easy to forget, so here's a shortcut." (find-file (concat (erk--project-root) (cdr (assoc-string file erk--find-paths))))) ;;;###autoload -(defun erk-find-ci-dco () +(defun erk-find-ci-dco-action () "Shortcut for `erk-find'." (interactive) - (erk-find "ci-dco")) + (erk-find "ci-dco-action")) ;;;###autoload (defun erk-find-ci-nix-flake () @@ -700,10 +857,10 @@ This list's name is easy to forget, so here's a shortcut." (erk-find "ci-run-shim")) ;;;###autoload -(defun erk-find-ci-tests () +(defun erk-find-ci-tests-action () "Shortcut for `erk-find'." (interactive) - (erk-find "ci-tests")) + (erk-find "ci-tests-action")) ;;;###autoload (defun erk-find-doc-contributing () diff --git a/test/erk-test.el b/test/erk-test.el index 3ea725d..933d971 100644 --- a/test/erk-test.el +++ b/test/erk-test.el @@ -82,6 +82,36 @@ (should (string= (car (erk--expand-filenames '("%s-foo.el") "doo")) "doo-foo.el"))) +(ert-deftest erk--lisp-directory-test () + (should (not (string-match-p "test" (erk--lisp-directory)))) + (should (string-match-p "lisp" (erk--lisp-directory)))) + +(ert-deftest erk--test-directory-test () + (should (not (string-match-p "lisp/?$" (erk--test-directory)))) + (should (string-match-p "test" (erk--test-directory)))) + +(ert-deftest erk-jump-features-test () + ;; jump to feature when in tests + (should + (save-excursion + ;; test normally executes in a temporary buffer but `erk-jump-features' + ;; relies on `current-buffer'. + (find-file (erk--project-root-feature-file)) + (erk-jump-features) + (string-match-p "test" default-directory))) + ;; jump to tests when in feature + (should + (save-excursion + (find-file (concat (erk--test-directory) "erk-test.el")) + (erk-jump-features) + (string-match-p "lisp" default-directory))) + ;; jump to feature when in root + (should + (save-excursion + (find-file (concat (erk--project-root) "README.md")) + (erk-jump-features) + (string-match-p "lisp" default-directory)))) + (ert-deftest erk--project-elisp-dir-test () (should (erk--project-elisp-dir))) @@ -94,6 +124,18 @@ (ert-deftest erk--nodash-test () (should (string= (erk--nodash "erk-") "erk"))) +(ert-deftest erk--make-defun-symbol-test () + (should (eq (erk--make-defun-symbol 'something-test) 'something))) + +(ert-deftest erk--make-test-symbol-test () + (should (eq (erk--make-test-symbol 'something) 'something-test))) + +(ert-deftest erk--project-contains-fun-p-test () + (should (erk--project-contains-fun-p 'erk-new))) + +(ert-deftest erk--project-contains-p-test () + (should (erk--project-contains-p (erk--project-root-feature-file)))) + (ert-deftest erk-clone-test () (let ((enable-local-variables nil) (clone-root (make-temp-file "erk-clone-test-" t))) @@ -109,6 +151,8 @@ (ert-deftest erk-new-test () (let ((enable-local-variables nil) + (erk-after-new-hook (when (require 'magit nil t) + '(magit-status))) (clone-root (make-temp-file "erk-clone-test-" t))) (erk-new (cdr (assoc 'erk-basic erk-templates)) clone-root