Repository navigation
feat(modules): run scripts when a module is deployed or undeployed - #318
Merged
Merged
Conversation
A module's settings.json can name scripts of the repository with pre_deploy, post_deploy, pre_undeploy and post_undeploy. modules deploy and modules undeploy now work on one module at a time: the pre_ script, the files (under the lock), then the post_ script (without it). A script that fails stops the command there unless --keep-going is given, and a pre_ script that fails leaves the files of its module as they are. --no-hooks runs none, --dry-run lists them, modules status shows them, and the history records them with their module. Closes #298
Also mentions them in the tutorial, the new machine and CI guides, and the concepts page.
Keeps both changelog entries, and links the new guide from the README.
…un ends early - A name that matches no script fails the command before anything is changed (it used to fail when its turn came, after earlier modules were deployed). With --keep-going it still fails in its turn. Dry runs now stop where the real run stops. - undeploy keeps its summary when the removed files cannot be checked. - modules status still shows the files when scripts/ cannot be listed. - A setting of the other command that cannot be used is a warning. - Says when a script is not run because its module has no file of the kind the command works on. - A lock that cannot be taken before anything was done is reported as before, and a lock folder that cannot be used is no longer a crash. - Records in the history the post_ scripts that were not run.
… scripts - A script that only has versions for other systems is skipped, with a message, instead of failing the command as a name that matches nothing. - The warning about a script not run because its module has no file of the kind is only shown for modules named on the command line. - Scripts not run on purpose are recorded in the history as script-skipped, not as failures. - The history keeps the modules left out on every way out of a run, and the reason the removed files could not be checked. - A dry run with --keep-going words a missing script like the real run. - Tests for the removed-files step failing, a cancelled question and the lock errors; signal tests no longer depend on a 0.3 s window.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #298.
configfile deploys the configuration files (dotfiles) of a git repository: each folder of
files/is a module whosesettings.jsonlists files to link into the home folder, andscripts/holds scripts run withconfigfile scripts run <name>.Many modules need a step around their files: tmux needs its plugins installed,
misehas to install the tools its configuration lists, a service has to be stopped before its configuration is removed. Until now these steps lived in one setup script run by hand, andconfigfile modules deploy tmuxalone left the module half set up.With this change, a module names the scripts to run when it is deployed or undeployed:
{ "files": [ { "source_path": "tmux.conf", "target_path": "~/.tmux.conf", "deploy": "global" } ], "post_deploy": "tmux-plugins", "pre_undeploy": "tmux-plugins-remove" }pre_deploymodules deploydeploys the files of the modulepost_deploymodules deployhas deployed all of thempre_undeploymodules undeployremoves the files, while they are still in placepost_undeploymodules undeployhas removed themThe issue asked for
post_deployonly. The three other settings follow the same rules and share the same code, so that a module can also check what it needs before its files are linked, and clean up what its script installed.Behavior
configfile scripts listshows it. The script is run the wayscripts runruns it (versions per system such astmux-plugins.macos.sh, interpreter detection, current folder), without arguments. It also getsCONFIGFILE_HOOK(the setting),CONFIGFILE_MODULEandCONFIGFILE_DEPLOY(globalorlocal).modules list: thepre_script, the files, thepost_script, then the next module.pre_script that fails leaves the files of its module as they are, and itspost_script is not run.post_deployscript is not run when a file of its module failed or was skipped, or an entry is invalid (with a warning).post_undeployis not run when a file could not be undeployed.1.--keep-goinggoes on with the remaining modules; the command still fails.--keep-goingit starts anyway, and the script fails when its turn comes.tmux-plugins.macos.shalone, on Linux) is skipped, with a message: the module is deployed or undeployed as if it named no script.--local) does not run its scripts; when that module was named on the command line, the command says so with a warning.modules listandstatusshow it as an error.scripts run. Once it ends, the command stops there, with or without--keep-going, and exits with the signal's code (130for Ctrl+C,143for SIGTERM).--no-hooksruns no script.--dry-runlists the scripts that would run, and fails like the real command on one that does not exist.modules statusshows the scripts of each module and flags a name that matches no script; whenscripts/cannot be listed, it warns and still shows the files.modules listnames the settings a module uses.~/.configfile/history.jsonl) records each script with its exit code, setting and module, and the scripts that were not run on purpose.undeploy --removed): their module or entry is gone, with its settings.Design
modules deployandundeployhold~/.configfile/lockwhile they change files. A script may run configfile itself, which would wait for that lock until it times out. The lock is therefore taken for the files of each module in turn, instead of the whole run. If it cannot be taken again for a later module, the command reports it and names the modules left out.eachModuleinsrc/commands/modules.tsholds the order, the stop and--keep-goinglogic and the summary;deployandundeployonly give it the work to do on the files of a module.scripts runfinds them.loadScriptsandscriptEnvironmentmoved fromsrc/commands/scripts.tstosrc/repository.tsto be shared;scripts runis unchanged.src/lock.ts) is now an expected error with a message, where a folder that could not be created or written used to be reported as an unexpected error.runinsrc/process.tsgets anonSignalcallback, so that the caller knows configfile was asked to stop while the script ran, whatever the script's exit code.Other Informations
Compatibility
Nothing changes for a repository that uses none of the new settings, with four exceptions. The first two are under "Changed" in the changelog, the third under "Fixed":
When a local file already exists (
modules deploy --local), the question to replace it is asked once the other files of its module are deployed, no longer at the very end of the run.Two deployments running at the same time can take turns between two modules, where one used to wait for the other to finish. Files are still never changed by two processes at once.
When
~/.configfileor its lock file cannot be created or written, the commands say that the lock cannot be taken, and why, instead of reporting an unexpected error.When the lock or the deployment record can no longer be used partway through a run (after a first module, or for the files the repository no longer deploys at the end of
undeploy --all), the command reports the error, then says what was done and which modules were left out, instead of stopping on the error alone.The history format stays at version 1: script records gain two optional fields (
hook,module), and a new kind of record,script-skipped, is added for the scripts not run on purpose (readers skip the kinds they do not know).Review guide
src/repository.ts: the settings are read (HOOKS,hooks,hookProblems).src/commands/modules.ts:loadHookScripts(the names are checked first),eachModule,runHookandplanHook, thendeployandundeploy, which use them.src/process.ts,src/history.ts,src/commands/history.ts: signals and history.docs/: the reference (reference/modules.md), a new guide (guides/module-scripts.md), and the JSON Schema ofsettings.json.Tests
pnpm checkpasses. New tests, mostly intests/commands.test.ts("scripts run when a module is deployed or undeployed"), run real scripts through the CLI and check files, exit codes and the history:--keep-going, for the four settings; a script that does not exist, found before anything is changed;--no-hooks,--dry-run,modules statusandlist, the history;undeploy --all; a lock that cannot be used before anything was done; a question cancelled with Ctrl+C;modules statuswhenscripts/cannot be listed;undeploy --alland when it does not;tests/e2e.test.ts): SIGINT during apre_deployorpost_deployscript stops a--keep-goingdeployment with exit code 130, and SIGTERM during apre_undeployscript stops the undeployment with 143.Manual test
From the root of a checkout of this branch (Node.js 24.11 or later). It uses a throwaway home folder:
Step 4 ends with:
Follow-up: scripts run after
updateandinit, which belong to the repository and not to a module, are proposed in #317.