diff --git a/.github/flake.lock b/.github/flake.lock index a033b73..7e34aff 100644 --- a/.github/flake.lock +++ b/.github/flake.lock @@ -7,14 +7,15 @@ ], "nixpkgs": [ "nixpkgs" - ] + ], + "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1670637348, - "narHash": "sha256-4FLOEi02WS+St6i1MSUxGfA32FL1SFIpwWCDsABAZkk=", + "lastModified": 1697857699, + "narHash": "sha256-VGtcRMKi3w1hsPKduOqiSLVnSX1nO/2oixHV7mlVLFw=", "owner": "nix-community", "repo": "emacs-overlay", - "rev": "3a0b5dd7756173e63c2bbbe70dd5484a7463257d", + "rev": "e58f332cc035aaaf82c9c9f8e07b3e21ac977d77", "type": "github" }, "original": { @@ -40,11 +41,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1670482022, - "narHash": "sha256-xzzjWulnNMCSTEGosGaAg7sLsAtF7stA3sQljZ410/w=", + "lastModified": 1688392541, + "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c50e5b63e9d65ff0e70cc06b7042a72c6a6583bc", + "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", "type": "github" }, "original": { @@ -54,6 +55,22 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1697655685, + "narHash": "sha256-79Kuv+QdgsVc+rkibuAgWHnh8IXrLBTOKg5nM0Qvux0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "80c1aab725151632ddc2a20caeb914e76dd0673c", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "emacs-overlay": "emacs-overlay", diff --git a/.github/flake.nix b/.github/flake.nix index be1563e..c05489a 100644 --- a/.github/flake.nix +++ b/.github/flake.nix @@ -95,6 +95,13 @@ # needed by your repo after cloning. epkgs.elpaPackages.project epkgs.melpaPackages.auto-compile + epkgs.melpaPackages.license-templates + + # Optionally override derivations. + # melpaPackages.weechat.overrideAttrs(old: { + # # probably you want to override the src attribute + # patches = [ ./weechat-el.patch ]; + # }) ])) ]; }); diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e5d84e1..1c6de88 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,12 +1,13 @@ ## Pull Requests -Generated changes to the flake.lock should be kept in a separate commit for the -reviewer. Title your commit as "generated". +**Generated** changes to the documentation and flake.lock should be kept in a +separate commit for the reviewer. Title your commit as "generated". Keeping these changes isolated in specific commits makes it much easier to pull -in your changes in parallel with other features. Maintainers may harvest your -changes. We only guarantee to preserve authorship, signature, and sign-off in -the git log. +in your changes in parallel with other features. + +Maintainers may harvest your changes. We only guarantee to preserve authorship, +signature, and sign-off in the git log. ### Creating pull requests @@ -18,3 +19,14 @@ the git log. checkbox) 1. Once the pull request has been approved, you will be thanked and observe your changes applied with authorship, signature, and sign-off in the git log + +### Reminder about PR access to upstream CI + +Pull-requests run with the `pull_request_target` Github Actions configuration. +Your PR can access secrets, but it run's the **upstream's** CI, not the CI +changes you are submitting. + +If you need to make changes to CI itself in order to pass checks, you will need +to work with the repository's owner. The owner will pull the branch into a +non-master branch. There, they can trigger the CI manually with the branch's +own configuration using `workflow_dispatch` in the Actions tab. diff --git a/.github/run-shim.el b/.github/run-shim.el index a0c3ed8..cab6764 100644 --- a/.github/run-shim.el +++ b/.github/run-shim.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2022 Positron Solutions -;; Author: +;; Author: Positron Solutions ;; Permission is hereby granted, free of charge, to any person obtaining a copy of ;; this software and associated documentation files (the "Software"), to deal in diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6055e3b..36072d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,8 @@ jobs: os: [ubuntu-latest, macos-latest] emacsPkg: [ emacs, emacsGit, ] runs-on: ${{ matrix.os }} + # If you update steps, be sure to update the approved actions list in the + # manual and in your repositories! steps: - uses: actions/checkout@v3.2.0 with: diff --git a/.gitignore b/.gitignore index 6e37a15..5aa1681 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ ***autoloads.el **trash **~ -**.~undo-tree \ No newline at end of file +**.~undo-tree +/docs/manual.info \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ddab93c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,269 @@ + + +Development process & infrastructure guide. + + +# Table of Contents + +- [Submitting Pull Requests](#orgd4b2111) +- [Development](#org6f22716) + - [Running tests](#org39f87e2) + - [Lint and byte-compile code](#orgdb6a70c) + - [Loading and re-loading your package](#org2288378) + - [Re-generating Documentation](#orgb891c6a) +- [License](#orgb46cd92) +- [Developer Certificate of Origin (DCO)](#orge70b7bb) + - [Sign-off](#orgc3f586e) + - [GPG signature](#org6b02174) + - [User setup for submitting changes](#orgc65857c) + - [Maintaining versions](#orgcaf43a4) + + + + +# Submitting Pull Requests + +Be sure to abide by instructions in [the pull request template](../.github/pull_request_template.md). + + + + +# Development + +This repository was created with [elisp-repo-kit](https://github.com/positron-solutions/elisp-repo-kit/). You can use it to streamline +development workflows. + + + + +## Running tests + +Run `erk-ert-project` within this project. The tests will be +discovered, rebuilt & reloaded if necessary, and run. There are a +few other commands to augment the [ert](https://www.gnu.org/software/emacs/manual/html_node/ert/) package. + + +### Running tests CI style + +If you cannot reproduce a failure (or success) on CI, then you may +want to switch to using [nix](https://nixos.org/download.html) to get a reprodicible toolchain so you +can further develop with frozen versions from the nix [flake's](https://nixos.wiki/wiki/Flakes) +flake.lock. + + + nix develop .github# # loads the devShells.default from flake.nix + cd .github && direnv allow # same as above with file watching + + emacs --quick --load .github/run-shim.el -- test # graphical + emacs --script .github/run-test.el -- test # terminal, batch style + +You can *totally* run the tests locally on whatever version of Emacs you +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 + +This package uses [elisp-lint](https://github.com/gonewest818/elisp-lint) to detect issues with byte compiling, package +format, code structure and others. + +The configuration is found inside [.github/run-shim.el](../.github/run-shim.el). The CI run is invoked +inside of [ci.yml](../.github/workflows/ci.yml) using Emacs in script mode. Most of the configuration is in +the run shim. + +The tests are also linted, to a less restrictive standard, also found in +[run-shim.el](../.github/run-shim.el) + +You can run the lints manually almost the same as running tests. + + + nix develop .github# + # nix develop .#emacs28 + # nix develop .#emacsGit + emacs --script .github/run-shim.el -- lint + emacs --script .github/run-shim.el -- lint-tests + + + + +## Loading and re-loading your package + +Run `erk-reload-package` in one of your project files. All features +in the /lisp directory will be re-compiled and loaded appropriately. + +**Note**, during reloading, unloading the current module is forced. If other +packages you use depend on the project feature, results may be unpredicatable. +This is one reason batch style testing can be preferred. + + +### Manual Loading & Reloading + +To manually unload, run built-in command `unload-feature` and select your +package name. If you do not unload, reloading has no effect and you will see +stale behavior. + +Next, add the package to your load-path and then require it or, more +directly, call `emacs-lisp-byte-compile-and-load` or +`emacs-lisp-native-compile-and-load`. + + + + +## Re-generating Documentation + +If you are using `elisp-repo-kit`, `erk-export-docs` will take care of +everything. See the related commands for exporting individually and +viewing the outputs. If you enable local variables, declared at the end of +each document, saving will automatically export. + +To manually export, use the appropriate org mode command: + +- `org-md-export-as-markdown` +- `org-texinfo-export-to-info` + +You can view the manual with prefix argument, `info-display-manual`, and +manually type the path to the `manual.info`. Honestly, just use +`elisp-repo-kit`. This stuff is tedious. + +**NOTE** changes to the `manual.org` requires re-generation of other documents + because they use `#+include:` directives. + + + + +# License + +This package is distributed under the terms of the [included license](./COPYING). The CI +configuration, documentation, and scripts are MIT licensed. + + + + +# Developer Certificate of Origin (DCO) + +This project is distributed with a Developer Certificate of Origin. By adding +a sign-off notice and GPG signature to each commit, you will provide means to +authenticate your sign-off later strengthening your attestations stated in the +DCO, upholding the overall integrity of the license coverage over the project. + +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 + +A sign-off means adding a "trailer" to your commit that looks like the +following: + + + Signed-off-by: Random J Developer + + + + +## GPG signature + +A GPG signed commit shows that the owner of the private key submitted the +changes. Wherever signatures are recorded in chains, they can demonstrate +participation in changes elsewhere and awareness of what the submitter is +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 + +Follow these instructions before you get ready to submit a pull-request. + +Refer to the [GitHub signing commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) instructions to set up your git client +to add GPG signatures. File issues if you run into Emacs-specific problems. +Be sure to use a Github verified email. + +Because signing is intended to be a conscious process, please remember to +read and understand the [Developer Certificate of Origin](../DCO) before confinguring +your client to automatically sign-off on commits. + + +### Automatically add sign-off + +In magit, set the `-s` switch. Use `C-x C-s` (`transient-save`) to +preserve this switch on future uses. (Note, this is not per-project).You +can also set the signature flag this way. + + +### Automatic GPG signing with per-project keys + +In order to specify which projects you intend to sign with which keys, you +will want to configure your git client using path-specific configurations. + +Configuing git for this can be done with the following directory structure: + + + /home/rjdeveloper/ + ├── .gitconfig + └── .gitconfig.d + ├── sco-linux-projects.conf + ├── other-projects.conf + └── gpg-signing-projects.conf + +In your root config, `.gitconfig`, add an `includeIf` directive that will +load the configuration you use for projects you intend to GPG sign commits +for. + + + [includeIf "gitdir:/home/rjdeveloper/**/gpg-signing/**/.git"] + path = "~/.gitconfig.d/gpg-signing-projects.conf" + +In the `gpg-signing-projects.conf` add your GPG signing configuration from +earlier. `sign` adds the GPG signature automatically. File an issue if you +need help with multiple GPG homes or other configurations. + + + [user] + name = "Random J Developer" + email = "random@developer.example.org" + signingkey = "5FF0EBDC623B3AD4" + + [commit] + sign = true + gpgSign = true + + +### Manually signing & adding sign-off + +If you don't like these configurations and want to individually indicate you +have read and intend to apply the DCO to your changes, these commands are +equivalent: + + git commit -s -S --message "I don't like using .gitconfig" + + # To clean up a commit + git commit --amend -s -S --no-message + + # Combine with rebase to sign / sign-off multiple existing commits + git rebase -i + + + + +## Maintaining versions + +The Nix [flake.nix](../github/flake.nix) is where versions are declared. The [flake.lock](../.github/flake.lock) stores a +fixed value for these declarations. These fixed versions need periodic +update. Nixpkgs has a new release about every six months. You can check +their [branches](https://github.com/NixOS/nixpkgs/branches) and [tags](https://github.com/NixOS/nixpkgs/tags) to see what's current. The effect is similar to +updating linux versions. The `nix` command has a lot of support for +specifying versions besides just updating. + + nix flake lock --update-input nixpkgs + +The `emacs-overlay`, which contains fixed versions of Emacs and snapshots of +Elisp repository package sets can be updated by running: + + nix flake lock --update-input emacs-overlay + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e828b3e --- /dev/null +++ b/README.md @@ -0,0 +1,579 @@ + + +melpa package melpa stable package +CI workflow status +DCO Check + + +# Elisp Repo Kit + +This repository is a kit to start a new elisp package repository on GitHub. The +package contained has commands to streamline elisp development. + + +# Quickly set up an Emacs Lisp repository on GitHub with: + +- An [elisp](https://www.youtube.com/watch?v=RQK_DaaX34Q&list=PLEoMzSkcN8oPQtn7FQEF3D7sroZbXuPZ7) package +- CI with [GitHub Actions](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs), configured for Darwin (MacOS) and Linux +- Built-in Emacs info manual generation +- [Nix](https://nixos.org/#examples) environment for obtaining dependencies or reproducibly developing CI + locally +- Licensing, [DCO](https://developercertificate.org/), DCO sign-off checks, PR template and [CONTRIBUTING](./CONTRIBUTING.md) instructions +- [MELPA](https://github.com/melpa/melpa) publishing compatible + +**To get started:** + +Install the package and run `erk-new`, provide a directory, and +answer all the questions. + + +# Install ERK + + + (use-package erk) ; vanilla, assuming you have MELPA configured + + ;; using elpaca's with explicit recipe + (use-package erk + :elpaca (erk :host github :repo "positron-solutions/elisp-repo-kit")) + + ;; straight with explicit recipe + (use-package erk + :straight (erk :type git :host github :repo "positron-solutions/elisp-repo-kit")) + + ;; 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 +your secrets for Cachix. Read the CI customization section. + + +# Table of Contents + +- [Creating Packages](#orgbf14bf1) +- [Using ERK for development](#org98720bd) + - [Find Files](#orgf73e424) +- [File contents and structure](#org8d1783d) +- [Finish setting up your new GitHub repo](#org9cd1ddb) + - [Optional Steps](#org833c99e) +- [Customizing CI](#orgaa9298a) +- [Licensing, Developer Certificate of Origin](#org3be8afd) +- [Publishing to MELPA](#org09c7298) + - [Creating the recipe](#org7388169) + - [Testing package build](#org629febf) + - [Testing stable package build](#orgb99d9c6) + - [MELPA Lints](#orgfaacc4b) +- [Maintaining versions](#org8ed3d8a) +- [Package scope and relation to other work](#org401c164) + - [Dependency Management](#orge880f34) + - [Discovering and Running Tests & Lints](#org5248f8c) + - [Comparisons](#org6350b87) +- [Contributing](#org9a4b34c) +- [Footnote on FSF and Emacs Core Licensing](#org4703ea1) +- [Shout-outs](#org2ad2eb5) + + +# Creating Packages + +The simplest and intended way is to call `erk-new`. It will first +ask you for: + +- Choose a template +- Root directory you want to clone to +- Package title +- Package feature +- Package 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](#org9cd1ddb) Have fun! + + +# Using ERK for development + +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. + +- 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 + +Accidentally editing generated files or forgetting the path to a file is +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-nix-flake +- ci-run-shim +- ci-tests +- docs-contributing +- docs-manual +- docs-readme + +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-nix-flake` + +- `erk-find-ci-run-shim` + +- `erk-find-ci-test` + +- `erk-find-docs-contributing` + +- `erk-find-find-docs-manual` + +- `erk-find-docs-readme` + + +# File contents and structure + +*After cloning and renaming,* you will have a file tree like this: + + + ├── .gitignore # ignores for byte compiles, autoloads etc + │ + ├── README.md # this file + ├── CONTRIBUTING.md # typical instructions for development + ├── COPYING # a GPL3 license + ├── DCO # Developer Certificate of Origin + │ + ├── .github + │ ├── .envrc # direnv integration with `nix develop` + │ ├── flake.nix # dependencies for this project + │ ├── flake.lock # version controlled lock of flake.nix input versions + │ ├── run-shim.el # elisp script with test & lint routines + │ ├── pull_request_template.md # reminders for PR contributors + │ └── workflows + │ ├── ci.yml # workflow for lints and tests + │ └── dco.yml # workflow to check DCO sign-offs + │ + ├── docs + │ ├── README.org # generates README.md + │ ├── CONTRIBUTING.org # generates CONTRIBUTING.md + │ ├── manual.org # actual manual + │ └── manual.texi # generated manual for distribution + │ + ├── lisp + │ └── erk.el # the package + │ + └── test + └── 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` +directories. + + +# Finish setting up your new GitHub repo + +You can copy this checklist to your org agenda files: + +- [X] Create a repository (from [install](#orgc87b537) 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](#org3be8afd) 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, + actions/setup-python@v4, + + **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](#org09c7298) to MELPA +- [ ] Make a post on [reddit](https://reddit.com/r/emacs/) and [mastodon](https://emacs.ch/) about your new package + + +## Optional Steps + +- [ ] Branch protect and enable check requirements for your default branch + (usually master). Merge commits, verified only, and no force push are + recommended settings. +- [ ] Enable requiring contributors to sign-off on web-based commits + +- [ ] For **security** of your Cachix secrets (and any others), require + Actions approval for all outside contributors. Disabling write and + disabling creation & approval of PR's is least privilege. + +Cachix is somewhat optional. It's free for open-source projects. It's +about as easy to sign up and generate the token as to remove the step from +the GitHub actions, so you won't save much time by avoiding it. + +If you opt out of cachix or any other binary cache, you will definitely want +to turn off tests for `emacsGit` etc because the build times are about +30min-1hr per run when a fresh Emacs must be built. + + +# Customizing CI + +The [run-shim.el](../.github/run-shim.el) script is just provides a CLI interface for adding commands in +the [ci.yml](../.github/workflows/ci.yml) CI declaration. Each action step just loads the shell, declared in +the [flake.nix](../.github/flake.nix) and then runs the shim in Emacs. The shim consumes the CLI +command arguments, so you can parameterize the invocations that way. + +- If you need extra elisp dependencies during CI, add them to the `epkgs` list + in the flake.nix. + +- If you need extra 3rd party dependencies, add them to `packages` in the call + to `mkShell`. +- To invoke different elisp operations, add / modify the commands in + [run-shim.el](../.github/run-shim.el). + +There's more information in [CONTRIBUTING](../CONTRIBUTING.md) about running commands locally. You +will want this information in your new repository. + + +# Licensing, Developer Certificate of Origin + +This project and its derivative templates are distributed with an MIT +license. `erk-new` will also run `erk-rename-relicense`, which will +automatically switch to the GPL3 license. **The MIT license allows +re-licensing, and so this change is compatible.** If you accept non-trivial +changes to your project, it will be very hard to change to the GPL3 later, so +consider this choice. + +The new repository will also come with DCO sign-off checking on PR's. The +instructions are in the [CONTRIBUTING](../CONTRIBUTING.md) guide. A DCO sign-off policy will give +your project a clear attestation of sufficient direct or transitive authority +from each contributor to submit changes under the terms of your project's +license. This can only improve your legal protection from incidentally +handling copyrighted code. + +The DCO choice in this repository is also meant to encourage & push stodgy +organizations whose responsibility it was to invent better processes towards +lower friction paths to contribute code. If you fail to implement the DCO +sign-off scheme, there is less hope that the FSF will someday independently +merge changes that accumulate in your package because there will not be a +clear chain of license compliance. + + +# Publishing to MELPA + +If you have green CI, you have already passed many requirements of publishing a +MELPA package. **You still need to build your package and verify your recipe.** +You are going to clone melpa in order to make your PR. You can use the clone to +verify the recipe. + + +## Creating the recipe + +Fork MELPA personally (not for organization) and clone it to wherever you keep +your upstreams. It's a good idea to separate upstreams from projects you +actively maintain so you can see and delete upstreams when not in use. + + + mkdir -p upstream + cd upstream + git clone git@github.com:$GITHUB_USER/melpa.git # replace $GITHUB_USER + +Install package-build + + (use-package package-build) + +`package-build-create-recipe` will give you something like: + + (erk :fetcher github :repo "positron-solutions/elisp-repo-kit") + +The following template can be filled in and pull-requested to MELPA to publish. +You don't need to touch `:files`. The `commit` and `branch` are optional +depending on how you version / develop / tag your releases. + +Copy the recipe into `recipes/erk` inside your MELPA clone. + + +## Testing package build + +Inside the MELPA clone root: + + + # Builds the package + make recipes/erk + # Test sandbox installation (will affect ~/.emacs.d/elpa So much for sandbox ¯\_(ツ)_/¯ + EMACS_COMMAND=$(which emacs) make sandbox INSTALL=erk + + +## Testing stable package build + +You need a tag on your default (usually master) branch of your repo, +`positron-solutions/elisp-repo-kit`. Use `git tag -S v0.1.0` and `git push + origin v0.1.0`. You can also just create a release in the GitHub interface. + + + # Test stable builds against your tags + STABLE=t make recipes/erk + + +## MELPA Lints + +Lastly, install [melpazoid](https://github.com/riscy/melpazoid) and call `melpazoid` on your main feature. It does +some additional lints. You may need to install `package-lint` if you don't have +it. It's not declared in melpazoid's requirements. Getting the package in Nix +is not easy yet since melpazoid is not yet on Melpa. + + + ;; using elpaca's with explicit recipe + (use-package melapzoid + :elpaca (melpazoid :host github :repo "riscy/melpazoid")) + + ;; using straight + (straight-use-package + '(melpazoid :type git :host github :repo "riscy/melpazoid" :files ("melpazoid/melpazoid.el"))) + +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 versions + +The Nix [flake.nix](../github/flake.nix) is where versions are declared. The [flake.lock](../.github/flake.lock) stores a +fixed value for these declarations. These fixed versions need periodic +update. Nixpkgs has a new release about every six months. You can check +their [branches](https://github.com/NixOS/nixpkgs/branches) and [tags](https://github.com/NixOS/nixpkgs/tags) to see what's current. The effect is similar to +updating linux versions. The `nix` command has a lot of support for +specifying versions besides just updating. + + nix flake lock --update-input nixpkgs + +The `emacs-overlay`, which contains fixed versions of Emacs and snapshots of +Elisp repository package sets can be updated by running: + + nix flake lock --update-input emacs-overlay + + +# Package scope and relation to other work + +There are two functional goals of this repository: + +- Automate the annoying work necessary to set up a new repository +- Streamline common elisp development workflows + +Commands within this package will focus on cleaner integration of the tests +and lints with Emacs. There has been a lot of work in this area, but much of +it is tangled with dependency management and sandbox creation. Much of it is +done in languages other than elisp and focused on non-interactive workflows +with no interactive integration on top. + +Providing close to out-of-box CI is a big focus. By making it easier to +qualify changes from other users, it becomes less burdonsome to maintain +software, and by extension, less burdensom to publish and create software. The +effect is to drive creation of elisp in a way that can accelerate the flow of +elisp into Emacs itself. + + +## Dependency Management + +This repository uses pure dependency management and then leverages it to +provide dependencies for development and CI environments. The resulting user +experience is built around CI for reproducibility and interactive testing for +development speed. + +Because most elisp dependencies can be obtained without extensive system +dependency management, many tools for testing Emacs packages provide +dependency management and loading those dependencies into a fresh Emacs +instance. This aligns well with ad-hoc sandboxed local testing. This was +fine in the old days of impure dependency management and dirty environments. + +The [Emacs Nix Overlay](https://github.com/nix-community/emacs-overlay) and Emacs support within nixpkgs make it possible to +stating and obtaining elisp dependencies in a completely pure way. Non-elisp +dependencies are trivially provided form nixpkgs. Nix is extremely reliable +at dependency management, and it is no surprise that much complexity is +normalized away by just the basic behavior model of Nix. In addition, **if +your project needs or includes additional binary dependencies or modules**, +Nix is an excellent way to provide them to CI and users. + + +## Discovering and Running Tests & Lints + +During development, the commands provided under the `erk-` prefix make it +more convenient to reload your package and test features. You can run the +ert tests for a project while working on multiple packages. + +During CI, this repository uses an elisp shim for discovering and running +tests. The commands within the package for convenience during development +are not depeneded upon during CI. + +The CI actions obtain an environment with dependencies using Nix, so this can +also be done locally using Nix, meaning re-creating environments is available +to the user without leaning on CI. + + +## Comparisons + +There are many tools for running Elisp tests. Most of them are well +integrated with some dependency management. Most of them have some published +CI integrations to draw inspiration from. These patterns are common because +the needs arise together. + + +### ERK's Key Characteristics + +- Quickly spin up nearly complete starter projects +- Fully working CI, packaging, and documentation manual scheme +- Immediately run tests and get into the virtuous feedback loop +- Nix dependency management, bringing both Elisp and 3rd party dependencies + under full control + +As a template for your project, **ERK leans towards being opinionated** in +order to provide complete behavior out of the box. The trade-off of being +closer to a completed project while still being minimal is only supporting +one hosting platform, **Github, and its Github Actions CI infrastructure.** You +can adapt around this easily because it's not a complex project, but you will +do it on your own. + +This project favors the **Nix dependency tooling** for extreme reproducibility +and access to a **huge** number of 3rd party dependencies on most platforms. +If you want to implement sandboxed tests or test with a specific version of +dependencies, you can do it in a pollution-free way across numerous versions +with Nix. Everyone on most platforms can reproduce your work in a way that +doesn't pollute their system either (beyond installing Nix). + + +### Other Comparisons + +There are many comparisons available to understand the roles of similar tools +and how they relate to each other. + +- [makem.sh](https://github.com/alphapapa/makem.sh#comparisons) +- [Eldev](https://github.com/doublep/eldev#see-also) +- [nomake](https://github.com/emacs-twist/nomake) Is another project with Nix work + +[nix-emacs-ci](https://github.com/purcell/nix-emacs-ci) capture the work needed to provide a running Emacs to CI. Tools +like [eldev](https://github.com/doublep/eldev#continuous-integration) and [makem.sh](https://github.com/alphapapa/makem.sh/blob/master/test.yml) have support for providing dependencies to that Emacs. +The Nix flake [in this project](../flake.nix) describes both of these tasks. Makem and Eldev +etc document Gihub workflows, but **the workflows in this repository are meant to +be used out-of-the-box after cloning**, although to be fair, there's more +decisions than actual work. + +Nix-emacs-ci provides a lot of backwards-compatibility versions of Emacs. +The emacs-nix-overlay that this project employs is more forward looking, +providing `emacsGit` and sometimes other upstream branches when a big +feature like native compilation is in the pipeline. Nix-emacs-ci is also +still using legacy Nix, without flakes. Flakes are just nicer and the way +Nix is going. + + +# Contributing + +For turn-key contribution to the software ecosystem that keeps you moving, see +the [funding links](https://github.com/sponsors/positron-solutions). + +For code-based contribution, first decide if you want to work on this +repository or fork it to something entirely different. + +The [CONTRIBUTING](./CONTRIBUTING.md) guide in this repo contains development instructions, +including singing & sign-off configuration. You will usually want this file +in your own repositories. + +Non-exhaustive list of changes that are very welcome: + +- More interactive integrations with high-value elisp development workflows +- Running additional or better kinds of tests & lints +- Fix bugs +- More generic support for renaming and selecting different base repository + templates +- Expose trivial options where a structural choice has limited them + unnecessarily +- Behave the same, but with a less complicated code +- Guix or other pure dependency management support + +Changes will likely be rejected if it is aimed at: + +- Non-elisp interfaces meant for invocation outside of Emacs or with scripting + implemented in a language besides elisp. +- Managing dependencies outside of Nix (or other pure dependency management) + expressions +- CI infrastructure support for non-Actions infrastructure (which cannot be + tested in this repo) +- Backwards compatibility for Emacs two versions behind next release. Master, + current stable release, and release - 1 are the only versions being supported +- pre-flake Nix support +- Guix support **if it interferes with Nix support** + + +# Footnote on FSF and Emacs Core Licensing + +Free Software Foundation (FSF) frequently requires copyright assignment on all +code that goes into Emacs core. Many free software projects formerly requiring +copyright assignment have since switched to using a Developer Certificate of +Origin. DCO sign-off is a practice accepted by git, GCC, and the [Linux +Kernel](https://wiki.linuxfoundation.org/dco). + +Doing DCO sign-off is not the same as copyright assignment, and serves a +slightly different purpose. DCO sign-off is an attestation from the submitter +stating that they have sufficient direct or transitive authority make their +submission under the terms of the license of the recieving project. Copyright +assignment serves a more offensive role in the case of GPL non-compliance, +giving FSF alone legal standing. If you don't care about FSF being able to +sue people, the DCO should suffice. + +Using the DCO **may** make it easier for code in your project to be included in +Emacs core later. **It is the intent of this choice to steer FSF towards +DCO-like solutions in order to accelerate code flow into Emacs.** Regardless of +FSF's ongoing position on use of DCO's, by requiring DCO sign-off and GPG +signature, you can be assured that changes submitted to a code base you +control are strongly attested to be covered by the license you chose. + + +# Shout-outs + +- [alphapapa](https://github.com/alphapapa) for being super prolific at everything, including package writing, + documentation, and activity on various social platforms +- [adisbladis](https://github.com/adisbladis) for the Nix overlay that makes the CI and local development so nice +- [NobbZ](https://github.com/NobbZ) for being all over the Nix & Emacs interwebs +- [FSF](https://www.fsf.org/) and all contributors to Emacs & packages for the Yak shaving club + diff --git a/README.org b/README.org deleted file mode 100644 index 07e2fe5..0000000 --- a/README.org +++ /dev/null @@ -1,492 +0,0 @@ -#+TITLE: Elisp Repo Kit (ERK) - -#+HTML: melpa package melpa stable package -#+HTML: CI workflow status -#+HTML: DCO Check - -This repository is a kit to start a new elisp package repository on GitHub. The -package contained has commands to streamline elisp development. - -* Quickly set up an Emacs Lisp repository on GitHub with: - -- An [[https://www.youtube.com/watch?v=RQK_DaaX34Q&list=PLEoMzSkcN8oPQtn7FQEF3D7sroZbXuPZ7][elisp]] package -- CI with [[https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs][GitHub Actions]], configured for Darwin (MacOS) and Linux -- [[https://nixos.org/#examples][Nix]] environment for obtaining dependencies or reproducibly developing CI - locally -- Licensing, [[https://developercertificate.org/][DCO]], DCO sign-off checks, PR template and [[./CONTRIBUTING.org][CONTRIBUTING]] instructions -- [[https://github.com/melpa/melpa][MELPA]] publishing compatible - -*To get started:* - -Install the package and run =erk-new=, provide a directory, and -answer all the questions. - -** Install ERK - - #+begin_src elisp - - (use-package erk) ; vanilla - - ;; using elpaca's with explicit recipe - (use-package erk - :elpaca (erk :host github :repo "positron-solutions/elisp-repo-kit")) - - ;; straight with explicit recipe - (use-package erk - :straight (erk :type git :host github :repo "positron-solutions/elisp-repo-kit")) - - ;; or use melpa, manual load-path & require, you brave yak shaver - - #+end_src - - =erk-new= will ask for: - - - Root directory you want to clone to - - Package name - - Package prefix - - Author name - - GitHub user or organization - - Email address - - The =erk-new= calls =erk-rename-relicense= to rename all of the files, string - replace names, and re-license to GPL3. Now just follow the steps in - [[#finish-setting-up-your-new-github-repo][finish setting up]] Have fun! - -*** Manual cloning - - The standalone command, =erk-clone= will clone without renaming. - - This repo is also a [[https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template][template repository]], so you can fork without forking - (Bruce Lee). - - If you create via 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. - -* Contents -:PROPERTIES: -:TOC: :include siblings :ignore this -:END: -:CONTENTS: -- [[#using-erk-for-development][Using ERK for development]] - - [[#run-tests][Run tests]] - - [[#loading-and-re-loading-your-package][Loading and re-loading your package]] - - [[#duplicating-ci-locally][Duplicating CI Locally]] -- [[#finish-setting-up-your-new-github-repo][Finish setting up your new GitHub repo]] - - [[#optional-steps][Optional Steps]] -- [[#overview-of-file-contents-and-structure][Overview of file contents and structure]] -- [[#customizing-ci][Customizing CI]] -- [[#licensing-developer-certificate-of-origin][Licensing, Developer Certificate of Origin]] -- [[#publishing-to-melpa][Publishing to MELPA]] - - [[#creating-the-recipe][Creating the recipe]] - - [[#testing-package-build][Testing package build]] - - [[#testing-stable-package-build][Testing stable package build]] - - [[#melpa-lints][MELPA Lints]] -- [[#maintaining-nixpkgs-versions][Maintaining nixpkgs versions]] -- [[#package-scope-and-relation-to-other-work][Package scope and relation to other work]] - - [[#dependency-management][Dependency Management]] - - [[#discovering-and-running-tests--lints][Discovering and Running Tests & Lints]] - - [[#comparisons][Comparisons]] -- [[#contributing][Contributing]] -- [[#footnote-on-fsf-and-emacs-core-licensing][Footnote on FSF and Emacs Core Licensing]] -- [[#shout-outs][Shout-outs]] -:END: - -* Using ERK for development - - 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. - -** Run tests - - Run =erk-ert-project= within your project. The tests will be discovered, - rebuilt & reloaded if necessary, and run. There are a few other commands to - augment the [[https://www.gnu.org/software/emacs/manual/html_node/ert/][ert]] package. - -** Loading and re-loading your package - - Run =erk-reload-project-package= in one of your project files. The features - will be recompiled and reloaded. - - The =erk-reload-project-tests= is the complementary command for tests. - -** Duplicating CI Locally - - The CI configuration is all stored in [[.github/][.github]]. Usually you will want - development instructions in your new repository. The [[./CONTRIBUTING.org][CONTRIBUTING]] guide - contains instructions to reproduce the CI behavior. - -* Finish setting up your new GitHub repo - - You can copy this checklist to your org agenda files: - - - [X] Create a repository (from [[#Install erk][install]] instructions) - - [ ] Create an empty GitHub repository and check the git remote configuration - - [ ] Set up your git commit signing (and verification so that it's obvious) - *and* [[#sign-off][sign-off]] so that it will be [[#Footnote-on-FSF-and-Emacs-Core-Licensing][straightforward]] for for FSF to pull in your - changes if they later change to DCO instead of copyright assignment. - - [ ] Sign up for [[https://app.cachix.org/][cachix]] and, create a binary cache with API tokens and public - read access - - [ ] Add repository secrets necessary for your GitHub actions =CACHIX_AUTH_TOKEN= and - =CACHIX_CACHE_NAME= (settings -> secrets -> new repository secret) - - [ ] Enable actions and add the following actions to your allowed actions - list: - - #+begin_src - - 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. - - - [ ] Get your package working, pushed, actions run, and CI badges all green - - [ ] [[#Publishing-to-melpa][Publish]] to MELPA - - [ ] Make a post on [[https://old.reddit.com/r/emacs/][reddit]] and [[https://emacs.ch/][mastodon]] about your new package - -** Optional Steps - - - [ ] Install =org-make-toc= for the automatic TOC generation in this org - document to work. - - [ ] Branch protect and enable check requirements for your default branch - (usually master). Merge commits, verified only, and no force push are - recommended settings. - - [ ] Enable requiring contributors to sign-off on web-based commits - - [ ] For security of your Cachix secrets and any other secrets you may someday - add, require Actions approval for all outside contributors and leave - repository permissions at read-only - - Cachix is somewhat optional. It's free for open-source projects. It's about as - easy to sign up and generate the token as to remove the step from the GitHub - actions, so you won't save much time by avoiding it. - - *If you opt out of cachix or any other binary cache, you will - definitely want to turn off tests for =emacsGit= etc because the - build times are about 30min-1hr per run when a fresh Emacs must be - built.* - -* Overview of file contents and structure - - /After cloning and renaming,/ you will have a file tree like this: - - #+begin_src shell - - ├── .gitignore # ignores for byte compiles, autoloads etc - │ - ├── README.org # this file - ├── CONTRIBUTING.org # typical instructions for development - ├── COPYING # a GPL3 license - ├── DCO # Developer Certificate of Origin - │ - ├── .github - │ ├── .envrc # direnv integration with `nix develop` - │ ├── flake.nix # dependencies for this project - │ ├── flake.lock # version controlled lock of flake.nix input versions - │ ├── run-shim.el # elisp script with test & lint routines - │ ├── pull_request_template.md # reminders for PR contributors - │ └── workflows - │ ├── ci.yml # workflow for lints and tests - │ └── dco.yml # workflow to check DCO sign-offs - │ - ├── lisp - │ └── erk.el # the package - │ - └── test - └── erk-test.el # ERT unit tests - - #+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~ - directories. - -* Customizing CI - - The [[.github/run-shim.el][run-shim.el]] script is just provides a CLI interface for adding commands in - the [[.github/workflows/ci.yml][ci.yml]] CI declaration. Each action step just loads the shell, declared in - the [[.github/flake.nix][flake.nix]] and then runs the shim in Emacs. The shim consumes the CLI - command arguments, so you can parameterize the invocations that way. - - - If you need extra elisp dependencies during CI, add them to the =epkgs= list - in the flake.nix. - - If you need extra 3rd party dependencies, add them to =packages= in the call - to =mkShell=. - - To invoke different elisp operations, add / modify the commands in - [[.github/run-shim.el][run-shim.el]]. - - There's more information in [[CONTRIBUTING.org][CONTRIBUTING]] about running commands locally. You - will want this information in your new repository. - -* Licensing, Developer Certificate of Origin - - This template project is distributed with the MIT license. =erk-new= will also - run =erk-rename-relicense=, which will automatically switch to the GPL3 - license. *The MIT license allows re-licensing, and so this change is - compatible.* If you accept non-trivial changes to your project, it will be - very hard to change to the GPL3 later, so consider this choice. - - The new repository will also come with DCO sign-off checking on PR's. The - instructions are in the [[./CONTRIBUTING.org][CONTRIBUTING]] guide. A DCO sign-off policy will give - your project a clear attestation of sufficient direct or transitive authority - from each contributor to submit changes under the terms of your project's - license. This can only improve your legal protection from incidentally - handling copyrighted code. - - The DCO choice in this repository is also meant to encourage & push stodgy - organizations whose responsibility it was to invent better processes towards - lower friction paths to contribute code. If you fail to implement the DCO - sign-off scheme, there is less hope that the FSF will someday independently - merge changes that accumulate in your package because there will not be a - . - -* Publishing to MELPA - - If you have green CI, you have already passed many requirements of publishing a - MELPA package. *You still need to build your package and verify your recipe.* - You are going to clone melpa in order to make your PR. You can use the clone to - verify the recipe. - -** Creating the recipe - - Fork MELPA personally (not for organization) and clone it to wherever you keep - your upstreams. It's a good idea to separate upstreams from projects you - actively maintain so you can see and delete upstreams when not in use. - - #+begin_src shell - - mkdir -p upstream - cd upstream - git clone git@github.com:$GITHUB_USER/melpa.git # replace $GITHUB_USER - - #+end_src - - Install package-build - - #+begin_src elisp - (use-package package-build) - #+end_src - - =package-build-create-recipe= will give you something like: - - #+begin_src elisp - (erk :fetcher github :repo "positron-solutions/elisp-repo-kit") - #+end_src - - The following template can be filled in and pull-requested to MELPA to publish. - You don't need to touch ~:files~. The ~commit~ and ~branch~ are optional - depending on how you version / develop / tag your releases. - - Copy the recipe into =recipes/erk= inside your MELPA clone. - -** Testing package build - - Inside the MELPA clone root: - - #+begin_src shell - - # Builds the package - make recipes/erk - # Test sandbox installation (will affect ~/.emacs.d/elpa So much for sandbox ¯\_(ツ)_/¯ - EMACS_COMMAND=$(which emacs) make sandbox INSTALL=erk - - #+end_src - -** Testing stable package build - - You need a tag on your default (usually master) branch of your repo, - =positron-solutions/elisp-repo-kit=. Use =git tag -S v0.1.0= and =git push - origin v0.1.0=. You can also just create a release in the GitHub interface. - - #+begin_src shell - - # Test stable builds against your tags - STABLE=t make recipes/erk - - #+end_src - -** MELPA Lints - - Lastly, install [[https://github.com/riscy/melpazoid][melpazoid]] and call =melpazoid= on your main feature. It does - some additional lints. You may need to install =package-lint= if you don't have - it. It's not declared in melpazoid's requirements. Getting the package in Nix - is not easy yet since melpazoid is not yet on Melpa. - - #+begin_src elisp - - (straight-use-package - '(melpazoid :type git :host github :repo "riscy/melpazoid" :files ("melpazoid/melpazoid.el"))) - - #+end_src - - 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 nixpkgs versions - - Nixpkgs has a new release about every six months. You can check their [[https://github.com/NixOS/nixpkgs/branches][branches]] - and [[https://github.com/NixOS/nixpkgs/tags][tags]] to see what's current. To get updated dependencies from MELPA, it's - necessary to update the emacs-overlay with =nix flake lock --update-input - emacs-overlay=. You can also specify revs and branches if you need to roll - back. There is a make shortcut: =make flake-update= MacOS tends to get a little - less test emphasis, and so =nixpkgs-darwin-= branches exist and are - required to pass more Darwin tests before merging. This is more stable if you - are on MacOS. =nixpkgs-unstable= or =master= are your other less common options. - -* Package scope and relation to other work - - There are two functional goals of this repository: - - - Automate the annoying work necessary to set up a new repository - - Streamline common elisp development workflows - - Commands within this package will focus on cleaner integration of the tests - and lints with Emacs. There has been a lot of work in this area, but much of - it is tangled with dependency management and sandbox creation. Much of it is - done in languages other than elisp and focused on non-interactive workflows - with no interactive integration on top. - - Providing close to out-of-box CI is a big focus. By making it easier to - qualify changes from other users, it becomes less burdonsome to maintain - software, and by extension, less burdensom to publish and create software. The - effect is to drive creation of elisp in a way that can accelerate the flow of - elisp into Emacs itself. - -** Dependency Management - - This repository uses pure dependency management and then levarages it to - provide dependencies for development and CI environments. The resulting user - experience is built around CI for reproducibility and interactive testing for - development speed. - - Because most elisp dependencies can be obtained without extensive system - dependency management, many tools for testing Emacs packages provide - dependency management and loading those dependencies into a fresh Emacs - instance. This aligns well with ad-hoc sandboxed local testing. This was - fine in the old days of impure dependency management and dirty environments. - - The [[https://github.com/nix-community/emacs-overlay][Emacs Nix Overlay]] and Emacs support within nixpkgs make it possible to - stating and obtaining elisp dependencies in a completely pure way. Non-elisp - dependencies are trivially provided form nixpkgs. Nix is extremely reliable - at dependency management, and it is no surprise that much complexity is - normalized away by just the basic behavior model of Nix. In addition, *if - your project needs or includes additional binary dependencies or modules*, - Nix is an excellent way to provide them to CI and users. - -** Discovering and Running Tests & Lints - - During development, the commands provided under the =erk-= prefix make it - more convenient to reload your package and test features. You can run the - ert tests for a project while working on multiple packages. - - During CI, this repository uses an elisp shim for discovering and running - tests. The commands within the package for convenience during development - are not depeneded upon during CI. - - The CI actions obtain an environment with dependencies using Nix, so this can - also be done locally using Nix, meaning re-creating environments is available - to the user without leaning on CI. - -** Comparisons - - There are many comparisons available to understand the roles of similar tools - and how they relate to each other. - - - [[https://github.com/alphapapa/makem.sh#comparisons][makem.sh]] - - [[https://github.com/doublep/eldev#see-also][Eldev]] - - [[https://github.com/emacs-twist/nomake][nomake]] Is another project with Nix work - - [[https://github.com/purcell/nix-emacs-ci][nix-emacs-ci]] capture the work needed to provide a running Emacs to CI. Tools - like [[https://github.com/doublep/eldev#continuous-integration][eldev]] and [[https://github.com/alphapapa/makem.sh/blob/master/test.yml][makem.sh]] have support for providing dependencies to that Emacs. - The Nix flake [[./flake.nix][in this project]] describes both of these tasks. Makem and Eldev - etc document Gihub workflows, but *the workflows in this repository are meant to - be used out-of-the-box after cloning*, although to be fair, there's more - decisions than actual work. - - Nix-emacs-ci provides a lot of backwards-compatibility versions of Emacs. The - nix-overlay is more forward looking, providing =emacsGit= and sometimes other - upstream branches when a big feature like native compilation is in the pipeline. - Nix-emacs-ci is also still using legacy Nix, without flakes. Flakes are just - nicer and the way Nix is going. - -* Contributing - - For turn-key contribution to the software ecosystem that keeps you moving, see - the funding links. - - For code-based contribution, first decide if you want to work on this - repository or fork it to something entirely different. - - The [[./CONTRIBUTING.org][CONTRIBUTING]] guide in this repo contains development instructions, - including singing & sign-off configuration. You will usually want this file - in your own repositories. - - Non-exhaustive list of changes that are very welcome: - - - More interactive integrations with high-value elisp development workflows - - Running additional or better kinds of tests & lints - - Fix bugs - - Expose trivial options where a structural choice has limited them - unnecessarily - - Behave the same, but with a less complicated code - - Guix or other pure dependency management support - - Changes will likely be rejected if it is aimed at: - - - Non-elisp interfaces meant for invocation outside of Emacs or with scripting - implemented in a language besides elisp. - - Managing dependencies outside of Nix (or other pure dependency management) - expressions - - CI infrastructure support for non-Actions infrastructure (which cannot be - tested in this repo) - - Backwards compatibility for Emacs two versions behind next release. Master, - current stable release, and release - 1 are the only versions being supported - - pre-flake Nix support - - Guix support that interferes with Nix support - -* Footnote on FSF and Emacs Core Licensing - - Free Software Foundation (FSF) frequently requires copyright assignment on all - code that goes into Emacs core. Many free software projects formerly requiring - copyright assignment have since switched to using a Developer Certificate of - Origin. DCO sign-off is a practice accepted by git, GCC, and the [[https://wiki.linuxfoundation.org/dco][Linux - Kernel]]. - - Doing DCO sign-off is not the same as copyright assignment, and serves a - slightly different purpose. DCO sign-off is an attestation from the submitter - stating that they have sufficient direct or transitive authority make their - submission under the terms of the license of the recieving project. Copyright - assignment serves a more offensive role in the case of GPL non-compliance, - giving FSF alone legal standing. If you don't care about FSF being able to - sue people, the DCO should suffice. - - Using the DCO *may* make it easier for code in your project to be included in - Emacs core later. *It is the intent of this choice to steer FSF towards - DCO-like solutions in order to accelerate code flow into Emacs.* Regardless of - FSF's ongoing position on use of DCO's, by requiring DCO sign-off and GPG - signature, you can be assured that changes submitted to a code base you - control are strongly attested to be covered by the license you chose. - -* Shout-outs - - - [[https://github.com/alphapapa][alphapapa]] for being super prolific at everything, including package writing, - documentation, and activity on various social platforms - - [[https://github.com/adisbladis][adisbladis]] for the Nix overlay that makes the CI and local development so nice - - [[https://github.com/NobbZ][NobbZ]] for being all over the Nix & Emacs interwebs - - [[https://www.fsf.org/][FSF]] and all contributors to Emacs & packages for the Yak shaving club - -# Local Variables: -# before-save-hook: (lambda () (when (require 'org-make-toc nil t) (org-make-toc))) -# org-make-toc-link-type-fn: org-make-toc--link-entry-github -# End: diff --git a/CONTRIBUTING.org b/docs/CONTRIBUTING.org similarity index 70% rename from CONTRIBUTING.org rename to docs/CONTRIBUTING.org index 14ceb23..3a9bb49 100644 --- a/CONTRIBUTING.org +++ b/docs/CONTRIBUTING.org @@ -1,29 +1,17 @@ #+TITLE: Contributing +#+EXPORT_FILE_NAME: ../CONTRIBUTING.md +#+OPTIONS: toc:nil broken-links:mark num:nil + +#+begin_export html + +#+end_export Development process & infrastructure guide. -* Contents -:PROPERTIES: -:TOC: :include siblings :ignore this -:END: -:CONTENTS: -- [[#development][Development]] - - [[#running-tests][Running tests]] - - [[#running-tests-ci-style][Running tests CI style]] - - [[#lint-and-byte-compile-code][Lint and byte-compile code]] - - [[#loading-and-re-loading-your-package][Loading and re-loading your package]] - - [[#manual-loading--reloading][Manual Loading & Reloading]] -- [[#license][License]] -- [[#developer-certificate-of-origin-dco][Developer Certificate of Origin (DCO)]] - - [[#sign-off][Sign-off]] - - [[#gpg-signature][GPG signature]] - - [[#user-setup-for-submitting-changes][User setup for submitting changes]] - - [[#automatically-add-sign-off][Automatically add sign-off]] - - [[#automatic-gpg-signing-with-per-project-keys][Automatic GPG signing with per-project keys]] - - [[#manually-signing--adding-sign-off][Manually signing & adding sign-off]] -- [[#maintaining-nixpkgs-versions][Maintaining nixpkgs versions]] -:END: +#+TOC: headlines 2 +* Submitting Pull Requests + Be sure to abide by instructions in [[../.github/pull_request_template.md][the pull request template]]. * Development This repository was created with [[https://github.com/positron-solutions/elisp-repo-kit/][elisp-repo-kit]]. You can use it to streamline @@ -61,12 +49,12 @@ Development process & infrastructure guide. This package uses [[https://github.com/gonewest818/elisp-lint][elisp-lint]] to detect issues with byte compiling, package format, code structure and others. - The configuration is found inside [[.github/run-shim.el][.github/run-shim.el]]. The CI run is invoked - inside of [[.github/workflows/ci.yml][ci.yml]] using Emacs in script mode. Most of the configuration is in + The configuration is found inside [[../.github/run-shim.el][.github/run-shim.el]]. The CI run is invoked + inside of [[../.github/workflows/ci.yml][ci.yml]] using Emacs in script mode. Most of the configuration is in the run shim. The tests are also linted, to a less restrictive standard, also found in - [[.github/run-shim.el][run-shim.el]] + [[../.github/run-shim.el][run-shim.el]] You can run the lints manually almost the same as running tests. #+begin_src bash @@ -97,11 +85,27 @@ Development process & infrastructure guide. Next, add the package to your load-path and then require it or, more directly, call =emacs-lisp-byte-compile-and-load= or =emacs-lisp-native-compile-and-load=. +** Re-generating Documentation + If you are using ~elisp-repo-kit~, ~erk-export-docs~ will take care of + everything. See the related commands for exporting individually and + viewing the outputs. If you enable local variables, declared at the end of + each document, saving will automatically export. + + To manually export, use the appropriate org mode command: + + - ~org-md-export-as-markdown~ + - ~org-texinfo-export-to-info~ + You can view the manual with prefix argument, ~info-display-manual~, and + manually type the path to the ~manual.info~. Honestly, just use + ~elisp-repo-kit~. This stuff is tedious. + + *NOTE* changes to the ~manual.org~ requires re-generation of other documents + because they use =#+include:= directives. * License This package is distributed under the terms of the [[./COPYING][included license]]. The CI - configuration and scripts are MIT licensed. + configuration, documentation, and scripts are MIT licensed. * Developer Certificate of Origin (DCO) @@ -138,9 +142,10 @@ Development process & infrastructure guide. Refer to the [[https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits][GitHub signing commits]] instructions to set up your git client to add GPG signatures. File issues if you run into Emacs-specific problems. + Be sure to use a Github verified email. Because signing is intended to be a conscious process, please remember to - read and understand the [[./DCO][Developer Certificate of Origin]] before confinguring + read and understand the [[../DCO][Developer Certificate of Origin]] before confinguring your client to automatically sign-off on commits. *** Automatically add sign-off @@ -211,18 +216,31 @@ Development process & infrastructure guide. git rebase -i #+end_src -* Maintaining nixpkgs versions - - Nixpkgs has a new release about every six months. You can check their [[https://github.com/NixOS/nixpkgs/branches][branches]] - and [[https://github.com/NixOS/nixpkgs/tags][tags]] to see what's current. To get updated dependencies from MELPA, it's - necessary to update the emacs-overlay with =nix flake lock --update-input - emacs-overlay=. You can also specify revs and branches if you need to roll - back. There is a make shortcut: =make flake-update= MacOS tends to get a little - less test emphasis, and so =nixpkgs-darwin-= branches exist and are - required to pass more Darwin tests before merging. This is more stable if you - are on MacOS. =nixpkgs-unstable= or =master= are your other less common options. - +** Maintaining versions +#+begin_comment +This heading is duplicated in the manual.org. The Contributing guide is +frequently copied around whole because it's not that unique between +repositories using ERK tooling. +#+end_comment + + The Nix [[../github/flake.nix][flake.nix]] is where versions are declared. The [[../.github/flake.lock][flake.lock]] stores a + fixed value for these declarations. These fixed versions need periodic + update. Nixpkgs has a new release about every six months. You can check + their [[https://github.com/NixOS/nixpkgs/branches][branches]] and [[https://github.com/NixOS/nixpkgs/tags][tags]] to see what's current. The effect is similar to + updating linux versions. The ~nix~ command has a lot of support for + specifying versions besides just updating. + + #+begin_src shell + nix flake lock --update-input nixpkgs + #+end_src + + The ~emacs-overlay~, which contains fixed versions of Emacs and snapshots of + Elisp repository package sets can be updated by running: + + #+begin_src shell + nix flake lock --update-input emacs-overlay + #+end_src + # Local Variables: -# before-save-hook: (lambda () (when (require 'org-make-toc nil t) (org-make-toc))) -# org-make-toc-link-type-fn: org-make-toc--link-entry-github +# after-save-hook: (lambda () (when (require 'erk nil t) (erk-export-contributing))) # End: diff --git a/docs/README.org b/docs/README.org new file mode 100644 index 0000000..bcd6b15 --- /dev/null +++ b/docs/README.org @@ -0,0 +1,233 @@ +#+title: Elisp Repo Kit (ERK) +#+export_file_name: ../README.md +#+options: toc:nil broken-links:mark num:nil + +#+begin_export html + +#+end_export + +#+begin_export html +melpa package melpa stable package +CI workflow status +DCO Check +#+end_export + +* Elisp Repo Kit + :PROPERTIES: + :UNNUMBERED: notoc + :END: + +This repository is a kit to start a new elisp package repository on GitHub. The +package contained has commands to streamline elisp development. + +* Quickly set up an Emacs Lisp repository on GitHub with: + :PROPERTIES: + :UNNUMBERED: notoc + :END: + +- An [[https://www.youtube.com/watch?v=RQK_DaaX34Q&list=PLEoMzSkcN8oPQtn7FQEF3D7sroZbXuPZ7][elisp]] package +- CI with [[https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs][GitHub Actions]], configured for Darwin (MacOS) and Linux +- Built-in Emacs info manual generation +- [[https://nixos.org/#examples][Nix]] environment for obtaining dependencies or reproducibly developing CI + locally +- Licensing, [[https://developercertificate.org/][DCO]], DCO sign-off checks, PR template and [[./CONTRIBUTING.org][CONTRIBUTING]] instructions +- [[https://github.com/melpa/melpa][MELPA]] publishing compatible + +*To get started:* + +Install the package and run =erk-new=, provide a directory, and +answer all the questions. + +* Install ERK + :PROPERTIES: + :UNNUMBERED: notoc + :END: +#+include: "manual.org::*Install ERK" :only-contents t +#+toc: headlines 2 +#+include: "manual.org::*Creating Packages" :minlevel 1 +#+include: "manual.org::*Using ERK for development" :minlevel 1 +#+include: "manual.org::*File contents and structure" :minlevel 1 + +* Finish setting up your new GitHub repo +#+include: "manual.org::*Setting Up Your Github Repository" :only-contents t + +#+include: "manual.org::*Customizing CI" :minlevel 1 +#+include: "manual.org::*Licensing, Developer Certificate of Origin" :minlevel 1 +#+include: "manual.org::*Publishing to MELPA" :minlevel 1 +#+include: "manual.org::*Maintaining versions" :minlevel 1 + +* Package scope and relation to other work + + There are two functional goals of this repository: + + - Automate the annoying work necessary to set up a new repository + - Streamline common elisp development workflows + + Commands within this package will focus on cleaner integration of the tests + and lints with Emacs. There has been a lot of work in this area, but much of + it is tangled with dependency management and sandbox creation. Much of it is + done in languages other than elisp and focused on non-interactive workflows + with no interactive integration on top. + + Providing close to out-of-box CI is a big focus. By making it easier to + qualify changes from other users, it becomes less burdonsome to maintain + software, and by extension, less burdensom to publish and create software. The + effect is to drive creation of elisp in a way that can accelerate the flow of + elisp into Emacs itself. + +** Dependency Management + + This repository uses pure dependency management and then leverages it to + provide dependencies for development and CI environments. The resulting user + experience is built around CI for reproducibility and interactive testing for + development speed. + + Because most elisp dependencies can be obtained without extensive system + dependency management, many tools for testing Emacs packages provide + dependency management and loading those dependencies into a fresh Emacs + instance. This aligns well with ad-hoc sandboxed local testing. This was + fine in the old days of impure dependency management and dirty environments. + + The [[https://github.com/nix-community/emacs-overlay][Emacs Nix Overlay]] and Emacs support within nixpkgs make it possible to + stating and obtaining elisp dependencies in a completely pure way. Non-elisp + dependencies are trivially provided form nixpkgs. Nix is extremely reliable + at dependency management, and it is no surprise that much complexity is + normalized away by just the basic behavior model of Nix. In addition, *if + your project needs or includes additional binary dependencies or modules*, + Nix is an excellent way to provide them to CI and users. + +** Discovering and Running Tests & Lints + + During development, the commands provided under the =erk-= prefix make it + more convenient to reload your package and test features. You can run the + ert tests for a project while working on multiple packages. + + During CI, this repository uses an elisp shim for discovering and running + tests. The commands within the package for convenience during development + are not depeneded upon during CI. + + The CI actions obtain an environment with dependencies using Nix, so this can + also be done locally using Nix, meaning re-creating environments is available + to the user without leaning on CI. + +** Comparisons + There are many tools for running Elisp tests. Most of them are well + integrated with some dependency management. Most of them have some published + CI integrations to draw inspiration from. These patterns are common because + the needs arise together. +*** ERK's Key Characteristics + + - Quickly spin up nearly complete starter projects + - Fully working CI, packaging, and documentation manual scheme + - Immediately run tests and get into the virtuous feedback loop + - Nix dependency management, bringing both Elisp and 3rd party dependencies + under full control + + As a template for your project, *ERK leans towards being opinionated* in + order to provide complete behavior out of the box. The trade-off of being + closer to a completed project while still being minimal is only supporting + one hosting platform, *Github, and its Github Actions CI infrastructure.* You + can adapt around this easily because it's not a complex project, but you will + do it on your own. + + This project favors the *Nix dependency tooling* for extreme reproducibility + and access to a *huge* number of 3rd party dependencies on most platforms. + If you want to implement sandboxed tests or test with a specific version of + dependencies, you can do it in a pollution-free way across numerous versions + with Nix. Everyone on most platforms can reproduce your work in a way that + doesn't pollute their system either (beyond installing Nix). + +*** Other Comparisons + + There are many comparisons available to understand the roles of similar tools + and how they relate to each other. + + - [[https://github.com/alphapapa/makem.sh#comparisons][makem.sh]] + - [[https://github.com/doublep/eldev#see-also][Eldev]] + - [[https://github.com/emacs-twist/nomake][nomake]] Is another project with Nix work + + [[https://github.com/purcell/nix-emacs-ci][nix-emacs-ci]] capture the work needed to provide a running Emacs to CI. Tools + like [[https://github.com/doublep/eldev#continuous-integration][eldev]] and [[https://github.com/alphapapa/makem.sh/blob/master/test.yml][makem.sh]] have support for providing dependencies to that Emacs. + The Nix flake [[../flake.nix][in this project]] describes both of these tasks. Makem and Eldev + etc document Gihub workflows, but *the workflows in this repository are meant to + be used out-of-the-box after cloning*, although to be fair, there's more + decisions than actual work. + + Nix-emacs-ci provides a lot of backwards-compatibility versions of Emacs. + The emacs-nix-overlay that this project employs is more forward looking, + providing =emacsGit= and sometimes other upstream branches when a big + feature like native compilation is in the pipeline. Nix-emacs-ci is also + still using legacy Nix, without flakes. Flakes are just nicer and the way + Nix is going. + +* Contributing + + For turn-key contribution to the software ecosystem that keeps you moving, see + the [[https://github.com/sponsors/positron-solutions][funding links]]. + + For code-based contribution, first decide if you want to work on this + repository or fork it to something entirely different. + + The [[./CONTRIBUTING.org][CONTRIBUTING]] guide in this repo contains development instructions, + including singing & sign-off configuration. You will usually want this file + in your own repositories. + + Non-exhaustive list of changes that are very welcome: + + - More interactive integrations with high-value elisp development workflows + - Running additional or better kinds of tests & lints + - Fix bugs + - More generic support for renaming and selecting different base repository + templates + - Expose trivial options where a structural choice has limited them + unnecessarily + - Behave the same, but with a less complicated code + - Guix or other pure dependency management support + + Changes will likely be rejected if it is aimed at: + + - Non-elisp interfaces meant for invocation outside of Emacs or with scripting + implemented in a language besides elisp. + - Managing dependencies outside of Nix (or other pure dependency management) + expressions + - CI infrastructure support for non-Actions infrastructure (which cannot be + tested in this repo) + - Backwards compatibility for Emacs two versions behind next release. Master, + current stable release, and release - 1 are the only versions being supported + - pre-flake Nix support + - Guix support *if it interferes with Nix support* + +* Footnote on FSF and Emacs Core Licensing + + Free Software Foundation (FSF) frequently requires copyright assignment on all + code that goes into Emacs core. Many free software projects formerly requiring + copyright assignment have since switched to using a Developer Certificate of + Origin. DCO sign-off is a practice accepted by git, GCC, and the [[https://wiki.linuxfoundation.org/dco][Linux + Kernel]]. + + Doing DCO sign-off is not the same as copyright assignment, and serves a + slightly different purpose. DCO sign-off is an attestation from the submitter + stating that they have sufficient direct or transitive authority make their + submission under the terms of the license of the recieving project. Copyright + assignment serves a more offensive role in the case of GPL non-compliance, + giving FSF alone legal standing. If you don't care about FSF being able to + sue people, the DCO should suffice. + + Using the DCO *may* make it easier for code in your project to be included in + Emacs core later. *It is the intent of this choice to steer FSF towards + DCO-like solutions in order to accelerate code flow into Emacs.* Regardless of + FSF's ongoing position on use of DCO's, by requiring DCO sign-off and GPG + signature, you can be assured that changes submitted to a code base you + control are strongly attested to be covered by the license you chose. + +* Shout-outs + + - [[https://github.com/alphapapa][alphapapa]] for being super prolific at everything, including package writing, + documentation, and activity on various social platforms + - [[https://github.com/adisbladis][adisbladis]] for the Nix overlay that makes the CI and local development so nice + - [[https://github.com/NobbZ][NobbZ]] for being all over the Nix & Emacs interwebs + - [[https://www.fsf.org/][FSF]] and all contributors to Emacs & packages for the Yak shaving club + +# Local Variables: +# after-save-hook: (lambda () (when (require 'erk nil t) (erk-export-readme))) +# End: diff --git a/docs/manual.org b/docs/manual.org new file mode 100644 index 0000000..b4dfe18 --- /dev/null +++ b/docs/manual.org @@ -0,0 +1,760 @@ +#+title: Elisp Repo Kit +#+subtitle: for version {{{package-version}}} +#+author: {{{package-author}}} +#+email: {{{package-email}}} +#+date: 2022-{{{year}}} +#+property: header-args :eval no` +#+options: broken-links:warn \n:nil ::t |:t ^:nil -:t f:t *:t <:t e:t ':t +#+options: d:nil todo:nil pri:nil tags:not-in-toc stat:nil +#+language: en +#+texinfo_dir_category: Emacs +#+texinfo_dir_title: Elisp Repo Kit: (erk) +#+texinfo_dir_desc: Create, distribute, and maintain Emacs Lisp packages +#+macro: package-version (eval (if (require 'erk nil t) (erk-package-version) "0.4.0")) +#+macro: package-author (eval (if (require 'erk nil t) (erk-package-author) "Positron Solutions")) +#+macro: package-email (eval (if (require 'erk nil t) (erk-package-email "contact@positron.solutions"))) +#+macro: year (eval (format-time-string "%Y")) +#+texinfo_header: @comment !!!THIS FILE HAS BEEN GENERATED!!! Edit manual.org instead! + +Setting up a /complete/ Emacs Lisp package doesn't need to be a lot of work. +Elisp Repo Kit provides a convenient starting point for collaborating on a +public Elisp package repository: + +- An Elisp package +- Tests +- CI & dependency management +- README and manual +- Licensing + +Furthermore, to get rid of pitfalls and streamline productive workflows when +working with Elisp packages, Elisp Repo Kit provides shortcuts: + +- Loading and unloading modules +- Compiling +- Running tests +- Re-generating documentation +- Navigating the project + +* High Level Overview + + - Elisp Repo Kit provides a scheme for how to organize and distribute your + project, which it also uses in its own repository. + + - As an Elisp package, Elisp Repo Kit (erk) provides shortcuts for working on + package using the Elisp Repo Kit organization structure (as well as other + Elisp packages). + +** 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 + ├── CONTRIBUTING.md # typical instructions for development + ├── COPYING # a GPL3 license + ├── DCO # Developer Certificate of Origin + │ + ├── .github + │ ├── .envrc # direnv integration with `nix develop` + │ ├── flake.nix # dependencies for this project + │ ├── flake.lock # version controlled lock of flake.nix input versions + │ ├── run-shim.el # elisp script with test & lint routines + │ ├── pull_request_template.md # reminders for PR contributors + │ └── workflows + │ ├── ci.yml # workflow for lints and tests + │ └── dco.yml # workflow to check DCO sign-offs + │ + ├── docs + │ ├── README.org # generates README.md + │ ├── CONTRIBUTING.org # generates CONTRIBUTING.md + │ ├── manual.org # actual manual + │ └── manual.texi # generated manual for distribution + │ + ├── lisp + │ └── erk.el # the package + │ + └── 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~ + directories. +** Developing + - The package is stored in /lisp and its tests in /test. + - Many shortcuts are provided to run tests or rebuild and reload the package + or to visit the unit tests for a function under point etc. + - Most of the Elisp shortcuts lean on Emacs settings or packages such as + ~auto-compile~ and ~ert~. +** Documenting + - The document inputs are stored in /docs. + - They are *written* in org format + - The README for the Github home page is *exported* to markdown + - The Manual, which can be used to supplement the README, is itself + *exported* to texi which is converted to info for viewing within Emacs + - The CONTRIBUTING guide is *exported* to markdown +** Distributing + - The repository is designed to be hosted on Github + - All CI files are stored under [[../.github/][./github]]. (You can include this directory + with minor modifications into other Elisp projects.) + - Github actions are included for CI. + - Dependencies for CI are obtained via Nix. + - Actions call into Elisp by running Emacs with arguments passed to + [[../.github/ruh-shim.el][.github/run-shim.el]]. + - Many people install packages directly from repositories (because we like + supply-chain attacks), so keep your installation instructions up to date. + - You are free to submit your package for distribution on MELPA etc. This + 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 + (use-package erk + :elpaca (erk :host github :repo "positron-solutions/elisp-repo-kit")) + + ;; straight with explicit recipe + (use-package erk + :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. + +* Creating Packages + + #+findex: erk-new + The simplest and intended way is to call ~erk-new~. It will first + ask you for: + + - Choose a template + - Root directory you want to clone to + - Package title + - Package feature + - Package prefix + - Author name + - Email address + - GitHub user or organization + + #+findex: erk-rename-relicense + ~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 your new GitHub repo][finish setting up]] Have fun! +* Using ERK for development + 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 + #+findex: erk-reload-project-package + ~erk-reload-project-package~ will unload and recompile your package if + necessary and then reload it. + + #+findex: erk-reload-project-tests + ~erk-reload-project-tests~ is the complementary command for reloading tests. + - 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. + + - Duplicating CI Locally + The CI configuration is all stored in [[../.github/][.github]]. Usually you will want + development instructions in your new repository. The [[./CONTRIBUTING.org][CONTRIBUTING]] guide + contains instructions to reproduce the CI behavior. +** Find Files + Accidentally editing generated files or forgetting the path to a file is + annoying. ERK provides a helper to find files based on their purpose. + + #+findex: erk-find + ~erk-find~ will ask you to pick the file based on what it does. It's + choices: + + - ci-dco + - ci-nix-flake + - ci-run-shim + - ci-tests + - docs-contributing + - docs-manual + - docs-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 + - ~erk-find-ci-dco~ + #+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-docs-contributing + - ~erk-find-docs-contributing~ + #+findex: erk-find-docs-manual + - ~erk-find-find-docs-manual~ + #+findex: erk-find-docs-readme + - ~erk-find-docs-readme~ + +* Documenting Your Package +#+cindex: document re-structuring +#+cindex: document export +How this manual is created and maintained. Elisp Repo Kit's manual scheme +overview: +- Contents for the README and built-in manual are mostly stored in + [[./manual.org][/docs/manual.org]] +- Each document stitches together the contents of [[./manual.org][/docs/manual.org]] using + =#+include:= directives. Some of the header, footer, and export information + just controls formatting. +- The [[../README.md][README.md]] for your repository's home page is generated from [[./README.org][/docs/README.org]] +- The [[./manual.texi]] is generated from [[./manual.org][/docs/manual.org]] +- Re-generation-on-save is automatically configured at the end of each document +- The .gitignore will ignore any intermediate outputs, so there's no need to + store them in /tmp or clean them + +** What makes a good manual + #+cindex: what makes a good manual + Manuals can do four things that code and code-based automatic indices are + fundamentally terrible at: + + - Describe ideas at higher levels of abstraction, concepts which may have no + correct docstring to live in. This includes providing several levels of + abstraction or abstraction from different perspectives. + - Describe behavior or state models in implementations or external systems, + things that aren't even apparent from reading the code. Think + microprocessor behavior, 3rd party API behavior, or a code convention + implemented in multiple functions + - Examples that distill and illustrate ideas in isolation instead of how they + are naturally composed and become complex when applied in practice. + - Illuminate workflows and paths of understanding that do not follow the + linear ordering of code or any natural automatic index. + + In short, a good manual ties together the very large or uncovers what is + opaque. Neither of these types of information have a 1:1 correspondence to + the definitions in your code. + + From this lack of correspondence, we can also conclude that good manuals are + in fact *not exhaustive*. Exhaustive manuals are merely *exhausting*, + requiring tremendous maintenance to do what is already available through + automation or even just reading the code. + +*** Generative AI and manuals + One thing manuals had been good at was linking to concepts based on + conceptual relationship. While semantic search queries have been around for + a while, large language models have transformed the process, providing the + semantic output to the semantic search. As semantic search and semantic synthesis of + output become more accessible, it's becoming less important to conceptually + relating information to bridge the limitations of more exact types of search. + + Increasingly, machine learning is becoming able to synthesize information + from numerous sources and present it via *natural language queryable + indices*. Keep in mind, when writing a manual, if the information you are + providing doesn't add information that is easily decideable from the other + information already available about your package, your manual is likely to + become increasingly redundant information. +*** Automatic indexing + Your package's code also already has a ton of automatic indexing via Emacs + discovery: + + - ~describe-function~ etc + - completions for functions, variables, commands, macros etc + - signature hints from eldoc + - searching within the code + - symbol-based navigation within the code + + Documenting full enumerations of things in the same order that they live in + code or in the same order as automatic indexing commonly produces throughout + normal development is rarely useful. This information *does* have a natural + 1:1 correspondence to readily available information, and is therefore likely + redundant. If you wind up with a manual like this, it would be better to + leave the information in the code and generate the manual from the docstrings. +** Re-Generating + Re-generation is configured to occur on save at the end of each document. + You can manually re-generate the documentation using the elisp-repo-kit + commands: + + #+findex: erk-export-contributing + - ~erk-export-contributing~ + #+findex: erk-export-manual + - ~erk-export-manual~ + #+findex: erk-export-readme + - ~erk-export-readme~ + #+findex: erk-export-docs + - ~erk-export-docs~ is just a shortcut to just export all documents. + + With a prefix argument, the export commands will also show the resulting + output. +*** Finding and Editing + The inputs for exported documentation has shortcuts: + - ~erk-find-readme~ + - ~erk-find-manual~ + Using Org mode settings, these manuals will automatically trigger + re-generation when you save, helping you avoid shipping a stale or broken + manual. +*** Previewing + Additionally, if you want to re-export and preview in one command, there are + shortcuts that export and then open the result, either in view mode for + markdown or info mode for the manual. + + #+findex: erk-export-preview-manual + - ~erk-export-preview-manual~ + This will export the manual.org to texi, process it to info, and finally + load the manual.info file. + #+findex: erk-export-preview-contributing + - ~erk-export-preview-contributing~ + #+findex: erk-export-preview-readme + - ~erk-export-preview-readme~ +** Formatting +#+cindex: document formatting + Texi supports a lot of features specific to automatic manual maintenance. Be + sure that your beautiful org document also generates a useful manual. +*** Quoting symbols :item: + Symbols will \~display\~ `quoted' in the manual. Mentioning a symbol will + *not* place it in the manual's index. +*** Elisp Source Blocks :item: + #+begin_src elisp + (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 + formats such as running elisp expressions through links may not work. + + [[https://git.sr.ht/~ushin/hyperdrive.el/tree/master/item/doc/hyperdrive-manual.org][This link to the hyperdrive manual]] works like you expect. + + 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 + manual, arranged by entities of similar type. + + Have a look at the docs generation for org, magit, transient, and + hyperdrive for more extensive examples. Typically the are using a setup file + to declare export options. +*** Creating Indices :item: + Texinfo includes six *predefined* index types: + + - concept + - function + - variables + - keystroke + - program + - 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. + + #+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 + would such an index point to? You have to manually create index entries. + + To cause an index entry to be created and to link to a heading, add a + property within that heading. Adding to the predefined indexes: + + - #+cindex: my-concept + - #+findex: my-function + - #+vindex: my-variable + - #+kindex: my-key + - #+pindex: my-program + - #+tindex: my-type + + These will create index entries that can be used to navigate your manual by + listings of related entities. + +*** 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 + You can copy this checklist to your org agenda files: + + - [X] Create a repository (from [[*Install ERK][install]] 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* [[*Licensing, Developer Certificate of Origin][sign-off]] so that it will be hypothetically [[file:README.org::*Footnote on FSF and Emacs Core Licensing][straightforward]] for for FSF + to pull in your changes if they later change to DCO instead of copyright + 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 + - [ ] 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: + + #+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. + + - [ ] Get your package working, pushed, actions run, and CI badges all green + - [ ] [[*Publishing to MELPA][Publish]] to MELPA + - [ ] Make a post on [[https://reddit.com/r/emacs/][reddit]] and [[https://emacs.ch/][mastodon]] about your new package + +*** Optional Steps + #+cindex: security github branch protect + - [ ] Branch protect and enable check requirements for your default branch + (usually master). Merge commits, verified only, and no force push are + recommended settings. + - [ ] Enable requiring contributors to sign-off on web-based commits + #+cindex: security github actions + - [ ] For *security* of your Cachix secrets (and any others), require + Actions approval for all outside contributors. Disabling write and + disabling creation & approval of PR's is least privilege. + + Cachix is somewhat optional. It's free for open-source projects. It's + about as easy to sign up and generate the token as to remove the step from + the GitHub actions, so you won't save much time by avoiding it. + + If you opt out of cachix or any other binary cache, you will definitely want + to turn off tests for ~emacsGit~ etc because the build times are about + 30min-1hr per run when a fresh Emacs must be built. + +** Customizing CI + + The [[../.github/run-shim.el][run-shim.el]] script is just provides a CLI interface for adding commands in + the [[../.github/workflows/ci.yml][ci.yml]] CI declaration. Each action step just loads the shell, declared in + the [[../.github/flake.nix][flake.nix]] and then runs the shim in Emacs. The shim consumes the CLI + command arguments, so you can parameterize the invocations that way. + + - If you need extra elisp dependencies during CI, add them to the ~epkgs~ list + in the flake.nix. + #+cindex: nix adding 3rd party dependencies + - If you need extra 3rd party dependencies, add them to ~packages~ in the call + to ~mkShell~. + - To invoke different elisp operations, add / modify the commands in + [[../.github/run-shim.el][run-shim.el]]. + + There's more information in [[../CONTRIBUTING.md][CONTRIBUTING]] about running commands locally. You + will want this information in your new repository. + +** Licensing, Developer Certificate of Origin + This project and its derivative templates are distributed with an MIT + license. ~erk-new~ will also run ~erk-rename-relicense~, which will + automatically switch to the GPL3 license. *The MIT license allows + re-licensing, and so this change is compatible.* If you accept non-trivial + changes to your project, it will be very hard to change to the GPL3 later, so + consider this choice. + + The new repository will also come with DCO sign-off checking on PR's. The + instructions are in the [[../CONTRIBUTING.md][CONTRIBUTING]] guide. A DCO sign-off policy will give + your project a clear attestation of sufficient direct or transitive authority + from each contributor to submit changes under the terms of your project's + license. This can only improve your legal protection from incidentally + handling copyrighted code. + + The DCO choice in this repository is also meant to encourage & push stodgy + organizations whose responsibility it was to invent better processes towards + lower friction paths to contribute code. If you fail to implement the DCO + sign-off scheme, there is less hope that the FSF will someday independently + merge changes that accumulate in your package because there will not be a + clear chain of license compliance. + +** Publishing to MELPA + + If you have green CI, you have already passed many requirements of publishing a + MELPA package. *You still need to build your package and verify your recipe.* + You are going to clone melpa in order to make your PR. You can use the clone to + verify the recipe. + +*** Creating the recipe + + Fork MELPA personally (not for organization) and clone it to wherever you keep + your upstreams. It's a good idea to separate upstreams from projects you + actively maintain so you can see and delete upstreams when not in use. + + #+begin_src shell + + mkdir -p upstream + cd upstream + git clone git@github.com:$GITHUB_USER/melpa.git # replace $GITHUB_USER + + #+end_src + + Install package-build + + #+begin_src elisp + (use-package package-build) + #+end_src + + ~package-build-create-recipe~ will give you something like: + + #+begin_src elisp + (erk :fetcher github :repo "positron-solutions/elisp-repo-kit") + #+end_src + + The following template can be filled in and pull-requested to MELPA to publish. + You don't need to touch ~:files~. The ~commit~ and ~branch~ are optional + depending on how you version / develop / tag your releases. + + Copy the recipe into =recipes/erk= inside your MELPA clone. + +*** Testing package build + + Inside the MELPA clone root: + + #+begin_src shell + + # Builds the package + make recipes/erk + # Test sandbox installation (will affect ~/.emacs.d/elpa So much for sandbox ¯\_(ツ)_/¯ + EMACS_COMMAND=$(which emacs) make sandbox INSTALL=erk + + #+end_src + +*** Testing stable package build + + You need a tag on your default (usually master) branch of your repo, + =positron-solutions/elisp-repo-kit=. Use ~git tag -S v0.1.0~ and ~git push + origin v0.1.0~. You can also just create a release in the GitHub interface. + + #+begin_src shell + + # Test stable builds against your tags + STABLE=t make recipes/erk + + #+end_src + +*** MELPA Lints + + Lastly, install [[https://github.com/riscy/melpazoid][melpazoid]] and call =melpazoid= on your main feature. It does + some additional lints. You may need to install =package-lint= if you don't have + it. It's not declared in melpazoid's requirements. Getting the package in Nix + is not easy yet since melpazoid is not yet on Melpa. + + #+begin_src elisp + + ;; using elpaca's with explicit recipe + (use-package melapzoid + :elpaca (melpazoid :host github :repo "riscy/melpazoid")) + + ;; using straight + (straight-use-package + '(melpazoid :type git :host github :repo "riscy/melpazoid" :files ("melpazoid/melpazoid.el"))) + + #+end_src + + 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 + After creating an ERK project, it becomes independent with respect to version + control and is not expected to maintain commit compatibility (this is a + terrible idea, so don't try it). + + Many files don't actually change much from project to project. Here's a list + of files that you generally can just copy and enjoy the updates for: + + - .github/pull_request_template.md + - docs/CONTRIBUTING.org + + Some files are usually tweaked, but only a little bit. Drastic overhauls + will be mentioned in change notes. You probably want to update by hand: + + - .github/run-shim.el + - .github/workflows/dco.yml + - .github/workflows/ci.yml + - .github/flake.nix + + Upgrades to ERK will mention if any values need to be changed before copying + any project-indpendent files. *Changes to github actions will require you to + update the approved actions setting.* + + Other files which occasionally receive updates, but changes that you likely + *do not* want at all: + + - docs/README.org + - docs/manual.org + + These files are *generated*, so you can ignore upstream changes even though + they are under version control: + + - .github/flake.lock + - README.md + - CONTRIBUTING.md + - docs/manual.texi + +** Nix + 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 + + The Nix [[../github/flake.nix][flake.nix]] is where versions are declared. The [[../.github/flake.lock][flake.lock]] stores a + fixed value for these declarations. These fixed versions need periodic + update. Nixpkgs has a new release about every six months. You can check + their [[https://github.com/NixOS/nixpkgs/branches][branches]] and [[https://github.com/NixOS/nixpkgs/tags][tags]] to see what's current. The effect is similar to + updating linux versions. The ~nix~ command has a lot of support for + specifying versions besides just updating. + + #+begin_src shell + nix flake lock --update-input nixpkgs + #+end_src + + The ~emacs-overlay~, which contains fixed versions of Emacs and snapshots of + Elisp repository package sets can be updated by running: + + #+begin_src shell + nix flake lock --update-input emacs-overlay + #+end_src + +*** Customizing Versions + + #+cindex: nix custom Emacs versions + There are a lot of ways to declare specific versions of Emacs in Nix. You + can customize the build as much as you want if you go deep. + + The ~emacs-overlay~ already contains many versions, which are updated over + time. You can view which attributes are available by inspecting the current + 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 ];} + + # pkgs.emacs will tab complete + pkgs.version + # "28.2" + pkgs.emacsUnstable.version + # "29.1" + + # 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 = { + url = "github:emacs-mirror/emacs/emacs-29.1"; + flake = false; + }; + #... other inputs + }; + + # In your outputs, override one of the existing Emacs to make a new one: + emacs29 = pkgs.emacs.overrideAttrs (old: { + name = "emacs29"; + version = emacs29-src.shortRev; + src = emacs29-src; + }); + # It's nix expressions. It's programming. + # Ask your favorite large langauge model! + + #+end_src + + #+cindex: nix binary cache + #+cindex: security binary cache + Just keep in mind that highly specific versions also means that the first + CI run with those versions will not be cached. You can use Cachix or you own + binary cache to share builds among users. *Only trusted users should ever be + authorized to push to a binary cache.* + + MacOS tends to get a little less test emphasis, and so + ~nixpkgs-darwin-~ branches exist and are required to pass more + Darwin tests before merging. This is more stable if you are on + MacOS. + + ~nixpkgs-unstable~ or ~master~ are your other less common choices. Usually + when you need bleeding-edge or custom packages, instead of changing the whole + nixpkgs version, you override the specific packages by writing an overlay. + If you need help, file an issue and [[https://github.com/sponsors/positron-solutions][consider donating to maintenance]]. + +*** Reproducing Issues + #+cindex: nix handling version mismaches + + Without Nix, users may have difficulty reproducing or describing exact + situations. This creates two issues: + + - They can't tell you if the issue is caused by version mismatches + - They can't give you a method to reproduce their issue using versions that + would allow you to confirm a fix + + 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. + +* Indices +** Command and Function index + :PROPERTIES: + :INDEX: fn + :END: + +** Concept index + :PROPERTIES: + :INDEX: cp + :END: + +* Licensing +:PROPERTIES: +:COPYING: t +:END: + +#+include: "../COPYING" + +# Local Variables: +# after-save-hook: (lambda () (when (require 'erk nil t) (erk-export-docs))) +# End: diff --git a/docs/manual.texi b/docs/manual.texi new file mode 100644 index 0000000..c5d726f --- /dev/null +++ b/docs/manual.texi @@ -0,0 +1,1251 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename manual.info +@settitle Elisp Repo Kit +@documentencoding UTF-8 +@documentlanguage en +@comment !!!THIS FILE HAS BEEN GENERATED!!! Edit manual.org instead! +@c %**end of header + +@copying +Copyright (C) 2022 Positron Solutions + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the ``Software''), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT@. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE@. +@end copying + +@dircategory Emacs +@direntry +* Elisp Repo Kit: (erk). Create, distribute, and maintain Emacs Lisp packages. +@end direntry + +@finalout +@titlepage +@title Elisp Repo Kit +@subtitle for version 0.4.0 +@author Positron Solutions +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@contents + +@ifnottex +@node Top +@top Elisp Repo Kit + +Setting up a @emph{complete} Emacs Lisp package doesn't need to be a lot of work. +Elisp Repo Kit provides a convenient starting point for collaborating on a +public Elisp package repository: + +@itemize +@item +An Elisp package +@item +Tests +@item +CI & dependency management +@item +README and manual +@item +Licensing +@end itemize + +Furthermore, to get rid of pitfalls and streamline productive workflows when +working with Elisp packages, Elisp Repo Kit provides shortcuts: + +@itemize +@item +Loading and unloading modules +@item +Compiling +@item +Running tests +@item +Re-generating documentation +@item +Navigating the project +@end itemize +@end ifnottex + +@menu +* High Level Overview:: +* Install ERK:: +* Creating Packages:: +* Using ERK for development:: +* Documenting Your Package:: +* Distributing Your Package:: +* Maintaining Your Package:: +* Indices: Indices (1). + +@detailmenu +--- The Detailed Node Listing --- + +High Level Overview + +* File contents and structure:: +* Developing:: +* Documenting:: +* Distributing:: + +Install ERK + +* Manual cloning:: +* Manually add just CI:: + +Using ERK for development + +* Find Files:: + +Documenting Your Package + +* What makes a good manual:: +* Re-Generating:: +* Formatting:: +* Indices:: + +What makes a good manual + +* Generative AI and manuals:: +* Automatic indexing:: + +Re-Generating + +* Finding and Editing:: +* Previewing:: + +Formatting + +* Quoting symbols:: +* Elisp Source Blocks:: +* Links:: + +Indices + +* Creating Indices:: +* Adding Entries:: +* Render the Index:: + +Distributing Your Package + +* Setting Up Your Github Repository:: +* Customizing CI:: +* Licensing, Developer Certificate of Origin: Licensing Developer Certificate of Origin. +* Publishing to MELPA:: + +Setting Up Your Github Repository + +* Optional Steps:: + +Publishing to MELPA + +* Creating the recipe:: +* Testing package build:: +* Testing stable package build:: +* MELPA Lints:: + +Maintaining Your Package + +* Upgrading ERK:: +* Nix:: + +Nix + +* Maintaining versions:: +* Customizing Versions:: +* Reproducing Issues:: + +Indices + +* Command and Function index:: +* Concept index:: + +@end detailmenu +@end menu + +@node High Level Overview +@chapter High Level Overview + +@itemize +@item +Elisp Repo Kit provides a scheme for how to organize and distribute your +project, which it also uses in its own repository. + +@item +As an Elisp package, Elisp Repo Kit (erk) provides shortcuts for working on +package using the Elisp Repo Kit organization structure (as well as other +Elisp packages). +@end itemize + +@menu +* File contents and structure:: +* Developing:: +* Documenting:: +* Distributing:: +@end menu + +@node File contents and structure +@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 +├── CONTRIBUTING.md # typical instructions for development +├── COPYING # a GPL3 license +├── DCO # Developer Certificate of Origin +│ +├── .github +│ ├── .envrc # direnv integration with `nix develop` +│ ├── flake.nix # dependencies for this project +│ ├── flake.lock # version controlled lock of flake.nix input versions +│ ├── run-shim.el # elisp script with test & lint routines +│ ├── pull_request_template.md # reminders for PR contributors +│ └── workflows +│ ├── ci.yml # workflow for lints and tests +│ └── dco.yml # workflow to check DCO sign-offs +│ +├── docs +│ ├── README.org # generates README.md +│ ├── CONTRIBUTING.org # generates CONTRIBUTING.md +│ ├── manual.org # actual manual +│ └── manual.texi # generated manual for distribution +│ +├── lisp +│ └── erk.el # the package +│ +└── 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} +directories. + +@node Developing +@section Developing + +@itemize +@item +The package is stored in /lisp and its tests in /test. +@item +Many shortcuts are provided to run tests or rebuild and reload the package +or to visit the unit tests for a function under point etc. +@item +Most of the Elisp shortcuts lean on Emacs settings or packages such as +@code{auto-compile} and @code{ert}. +@end itemize + +@node Documenting +@section Documenting + +@itemize +@item +The document inputs are stored in /docs. +@item +They are @strong{written} in org format +@item +The README for the Github home page is @strong{exported} to markdown +@item +The Manual, which can be used to supplement the README, is itself +@strong{exported} to texi which is converted to info for viewing within Emacs +@item +The CONTRIBUTING guide is @strong{exported} to markdown +@end itemize + +@node Distributing +@section Distributing + +@itemize +@item +The repository is designed to be hosted on Github +@item +All CI files are stored under @uref{../.github/, ./github}. (You can include this directory +with minor modifications into other Elisp projects.) +@item +Github actions are included for CI@. +@item +Dependencies for CI are obtained via Nix. +@item +Actions call into Elisp by running Emacs with arguments passed to +@uref{../.github/ruh-shim.el, .github/run-shim.el}. +@item +Many people install packages directly from repositories (because we like +supply-chain attacks), so keep your installation instructions up to date. +@item +You are free to submit your package for distribution on MELPA etc. This +package is made available on MELPA partly to maintain the structure and +workflows for doing so. +@end itemize + +@node Install ERK +@chapter Install ERK + +@lisp + +(use-package erk) ; vanilla, assuming you have MELPA configured + +;; using elpaca's with explicit recipe +(use-package erk + :elpaca (erk :host github :repo "positron-solutions/elisp-repo-kit")) + +;; straight with explicit recipe +(use-package erk + :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 + +Copy the .github folder and the contributing guide to your package. Set up +your secrets for Cachix. Read the CI customization section. + +@node Creating Packages +@chapter Creating Packages + +@findex erk-new +The simplest and intended way is to call @code{erk-new}. It will first +ask you for: + +@itemize +@item +Choose a template +@item +Root directory you want to clone to +@item +Package title +@item +Package feature +@item +Package prefix +@item +Author name +@item +Email address +@item +GitHub user or organization +@end itemize + +@findex erk-rename-relicense +@code{erk-new} also calls @code{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 Have fun! + +@node Using ERK for development +@chapter Using ERK for development + +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. +@itemize +@item +Loading and re-loading your package +@findex erk-reload-project-package +@code{erk-reload-project-package} will unload and recompile your package if +necessary and then reload it. + +@findex erk-reload-project-tests +@code{erk-reload-project-tests} is the complementary command for reloading tests. +@end itemize +@itemize +@item +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. + +@item +Duplicating CI Locally +The CI configuration is all stored in @uref{../.github/, .github}. Usually you will want +development instructions in your new repository. The @uref{./CONTRIBUTING.org, CONTRIBUTING} guide +contains instructions to reproduce the CI behavior. +@end itemize + +@menu +* Find Files:: +@end menu + +@node Find Files +@section Find Files + +Accidentally editing generated files or forgetting the path to a file is +annoying. ERK provides a helper to find files based on their purpose. + +@findex erk-find +@code{erk-find} will ask you to pick the file based on what it does. It's +choices: + +@itemize +@item +ci-dco +@item +ci-nix-flake +@item +ci-run-shim +@item +ci-tests +@item +docs-contributing +@item +docs-manual +@item +docs-readme +@end itemize + +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 +@itemize +@item +@code{erk-find-ci-dco} +@end itemize +@findex erk-find-ci-nix-flake +@itemize +@item +@code{erk-find-ci-nix-flake} +@end itemize +@findex erk-find-ci-run-shim +@itemize +@item +@code{erk-find-ci-run-shim} +@end itemize +@findex erk-find-ci-test +@itemize +@item +@code{erk-find-ci-test} +@end itemize +@findex erk-find-docs-contributing +@itemize +@item +@code{erk-find-docs-contributing} +@end itemize +@findex erk-find-docs-manual +@itemize +@item +@code{erk-find-find-docs-manual} +@end itemize +@findex erk-find-docs-readme +@itemize +@item +@code{erk-find-docs-readme} +@end itemize + +@node Documenting Your Package +@chapter Documenting Your Package + +@cindex document re-structuring +@cindex document export +How this manual is created and maintained. Elisp Repo Kit's manual scheme +overview: +@itemize +@item +Contents for the README and built-in manual are mostly stored in +@uref{./manual.org, /docs/manual.org} +@item +Each document stitches together the contents of @uref{./manual.org, /docs/manual.org} using +@samp{#+include:} directives. Some of the header, footer, and export information +just controls formatting. +@item +The @uref{../README.md, README.md} for your repository's home page is generated from @uref{./README.org, /docs/README.org} +@item +The @uref{./manual.texi} is generated from @uref{./manual.org, /docs/manual.org} +@item +Re-generation-on-save is automatically configured at the end of each document +@item +The .gitignore will ignore any intermediate outputs, so there's no need to +store them in /tmp or clean them +@end itemize + +@menu +* What makes a good manual:: +* Re-Generating:: +* Formatting:: +* Indices:: +@end menu + +@node What makes a good manual +@section What makes a good manual + +@cindex what makes a good manual +Manuals can do four things that code and code-based automatic indices are +fundamentally terrible at: + +@itemize +@item +Describe ideas at higher levels of abstraction, concepts which may have no +correct docstring to live in. This includes providing several levels of +abstraction or abstraction from different perspectives. +@item +Describe behavior or state models in implementations or external systems, +things that aren't even apparent from reading the code. Think +microprocessor behavior, 3rd party API behavior, or a code convention +implemented in multiple functions +@item +Examples that distill and illustrate ideas in isolation instead of how they +are naturally composed and become complex when applied in practice. +@item +Illuminate workflows and paths of understanding that do not follow the +linear ordering of code or any natural automatic index. + +In short, a good manual ties together the very large or uncovers what is +opaque. Neither of these types of information have a 1:1 correspondence to +the definitions in your code. + +From this lack of correspondence, we can also conclude that good manuals are +in fact @strong{not exhaustive}. Exhaustive manuals are merely @strong{exhausting}, +requiring tremendous maintenance to do what is already available through +automation or even just reading the code. +@end itemize + +@menu +* Generative AI and manuals:: +* Automatic indexing:: +@end menu + +@node Generative AI and manuals +@subsection Generative AI and manuals + +One thing manuals had been good at was linking to concepts based on +conceptual relationship. While semantic search queries have been around for +a while, large language models have transformed the process, providing the +semantic output to the semantic search. As semantic search and semantic synthesis of +output become more accessible, it's becoming less important to conceptually +relating information to bridge the limitations of more exact types of search. + +Increasingly, machine learning is becoming able to synthesize information +from numerous sources and present it via @strong{natural language queryable +indices}. Keep in mind, when writing a manual, if the information you are +providing doesn't add information that is easily decideable from the other +information already available about your package, your manual is likely to +become increasingly redundant information. + +@node Automatic indexing +@subsection Automatic indexing + +Your package's code also already has a ton of automatic indexing via Emacs +discovery: + +@itemize +@item +@code{describe-function} etc +@item +completions for functions, variables, commands, macros etc +@item +signature hints from eldoc +@item +searching within the code +@item +symbol-based navigation within the code +@end itemize + +Documenting full enumerations of things in the same order that they live in +code or in the same order as automatic indexing commonly produces throughout +normal development is rarely useful. This information @strong{does} have a natural +1:1 correspondence to readily available information, and is therefore likely +redundant. If you wind up with a manual like this, it would be better to +leave the information in the code and generate the manual from the docstrings. + +@node Re-Generating +@section Re-Generating + +Re-generation is configured to occur on save at the end of each document. +You can manually re-generate the documentation using the elisp-repo-kit +commands: + +@findex erk-export-contributing +@itemize +@item +@code{erk-export-contributing} +@end itemize +@findex erk-export-manual +@itemize +@item +@code{erk-export-manual} +@end itemize +@findex erk-export-readme +@itemize +@item +@code{erk-export-readme} +@end itemize +@findex erk-export-docs +@itemize +@item +@code{erk-export-docs} is just a shortcut to just export all documents. +@end itemize + +With a prefix argument, the export commands will also show the resulting +output. + +@menu +* Finding and Editing:: +* Previewing:: +@end menu + +@node Finding and Editing +@subsection Finding and Editing + +The inputs for exported documentation has shortcuts: +@itemize +@item +@code{erk-find-readme} +@item +@code{erk-find-manual} +@end itemize +Using Org mode settings, these manuals will automatically trigger +re-generation when you save, helping you avoid shipping a stale or broken +manual. + +@node Previewing +@subsection Previewing + +Additionally, if you want to re-export and preview in one command, there are +shortcuts that export and then open the result, either in view mode for +markdown or info mode for the manual. + +@findex erk-export-preview-manual +@itemize +@item +@code{erk-export-preview-manual} +This will export the manual.org to texi, process it to info, and finally +load the manual.info file. +@end itemize +@findex erk-export-preview-contributing +@itemize +@item +@code{erk-export-preview-contributing} +@end itemize +@findex erk-export-preview-readme +@itemize +@item +@code{erk-export-preview-readme} +@end itemize + +@node Formatting +@section Formatting + +@cindex document formatting +Texi supports a lot of features specific to automatic manual maintenance. Be +sure that your beautiful org document also generates a useful manual. + +@menu +* Quoting symbols:: +* Elisp Source Blocks:: +* Links:: +@end menu + +@node Quoting symbols +@subsection Quoting symbols :item: + +Symbols will \~display\~ `quoted' in the manual. Mentioning a symbol will +@strong{not} place it in the manual's index. + +@node Elisp Source Blocks +@subsection Elisp Source Blocks :item: + +@lisp +(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 +@subsection Links :item: + +Hyperlinks thankfully can be written like you expect, though many org +formats such as running elisp expressions through links may not work. + +@uref{https://git.sr.ht/~ushin/hyperdrive.el/tree/master/item/doc/hyperdrive-manual.org, This link to the hyperdrive manual} works like you expect. + +The syntax is @samp{[[http://somewhere.com][label for my link]]} if you are +unfamiliar. + +@node Indices +@section 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 +manual, arranged by entities of similar type. + +Have a look at the docs generation for org, magit, transient, and +hyperdrive for more extensive examples. Typically the are using a setup file +to declare export options. + +@menu +* Creating Indices:: +* Adding Entries:: +* Render the Index:: +@end menu + +@node Creating Indices +@subsection Creating Indices :item: + +Texinfo includes six @strong{predefined} index types: + +@itemize +@item +concept +@item +function +@item +variables +@item +keystroke +@item +program +@item +data type +@end itemize + +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 +@subsection Adding Entries :item: + +Quoting a symbol will not result in an index entry. Which quoted symbol +would such an index point to? You have to manually create index entries. + +To cause an index entry to be created and to link to a heading, add a +property within that heading. Adding to the predefined indexes: + +@itemize +@item +#+cindex: my-concept +@item +#+findex: my-function +@item +#+vindex: my-variable +@item +#+kindex: my-key +@item +#+pindex: my-program +@item +#+tindex: my-type +@end itemize + +These will create index entries that can be used to navigate your manual by +listings of related entities. + +@node Render the Index +@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 +@chapter Distributing Your Package + +@menu +* Setting Up Your Github Repository:: +* Customizing CI:: +* Licensing, Developer Certificate of Origin: Licensing Developer Certificate of Origin. +* Publishing to MELPA:: +@end menu + +@node Setting Up Your Github Repository +@section Setting Up Your Github Repository + +You can copy this checklist to your org agenda files: + +@itemize +@item +Create a repository (from @ref{Install ERK, , install} instructions) +@item +Create an empty GitHub repository configure it as your git remote +@item +Set up your git commit signing (and verification so that it's obvious) +@strong{and} @ref{Licensing Developer Certificate of Origin, , sign-off} so that it will be hypothetically @uref{README.org, straightforward} for for FSF +to pull in your changes if they later change to DCO instead of copyright +assignment. +@item +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 +@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 +@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. + +@item +Get your package working, pushed, actions run, and CI badges all green +@item +@ref{Publishing to MELPA, , Publish} to MELPA +@item +Make a post on @uref{https://reddit.com/r/emacs/, reddit} and @uref{https://emacs.ch/, mastodon} about your new package +@end itemize + +@menu +* Optional Steps:: +@end menu + +@node Optional Steps +@subsection Optional Steps + +@cindex security github branch protect +@itemize +@item +Branch protect and enable check requirements for your default branch +(usually master). Merge commits, verified only, and no force push are +recommended settings. +@item +Enable requiring contributors to sign-off on web-based commits +@end itemize +@cindex security github actions +@itemize +@item +For @strong{security} of your Cachix secrets (and any others), require +Actions approval for all outside contributors. Disabling write and +disabling creation & approval of PR's is least privilege. +@end itemize + +Cachix is somewhat optional. It's free for open-source projects. It's +about as easy to sign up and generate the token as to remove the step from +the GitHub actions, so you won't save much time by avoiding it. + +If you opt out of cachix or any other binary cache, you will definitely want +to turn off tests for @code{emacsGit} etc because the build times are about +30min-1hr per run when a fresh Emacs must be built. + +@node Customizing CI +@section Customizing CI + +The @uref{../.github/run-shim.el, run-shim.el} script is just provides a CLI interface for adding commands in +the @uref{../.github/workflows/ci.yml, ci.yml} CI declaration. Each action step just loads the shell, declared in +the @uref{../.github/flake.nix, flake.nix} and then runs the shim in Emacs. The shim consumes the CLI +command arguments, so you can parameterize the invocations that way. + +@itemize +@item +If you need extra elisp dependencies during CI, add them to the @code{epkgs} list +in the flake.nix. +@end itemize +@cindex nix adding 3rd party dependencies +@itemize +@item +If you need extra 3rd party dependencies, add them to @code{packages} in the call +to @code{mkShell}. +@item +To invoke different elisp operations, add / modify the commands in +@uref{../.github/run-shim.el, run-shim.el}. +@end itemize + +There's more information in @uref{../CONTRIBUTING.md, CONTRIBUTING} about running commands locally. You +will want this information in your new repository. + +@node Licensing Developer Certificate of Origin +@section Licensing, Developer Certificate of Origin + +This project and its derivative templates are distributed with an MIT +license. @code{erk-new} will also run @code{erk-rename-relicense}, which will +automatically switch to the GPL3 license. @strong{The MIT license allows +re-licensing, and so this change is compatible.} If you accept non-trivial +changes to your project, it will be very hard to change to the GPL3 later, so +consider this choice. + +The new repository will also come with DCO sign-off checking on PR's. The +instructions are in the @uref{../CONTRIBUTING.md, CONTRIBUTING} guide. A DCO sign-off policy will give +your project a clear attestation of sufficient direct or transitive authority +from each contributor to submit changes under the terms of your project's +license. This can only improve your legal protection from incidentally +handling copyrighted code. + +The DCO choice in this repository is also meant to encourage & push stodgy +organizations whose responsibility it was to invent better processes towards +lower friction paths to contribute code. If you fail to implement the DCO +sign-off scheme, there is less hope that the FSF will someday independently +merge changes that accumulate in your package because there will not be a +clear chain of license compliance. + +@node Publishing to MELPA +@section Publishing to MELPA + +If you have green CI, you have already passed many requirements of publishing a +MELPA package. @strong{You still need to build your package and verify your recipe.} +You are going to clone melpa in order to make your PR@. You can use the clone to +verify the recipe. + +@menu +* Creating the recipe:: +* Testing package build:: +* Testing stable package build:: +* MELPA Lints:: +@end menu + +@node Creating the recipe +@subsection Creating the recipe + +Fork MELPA personally (not for organization) and clone it to wherever you keep +your upstreams. It's a good idea to separate upstreams from projects you +actively maintain so you can see and delete upstreams when not in use. + +@example + +mkdir -p upstream +cd upstream +git clone git@@github.com:$GITHUB_USER/melpa.git # replace $GITHUB_USER + +@end example + +Install package-build + +@lisp +(use-package package-build) +@end lisp + +@code{package-build-create-recipe} will give you something like: + +@lisp +(erk :fetcher github :repo "positron-solutions/elisp-repo-kit") +@end lisp + +The following template can be filled in and pull-requested to MELPA to publish. +You don't need to touch @code{:files}. The @code{commit} and @code{branch} are optional +depending on how you version / develop / tag your releases. + +Copy the recipe into @samp{recipes/erk} inside your MELPA clone. + +@node Testing package build +@subsection Testing package build + +Inside the MELPA clone root: + +@example + +# Builds the package +make recipes/erk +# Test sandbox installation (will affect ~/.emacs.d/elpa So much for sandbox ¯\_(ツ)_/¯ +EMACS_COMMAND=$(which emacs) make sandbox INSTALL=erk + +@end example + +@node Testing stable package build +@subsection Testing stable package build + +You need a tag on your default (usually master) branch of your repo, +@samp{positron-solutions/elisp-repo-kit}. Use @code{git tag -S v0.1.0} and @code{git push + origin v0.1.0}. You can also just create a release in the GitHub interface. + +@example + +# Test stable builds against your tags +STABLE=t make recipes/erk + +@end example + +@node MELPA Lints +@subsection MELPA Lints + +Lastly, install @uref{https://github.com/riscy/melpazoid, melpazoid} and call @samp{melpazoid} on your main feature. It does +some additional lints. You may need to install @samp{package-lint} if you don't have +it. It's not declared in melpazoid's requirements. Getting the package in Nix +is not easy yet since melpazoid is not yet on Melpa. + +@lisp + +;; using elpaca's with explicit recipe +(use-package melapzoid + :elpaca (melpazoid :host github :repo "riscy/melpazoid")) + +;; using straight +(straight-use-package + '(melpazoid :type git :host github :repo "riscy/melpazoid" :files ("melpazoid/melpazoid.el"))) + +@end lisp + +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. + +@node Maintaining Your Package +@chapter Maintaining Your Package + +Keeping your project fresh. + +@menu +* Upgrading ERK:: +* Nix:: +@end menu + +@node Upgrading ERK +@section Upgrading ERK + +After creating an ERK project, it becomes independent with respect to version +control and is not expected to maintain commit compatibility (this is a +terrible idea, so don't try it). + +Many files don't actually change much from project to project. Here's a list +of files that you generally can just copy and enjoy the updates for: + +@itemize +@item +.github/pull_request_template.md +@item +docs/CONTRIBUTING.org +@end itemize + +Some files are usually tweaked, but only a little bit. Drastic overhauls +will be mentioned in change notes. You probably want to update by hand: + +@itemize +@item +.github/run-shim.el +@item +.github/workflows/dco.yml +@item +.github/workflows/ci.yml +@item +.github/flake.nix +@end itemize + +Upgrades to ERK will mention if any values need to be changed before copying +any project-indpendent files. @strong{Changes to github actions will require you to +update the approved actions setting.} + +Other files which occasionally receive updates, but changes that you likely +@strong{do not} want at all: + +@itemize +@item +docs/README.org +@item +docs/manual.org +@end itemize + +These files are @strong{generated}, so you can ignore upstream changes even though +they are under version control: + +@itemize +@item +.github/flake.lock +@item +README.md +@item +CONTRIBUTING.md +@item +docs/manual.texi +@end itemize + +@node Nix +@section Nix + +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. + +@menu +* Maintaining versions:: +* Customizing Versions:: +* Reproducing Issues:: +@end menu + +@node Maintaining versions +@subsection Maintaining versions + +@cindex nix dependency updating + +The Nix @uref{../github/flake.nix, flake.nix} is where versions are declared. The @uref{../.github/flake.lock, flake.lock} stores a +fixed value for these declarations. These fixed versions need periodic +update. Nixpkgs has a new release about every six months. You can check +their @uref{https://github.com/NixOS/nixpkgs/branches, branches} and @uref{https://github.com/NixOS/nixpkgs/tags, tags} to see what's current. The effect is similar to +updating linux versions. The @code{nix} command has a lot of support for +specifying versions besides just updating. + +@example +nix flake lock --update-input nixpkgs +@end example + +The @code{emacs-overlay}, which contains fixed versions of Emacs and snapshots of +Elisp repository package sets can be updated by running: + +@example +nix flake lock --update-input emacs-overlay +@end example + +@node Customizing Versions +@subsection Customizing Versions + +@cindex nix custom Emacs versions +There are a lot of ways to declare specific versions of Emacs in Nix. You +can customize the build as much as you want if you go deep. + +The @code{emacs-overlay} already contains many versions, which are updated over +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 ];@} + +# pkgs.emacs will tab complete +pkgs.version +# "28.2" +pkgs.emacsUnstable.version +# "29.1" + +# 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 = @{ + url = "github:emacs-mirror/emacs/emacs-29.1"; + flake = false; + @}; + #... other inputs +@}; + +# In your outputs, override one of the existing Emacs to make a new one: +emacs29 = pkgs.emacs.overrideAttrs (old: @{ + name = "emacs29"; + version = emacs29-src.shortRev; + src = emacs29-src; +@}); +# It's nix expressions. It's programming. +# Ask your favorite large langauge model! + +@end example + +@cindex nix binary cache +@cindex security binary cache +Just keep in mind that highly specific versions also means that the first +CI run with those versions will not be cached. You can use Cachix or you own +binary cache to share builds among users. @strong{Only trusted users should ever be +authorized to push to a binary cache.} + +MacOS tends to get a little less test emphasis, and so +@code{nixpkgs-darwin-} branches exist and are required to pass more +Darwin tests before merging. This is more stable if you are on +MacOS@. + +@code{nixpkgs-unstable} or @code{master} are your other less common choices. Usually +when you need bleeding-edge or custom packages, instead of changing the whole +nixpkgs version, you override the specific packages by writing an overlay. +If you need help, file an issue and @uref{https://github.com/sponsors/positron-solutions, consider donating to maintenance}. + +@node Reproducing Issues +@subsection Reproducing Issues + +@cindex nix handling version mismaches + +Without Nix, users may have difficulty reproducing or describing exact +situations. This creates two issues: + +@itemize +@item +They can't tell you if the issue is caused by version mismatches +@item +They can't give you a method to reproduce their issue using versions that +would allow you to confirm a fix +@end itemize + +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 Indices (1) +@chapter Indices + +@menu +* Command and Function index:: +* Concept index:: +@end menu + +@node Command and Function index +@section Command and Function index + +@printindex fn + +@node Concept index +@section Concept index + +@printindex cp + +@bye \ No newline at end of file diff --git a/gpl-3.0.txt b/gpl-3.0.txt deleted file mode 100644 index f288702..0000000 --- a/gpl-3.0.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General 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 General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/lisp/erk.el b/lisp/erk.el index 9f008d3..dba5364 100644 --- a/lisp/erk.el +++ b/lisp/erk.el @@ -1,10 +1,10 @@ ;;; erk.el --- Elisp (GitHub) Repository Kit -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Positron Solutions +;; Copyright (C) 2023 Positron Solutions -;; Author: -;; Keywords: convenience -;; Version: 0.2.0 +;; Author: Positron Solutions +;; Keywords: convenience, programming +;; Version: 0.4.0 ;; Package-Requires: ((emacs "28.1") (auto-compile "1.2.0") (dash "2.18.0")) ;; Homepage: http://github.com/positron-solutions/elisp-repo-kit @@ -28,11 +28,11 @@ ;;; Commentary: ;; Set up Emacs package with GitHub repository configuration, complete with -;; Actions CI, tests, lints, 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. +;; 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 & @@ -48,34 +48,61 @@ ;;; Code: ;; see flake.nix for providing dependencies for CI and local development. -(require 'project) (require 'auto-compile) (require 'dash) (require 'ert) +(require 'finder) +(require 'license-templates) +(require 'lisp-mnt) +(require 'project) (require 'vc) +(require 'org) +(require 'info) (eval-when-compile (require 'subr-x)) -(defgroup erk nil "Elisp repository kit." :prefix 'erk :group 'erk) +(defgroup erk nil "Elisp repository kit." :prefix 'erk :group 'programming) -(defcustom erk-github-package-name "elisp-repo-kit" - "Default GitHub for cloning templates. -If you rename this repository after forking, you need to set this -to clone from within the fork." - :group 'erk - :type 'string) +(defcustom erk-templates + '((erk-basic :github-path "positron-solutions/erk-basic")) + "Alist of template repositories configured for cloning in `erk-new'. +Each value form is a plist with keys: -(defcustom erk-package-prefix "erk" - "The prefix is used to features and file names." - :group 'erk - :type 'string) +- `:github-path`: Github style path, `user-or-org/repo'. + +- `:feature`: By convention, the root feature decides file names. + If nil, uses repo in the github-path. + +- `:prefix`: Elisp namespace prefix, usually the same as feature + but sometimes it is a contraction or initialism. If nil, uses + feature or repo in the github-path. + +- `:title`: Optional. The package usually has a longer name + present in it's root feature's header. Set title to specify + this or rely on `erk-package-title' to infer it. -(defcustom erk-github-userorg "positron-solutions" - "Default GitHub for cloning templates. -If you fork this repository, you need to set this to clone it -from within the fork." +Template information is likely to grow in the future to support +templates with different layouts and other requirements." :group 'erk - :type 'string) + :type '(alist :key-type symbol :value-type plist)) + +(defun erk--template-github-userorg (template) + "Get the github repo from a TEMPLATE." + (car (split-string (plist-get template :github-path) "/"))) + +(defun erk--template-github-repo (template) + "Get the github repo from a TEMPLATE." + (cadr (split-string (plist-get template :github-path) "/"))) + +(defun erk--template-feature (template) + "Get the feature from TEMPLATE. +Falls back to github repo from :github-path." + (or (plist-get template :feature) (erk--template-github-repo template))) + +(defun erk--template-prefix (template) + "Get the prefix from TEMPLATE. +Falls back to :prefix and then the repo from :github-path." + (or (plist-get template :prefix) (erk--template-feature template))) (defconst erk--gpl3-notice ";; This program is free software; \ you can redistribute it and/or modify @@ -91,18 +118,34 @@ you can redistribute it and/or modify ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see .") +;; TODO In order to work with a variety of repos with valid Elisp package +;; layouts, these variables should replaced with file discovery. Then the +;; templates should contain any custom renaming information specific to their +;; layout and content. Finally, re-licensing should be made dynamic and able to +;; use a combination of discovery and template information to. PR's for any of +;; these tweaks will be gladly accepted. (defconst erk--rename-maps - '(( nil "gpl-3.0.txt" "COPYING") - ("lisp/" "erk.el" nil) - ("test/" "erk-test.el" nil)) - "List of (directory file replacement-file) forms.") + '(("lisp/" "%s.el" nil) + ("test/" "%s-test.el" nil)) + "List of (DIR SRC DEST) forms.") (defconst erk--files-with-strings - '("README.org" - "CONTRIBUTING.org" - "lisp/erk.el" - "test/erk-test.el" - ".github/run-shim.el")) + '("docs/manual.org" + "docs/README.org" + "lisp/%s.el" + "test/%s-test.el" + ".github/run-shim.el") + "Files for general string replacement.") + +(defconst erk--files-with-copy-notices + '("lisp/%s.el") + "Files that need copy notice replacement.") + +(defun erk--expand-filenames (files feature) + "Render the FEATURE into the list of FILES." + (mapcar + (lambda (f) (format f feature)) + files)) (defconst erk--delete-files '(".github/FUNDING.yml") @@ -140,15 +183,19 @@ you can redistribute it and/or modify (byte-compile-file (concat dir "/" (symbol-name feature) ".el")))) (require feature)))) +(defun erk--dir-elisp-files (dir) + "Return a list of the elisp files in DIR. +Ignore autoloads." + (->> + (directory-files dir nil (rx ".el" string-end)) + (--reject (string-match-p (rx "autoloads.el" string-end) it)))) + (defun erk--dir-features (dir) "Return list of features provided by elisp files in DIR. -Except autoloads." - (let* ((package-files (directory-files dir nil (rx ".el" string-end))) - (package-files (->> package-files - (--reject (string-match-p (rx "autoloads.el" string-end) it))))) - (mapcar - (lambda (f) (intern (string-remove-suffix ".el" f))) - package-files))) +Ignore autoloads." + (mapcar + (lambda (f) (intern (string-remove-suffix ".el" f))) + (erk--dir-elisp-files dir))) (defun erk--package-features () "List the features defined by the project's package. @@ -156,12 +203,58 @@ This assumes the convention of one elisp file per feature and feature name derived file name" (erk--dir-features (concat (erk--project-root) "lisp" ))) +(defun erk--package-root-feature () + "Return the shortest feature in the package root." + (car (sort (erk--package-features) + (lambda (l r) + (< (length (symbol-name l)) + (length (symbol-name r))))))) + (defun erk--test-features () "List the features defined in project's test packages. This assumes the convention of one elisp file per feature and feature name derived file name" (erk--dir-features (concat (erk--project-root) "test" ))) +(defun erk--project-elisp-dir () + "Return the location of elisp files. +Only understands project root or root/lisp." + (let* ((project-root (erk--project-root)) + (lisp-subdir (concat project-root "lisp"))) + (if (file-exists-p lisp-subdir) lisp-subdir + project-root))) + +;; TODO quite redundant +(defun erk--project-root-feature-file () + "Return the path of the root feature for the project." + (let* ((project-elisp-dir (erk--project-elisp-dir)) + (package-files (directory-files project-elisp-dir nil (rx ".el" string-end))) + (package-files (->> package-files + (--reject (string-match-p (rx "autoloads.el" string-end) it)))) + (root-feature-file (car (sort package-files #'string<)))) + (concat project-elisp-dir "/" root-feature-file))) + +(defun erk-package-author () + "Return the author of this project's package." + (car (car (lm-authors (erk--project-root-feature-file))))) + +(defun erk-package-email () + "Return the email of this project's package." + (cdr (car (lm-authors (erk--project-root-feature-file))))) + +(defun erk-package-version () + "Return the version of this project's package." + (lm-version (erk--project-root-feature-file))) + +(defun erk-package-title () + "Return the title of the package for documentation." + (let ((file(erk--project-root-feature-file))) + (with-temp-buffer + (insert-file-contents file) + (goto-char (point-min)) + (when (re-search-forward "---\\(.*?\\)-\\*-" nil t) + (string-trim (match-string 1)))))) + ;;;###autoload (defun erk-reload-project-package () "Reload the features this project provides. @@ -259,34 +352,60 @@ ERROR is the error message." (pop-to-buffer "erk-clone") (error ,error))) -(defun erk--rename-package (dir old-package new-package) - "Rename FILES in DIR. -`erk--rename-map' is a list of (subdir filename -replacement-filename) triples. When subdir is nil, it means use -DIR. If replacement-filename is nil means replace OLD-PACKAGE -with NEW-PACKAGE, using `replace-regexp-in-string'. DIR is the -root of where we are renaming. Existing files will be -clobbered." - (mapc (lambda (f) (delete-file (concat (file-name-as-directory dir) f))) +(defun erk--rename-package (template clone-dir replacements) + "Rename files in CLONE-DIR. +TEMPLATE is a form described in `erk-templates'. REPLACEMENTS is +a form from `erk-new'. + +`erk--rename-map' is a list of (SUBDIR SRC DEST) triples. When +SUBDIR is nil, the path is CLONE-DIR itself. If DEST is nil +means replace old-feature with new-feature, using +`replace-regexp-in-string'. CLONE-DIR is the root of where we +are renaming. Existing files will be clobbered." + (mapc (lambda (f) (delete-file (concat (file-name-as-directory clone-dir) f))) erk--delete-files) - (mapc (lambda (rename-map) - (let* ((dir (concat dir (or (pop rename-map) ""))) - (src (pop rename-map)) - (replacement-filename (pop rename-map)) - (git-bin (executable-find "git")) - (output (get-buffer-create "erk-clone")) - (dest (or replacement-filename - (replace-regexp-in-string old-package new-package src))) - (default-directory dir)) - (when (file-exists-p dest) + ;; Note, dashes removed from inputs in `erk-new', but left here just in case + ;; called from Elisp. + (let* ((old-feature (erk--nodash (erk--template-feature template))) + (new-feature (erk--nodash (plist-get replacements :feature))) + (output (get-buffer-create "erk-clone")) + (git-bin (executable-find "git")) + (default-directory clone-dir)) + + (let ((new-license (concat clone-dir "LICENSE")) + (old-license (concat clone-dir "COPYING"))) + ;; TODO dynamic re-licensing would be nice since `license-templates' + ;; contains a lot. At least we're no longer worried about shipping + ;; licenses. There's another package with headers and yet another for + ;; spdx identifiers. + (license-templates-new-file "gpl-3.0" default-directory) + (erk--nze + (call-process "cp" 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) + (format "Could not stage %s update." old-license))) + + (mapc (lambda (rename-map) + (let* ((dir (concat clone-dir (or (pop rename-map) ""))) + (default-directory dir) + (src (format (pop rename-map) old-feature)) + (new-filename (when-let ((file (pop rename-map))) + (format + file + new-feature))) + (dest (or new-filename + (replace-regexp-in-string + (rx (literal old-feature)) new-feature src)))) + (when (file-exists-p dest) + (erk--nze + (call-process git-bin nil output nil "rm" "-f" dest) + (format "Could not delete: %s" dest))) (erk--nze - (call-process git-bin nil output nil "rm" "-f" dest) - (format "Could not delete: %s" dest))) - (erk--nze - (call-process git-bin nil output nil "mv" src dest) - (format "Could not move: %s to %s" src dest)))) - erk--rename-maps)) + (call-process git-bin nil output nil "mv" src dest) + (format "Could not move: %s to %s" src dest)))) + erk--rename-maps))) (defun erk--nodash (prefix) "Strip dash from PREFIX if present." @@ -300,89 +419,102 @@ clobbered." (group (literal (erk--nodash prefix))) (or whitespace punctuation eol))) -(defun erk--replace-strings (dir package-name package-prefix author user-org email) +(defun erk--replace-strings (template clone-path replacements) "Replace values in files that need renaming or re-licensing. -DIR is where we are replacing. PACKAGE-NAME is the new -package. PACKAGE-PREFIX is the elisp prefix. AUTHOR will be -used in copyright notices. USER-ORG will be used as the first -part of the new github path. EMAIL is shown after AUTHOR in -package headers." - (let ((default-directory dir) - (erk-github-path (concat erk-github-userorg "/" - erk-github-package-name)) - (github-path (concat user-org "/" package-name)) - (package-prefix package-prefix) - (replace-prefix (erk--prefix-match erk-package-prefix)) - (capitalized-package-title - (string-join (mapcar #'capitalize - (split-string package-name "-")) - " ")) - (replace-package-title - (string-join (mapcar #'capitalize - (split-string erk-github-package-name "-")) - " "))) +TEMPLATE forms are from `erk-templates'. CLONE-PATH is where we +are renaming. The REPLACEMENTS form is described in `erk-new'." + (let* ((default-directory clone-path) + (old-title (or (plist-get template :title) + (erk-package-title))) + (new-title (plist-get replacements :title)) + (old-github-path (plist-get template :github-path)) + (new-github-path (concat (plist-get replacements :user-org) + "/" + (plist-get replacements :feature))) + (old-author (erk-package-author)) + (new-author (plist-get replacements :author)) + (old-email (erk-package-email)) + (new-email (plist-get replacements :email)) + ;; Note, dashes are stripped off of inputs in the read, but left here + ;; in case this is called from Elisp. + (old-feature (erk--template-feature template)) + (old-feature-rx (erk--prefix-match old-feature)) + (new-feature (erk--nodash (plist-get replacements :feature))) + (old-prefix-rx (erk--prefix-match (erk--template-prefix template))) + (new-prefix (erk--nodash (plist-get replacements :prefix))) + ;; Render old-feature into files. + (files (erk--expand-filenames erk--files-with-strings old-feature)) + (copy-notice-files (erk--expand-filenames erk--files-with-copy-notices old-feature))) (mapc (lambda (file) - (with-current-buffer (find-file-noselect (concat dir file) t t) - ;; append new author to copyright + (with-current-buffer (find-file-noselect (concat clone-path file) t t) (print (format "visiting: %s" (buffer-file-name))) (mapc (lambda (s) (while (re-search-forward (rx (literal s)) nil t) (replace-match "") (goto-char (point-min)))) erk--remove-strings) - (when (re-search-forward (rx bol ";; Copyright") nil t) - (end-of-line) - (insert ", " author)) + ;; Try to replace strings in an order that limits the likelihood of + ;; substring collisions. + + ;; replace title, case-insensitively + (goto-char (point-min)) + (while (re-search-forward (rx (literal old-title)) nil t) + (replace-match new-title nil t)) + ;; replace github paths (goto-char (point-min)) - (when (re-search-forward (rx "" eol) nil t) - (replace-match (concat author " <" email ">") nil t)) + (while (re-search-forward (rx (literal old-github-path)) nil t) + (replace-match new-github-path nil t)) + ;; replace author (goto-char (point-min)) - ;; replace license with GPL3 notice - (when (re-search-forward ";; Permission \\(.\\|\n\\)*SOFTWARE.$" nil t) - (replace-match erk--gpl3-notice nil t)) + (while (re-search-forward (rx (literal old-author)) nil t) + (replace-match new-author nil t)) + ;; replace email (goto-char (point-min)) - ;; update github paths for README links - (while (re-search-forward erk-github-path nil t) - (replace-match github-path nil t)) + (while (re-search-forward (rx (literal old-email)) nil t) + (replace-match new-email nil t)) + ;; replace feature (goto-char (point-min)) - ;; replace package prefix. Uses group replacement. - (while (re-search-forward replace-prefix nil t) - (replace-match package-prefix nil t nil 1)) + (while (re-search-forward old-feature-rx nil t) + (replace-match new-feature nil t nil 1)) ; replace sub-expression + ;; replace prefix (goto-char (point-min)) - ;; update remaining package name strings. - (while (re-search-forward erk-github-package-name nil t) - (replace-match package-name nil t)) + (while (re-search-forward old-prefix-rx nil t) + (replace-match new-prefix nil t nil 1)) ; replace sub-expression + (when (member file copy-notice-files) + ;; replace license with GPL3 notice + (when (re-search-forward ";; Permission \\(.\\|\n\\)*SOFTWARE.$" nil t) + (replace-match erk--gpl3-notice nil t))) (goto-char (point-min)) - (while (re-search-forward replace-package-title nil t) - (replace-match capitalized-package-title nil t)) (save-buffer 0) (kill-buffer))) - erk--files-with-strings))) + files))) + +(defun erk-clone (template clone-root replacements) + "Clone the template repository to CLONE-ROOT and apply rename. +TEMPLATE is the same as forms in `erk-templates'. +REPLACEMENTS is the form described in `erk-new'. + +The folder will be renamed to the `:feature' value in +REPLACEMENTS. The remote will be configured to point to the +`:user-org' value in REPLACEMENTS." -;;;###autoload -(defun erk-clone (clone-root package-name user-org &optional rev) - "Clone elisp-repo-kit to CLONE-ROOT and apply rename. -PACKAGE-NAME will instruct git how to name the clone. USER-ORG -is the user or organization you will use for your GitHub -repository. REV can be used to check out a specific revision. -Warning! The revision may have lost compatibility with the -rename script. Each rev is intended only to be able to rename -itself, as a quine and for forking as a new template repository." - (interactive "DClone to directory:") (if-let ((git-bin (executable-find "git"))) (let ((default-directory clone-root) - (output (get-buffer-create "erk-clone"))) + (output (get-buffer-create "erk-clone")) + (old-github-path (plist-get template :github-path)) + (new-feature (plist-get replacements :feature)) + (user-org (plist-get replacements :user-org))) (erk--nze (call-process git-bin nil output nil "clone" - (format "https://github.com/%s/%s.git" - erk-github-userorg erk-github-package-name) - (concat default-directory "/" package-name)) + (format "https://github.com/%s.git" old-github-path) + (concat default-directory "/" new-feature)) "Clone failed") - (let ((default-directory (concat clone-root "/" package-name)) - (rev (when rev (if (string-empty-p rev) nil rev)))) + (let* ((default-directory (concat clone-root "/" new-feature)) + (rev (plist-get template :rev)) + (rev (when rev (if (string-empty-p rev) nil rev)))) (when rev (erk--nze (call-process git-bin nil output nil "checkout" rev) (format "Checkout %s failed." rev))) @@ -396,86 +528,284 @@ itself, as a quine and for forking as a new template repository." (erk--nze (call-process git-bin nil output nil "remote" "add" "origin" (format "git@github.com:%s/%s.git" - user-org package-name)) + user-org new-feature)) "Adding new remote failed.") ;; return value for renaming - (concat clone-root "/" package-name "/"))) - (error "Could not find git executible"))) + (concat clone-root "/" new-feature "/"))) + (error "Could not find git executable"))) -;;;###autoload -(defun erk-rename-relicense (clone-dir package-name package-prefix author user-org email) - "Rename and relicense your clone of ERK. -CLONE-DIR is your elisp-repo-clone root. PACKAGE-NAME should be -the long name of the package, what will show up in melpa -etc. PACKAGE-PREFIX is the elisp symbol prefix. AUTHOR will be used -in copyright notices. USER-ORG is either your user or -organization, which forms the first part of a github repo path. -EMAIL is shown after AUTHOR in package headers. - -This command replaces all instances of: - -1. package name -2. author name (appended to copyright) -3. MIT license with GPL3 license notices in package files (not test files or CI) -4. github organization -5. COPYING is changed to just the GPL3. - -Then renames the files to reflect package name - -Finally, MIT licenses are swapped with GPL3 license notices. -Re-licensing is fully permitted by the MIT license and intended -by the author of this repository." - (interactive "DCloned directory: \nsPackage name: \nsAuthor: \ -\nsGitHub organization or username: \nsEmail: ") +(defun erk-rename-relicense (template clone-dir replacements) + "Rename and re-license your new cloned template. +CLONE-DIR is the root for renaming and string +replacement. TEMPLATE is the same forms described in +`erk-templates'. REPLACEMENTS is the form described in +`erk-new'. + +This function replaces all instances of: + +1. package title + +2. package feature + +3. package prefix + +4. author name (appended to copyright) + +5. author email + +6. github user-org + +7. MIT license with GPL3 license notices in package files (not +test, manual, or CI though) + +8. COPYING is changed to just the GPL3. + +9. Documentation is re-generated, containing the new strings." (erk--replace-strings - clone-dir package-name package-prefix author user-org email) - (erk--rename-package clone-dir erk-package-prefix package-name)) + template clone-dir replacements) + (erk--rename-package template clone-dir replacements) + (let ((default-directory clone-dir)) + (erk-export-docs))) ;;;###autoload -(defun erk-new (package-name package-prefix clone-root author user-org email &optional rev) - "Clone elisp-repo-kit, rename, and re-license in one step. -CLONE-ROOT is where you want to clone your package to (including -the clone dir). PACKAGE-NAME should be the long name of the -package, what will show up in melpa etc. PACKAGE-PREFIX can be -either the same as the package or a contracted form, such as an -initialism. AUTHOR will be used in copyright notices. USER-ORG -is either your user or organization, which forms the first part -of a github repo path. EMAIL is shown after AUTHOR in package -headers. Optional REV is either a tag, branch or revision used -in git checkout. +(defun erk-new (template clone-root replacements) + "Clone an ERK style template and rename files in one step. +TEMPLATE is the same forms described in `erk-templates'. + +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 + README, manual, and the first line of Elisp files. + +- `:feature': By convention, all Elisp file names will include + the root feature. This will also be assumed to form the repo + segment of the new Github remote path. It's also what shows up + in MELPA and `use-package' declarations etc. + +- `:prefix': The Elisp namespace prefix, usually the same as the + root feature, but could be an initialism or contraction. + +- `:author': The new author's name. + +- `:email': Author email. + +- `:user-org': The Github username or organization, the first + part of a Github style path. + +- `:rev': Optional. Either a tag, branch or revision used in git + checkout. See comments in `erk-clone' and `erk-rename-relicense' for implementation information and more details about argument usage." (interactive (let* - ((package-name - (read-string - (format "Package name, such as %s: " erk-github-package-name) - "foo")) - (package-prefix - (erk--nodash - (read-string - (format "Package prefix, such as %s: " erk-package-prefix)))) + ((template (cdr (assoc-string + (completing-read "Select a template: " erk-templates) + erk-templates))) + (rev (read-string + "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))) + (title + (read-string + "Package full name, for documentation: " + "My Package")) + (feature + (erk--nodash + (read-string + "Package feature, used in use-package and MELPA etc: " + "my-feature"))) + (prefix + (erk--nodash + (read-string + "Package prefix, such as mp, my-package, mypak etc: " + "my-package"))) + (user-org (read-string "Github user or organization name: ")) (author (let ((default (when (executable-find "git") (string-trim (shell-command-to-string "git config user.name"))))) (read-string "Author: " default))) - (user-org (read-string "User or organization name: ")) + (email (let ((default (when (executable-find "git") (string-trim (shell-command-to-string "git config user.email"))))) - (read-string "Email: " default))) - (rev (read-string - "Rev, tag, or branch (empty implies default branch): "))) - (list package-name package-prefix clone-root author user-org email rev))) - (erk-rename-relicense - (erk-clone clone-root package-name user-org rev) - package-name package-prefix author user-org email)) + (read-string "Email: " default)))) + (list + template + clone-root + (list + :title title + :feature feature + :prefix prefix + :user-org user-org + :author author + :email email)))) + (let ((cloned (erk-clone template clone-root replacements))) + (erk-rename-relicense template cloned replacements))) + +;;;###autoload +(defun erk-insert-package-keyword (keyword) + "Insert package KEYWORD, from `finder-known-keywords'. +This list's name is easy to forget, so here's a shortcut." + (interactive + (list + (completing-read + "Insert package keyword:" + finder-known-keywords + nil t nil nil + (lambda (item) + (format "%s\t%s" (car item) (cdr item)))))) + (insert (format "\"%s\"" keyword))) + +(defconst erk--find-paths + '((ci-dco . ".github/workflows/dco.yml") + (ci-nix-flake . ".github/flake.nix") + (ci-run-shim . ".github/run-shim.el") + (ci-tests . ".github/workflows/ci.yml") + (docs-contributing . "docs/CONTRIBUTING.org") + (docs-manual . "docs/manual.org") + (docs-readme . "docs/README.org")) + "Paths that exist in an ERK style project.") + +;;;###autoload +(defun erk-find (file) + "Find FILE within projects using erk's project structure." + (interactive (list (completing-read "Select file" erk--find-paths))) + (find-file (concat (erk--project-root) (cdr (assoc-string file erk--find-paths))))) + +;;;###autoload +(defun erk-find-ci-dco () + "Shortcut for `erk-find'." + (interactive) + (erk-find "ci-dco")) + +;;;###autoload +(defun erk-find-ci-nix-flake () + "Shortcut for `erk-find'." + (interactive) + (erk-find "ci-nix-flake")) + +;;;###autoload +(defun erk-find-ci-run-shim () + "Shortcut for `erk-find'." + (interactive) + (erk-find "ci-run-shim")) + +;;;###autoload +(defun erk-find-ci-tests () + "Shortcut for `erk-find'." + (interactive) + (erk-find "ci-tests")) + +;;;###autoload +(defun erk-find-docs-contributing () + "Shortcut for `erk-find'." + (interactive) + (erk-find "docs-contributing")) + +;;;###autoload +(defun erk-find-docs-manual () + "Shortcut for `erk-find'." + (interactive) + (erk-find "docs-manual")) + +;;;###autoload +(defun erk-find-docs-readme () + "Shortcut for `erk-find'." + (interactive) + (erk-find "docs-readme")) + + +(defun erk--export (filename export-fun) + "Export FILENAME to markdown using EXPORT-FUN." + (let* ((buffer (find-buffer-visiting filename)) + (buffer (or buffer (find-file-noselect filename)))) + (set-buffer buffer) + (when (buffer-modified-p buffer) + (when (yes-or-no-p "Save buffer? ") + ;; The docs all have a save hook we want to ignore + (let ((enable-local-variables nil)) (save-buffer)))) + (save-restriction + (widen) + (save-excursion + (when (region-active-p) (deactivate-mark)) + (funcall export-fun))))) + +;;;###autoload +(defun erk-export-contributing (&optional preview) + "Export the contributing doc to markdown. +With prefix argument, PREVIEW the buffer." + (interactive "P") + (erk--export + (concat (erk--project-root) + (cdr (assoc 'docs-contributing erk--find-paths))) + #'org-md-export-to-markdown) + (when preview + (find-file-read-only-other-window + (concat (erk--project-root) + "CONTRIBUTING.md")))) + +;;;###autoload +(defun erk-export-manual (&optional preview) + "Export the manual doc to markdown. +With prefix argument, PREVIEW the buffer." + (interactive "P") + (erk--export + (concat (erk--project-root) + (cdr (assoc 'docs-manual erk--find-paths))) + #'org-texinfo-export-to-info) + (when preview + (let ((exported-path (concat (erk--project-root) + "docs/manual.info"))) + (info-initialize) + (info-other-window (Info-find-file exported-path) + (generate-new-buffer-name "*info*"))))) + +;;;###autoload +(defun erk-export-readme (&optional preview) + "Export the readme doc to markdown. +With prefix argument, PREVIEW the buffer." + (interactive "P") + (erk--export + (concat (erk--project-root) + (cdr (assoc 'docs-readme erk--find-paths))) + #'org-md-export-to-markdown) + (when preview + (find-file-read-only-other-window + (concat (erk--project-root) + "README.md")))) + +;;;###autoload +(defun erk-export-docs () + "Shortcut to export all docs." + (interactive) + (erk-export-contributing) + (erk-export-readme) + (erk-export-manual)) + +;;;###autoload +(defun erk-preview-contributing () + "Export and show the contributing." + (interactive) + (let ((current-prefix-arg '(4))) (call-interactively #'erk-export-contributing))) + +;;;###autoload +(defun erk-preview-manual () + "Export and show the manual." + (interactive) + (let ((current-prefix-arg '(4))) (call-interactively #'erk-export-manual))) + +;;;###autoload +(defun erk-preview-readme () + "Export and show the readme." + (interactive) + (let ((current-prefix-arg '(4))) (call-interactively #'erk-export-readme))) (provide 'erk) ;;; erk.el ends here diff --git a/test/erk-test.el b/test/erk-test.el index 4cd06d2..3ea725d 100644 --- a/test/erk-test.el +++ b/test/erk-test.el @@ -2,7 +2,7 @@ ;; Copyright (C) 2022 Positron Solutions -;; Author: +;; Author: Positron Solutions ;; Permission is hereby granted, free of charge, to any person obtaining a copy of ;; this software and associated documentation files (the "Software"), to deal in @@ -52,19 +52,72 @@ (ert-deftest erk--test-features-test () (should (member 'erk-test (erk--test-features)))) -(ert-deftest erk-clone-and-rename-test () - "Clone the repo and rename it, single step." - (let ((rev (string-trim - (shell-command-to-string "git rev-parse HEAD"))) +(ert-deftest erk--template-github-userorg-test () + (should (string= (erk--template-github-userorg '(:github-path "positron-solutions/bitcoin-miner")) + "positron-solutions"))) + +(ert-deftest erk--template-github-repo-test () + (should (string= (erk--template-github-repo '(:github-path "positron-solutions/bitcoin-miner")) + "bitcoin-miner"))) + +(ert-deftest erk--template-feature-test () + (should (string= (erk--template-feature '(:github-path "positron-solutions/bitcoin-miner")) + "bitcoin-miner")) + (should (string= (erk--template-feature '(:github-path "positron-solutions/bitcoin-miner" + :feature "floozly")) + "floozly"))) + +(ert-deftest erk--template-prefix-test () + (should (string= (erk--template-prefix '(:github-path "positron-solutions/bitcoin-miner")) + "bitcoin-miner")) + (should (string= (erk--template-prefix '(:github-path "positron-solutions/bitcoin-miner" + :feature "floozly")) + "floozly")) + (should (string= (erk--template-prefix '(:github-path "positron-solutions/bitcoin-miner" + :feature "floozly" + :prefix "flombow")) + "flombow"))) + +(ert-deftest erk--expand-filenames-test () + (should (string= (car (erk--expand-filenames '("%s-foo.el") "doo")) + "doo-foo.el"))) + +(ert-deftest erk--project-elisp-dir-test () + (should (erk--project-elisp-dir))) + +(ert-deftest erk--project-package-email-test () + (should (string= (erk-package-email) "contact@positron.solutions"))) + +(ert-deftest erk--project-package-author-test () + (should (string= (erk-package-author) "Positron Solutions"))) + +(ert-deftest erk--nodash-test () + (should (string= (erk--nodash "erk-") "erk"))) + +(ert-deftest erk-clone-test () + (let ((enable-local-variables nil) (clone-root (make-temp-file "erk-clone-test-" t))) - (erk-new - "new-project" ; package-name - "nupro" ; package-prefix - clone-root - "Selindis Raszagal" ; Author - "new-shakuras" ; user-org - "selindis.r@new-shakuras.planet" ; email - rev) ; possibly nil + (erk-clone (cdr (assoc 'erk-basic erk-templates)) + clone-root + '(:title "Omg Test" + :prefix "ggg" + :email "mail@template.com" + :author "Huh Idk" + :feature "lol-pkg" + :user-org "zerglingscancode2")) + (delete-directory clone-root t))) + +(ert-deftest erk-new-test () + (let ((enable-local-variables nil) + (clone-root (make-temp-file "erk-clone-test-" t))) + (erk-new (cdr (assoc 'erk-basic erk-templates)) + clone-root + '(:title "Hilariously Good Package" + :feature "laughing-package" + :prefix "lol-pkg" + :user-org "zerglingscancode2" + :email "iamzerg@zergs.pwn" + :author "Zagara")) (delete-directory clone-root t))) (provide 'erk-test)