Understanding the startup.jl file -- is there a environment-specific version?

My understanding of the startup.jl file is that it applies to all Julia instances/environments and there is no way to apply different startup scripts to different environments. I tried including a local startup.jl file in a play environment, but nothing happened. And the documentation seems to suggest a single (global) file is used and located in ~/.julia/config/startup.jl.

I ask as having a means of running an environment/project specific startup script could be useful for working with, for example, super computers, where users may not be able to edit the startup.jl file, but may still need to set paths or environmental variables consistently (and without tedium) across scripts. In R I do this using local/project-specific versions of .Rprofile and .Renviron. It would also just be nice to have the ability to alter environment-specific options (like, maybe I don’t want to work with OhMyREPL on this project, or maybe my workflow changed and now I don’t want this old project to use the same startup script as my new projects).

I see there are both JULIA_DEPOT_PATH and DEPOT_PATH environmental variables, which may apply, but I’m not really sure.

tl;dr: Is there a way to use different startup.jl files for specific environments, rather than just applying the file to all instances of Julia (kind of like .Rprofile and .Renviron in R)?

Edit: punctuation.

4 Likes

I don’t know about a builtin way to do this. But you could add the following in your global startup.jl:

let
    local_startup = joinpath(dirname(Base.active_project()), "startup.jl")
    if isfile(local_startup)
        include(local_startup)
    end
end

If you had this problem, you can set the environment variable JULIA_DEPOT_PATH to modify where your Julia packages are installed and where your global startup.jl is read from.

7 Likes

This will suffer from the same security issues that .localrc does in vim. Imagine you clone a repo from somewhere, and while in that directory you open up a repl to do a quick calculation. Now whatever 100% guaranteed to be malicious code the repo owner decided to put in localstartup.jl is run without your explicit consent.

1 Like

I don’t think this is correct. You need to explicitly set --project=. for Base.active_project() to be set to the current directory.

Regardless of that, if you think the code above is insecure you can customize it with your own security rules.

You could always name the locat startup file something unique to you and the evil repo would be very unlikely to guess that.

local_ctkelley_startup.jl

for example.

1 Like

That was always my strategy in vim. Had an idea of salting my localrcs with a hash of the current directory path - but I stopped using them instead.

You can use direnv which has a builtin security feature where you need to manually approve the file content. For example:

$ cat local_startup.jl 
println("hello, world")

$ cat .envrc 
julia_args -L local_startup.jl

$ julia -e ''
hello, world

See my post about Project specific Julia configuration with direnv.

1 Like

So it sounds like there is not local startup.jl option. I wonder if using the “global” startup.jl file to control local version could potentially cause conflicts if I wasn’t paying close attention. No conflicts come to mind at the moment, but in R only one .Renviron or .Rprofile file get run, either the local of global version, specifically to avoid conflicts.

Seems like making my own startup.jl file and then adding it the beginning of each script might be the safer solution. Maybe I could call it something else, just to avoid confusion, too. So if the startup.jl file were in the active project as project_startup.jl then I could just add a line near the header of the file, like:

include(project_startup.jl)

Not super elegant, but that might be good enough to serve my purposes (at least until I better understand some of the other options proposed).

Say your project environment is called MyProject. Create a subfolder named src and put MyProject.jl in it (basically, you end up with path/to/MyProject/src/MyProject.jl). Now, in this file you create a module exactly named MyProject and put your startup code inside the __init__() function:

module MyProject

function __init__()
    #stuff to do at startup
end

end

In every script of your MyProject environment, you can call using MyProject at the beginning: this will execute whatever you put inside the __init__() function in the MyProject module. It has also the added benefit of being folder independent (you simply call using MyProject without caring about what folder your script is currently in) and you make sure that whatever you want to be executed at startup is called exactly once in the lifetime of your REPL session. In fact, you can include scripts inside other scripts, each of which have using MyProject at the beginning, and there won’t be any re-execution of the startup code (which instead would happen if your startup code is included in every script). Of course, you can also define and export any function you like in the MyProject module, and it will be instantly available when calling using MyProject. I think this is the easiest and most flexible solution, without an actual startup.jl file. Also, it can be useful having this code not being automatically loaded, in case you want to debug without having it loaded inside your environment.

5 Likes

After playing around with this a bit, I think this is most in-line with the behavior I was expecting. That and playing around with some settings from vscode to get environmental variables changed, though that may not be that big of an issue from the command line of a super computer (e.g., setting threads with -t/–threads when starting Julia). Cheers!

1 Like