Environments: store spack version/commit in spack.lock (#32801)

Add a section to the lock file to track the Spack version/commit that produced
an environment. This should (eventually) enhance reproducibility, though we
do not currently do anything with the information. It just adds to provenance
at the moment.

Changes include:
- [x] adding the version/commit to `spack.lock`
- [x] refactor `spack.main.get_version()
- [x] fix a couple of environment lock file-related typos
This commit is contained in:
Tamara Dahlgren 2023-05-11 20:13:36 -07:00 committed by GitHub
parent b06d20be19
commit 8e18297cf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 25 deletions

View File

@ -16,18 +16,24 @@
The high-level format of a Spack lockfile hasn't changed much between versions, but the
contents have. Lockfiles are JSON-formatted and their top-level sections are:
1. ``_meta`` (object): this contains deatails about the file format, including:
1. ``_meta`` (object): this contains details about the file format, including:
* ``file-type``: always ``"spack-lockfile"``
* ``lockfile-version``: an integer representing the lockfile format version
* ``specfile-version``: an integer representing the spec format version (since
``v0.17``)
2. ``roots`` (list): an ordered list of records representing the roots of the Spack
2. ``spack`` (object): optional, this identifies information about Spack
used to concretize the environment:
* ``type``: required, identifies form Spack version took (e.g., ``git``, ``release``)
* ``commit``: the commit if the version is from git
* ``version``: the Spack version
3. ``roots`` (list): an ordered list of records representing the roots of the Spack
environment. Each has two fields:
* ``hash``: a Spack spec hash uniquely identifying the concrete root spec
* ``spec``: a string representation of the abstract spec that was concretized
3. ``concrete_specs``: a dictionary containing the specs in the environment.
4. ``concrete_specs``: a dictionary containing the specs in the environment.
Compatibility
-------------
@ -271,6 +277,8 @@
Dependencies are keyed by ``hash`` (DAG hash) as well. There are no more ``build_hash``
fields in the specs, and there are no more issues with lockfiles being able to store
multiple specs with the same DAG hash (because the DAG hash is now finer-grained).
An optional ``spack`` property may be included to track version information, such as
the commit or version.
.. code-block:: json
@ -278,8 +286,8 @@
{
"_meta": {
"file-type": "spack-lockfile",
"lockfile-version": 3,
"specfile-version": 2
"lockfile-version": 4,
"specfile-version": 3
},
"roots": [
{
@ -326,7 +334,6 @@
}
}
}
"""
from .environment import (

View File

@ -31,6 +31,7 @@
import spack.error
import spack.hash_types as ht
import spack.hooks
import spack.main
import spack.paths
import spack.repo
import spack.schema.env
@ -2072,6 +2073,14 @@ def _to_lockfile_dict(self):
hash_spec_list = zip(self.concretized_order, self.concretized_user_specs)
spack_dict = {"version": spack.spack_version}
spack_commit = spack.main.get_spack_commit()
if spack_commit:
spack_dict["type"] = "git"
spack_dict["commit"] = spack_commit
else:
spack_dict["type"] = "release"
# this is the lockfile we'll write out
data = {
# metadata about the format
@ -2080,6 +2089,8 @@ def _to_lockfile_dict(self):
"lockfile-version": lockfile_format_version,
"specfile-version": spack.spec.SPECFILE_FORMAT_VERSION,
},
# spack version information
"spack": spack_dict,
# users specs + hashes are the 'roots' of the environment
"roots": [{"hash": h, "spec": str(s)} for h, s in hash_spec_list],
# Concrete specs by hash, including dependencies

View File

@ -126,6 +126,36 @@ def add_all_commands(parser):
parser.add_command(cmd)
def get_spack_commit():
"""Get the Spack git commit sha.
Returns:
(str or None) the commit sha if available, otherwise None
"""
git_path = os.path.join(spack.paths.prefix, ".git")
if not os.path.exists(git_path):
return None
git = spack.util.git.git()
if not git:
return None
rev = git(
"-C",
spack.paths.prefix,
"rev-parse",
"HEAD",
output=str,
error=os.devnull,
fail_on_error=False,
)
if git.returncode != 0:
return None
match = re.match(r"[a-f\d]{7,}$", rev)
return match.group(0) if match else None
def get_version():
"""Get a descriptive version of this instance of Spack.
@ -134,25 +164,9 @@ def get_version():
The commit sha is only added when available.
"""
version = spack.spack_version
git_path = os.path.join(spack.paths.prefix, ".git")
if os.path.exists(git_path):
git = spack.util.git.git()
if not git:
return version
rev = git(
"-C",
spack.paths.prefix,
"rev-parse",
"HEAD",
output=str,
error=os.devnull,
fail_on_error=False,
)
if git.returncode != 0:
return version
match = re.match(r"[a-f\d]{7,}$", rev)
if match:
version += " ({0})".format(match.group(0))
commit = get_spack_commit()
if commit:
version += " ({0})".format(commit)
return version

View File

@ -7,6 +7,7 @@
import pytest
import spack.environment as ev
from spack import spack_version
from spack.main import SpackCommand
pytestmark = pytest.mark.usefixtures("config", "mutable_mock_repo")
@ -54,3 +55,6 @@ def test_concretize_root_test_dependencies_are_concretized(unify, mutable_mock_e
add("b")
concretize("--test", "root")
assert e.matching_spec("test-dependency")
data = e._to_lockfile_dict()
assert data["spack"]["version"] == spack_version