RFC: Freeze package versions

I wrote the following function freeze():

# freeze the current package versions; overwrites the current Project.toml file
using Pkg, TOML
"""
    freeze()

Freezes the current package versions by adding them to the Project.toml file.
"""

function freeze()
    project_file, compat = project_compat()
    deps = (TOML.parsefile(project_file))["deps"]
    open(project_file, "w") do io
        println(io, "[deps]")
        TOML.print(io, deps)
        println(io)
        println(io, "[compat]")
        TOML.print(io, compat)
    end
    nothing
end

"""
    project_compat()

Create a dictionary of package dependencies and their current versions.

Returns the full file name of the Project.toml file and the dictionary
`compat` that can be added to the Project.toml file to freeze the package
versions.
"""
function project_compat()
    io = IOBuffer();
    Pkg.status(; io)
    st = String(take!(io))
    i = 1
    project_file=""
    compat = Dict{String, Any}()
    for line in eachline(IOBuffer(st))
        if i == 1
            project_file=line
        else
            pkg_vers = split(line, "] ")[2]
            pkg  = split(pkg_vers, " v")[1]
            vers = split(pkg_vers, " v")[2]
            push!(compat, (String(pkg)=>String("~"*vers)))
        end
        i += 1
    end
    project_file = expanduser(split(project_file,'`')[2])
    project_file, compat
end

The idea is to fix the current versions of the project dependencies and to commit the generated Project.toml file to git. Compared to committing the Manifest.toml this has the following advantages:

  • the project stays compatible with different Julia versions
  • changes in the git log are readable (I find the Manifest.toml file unreadable, YMMV)

I might make this more robust, add some options and create a package out of it.

What do you think about this idea?

4 Likes

Just note that it only “freezes” direct dependency versions, indirect deps versions can become different.

Also

this part is only different to Manifest compatibility because here you don’t fix indirect deps versions. So, Manifest breaks on Julia update when the required version of any dependency is not compatible with new Julia – and your Project.toml breaks when the version of any direct dep is not compatible with new Julia.

1 Like

I have not seen this yet in practice, at least not when going from 1.9 to 1.10 …

With transitive deps fixed some breakage happens very often, almost on every minor julia version. Of course it will be less of an issue with fixing direct deps only.
It’s a compromise: fix all dependency versions (Manifest) vs allow any version of indirect deps (Project + strict compat).

1 Like

I created a package that provides the function freeze, improved version compared to the version I posted here. Have a look:

Usage is explained in the README.md .

Feel free to test it and create issues if you find a bug or miss a feature.