-
Notifications
You must be signed in to change notification settings - Fork 133
Fix #1178 #1203
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
Fix #1178 #1203
Conversation
…etection Parse RHS of file= argument in app_config.R using AST traversal to robustly support expressions like Sys.getenv("CONFIG_PATH", app_sys("...")). Fixes bug introduced post-0.4.1 where regex extraction failed on nested calls.
Related to this there is #1179, giving a more complicated expression to the argument In general, I am not sure if one should support this type of behavior. One could simply restrict the possible values to the
Ready to implement other ideas. |
Sometimes, there are commented lines in app_config.R for the file argument to test different arguments for `file` in the `get_golem_config()` function. Ensures that commented-out config candidates like '# file = app_sys(...)' are not parsed and mistakenly used for user config path resolution. Improves robustness of multi-line detection in guess_lines_to_config_file().
Thanks a lot @ilyaZar 🫶 I feel like the code for guess_where_config is getting a bit complex 🤔 and I'm not quite sure if we should continue supporting the "try and guess" behavior. Maybe we should provide an explicit way to change the config path using an env var, and stop trying to guess where it is — a large portion of the devs will not change the default path, so we can expect from devs that are playing with config files to be comfortable enough with environment variable. So, my proposition is :
I suggest modifying the guess_where_config <- function(
path = golem::pkg_path(),
file = "inst/golem-config.yml"
) {
if (Sys.getenv("GOLEM_CONFIG_PATH") == "") {
path_to_config <- fs_path(
path,
file
)
} else {
path_to_config <- Sys.getenv("GOLEM_CONFIG_PATH")
}
path_to_config <- fs_path_abs(path_to_config)
if (!fs_file_exists(path_to_config)) {
stop(
"Unable to locate config file. Either use the default path at inst/golem-config.yml or set it with a GOLEM_CONFIG_PATH environment variable"
)
}
} Then, in the R/app_config.R file, switching to: get_golem_config <- function(
value,
config = Sys.getenv(
"GOLEM_CONFIG_ACTIVE",
Sys.getenv(
"R_CONFIG_ACTIVE",
"default"
)
),
use_parent = TRUE,
# If you don't want to use the default config file:
# - replace the function call
# and write the path to your config file
# - set a `GOLEM_CONFIG_PATH` env var that will
# be picked by golem::get_current_config()
file = golem::get_current_config()
) {
config::get(
value = value,
config = config,
file = file,
use_parent = use_parent
)
} So basically, we stop parsing the files from the package to get the config file path, we either rely on the default value, an env var, or an hard coded path. @LDSamson @ilyaZar, what do you both think of this approach? |
This sounds like a nice and simpler solution, good balance between flexibility and stability, personally I like it. Furthermore: please correct me if I am wrong, but I think with this approach it also does not matter if arbitrary other custom functions are declared in the |
@LDSamson thanks for your feedback. I also think this would make using different config file name or path easier — mosts user will use the default and advanced user can configure with their needs, and yes you're right, it would no longer parse |
Hi @ColinFay yes definitely agreed, and as said above I was never a big of the "try and guess" behavior ... although I contributed to exactly that code :) However, I have a feeling that the Ideas for slight change
guess_where_config <- function(
path = golem::pkg_path(),
file = "inst/golem-config.yml"
) {
# 1. DEV USER: envir var is set, and if the file does not exist hard stop
if (Sys.getenv("GOLEM_CONFIG_PATH") != "") {
path_to_config <- fs_path_abs(Sys.getenv("GOLEM_CONFIG_PATH"))
if (!fs_file_exists(path_to_config)) {
msg_err <- paste0(
"Unable to locate a config file using the environment variable
'GOLEM_CONFIG_PATH'. Check for typos in the (path to the) filename.")
stop(msg_err)
}
} else if (Sys.getenv("GOLEM_CONFIG_PATH") == "") {
path_to_config <- fs_path(
path,
file
)
# 2.A standard user: sets default path -> all is fine
CHECK_DEFAULT_PATH <- grepl("*./inst/golem-config.yml$", path_to_config)
if (isFALSE(CHECK_DEFAULT_PATH)) {
# 2.B DEV USER: sets non-default path OR has typo in the envir variable name:
# if the file does not exist hard stop
if (!fs_file_exists(path_to_config)) {
msg_err <- paste0(
"Unable to locate a config file from either the 'path' and 'file'",
"arguments, or the 'GOLEM_CONFIG_PATH' environment variable.",
"Check for typos."
)
stop(msg_err)
}
}
}
return(path_to_config)
} Benefits
get_current_config <- function(path = getwd()) {
# We check whether we can guess where the config file is
path_conf <- guess_where_config(path)
if (!fs_file_exists(path_conf)) {
if (rlang_is_interactive()) {
ask <- ask_golem_creation_upon_config(path_conf)
# Return early if the user doesn't allow
if (!ask) {
return(NULL)
}
fs_file_copy(
path = golem_sys("shinyexample/inst/golem-config.yml"),
new_path = fs_path(
path,
"inst/golem-config.yml"
)
)
fs_file_copy(
path = golem_sys("shinyexample/R/app_config.R"),
new_path = fs_path(
path,
"R/app_config.R"
)
)
replace_word(
fs_path(
path,
"R/app_config.R"
),
"shinyexample",
golem::pkg_name(path = path)
)
...
...
... Just some ideas, ... ready to make other changes ! |
- implements different cases with different error messages - error messages inform mostly about what case exactly went wrong
Changes made and To-Do
|
I encountered some issues. IssuesActually, the In the old implementation, which used to parse the Verify the problemHere, setting get_golem_config <- function(
value,
config = Sys.getenv(
"GOLEM_CONFIG_ACTIVE",
Sys.getenv(
"R_CONFIG_ACTIVE",
"default"
)
),
use_parent = TRUE,
# If you don't want to use the default config file:
# - replace the function call
# and write the path to your config file
# - set a `GOLEM_CONFIG_PATH` env var that will
# be picked by golem::get_current_config()
# file = golem::get_current_config()
file = "./inst/golem-config2.yml"
) {
config::get(
value = value,
config = config,
file = file,
use_parent = use_parent
)
} So changing to hard coded path at the beginning of a new golem-skeleton fails. Probably also @ColinFay any ideas ? I may try to rework those |
Just some brainstorming ideas below. What if you would create a file in the Then the logic in
If the file is not available the Furthermore, I am probably missing something here: |
@LDSamson , yes this is a good idea and all doable I do not have any hard opinion about the correct way. The underlying problem to address is always the same:
Your suggestion (if I understand correctly), makes the implementation inside
Instead, config-files are retrieved either as envir-vars or a separate Sounds liike a good way to me, it would clean up some exotic internal implementations as well, or @ColinFay ? |
Regarding
Yes, it’s even possible to “purge” |
Thank you both for the discussion. I'm under the impression that we should support two cases:
I'm ok with not givin too much flexibility here, as we can't provide a way to safely allow all possibility and still be reliable. Flexibility might create too much complexity here :) So, to get back to the original issue: I agree with the original message from @ilyaZar
Parsing the file will indeed be too complex because of the infinite possibilities the user could do. For example, what if I suggest to then stick to the following:
Then
And we do not (natively) support:
Then, if advanced developers want to write another way to access this file, I feel like they would be advanced enough to write their own wrapper function, as @LDSamson did. Also, to answer remarks from @ilyaZar:
Your idea for stopping if env var is set but the file doesn't exist is great though. Given that I've merged #1209 and the args have been renamed, I'll cherry pick these two commits into a new branch and merge to master. Thanks a lot for this insightful discussion 🫶 |
I think I'll go for something even simpler in app_config, so that it's more obvious that you can tweak things here if you need to get_golem_config <- function(
value,
config = Sys.getenv(
"GOLEM_CONFIG_ACTIVE",
Sys.getenv(
"R_CONFIG_ACTIVE",
"default"
)
),
use_parent = TRUE,
# If you don't want to use the default config file,
# set a `GOLEM_CONFIG_PATH` environment variable that points
# to your custom config file.
file = Sys.getenv(
"GOLEM_CONFIG_PATH",
app_sys("golem-config.yml")
)
) {
config::get(
value = value,
config = config,
file = file,
use_parent = use_parent
)
} |
This has been rebased and integrated in #1210 |
This PR fixes a regression in how
guess_where_config()
resolves custom config paths inR/app_config.R
. Specifically, it now correctly handles nested expressions of the form:Previously, the logic relied on regex and failed to correctly extract the path from nested calls. This led to malformed paths and erroneous conflict detection between the user and default config files.
Also, when experimenting in package development there could be several
file = ...
calls, one being commented out, in theapp_config.R
i.e. :This is fixed as well, the new behavior identifies the correct (not commented) case only now.
Changes made and To-Do
app_sys(...)
andSys.getenv(..., app_sys(...))
file =
and neglected commented lines i.e.# file = ...
app_config()
#1179Fix:
#1178
#1179