RFC: new expression syntax #990
base: main
Are you sure you want to change the base?
Conversation
Codecov Report
@@ Coverage Diff @@
## main #990 +/- ##
=========================================
Coverage 83.86% 83.86%
Complexity 2686 2686
=========================================
Files 353 353
Lines 11773 11773
Branches 1412 1412
=========================================
Hits 9874 9874
Misses 1598 1598
Partials 301 301
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report at Codecov.
|
Can you share some concrete use case projects? |
The primary use case for this is to enable more configurability for bundles. Two particular examples come to mind: Example 1: imagine a bundle that provides an opinionated build environment for Golang, and defaults to a known stable version of the Golang toolchain. As a consumer of that bundle, I might want to be able to use an older or newer version of Golang, but retain all the other opinionated bits and pieces that are included in the bundle. At the moment, the only way to achieve this would be through build args to build an image with the correct version of Golang, but with the addition of being able to use expressions (almost) everywhere, this could just change the image used for the build environment. (Imagine a container with Example 2: imagine a bundle that provides a linting tool, and we only want to allow consumers of the bundle to tweak a limited number of configuration options for the tool. With the current functionality, the only way this could be done is either: have some kind of script that wraps the tool and reads those options from a configuration file in a hardcoded location in the consuming project, or provide config variables that are then passed through to the tool via environment variables. For example: Bundle: containers:
tool:
command: sh -c "tool --scan '$SCAN_DIRECTORY'"
environment:
SCAN_DIRECTORY: <{scan_directory} Consuming project's scan_directory: scripts This proposal, in conjunction with #991, would allow something like this: Bundle: containers:
tool:
command: tool --scan '${{ var.scan_directory }}' (note that Consuming project's configuration file: bundles:
- type: git
repo: ...
config:
scan_directory: scripts Does that make sense? Open to other ideas and suggestions. |
For the implementation side, do you have an idea what you'll use an an expression evaluator? There are a few that could be dropped-in such as JEXL, though finding one that doesn't add too much functionality is tricky. I'd prefer a simpler internal syntax, such as with envsubst, where the only operations available (broadly speaking) are length, casing, slices, trimming, defaults, and replacement. Anything beyond that starts to stray into logic and scripting. Not sure if it's helpful, but there's a neat interpolation library that I found while looking for a java envsubst implementation: https://github.com/lantunes/interpolatd |
My initial plan was to extend the existing expression parsing and evaluation logic that already exists in Batect. I'm hesitant to take on any dependencies, as they will make it harder to move to Kotlin/Native (and away from the JVM), which is something I'd like to do sooner rather than later. I'm also cautious that many libraries bring in many other features that aren't needed, which might lead to unexpected behaviour, security or stability issues or difficulty replacing the library with something else in the future.
+1, I definitely don't want to encourage / permit people to attempt to use YAML as a programming language. I think the examples you've given there are good examples of the kinds of things that I'd be OK with adding in the future. Anything beyond that belongs as a script or application inside containers. |
Legit. I guess the question then becomes whether to use a syntax as terse as envsubst, or use actual words for the functions. I think I lean on the side of words, so as to promote readability. I'd want to keep the functions short though, or use reasonably well-known shorthand to avoid lengthy expressions. The gotpl pipeline syntax used in Helm templating is a reasonable example. There's a very limited list of functions, and the output of one function can be piped into another to have one fluid expression, though parentheses can be used to have sub-expressions, e.g. Having symbolic shorthands for things might be nice, though it would complicate the parser more than necessary, as you'd probably need to implement prefix and postfix notation handling. I hope my rambling is helpful. |
Oh, and for another concrete use-case: how to execute the same set of tasks on different submodules of the same project. For instance:
Given this project, how do I run the exact same tasks (build, test, package) on both modules without explicitly defining each task multiple times ( |
Definitely helpful. I was leaning more towards the |
📢 Update on implementing this RFC I realise there's been radio silence on this RFC since I posted it and so I wanted to give an update on what's going on. The short version is that while I'm very, very keen to implement this, I'm currently prioritising some other work to improve the experience of working on Batect itself. Batect has grown quite a bit since it first began a few years ago, and unfortunately that is starting to show - builds take longer to run than I'd like (both locally and on CI), flaky tests are going unfixed and I'm finding more and more of my precious time diverted away to fixing things that are invisible to most users, like edge cases in the Docker client. All of these things make it less pleasant to work on Batect, and make it much harder for others to contribute as well. I have two major things on my backlog to address these issues:
Once I've finished addressing these issues, I will come back to this feature as a priority. |
📢 Update Apologies for the continuing radio silence on this RFC. Replacing the Docker client in Batect has taken much longer than I planned / hoped / expected, however, I can see the light at the end of the tunnel. As soon as this is complete, the features described here are my next priority. |
It would be nice if a new expression syntax would also allow some simple kind of "mapping". I'm facing a situation where I'd like to translate a boolean config variable to either the name of a command line option (that I pass on to a command), or an empty string (to not use that option). Currently, I'm struggling to do that. |
Unfortunately, Batect's expressions [1] are not yet powerful enough [2] to elegantly map booleans to strings, so a rather lengthy `sed` construct has to be used. Gradle build scans are disabled by default. In order to enable them, run `./batect --config-var gradle_build_scan=true`. [1]: https://batect.dev/docs/reference/config/expressions [2]: batect/batect#990 Signed-off-by: Sebastian Schuberth <sschuberth@gmail.com>
Unfortunately, Batect's expressions [1] are not yet powerful enough [2] to elegantly map booleans to strings, so a rather lengthy `sed` construct has to be used. Gradle build scans are disabled by default. In order to enable them, run `./batect --config-var gradle_build_scan=true`. [1]: https://batect.dev/docs/reference/config/expressions [2]: batect/batect#990 Signed-off-by: Sebastian Schuberth <sschuberth@gmail.com>
Unfortunately, Batect's expressions [1] are not yet powerful enough [2] to elegantly map booleans to strings, so a rather lengthy `sed` construct has to be used. Gradle build scans are disabled by default. In order to enable them, run `./batect --config-var gradle_build_scan=true`. [1]: https://batect.dev/docs/reference/config/expressions [2]: batect/batect#990 Signed-off-by: Sebastian Schuberth <sschuberth@gmail.com>
Just wonder is there any new updates on this? |
I've rereviewed the proposal, and see good use case examples. Observations
Suggestions Provide a BNF specification in the proposal doc. For example, this would make explicit that padding whitespace around the expression is OK inside of the double curlies. Questions Do any current OS's support whitespace in environment variables? The "C" API for Linux/UNIX let one craft a int main(int argc, char **argv, char **env); (See https://en.wikipedia.org/wiki/Entry_point#C_and_C.2B.2B) This is a sad path example if I pass an array as Maybe this isn't worth considering. 🤔 |
Unfortunately I don't have any news on this - I have far less time to work on Batect these days than I used to, and keeping the lights on is the highest priority over everything else, including this. If you're really keen to see this, I'm always open to PRs 😄 |
Thanks for all the feedback @binkley! Will definitely consider this when I get time to work on it. |
This is a request for comment and feedback on the idea in
proposal.md
.The RFC process is intended to allow anyone with an interest in Batect to get involved and help shape it to suit their needs, so please don't be shy! Any comments, suggestions, questions or constructive criticism are more than welcome.
Please comment directly on the file itself or add general comments in this PR.