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:
		| @@ -16,18 +16,24 @@ | |||||||
| The high-level format of a Spack lockfile hasn't changed much between versions, but the | 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: | 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"`` |       * ``file-type``: always ``"spack-lockfile"`` | ||||||
|       * ``lockfile-version``: an integer representing the lockfile format version |       * ``lockfile-version``: an integer representing the lockfile format version | ||||||
|       * ``specfile-version``: an integer representing the spec format version (since |       * ``specfile-version``: an integer representing the spec format version (since | ||||||
|         ``v0.17``) |         ``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: |       environment. Each has two fields: | ||||||
|       * ``hash``: a Spack spec hash uniquely identifying the concrete root spec |       * ``hash``: a Spack spec hash uniquely identifying the concrete root spec | ||||||
|       * ``spec``: a string representation of the abstract spec that was concretized |       * ``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 | Compatibility | ||||||
| ------------- | ------------- | ||||||
| @@ -271,6 +277,8 @@ | |||||||
| Dependencies are keyed by ``hash`` (DAG hash) as well. There are no more ``build_hash`` | 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 | 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). | 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 | .. code-block:: json | ||||||
| @@ -278,8 +286,8 @@ | |||||||
|     { |     { | ||||||
|         "_meta": { |         "_meta": { | ||||||
|             "file-type": "spack-lockfile", |             "file-type": "spack-lockfile", | ||||||
|             "lockfile-version": 3, |             "lockfile-version": 4, | ||||||
|             "specfile-version": 2 |             "specfile-version": 3 | ||||||
|         }, |         }, | ||||||
|         "roots": [ |         "roots": [ | ||||||
|             { |             { | ||||||
| @@ -326,7 +334,6 @@ | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from .environment import ( | from .environment import ( | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ | |||||||
| import spack.error | import spack.error | ||||||
| import spack.hash_types as ht | import spack.hash_types as ht | ||||||
| import spack.hooks | import spack.hooks | ||||||
|  | import spack.main | ||||||
| import spack.paths | import spack.paths | ||||||
| import spack.repo | import spack.repo | ||||||
| import spack.schema.env | import spack.schema.env | ||||||
| @@ -2072,6 +2073,14 @@ def _to_lockfile_dict(self): | |||||||
| 
 | 
 | ||||||
|         hash_spec_list = zip(self.concretized_order, self.concretized_user_specs) |         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 |         # this is the lockfile we'll write out | ||||||
|         data = { |         data = { | ||||||
|             # metadata about the format |             # metadata about the format | ||||||
| @@ -2080,6 +2089,8 @@ def _to_lockfile_dict(self): | |||||||
|                 "lockfile-version": lockfile_format_version, |                 "lockfile-version": lockfile_format_version, | ||||||
|                 "specfile-version": spack.spec.SPECFILE_FORMAT_VERSION, |                 "specfile-version": spack.spec.SPECFILE_FORMAT_VERSION, | ||||||
|             }, |             }, | ||||||
|  |             # spack version information | ||||||
|  |             "spack": spack_dict, | ||||||
|             # users specs + hashes are the 'roots' of the environment |             # users specs + hashes are the 'roots' of the environment | ||||||
|             "roots": [{"hash": h, "spec": str(s)} for h, s in hash_spec_list], |             "roots": [{"hash": h, "spec": str(s)} for h, s in hash_spec_list], | ||||||
|             # Concrete specs by hash, including dependencies |             # Concrete specs by hash, including dependencies | ||||||
|   | |||||||
| @@ -126,6 +126,36 @@ def add_all_commands(parser): | |||||||
|         parser.add_command(cmd) |         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(): | def get_version(): | ||||||
|     """Get a descriptive version of this instance of Spack. |     """Get a descriptive version of this instance of Spack. | ||||||
| 
 | 
 | ||||||
| @@ -134,25 +164,9 @@ def get_version(): | |||||||
|     The commit sha is only added when available. |     The commit sha is only added when available. | ||||||
|     """ |     """ | ||||||
|     version = spack.spack_version |     version = spack.spack_version | ||||||
|     git_path = os.path.join(spack.paths.prefix, ".git") |     commit = get_spack_commit() | ||||||
|     if os.path.exists(git_path): |     if commit: | ||||||
|         git = spack.util.git.git() |         version += " ({0})".format(commit) | ||||||
|         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)) |  | ||||||
| 
 | 
 | ||||||
|     return version |     return version | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| import pytest | import pytest | ||||||
| 
 | 
 | ||||||
| import spack.environment as ev | import spack.environment as ev | ||||||
|  | from spack import spack_version | ||||||
| from spack.main import SpackCommand | from spack.main import SpackCommand | ||||||
| 
 | 
 | ||||||
| pytestmark = pytest.mark.usefixtures("config", "mutable_mock_repo") | 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") |         add("b") | ||||||
|         concretize("--test", "root") |         concretize("--test", "root") | ||||||
|         assert e.matching_spec("test-dependency") |         assert e.matching_spec("test-dependency") | ||||||
|  | 
 | ||||||
|  |         data = e._to_lockfile_dict() | ||||||
|  |         assert data["spack"]["version"] == spack_version | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Tamara Dahlgren
					Tamara Dahlgren