-
-
Couldn't load subscription status.
- Fork 79
Add support for extvars in jsonnet_library
#257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add support for extvars in jsonnet_library
#257
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary of Changes
Hello @dbanetto, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly enhances the jsonnet_library Bazel rule by introducing robust support for Jsonnet external variables (extvars). It addresses the challenge of referencing generated files or external libraries deep within a jsonnet_library dependency graph by allowing extvars to be defined and propagated transitively. This change provides a more streamlined and powerful mechanism for integrating external data and code into Jsonnet builds, improving flexibility and reducing boilerplate.
Highlights
- Enhanced
jsonnet_libraryRule: Thejsonnet_libraryrule now supports a comprehensive set ofextvarattributes, includingext_code,ext_code_envs,ext_code_files,ext_code_libraries,ext_str_envs,ext_str_files, andext_strs, allowing for flexible external variable definition. - Transitive External Variable Propagation: A new
transitive_extvarsfield has been added toJsonnetLibraryInfoto enable the propagation and merging of external variables across the dependency graph, ensuring consistentextvardefinitions throughout a Jsonnet project. - Robust
extvarHandling Logic: Three new functions (_make_extvar_dict,_merge_extvars,_extvar_to_arguments) were introduced to manage the creation, merging (with conflict detection for duplicates), and conversion ofextvarsinto command-line arguments for the Jsonnet compiler. - Updated
jsonnet_to_jsonandjsonnet_to_json_test: Thejsonnet_to_jsonandjsonnet_to_json_testrules have been updated to leverage the new transitiveextvarinformation, simplifying the process of passing external variables to Jsonnet evaluation.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces support for transitive extvars in jsonnet_library, which is a great feature for managing dependencies. The implementation is well-structured with helper functions for creating, merging, and converting extvars.
I've found a critical issue with the propagation of transitive extvars in jsonnet_library, a high-severity issue with how file-based extvars with multiple files are handled, and a suggestion to improve maintainability by reducing code duplication. Please see my detailed comments.
Once these issues are addressed, this will be a solid contribution.
jsonnet/jsonnet.bzl
Outdated
| for file, key in ext_code_files.items(): | ||
| if key in extvars: | ||
| fail("duplicate extVar '{}': {}".format(key, extvars.keys())) | ||
|
|
||
| files = [file] | ||
| if type(file) != "File": | ||
| files = file[DefaultInfo].files.to_list() | ||
|
|
||
| extvars[key] = { | ||
| "value": files, | ||
| "type": "code_file", | ||
| "sources": [label], | ||
| } | ||
| for file, key in ext_code_libraries.items(): | ||
| if key in extvars: | ||
| fail("duplicate extVar '{}': {}".format(key, extvars.keys())) | ||
| extvars[key] = { | ||
| "value": file[DefaultInfo].files.to_list(), | ||
| "type": "code_library", | ||
| "sources": [label], | ||
| } | ||
| for key in ext_str_envs: | ||
| if key in extvars: | ||
| fail("duplicate extVar '{}': {}".format(key, extvars.keys())) | ||
| extvars[key] = { | ||
| "value": "", | ||
| "type": "string_env", | ||
| "sources": [label], | ||
| } | ||
| for file, key in ext_str_files.items(): | ||
| if key in extvars: | ||
| fail("duplicate extVar '{}': {}".format(key, extvars.keys())) | ||
|
|
||
| files = [file] | ||
| if type(file) != "File": | ||
| files = file[DefaultInfo].files.to_list() | ||
|
|
||
| extvars[key] = { | ||
| "value": files, | ||
| "type": "string_file", | ||
| "sources": [label], | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for handling file-based extvars (ext_code_files, ext_code_libraries, ext_str_files) has a flaw. When the backing target (e.g., a filegroup or jsonnet_library) contains multiple files, your code stores a list of files, but _extvar_to_arguments later silently uses only the first file. This can cause subtle bugs.
Since the jsonnet compiler only supports a single file per --ext-*-file flag for a given variable, you should enforce that the backing targets for these attributes provide exactly one file and fail the build otherwise.
You should also store the single File object directly in the value field instead of a list containing one file. This will require a small change in _extvar_to_arguments to access val["value"] instead of val["value"][0].
Here's how you could adjust the logic for ext_code_files:
for file, key in ext_code_files.items():
if key in extvars:
fail("duplicate extVar '{}': {}".format(key, extvars.keys()))
files = [file]
if type(file) != "File":
files = file[DefaultInfo].files.to_list()
if len(files) != 1:
fail("For ext_code_files, the target for extvar '{}' in '{}' must contain exactly one file, but it contained {}.".format(key, label, len(files)))
extvars[key] = {
"value": files[0],
"type": "code_file",
"sources": [label],
}Similar changes should be applied to ext_code_libraries and ext_str_files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the jsonnet compiler only supports a single file per --ext-*-file flag for a given variable, you should enforce that the backing targets for these attributes provide exactly one file and fail the build otherwise.
It has hit the nail on the head here, though as the current rules_jsonnet already has this behaviour in:
rules_jsonnet/jsonnet/jsonnet.bzl
Lines 472 to 475 in d303289
| ["--tla-str-file %s=%s" % | |
| (var, jfile.files.to_list()[0].short_path) for jfile, var in jsonnet_tla_str_files.items()] + | |
| ["--tla-code-file %s=%s" % | |
| (var, jfile.files.to_list()[0].short_path) for jfile, var in jsonnet_tla_code_files.items()] + |
I wouldn't want to add this has a new failure, this will cause existing code to break.
jsonnet/jsonnet.bzl
Outdated
| for key, code in ext_code.items(): | ||
| _make_extvar_dict_update(extvars, "code", key, code, label) | ||
| for key in ext_code_envs: | ||
| _make_extvar_dict_update(extvars, "code_env", key, None, label) | ||
| for file, key in ext_code_files.items(): | ||
| _make_extvar_dict_update(extvars, "code_file", key, file, label) | ||
| for file, key in ext_code_libraries.items(): | ||
| _make_extvar_dict_update(extvars, "code_library", key, file, label) | ||
| for key in ext_str_envs: | ||
| _make_extvar_dict_update(extvars, "string_env", key, None, label) | ||
| for val, key in ext_str_files.items(): | ||
| _make_extvar_dict_update(extvars, "string_file", key, val, label) | ||
| for key, val in ext_strs.items(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all looks very convoluted. All of the type strings, checking against the output of type(x), etc.
What's the reason we can't just extend JsonnetLibraryInfo to have individual fields for each of these options?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My rationale for going down this path is to make verifying that the extvar variable has not been defined & given different values as simple as possible.
The implementation is trading complexity in _make_extvar_dict & _extvar_to_arguments to handle the format that is optimised for _merge_extvars to reduce the likelihood of an incorrect transitive_extvars is made.
I think this would also come with some good DX as error messages can better identify & explain sources of duplication of extvar variables.
What's the reason we can't just extend
JsonnetLibraryInfoto have individual fields for each of these options?
After playing with the idea for a bit, it puts more complexity into _merge_extvars to handle each of the new fields & feels more error-prone. With _make_extvar_dict & _extvar_to_arguments not changing too much as they plumb through each field.
With that experiment, I was able to find a refactor (87392a7) to remove the need for of the {string,code}_* variants and reduce type down to being string or code which also lead to some (probably overzealous) use of zip() for the quoted section.
4fd009e to
87392a7
Compare
Intent
Implement the suggestion from #89 to resolve a problem of needing to reference generated files from deep in a
jsonnet_librarydependency graph.A concrete use case that this could help is having a
jsonnet_librarythat loads an external file, such as https://github.com/jsonnet-libs/k8s-libsonnet . To use this library internally you need to have the jsonnet code similar to:The above being for a WORKSPACE file, the will change depending how it is setup as a Bazel Mod and the Bazel version used.
With this change, this could be changed to:
Another use case, as the original author of #89 described, use of this is including generated files with
std.extVarinstead of usingimportstr.Design
The design of this implementation revolves around a
extvar_dictwhich gathers the required information aboutextvar's from the deps tree ofjsonnet_libraryand make them compatible withjsonnet_to_json{,_test}.Implementation Notes
To do this, the implementation adds a new field to
JsonnetLibraryInfo,transistive_extvars, which is a dictionary that is keyed onextvarname and contains a dict that contains:It also added three new functions to help handle
extvars from acrossjsonnet_libraryand how they propagate throughdepsto the finaljsonnet_to_json{,_test}targets:_make_extvar_dictwhose goal is to convert thectx.attr's into a uniform dict_merge_extvarswhose goal is to combine multipleextvar_dictand ensure that duplicates are handled correctly_extvar_to_argumentswhose goal is to convert back from the dict to string argumentsThis has the behaviour of ensuring that the same
extvarcannot be defined multiple times with different values. It does allow the sameextvars with the same value to be defined in multiple places to ensure having a common dependency across a dependency tree works. However, it does allow two different targets to define the sameextvarin two places to keep in sync.