Skip to content

Improved version tracking and deprecated SVN #403

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion EXOSIMS/MissionSim.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def check_ioscripts(self) -> None:
# and finally, let's look at the outspec
outspec = self.genOutSpec(modnames=True)
# these are extraneous things allowed to be in outspec:
whitelist = ["modules", "Revision", "seed", "nStars"]
whitelist = ["modules", "Version", "seed", "nStars"]
for w in whitelist:
_ = outspec.pop(w, None)

Expand Down
38 changes: 3 additions & 35 deletions EXOSIMS/Prototypes/SurveySimulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import os
import random as py_random
import re
import subprocess
import sys
import time
from pathlib import Path
Expand All @@ -23,6 +22,7 @@
from EXOSIMS.util.get_module import get_module
from EXOSIMS.util.vprint import vprint
from EXOSIMS.util._numpy_compat import copy_if_needed
from EXOSIMS.util.version_util import get_version


Logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -2534,40 +2534,8 @@ def genOutSpec(
if "SurveyEnsemble" not in out["modules"]:
out["modules"]["SurveyEnsemble"] = " "

# add in the SVN/Git revision
path = os.path.split(inspect.getfile(self.__class__))[0]
path = os.path.split(os.path.split(path)[0])[0]
# handle case where EXOSIMS was imported from the working directory
if path == "":
path = os.getcwd()
# comm = "git -C " + path + " log -1"
comm = "git --git-dir=%s --work-tree=%s log -1" % (
os.path.join(path, ".git"),
path,
)
rev = subprocess.Popen(
comm, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
)
(gitRev, err) = rev.communicate()
gitRev = gitRev.decode("utf-8")
if isinstance(gitRev, str) & (len(gitRev) > 0):
tmp = re.compile(
r"\S*(commit [0-9a-fA-F]+)\n[\s\S]*Date: ([\S ]*)\n"
).match(gitRev)
if tmp:
out["Revision"] = "Github " + tmp.groups()[0] + " " + tmp.groups()[1]
else:
rev = subprocess.Popen(
"svn info " + path + "| grep \"Revision\" | awk '{print $2}'",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
)
(svnRev, err) = rev.communicate()
if isinstance(svnRev, str) & (len(svnRev) > 0):
out["Revision"] = "SVN revision is " + svnRev[:-1]
else:
out["Revision"] = "Not a valid Github or SVN revision."
# get version and Git information
out["Version"] = get_version()

# dump to file
if tofile is not None:
Expand Down
148 changes: 148 additions & 0 deletions EXOSIMS/util/version_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
from importlib import metadata
import platform
import subprocess
import os
import json
import EXOSIMS


def get_git_info():
"""
Get Git information including commit hash and any uncommited changes

Args:
None

Returns:
tuple:
gitrev (str):
Hash of HEAD commit. None if git repo cannot be identified
uncommitted_changes (str):
Full text of any uncommitted changes. None if git repo cannot be
identified. '' if no uncommitted changes present.

"""

# see if the editable install is pulling from a git repo
path = os.path.split(os.path.split(EXOSIMS.__file__)[0])[0]
gitdir = os.path.join(path, ".git")
if not os.path.exists(gitdir):
return None, None

# grab current revision
# comm = "git rev-parse HEAD"
comm = ["git", f"--git-dir={gitdir}", f"--work-tree={path}", "rev-parse", "HEAD"]
res = subprocess.run(
comm,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)

if res.returncode != 0:
return None, None

gitrev = res.stdout.decode().strip()

# Check for uncommitted changes
# comm = "git diff HEAD"
comm = ["git", f"--git-dir={gitdir}", f"--work-tree={path}", "diff", "HEAD"]
res = subprocess.run(
comm,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
uncommitted_changes = res.stdout.decode()

return gitrev, uncommitted_changes


def is_editable_installation():
"""Check if EXOSIMS is installed in editable mode
Args:
None

Returns:
bool:
True if EXOSIMS package installed in editable mode. Otherwise False.

"""
direct_url = metadata.Distribution.from_name("EXOSIMS").read_text("direct_url.json")
if direct_url is None:
return False
else:
direct_url = json.loads(direct_url)

if "editable" in direct_url["dir_info"]:
return direct_url["dir_info"]["editable"]
else:
return False


def get_version():
"""Retrieve the Python version and EXOSIMS version.
Args:
None

Returns:
dict:
Dictonary containing keys 'Python Version', 'EXOSIMS Version',
'Package Versions', and 'Editable Installation'. If EXOSIMS is installed
in editable mode from a git repo, will also include the commit hash in
keyword 'Git Commit'. If the repo contains any uncommitted changes, will
contain full text of these in key 'Uncommitted Changes'.

"""

# Get basic versions
python_version = platform.python_version()
exosims_version = metadata.version("EXOSIMS")

# Check required package versions
reqs = metadata.distribution("EXOSIMS").requires
required_packages = [str(req) for req in reqs]

# Get installed versions of required packages
installed_packages = {
dist.metadata["Name"]: dist.version for dist in metadata.distributions()
}

# Filter installed packages to those listed in requirements
relevant_packages = {
pkg: installed_packages.get(pkg.split(">=")[0], "Not installed")
for pkg in required_packages
}

# Check for editable installation
editable = is_editable_installation()

out = {
"Python Version": python_version,
"EXOSIMS Version": exosims_version,
"Package Versions": relevant_packages,
"Editable Installation": editable,
}

if editable:
commit_hash, uncommitted_changes = get_git_info()
if commit_hash is not None:
out["Git Commit"] = commit_hash
if uncommitted_changes != "":
out["Uncommitted Changes"] = uncommitted_changes

return out


def print_version():
"""
Print out full version information.
"""
version_info = get_version()
for key, value in version_info.items():
if isinstance(value, dict):
print(f"{key}:")
for sub_key, sub_value in value.items():
print(f" {sub_key}:".ljust(25) + f"{sub_value}")
else:
print(f"{key}:".ljust(25) + f"{value}")
15 changes: 15 additions & 0 deletions tools/rec_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

from importlib import metadata
reqs = metadata.distribution('EXOSIMS').requires
required_packages = [str(req).split('>=')[0] for req in reqs]

with open('requirements.txt', 'r') as f:
lines = f.readlines()

for package in required_packages:
flag = False
for line in lines:
if package in line:
flag = True
assert flag, f'{package} not found in requirements.txt'
print(f'{package} found in requirements.txt')