Julia language: skip startup.jl file by ericphanson · Pull Request #3496 · pre-commit/pre-commit

Adds --startup-file=no to both invocations of Julia (install & run). The startup file is a Julia script that users can configure to run commands before the start of their Julia session. It is typically used to load developer packages like Revise and not used "in-production". Here when we run our pre-commit hooks we do not want to run the user's startup file which could have dependencies the current environment does not have.

 pytest tests -sk test_julia_hook_with_startup
=========================================== test session starts ===========================================
platform darwin -- Python 3.13.4, pytest-8.4.1, pluggy-1.6.0
rootdir: /Users/eph/pre-commit
configfile: tox.ini
plugins: env-1.1.5
collected 802 items / 801 deselected / 1 selected                                                         

tests/languages/julia_test.py F

================================================ FAILURES =================================================
______________________________________ test_julia_hook_with_startup _______________________________________

tmp_path = PosixPath('/private/var/folders/jb/plyyfc_d2bz195_0rc0n_zcw0000gp/T/pytest-of-eph/pytest-21/test_julia_hook_with_startup0')

    def test_julia_hook_with_startup(tmp_path):
        # We will temporarily use a new Julia depot so we can freely write a
        # startup.jl file
        existing_depot = os.getenv('JULIA_DEPOT_PATH')
        try:
            with tempfile.TemporaryDirectory() as depot_tempdir:
                os.environ['JULIA_DEPOT_PATH'] = depot_tempdir
                depot_path = Path(depot_tempdir)
                depot_path.joinpath('config').mkdir(exist_ok=True)
                startup = depot_path.joinpath('config', 'startup.jl')
                # write a startup.jl file that throws an error so
                # we know it's not used
                startup.write_text('error("Startup file used!")\n')
                # check that the test still succeeds even with a bad startup file
>               test_julia_hook(tmp_path)

tests/languages/julia_test.py:49: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/languages/julia_test.py:32: in test_julia_hook
    assert run_language(tmp_path, julia, 'src/main.jl') == expected
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
testing/language_helpers.py:26: in run_language
    language.install_environment(prefix, version, deps)
pre_commit/languages/julia.py:129: in install_environment
    cmd_output_b(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

check = True
cmd = ('/Users/eph/.juliaup/bin/julia', '-e', '\n        @assert length(ARGS) > 0\n        hook_env = ARGS[1]\n        deps ...ar/folders/jb/plyyfc_d2bz195_0rc0n_zcw0000gp/T/pytest-of-eph/pytest-21/test_julia_hook_with_startup0/juliaenv-default')
kwargs = {'cwd': '/private/var/folders/jb/plyyfc_d2bz195_0rc0n_zcw0000gp/T/pytest-of-eph/pytest-21/test_julia_hook_with_startup0', 'stderr': -1, 'stdin': -1, 'stdout': -1}
proc = <Popen: returncode: 1 args: ('/Users/eph/.juliaup/bin/julia', '-e', '\n     ...>, stdout_b = b''
stderr_b = b'ERROR: LoadError: Startup file used!\nStacktrace:\n [1] error(s::String)\n   @ Base ./error.jl:35\n [2] top-level sc...tup.jl:1\nin expression starting at /var/folders/jb/plyyfc_d2bz195_0rc0n_zcw0000gp/T/tmplwwx2crp/config/startup.jl:1\n'
returncode = 1

    def cmd_output_b(
            *cmd: str,
            check: bool = True,
            **kwargs: Any,
    ) -> tuple[int, bytes, bytes | None]:
        _setdefault_kwargs(kwargs)
    
        try:
            cmd = parse_shebang.normalize_cmd(cmd, env=kwargs.get('env'))
        except parse_shebang.ExecutableNotFoundError as e:
            returncode, stdout_b, stderr_b = e.to_output()
        else:
            try:
                proc = subprocess.Popen(cmd, **kwargs)
            except OSError as e:
                returncode, stdout_b, stderr_b = _oserror_to_output(e)
            else:
                stdout_b, stderr_b = proc.communicate()
                returncode = proc.returncode
    
        if check and returncode:
>           raise CalledProcessError(returncode, cmd, stdout_b, stderr_b)
E           pre_commit.util.CalledProcessError: command: ('/Users/eph/.juliaup/bin/julia', '-e', '\n        @assert length(ARGS) > 0\n        hook_env = ARGS[1]\n        deps = join(ARGS[2:end], " ")\n\n        # We prepend @stdlib here so that we can load the package manager even\n        # though `get_env_patch` limits `JULIA_LOAD_PATH` to just the hook env.\n        pushfirst!(LOAD_PATH, "@stdlib")\n        using Pkg\n        popfirst!(LOAD_PATH)\n\n        # Instantiate the environment shipped with the hook repo. If we have\n        # additional dependencies we disable precompilation in this step to\n        # avoid double work.\n        precompile = isempty(deps) ? "1" : "0"\n        withenv("JULIA_PKG_PRECOMPILE_AUTO" => precompile) do\n            Pkg.instantiate()\n        end\n\n        # Add additional dependencies (with precompilation)\n        if !isempty(deps)\n            withenv("JULIA_PKG_PRECOMPILE_AUTO" => "1") do\n                Pkg.REPLMode.pkgstr("add " * deps)\n            end\n        end\n        ', '--', '/private/var/folders/jb/plyyfc_d2bz195_0rc0n_zcw0000gp/T/pytest-of-eph/pytest-21/test_julia_hook_with_startup0/juliaenv-default')
E           return code: 1
E           stdout: (none)
E           stderr:
E               ERROR: LoadError: Startup file used!
E               Stacktrace:
E                [1] error(s::String)
E                  @ Base ./error.jl:35
E                [2] top-level scope
E                  @ /var/folders/jb/plyyfc_d2bz195_0rc0n_zcw0000gp/T/tmplwwx2crp/config/startup.jl:1
E               in expression starting at /var/folders/jb/plyyfc_d2bz195_0rc0n_zcw0000gp/T/tmplwwx2crp/config/startup.jl:1

pre_commit/util.py:111: CalledProcessError
========================================= short test summary info =========================================
FAILED tests/languages/julia_test.py::test_julia_hook_with_startup - pre_commit.util.CalledProcessError: command: ('/Users/eph/.juliaup/bin/julia', '-e', '\n        @asser...
==================================== 1 failed, 801 deselected in 1.30s ====================================

The second commit is the implementation change which shows that the the fix is effective:

pytest tests -sk test_julia_hook_with_startup
=========================================== test session starts ===========================================
platform darwin -- Python 3.13.4, pytest-8.4.1, pluggy-1.6.0
rootdir: /Users/eph/pre-commit
configfile: tox.ini
plugins: env-1.1.5
collected 802 items / 801 deselected / 1 selected                                                         

tests/languages/julia_test.py .

============================== 1 passed, 801 deselected in 64.48s (0:01:04) ===============================