-
Notifications
You must be signed in to change notification settings - Fork 253
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
use file:script if a .config.script file present #210
Conversation
Looks good to me. |
I am wondering what the use case of this feature is. It looks awfully powerful, but in some way the idea of live-patching a rebar.config sounds to me like a dirty hack. |
There are many use cases for dynamic reconfiguration. Sharing/extending common configuration, switching options depending on the environment (os, word size, etc) are just two examples. There have been suggestions about how to do these before (such as my patch for config merging - #156) but those require supporting a full feature in rebar itself, whereas this option puts the onus on the user. |
Dynamic or templated config. To be used instead of adding support |
This is basically an alternative to what you'd do with The issue with expanding variables is who decides what the set of environment variables is in order to support everyone's needs? That issue would plague the templated config option as well, as you'd still need to build up the available parameters for the templates to consume. |
I like this, but I have some questions. Why not just allow expressions in config files when parsing?As in https://github.com/hyperthunk/libconf/blob/master/src/config.erl#L8 or https://github.com/hyperthunk/rebar_dist_plugin/wiki/Configuring-Assemblies. {target_dir, filename:join(erlang:tuple_to_list(
rebar_rel_utils:get_rel_release_info(?FILE))}. Why not allow config files to contain template variables?Initialised with parent + global rebar config and perhaps the contents of os:getenv/0: {target_dir, "${app_name}/${app_version}"}.
%% etc .... |
Well, this is more or less what file:script/2 does: https://github.com/erlang/otp/blob/master/lib/kernel/src/file.erl#L1034 One could imagine raising the bar further by expanding special patterns in I have done this myself in e.g. the 'setup' application: https://github.com/esl/setup/blob/master/src/setup.erl#L119 ...allowing environment variables like: {kvdb_databases, [{rpc_queues, where the $DATA_DIR and $APP variables are automatically expanded by I believe Tuncer is fairly reluctant to go this route with rebar. BR, |
…ript rebar_rel_utils:is_rel_dir/1 will now return true if Dir contains a reltool.config.script file or a reltool.config file. The return value is {true, F}, where F is the found file (reltool.config.script if both were present). rebar_config:consult_file(F) now consults the config file and passes its contents to the .script (if present), bound to the variable CONFIG.
I'm reluctant to introduce more var-expansion syntax because it doesn't look |
Hi Ulf, yes I've had a crack at this a couple of times too. The main difference is that the approach I'm suggesting here behaves like file:consult (in that it returns a list of all the contained expressions so the file is more like a config file) but like file:script, it allows expressions such as function calls, rather than just supporting terms as file:consult does. Tuncer: In answer to 1, take a look at this bit of in-place eval: {erts_vsn, erlang:system_info(version)}.
{config_file_path,
filename:join(element(2, file:get_cwd()), "blah.config")}. That one, for example, works with https://github.com/hyperthunk/libconf/blob/master/src/config.erl#L8. If you want to get fancy and do other kinds of expansion, such as adding the parsed terms to the bindings as you're evaluating the file and supporting simple string interpolation, you can also do that: {erts_vsn, erlang:system_info(version)}.
{erts_string, "erts-${erts_vsn}"}.
{config_file_suffix, ".config"}.
{config_file_name, "${erts_string}.config"}.
{config_file_path,
filename:join(element(2, file:get_cwd()), resolve(config_file_name))}. Taken from https://github.com/hyperthunk/econfig/blob/master/itest/econfig_SUITE_data/terms.config. Now econfig was an attempt to unify the ideas I had from parsing configuration files containing internal/external function calls and expressions in both the rebar_dist_plugin (producing results such as this for example) and in https://github.com/hyperthunk/libconf/blob/master/src/config.erl#L8. I didn't really complete it as I had more pressing things and the other ideas of it were fulfilled by gproc so I parked it, although this config parsing bit works fine as a proof of concept. Point I'm raising is that in my little brain I prefer the look of this kind of config file, rather than a straight script. It is more complex to implement though, with the most complete example (last one) taking up the latter half of https://github.com/hyperthunk/econfig/blob/master/src/econfig.erl#L90. |
I'm not sure, but the implementation overhead and that you How would this expression Fun = fun(C1) ->
S = do_something(),
L = case foo(S) of
Bar -> modify_foo(Bar);
Baz -> something_else(Baz)
end,
list:foreach(fun(C2) -> do_final(IE) end, L)
end,
IsX = fun(X) -> ... end,
IsY = fun(Y) -> ... end,
list:filter(fun(Bar) -> IsX(Bar);
(Foo) -> IsY(Foo);
(_) -> false
end, Fun(CONFIG)). taken from a .script file look with in-place expression support? |
First of all, it would look pretty much the same, with begin/end in place to support the multiple steps required: {xyz, begin
Fun = fun(C1) ->
S = do_something(),
L = case foo(S) of
Bar -> modify_foo(Bar);
Baz -> something_else(Baz)
end,
list:foreach(fun(C2) -> do_final(IE) end, L)
end,
IsX = fun(X) -> X == 10 end,
IsY = fun(Y) -> Y == 20 end,
list:filter(fun(Bar) -> IsX(Bar);
(Foo) -> IsY(Foo);
(_) -> false
end, Fun([]))
end}. Secondly, I can't imagine you'd put so much logic into an embedded expression. This kind of complicated logic is exactly what plugins are good at, and plugins are loaded onto the code path when they're referenced, so any functions they export are made available to running code, including this code embedded in a config file. |
OK, it looks how I imagined it would. |
(edited and subsequent comments deleted) While this can be done with the "enhanced file:consult" style as well, I think the The file:script-style file, on the other hand, behaves the same way as if the commands BR, |
Ulf - I must concede that you're right about the clarity of scope handling here - a very complex expression is better not embedded, but placed either in a script or plugin. As you can see in my prior example, I got around the scope 'problem' by re-using previously evaluated elements, but I do realise that such an approach is a bit opaque at best. |
Thanks, merged. |
This patch makes rebar look for a file named
rebar.config.script
before readingrebar.config
. The main use of this would be to be able to "patch" the rebar config, based on the current environment.If the .script file is present, rebar will process it using
file:script(File, [{'SCRIPT', File}])
. The .script file may in its turn process rebar.config and use it as a basis for customizations. Note that the variableSCRIPT
is pre-bound to the name of the .script file.The rebar_config module looks for the rebar.config.script file in the same location as the rebar.config. To be precise, it appends ".script" to the name of the config file that would otherwise be read. Thus, if rebar is called with
rebar --config /tmp/foo ...
, then rebar will try to read /tmp/foo.script first.