===================================================================== A follow-up to V5 focused on hardening the environment system and expanding the ZSH logging/caching toolkit. No migration required from v5.0.x, though note the log-level reorganization below may quiet some messages at lower verbosities. --- New Features ------------------------- - persistent cross-runtime cache for ZSH-type scwrypts; extends the v5.0 runtime speedup cache to survive between runs with automatic hash-based invalidation (toggle via SCWRYPTS__ZSH_CACHE_ENABLED) - new 'trace' log level (5) and echo.trace, plus '-v VARNAME' state injection for echo.debug / echo.trace to surface variable values inline - additional scwrypts output formats: 'raw' and 'logfmt' (alongside pretty and json) - normalize.boolean utility for consistent truthy/falsey handling across ZSH-type scwrypts - utils.fzf.file-select for directory-scoped file selection prompts - automatic dependency wrappers providing default arguments, GNU coreutil resolution, and scwrypts trace on wrapped commands --- Changes ------------------------------ - log levels reorganized; success / status / reminder messages now emit at level 3, so lower verbosities will show fewer of these - environment library restructured around get-user-json as the single source of truth (replaces the previous user module and cache-output) - utility renames for consistency: user.Yn / user.yN (from utils.*) - utils.fail and utils.abort deprecated in favor of echo.error and echo.error.user-abort --- Bug Fixes ---------------------------- - CI runs now resolve config lookup-paths (.dotted.path) to their environment variable before reading, fixing false "not set" reports and invalid-export errors for lookup-path checks in CI - hardened environment type detection so array-valued configs reprint correctly after their first check - corrected exit-code capture in the cache layer (now reflects the cached command, not the downstream output filter) - scwrypt exit code now survives the logging pipeline - assorted script fixes (efs unmount file listing, postgres run-sql file discovery)
ZSH Scwrypts
Since they emulate direct user interaction, shell scripts are a (commonly dreaded) go-to for automation.
Although the malleability of shell scripts can make integrations quickly, the ZSH-type scwrypt provides a structure to promote extendability and clean code while performing a lot of the heavy lifting to ensure consistent execution across different runtimes.
The Basic Framework
Take a look at the simplest ZSH-type scwrypt: hello-world. The bare minimum API for ZSH-type scwrypts is to:
- include the shebang
#!/usr/bin/env zshon the first line of the file - wrap your zsh in a function called
MAIN() - make the file executable (e.g.
chmod +x hello-world)
Once this is complete, you are free to simply write valid zsh then execute the scwrypt with scwrypts hello world zsh!
Basics+
While it would be perfectly fine to use the echo function in our scwrypt, you'll notice that the hello-world scwrypt instead uses echo.success which is not valid ZSH by default.
This is a helper function provided by the scwrypts ZSH library, and it does a lot more work than you'd expect.
Although this function defaults to print user messages in color, notice what happens when you run scwrypts --output json hello world zsh:
{"timestamp":1745674060,"runtime":"c62737da-481e-4013-a370-4dedc76bf4d2","scwrypt":"start of hello-world scwrypts zsh","logLevel":"3","subscwrypt":0}
{"timestamp":1745674060,"runtime":"c62737da-481e-4013-a370-4dedc76bf4d2","status":"SUCCESS","message":"\"Hello, World!\""}
{"timestamp":1745674060,"runtime":"c62737da-481e-4013-a370-4dedc76bf4d2","status":"SUCCESS","message":"\"terminated with code 0\""}
We get a LOT more information.
It's 100% possible for you to include your own take on printing messages, but it is highly recommended to use the tools provided here.
What is loaded by default?
By default, every ZSH-type scwrypt will load the basic utilities suite, which is a little different from scwrypts ZSH modules, and a little bit complex. Although it's totally worth a deep-dive, here are the fundamentals you should ALWAYS use:
Printing User Messages or Logs
Whenever you want to print a message to the user or logs, rather than using echo, use the following:
| function name | minimum log level | description |
|---|---|---|
echo.success |
1 | indicate successful completion |
echo.error |
1 | indicate an error has occurred |
echo.reminder |
1 | an important, information message |
echo.status |
2 | a regular, information message |
echo.warning |
3 | a non-critical warning |
echo.debug |
4 | a message for scwrypt developers |
Of the echo family, there are two unique functions:
echo.errorwill increment theERRORSvariable then return an error code of$ERRORS(this makes it easy to chain with command failure by using||)echo.debugwill inject state information like the timestamp and current function stack
Yes / No Prompts
The two helpers user.Yn and user.yN take a user-friendly yes/no question as an argument.
- when the user responds "yes", the command returns 0 / success /
&& - when the user responds "no", the command returns 1 / error /
|| - when the user responds with nothing (e.g. just presses enter), the default is used
The two commands work identically; however, the capitalization denotes the default:
user.Yn= default "yes"user.yN= default "no"
Select from a List Prompt
When you want the user to select an item from a list, scwrypts typically use fzf.
There are a LOT of options to fzf, so there are two provided helpers.
The basic selector, utils.fzf (most of the time, you want to use this one) which outputs:
- the selection if the user made a choice
- nothing / empty string if the user cancelled or made an invalid choice
The user-input selector, utils.fzf.user-input which outputs:
- the selection if the user made a choice
- the text typed by the user if the user typed something other than the listed choices
- nothing / empty string if the user cancelled
- a secondary
utils.fzfprompt if the user's choice was ambiguous
Imports
Don't use source in ZSH-type scwrypts (I mean, if you're pretty clever you can get it to work, but DON'T THOUGH).
Instead, use use!
The use command, rather than specifying file directories, you reference the path to *.module.zsh.
This means you don't have to know the exact path to any given file.
For example, if I wanted to import the safety tool for aws CLI commands, I can do the following:
#!/usr/bin/env zsh
use cloud/aws
#####################################################################
MAIN() {
cloud.aws sts get-caller-identity
}