-
-
Notifications
You must be signed in to change notification settings - 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_library
Rule: Thejsonnet_library
rule now supports a comprehensive set ofextvar
attributes, 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_extvars
field has been added toJsonnetLibraryInfo
to enable the propagation and merging of external variables across the dependency graph, ensuring consistentextvar
definitions throughout a Jsonnet project. - Robust
extvar
Handling 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 ofextvars
into command-line arguments for the Jsonnet compiler. - Updated
jsonnet_to_json
andjsonnet_to_json_test
: Thejsonnet_to_json
andjsonnet_to_json_test
rules have been updated to leverage the new transitiveextvar
information, 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
JsonnetLibraryInfo
to 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_library
dependency graph.A concrete use case that this could help is having a
jsonnet_library
that 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.extVar
instead of usingimportstr
.Design
The design of this implementation revolves around a
extvar_dict
which gathers the required information aboutextvar
's from the deps tree ofjsonnet_library
and 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 onextvar
name and contains a dict that contains:It also added three new functions to help handle
extvar
s from acrossjsonnet_library
and how they propagate throughdeps
to the finaljsonnet_to_json{,_test}
targets:_make_extvar_dict
whose goal is to convert thectx.attr
's into a uniform dict_merge_extvars
whose goal is to combine multipleextvar_dict
and ensure that duplicates are handled correctly_extvar_to_arguments
whose goal is to convert back from the dict to string argumentsThis has the behaviour of ensuring that the same
extvar
cannot be defined multiple times with different values. It does allow the sameextvar
s 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 sameextvar
in two places to keep in sync.