SamuraiAku / PkgToSoftwareBOM.jl

Produces a Software Bill of Materials (SBOM) describing your Julia Pkg environment. SBOM is in the SPDX format
MIT License
17 stars 2 forks source link

Construct SBOM from a provided Manifest.toml #21

Open hannahilea opened 1 year ago

hannahilea commented 1 year ago

The current example of SBOM generation for a package involves creating a new environment, adding just that package to it, and then constructing an SBOM from there.

Rather than instantiating an environment and then constructing an SBOM from that environment, it would be nice to provide an entrypoint that works directly from the Manifest.toml, without doing any amount of environment resolution (which may re-resolve the provided Manifest.toml in a way that introduces changes, especially if the Julia version from the original manifest and the current environment are different).

SamuraiAku commented 1 year ago

The difficulty with this idea is that it requires a completely separate set of code to process the Manifest.toml file and then populate a new Pkg.API.PackageInfo Dictionary so that the rest of the code can use the information. And Pkg already does all that work already when it reads the Project.toml and Manifest.toml, so it's duplicative.

Reading the documentation on Pkg.instantiate, I interpret it as saying that if the Manifest.toml file is present it will not do any re-resolution. If the version of Julia in use is incompatible with items in the Manifest, that is an edge case and it would be interesting to know what Pkg will do in that situation. Possibly that is a case we don't support at this time.

If Pkg.instantiate is combined with Pkg.activate(; temp=true) then I think we can get the effect called for here, where the Manifest is instantiated within a fresh environment, the SBOM is created and the users own environment is untouched.

SamuraiAku commented 1 year ago

I've got a version of this idea in the tests now. I created a set of dummy packages and use a manifest to pull them in and create an SBOM. Shows it can be done.

peteristhegreat commented 1 month ago

I wrote a bunch of code around inspection of Manifest.toml using Pkg utilities without instantiating a project. The only item it needs is using Pkg. In my use case I wasn't using private registries, but there were several "develop" packages in the local file system. It works in Julia 1.6 and I just tested it on Julia 1.11 and works fine.

using Pkg

function inspect_julia_project_dir(julia_project_dir_in)
    global julia_project_dir = realpath(abspath(julia_project_dir_in))
    global manifest_path = abspath(joinpath(julia_project_dir, "Manifest.toml"))
    global manifest_dict = Pkg.TOML.parsefile(manifest_path)
    global project_dict = Pkg.TOML.parsefile(joinpath(julia_project_dir, "Project.toml"))
    global application = get(project_dict, "name", basename(julia_project_dir))
end

function pkgdir_from_manifest(package_name::String)
    if package_name == application
        return julia_project_dir
    end
    if !haskey(manifest_dict, package_name)
        error("No such package \"$package_name\" found in $manifest_path")
    end
    if length(manifest_dict[package_name]) != 1
        error("Ambiguous entry list for \"$package_name\" in $manifest_path")
    end
    package_dict = manifest_dict[package_name][1]
    if haskey(package_dict, "path")
        return abspath(joinpath(julia_project_dir, package_dict["path"]))
    end

    if haskey(package_dict, "uuid") && haskey(package_dict, "git-tree-sha1")
        return Pkg.Operations.find_installed(package_name,
            Pkg.Types.UUID(package_dict["uuid"]),
            Pkg.Types.SHA1(package_dict["git-tree-sha1"]))
    end

    if haskey(package_dict, "uuid")
        return abspath(joinpath(dirname(Base.find_package(package_name)), ".."))
    end

    @show package_dict
    error("Missing info for \"$package_name\" in $manifest_path: " *
        "Need uuid and git-tree-sha1 or path to look up the path to $package_name")
    return ""
end

## Usage
# inspect_julia_project_dir("path/to/julia/Project")
#
# pkgdir_from_manifest("SomePackage")
# "/Users/username/.julia/packages/SomePackage/abcd1"