From 08d71bd883087f108d639e05453591b975e0dcd8 Mon Sep 17 00:00:00 2001 From: Purnendu Chakraborty Date: Wed, 2 Oct 2024 07:51:04 -0500 Subject: [PATCH] Merge main into develop (#317) * Added ability to print version info (#309) * Add brew tap installation instructions (#302) * Add brew tap installation instructions * Add update-state to readme * Added ability to print version info * Updated Changelog * Testing mepo version against the last release version number * Removing lock files We really shouldn't need to version control lock files. Reusing already existing virtualenv Generating production lockfile: /home/pchakrab/workspace/code/mepo/develop/requirements.lock Generating dev lockfile: /home/pchakrab/workspace/code/mepo/develop/requirements-dev.lock Installing dependencies Found existing installation: mepo 2.0.0 Uninstalling mepo-2.0.0: Successfully uninstalled mepo-2.0.0 Looking in indexes: https://pypi.org/simple/ Obtaining file:///. (from -r /home/pchakrab/tmp/tmpatm5q2yh (line 1)) Installing build dependencies: started Installing build dependencies: finished with status 'done' Checking if build backend supports build_editable: started Checking if build backend supports build_editable: finished with status 'done' Getting requirements to build editable: started Getting requirements to build editable: finished with status 'done' Installing backend dependencies: started Installing backend dependencies: finished with status 'done' Preparing editable metadata (pyproject.toml): started Preparing editable metadata (pyproject.toml): finished with status 'done' Building wheels for collected packages: mepo Building editable for mepo (pyproject.toml): started Building editable for mepo (pyproject.toml): finished with status 'done' Created wheel for mepo: filename=mepo-2.0.0-py3-none-any.whl size=11085 sha256=758e0b5ae11be1d887f2598e6c04fc09648e00a736ffbe8ae47fbad802e2978f Stored in directory: /home/pchakrab/tmp/pip-ephem-wheel-cache-5f1wnqzn/wheels/97/54/f5/d849319cdfa096e074df352654ee2e7c919da8951f090690c6 Successfully built mepo Installing collected packages: mepo Successfully installed mepo-2.0.0 Done! will generate them from * Removing lock files Merging the changes from main brought in the lock files again * Using rye for Python and package management * Minor cleanup of workflows --------- Co-authored-by: Matt Thompson * Add spack install instructions (#310) * Update README (#315) * Store complete remote url in state file (#316) * Added pytest as a dev dependency to pyproject.toml Now we can run tests via 'rye test' * Store full remote path of every repo in state file * Moved logic to apply style to local path to a separate function * Remote URL is not relative anymore * Added pytests for Registry and MepoComponent classes * Running 'rye test' to run all tests * Generate default __eq__() method in MepoComponent to (rich) compare two instances of this class * Updated test_component for the case when remote is an https url instead of ssh * Switched to https protocol * bug fix * Remote was of a different repo. Fixed now. * Removed MepoState dependence of git.py The state file (state.json) stores the relative local path to each repo. However, during reading of the state, MepoState converts it to an absolute path, and every instantiation of GitRepository uses an absolute local path. So we don't need to call get_root_dir() from GitRepository * Added explicit init of MepoComponent * update-state writes compelete remote url * Removed unnecessary, and wrong, check for local path * Bumped the minor version number * Moved pytest from being a dependency to a dev-dependency. Fixed test that checks for version * Completion working now --------- Co-authored-by: Matt Thompson --- .github/workflows/run-formatter.yaml | 25 ++--- .github/workflows/run-linter.yaml | 24 ++--- .github/workflows/run-tests.yaml | 23 ++--- .gitignore | 1 + CHANGELOG.md | 2 + README.md | 13 ++- etc/mepo-completion.bash | 19 ++-- etc/mepo-path.py | 3 + pyproject.toml | 3 +- requirements-dev.lock | 68 -------------- requirements.lock | 14 --- src/mepo/cmdline/parser.py | 7 ++ src/mepo/command/update-state.py | 6 ++ src/mepo/component.py | 134 +++++++++++++-------------- src/mepo/git.py | 60 +++++------- tests/test_component.py | 68 ++++++++++++++ tests/test_mepo_commands.py | 4 + tests/test_registry.py | 19 ++++ 18 files changed, 252 insertions(+), 241 deletions(-) create mode 100644 etc/mepo-path.py delete mode 100644 requirements-dev.lock delete mode 100644 requirements.lock create mode 100644 tests/test_component.py create mode 100644 tests/test_registry.py diff --git a/.github/workflows/run-formatter.yaml b/.github/workflows/run-formatter.yaml index 2d42054a..a061e610 100644 --- a/.github/workflows/run-formatter.yaml +++ b/.github/workflows/run-formatter.yaml @@ -7,17 +7,18 @@ jobs: runs-on: ubuntu-latest name: Format code steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: 3.9 - cache: 'pip' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements-dev.lock - timeout-minutes: 5 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install rye + uses: eifinger/setup-rye@v4 + + - name: Sync dependencies + run: rye sync + - name: Run black - run: black --check . + run: | + source .venv/bin/activate + black --check . timeout-minutes: 5 diff --git a/.github/workflows/run-linter.yaml b/.github/workflows/run-linter.yaml index 0607441f..a3251c6c 100644 --- a/.github/workflows/run-linter.yaml +++ b/.github/workflows/run-linter.yaml @@ -7,17 +7,17 @@ jobs: runs-on: ubuntu-latest name: Lint code steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.9 - uses: actions/setup-python@v5 - with: - python-version: 3.9 - cache: 'pip' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements-dev.lock - timeout-minutes: 5 + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install rye + uses: eifinger/setup-rye@v4 + + - name: Sync dependencies + run: rye sync + - name: Run pylint - run: pylint --exit-zero src/mepo + run: | + source .venv/bin/activate + pylint --exit-zero src/mepo timeout-minutes: 5 diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index bbddc130..e5f8239e 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -8,26 +8,23 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - python-version: ['3.9', '3.10', '3.11', '3.12', 'pypy-3.9', 'pypy-3.10'] + python-version: ["3.9", "3.10", "3.11", "3.12", "pypy@3.9", "pypy@3.10"] name: Python ${{ matrix.python-version }} on ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' + - name: Checkout code + uses: actions/checkout@v4 - - name: Install dependencies + - name: Install rye + uses: eifinger/setup-rye@v4 + + - name: Sync dependencies run: | - python -m pip install --upgrade pip - pip install -r requirements.lock - timeout-minutes: 5 + rye pin ${{ matrix.python-version }} + rye sync - name: Run tests run: | - export PYTHONPATH=$(pwd)/src:$PYTHONPATH - python tests/test_mepo_commands.py -v + rye test -v timeout-minutes: 5 diff --git a/.gitignore b/.gitignore index 07e88d22..5f155ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *~ *.pyc *.egg-info +*.lock dist venv diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b65a305..9a21fec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added ability to print version info via `mepo --version` + ### Changed ## [2.0.0] - 2024-08-12 diff --git a/README.md b/README.md index 588388eb..f3fdf22f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # mepo [![Actions Status](https://github.com/pchakraborty/mepo/workflows/Unit%20testing%20of%20mepo/badge.svg)](https://github.com/pchakraborty/mepo/actions) [![DOI](https://zenodo.org/badge/215067850.svg)](https://zenodo.org/badge/latestdoi/215067850) [![Rye](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/rye/main/artwork/badge.json)](https://rye-up.com) -`mepo` is a tool, written in Python3 (3.9.0+), to manage (m)ultiple git r(epo)sitories, by attempting to create an illusion of a 'single repository' for multi-repository projects. Please see the [Wiki](../../wiki) for examples of `mepo` workflows. +`mepo` is a tool, written in Python3, to manage (m)ultiple git r(epo)sitories, by attempting to create an illusion of a 'single repository' for multi-repository projects. Please see the [Wiki](../../wiki) for examples of `mepo` workflows. + ## Installation @@ -9,7 +10,7 @@ To install `mepo` using `pip`, run the following command: ``` -`pip install mepo` +pip install mepo ``` ### Homebrew @@ -27,6 +28,14 @@ brew tap gmao-si-team/packages brew install mepo ``` +### Spack + +Mepo is also available via spack as a package. To install mepo using spack, run the following command: + +``` +spack install mepo +``` + ## Transitioning from `mepo` v1.x to v2.x If you try to use mepo v2.x within a mepo v1.x repository, you will get an warning message: diff --git a/etc/mepo-completion.bash b/etc/mepo-completion.bash index 8abe8ab3..faa49870 100644 --- a/etc/mepo-completion.bash +++ b/etc/mepo-completion.bash @@ -2,18 +2,17 @@ # complete -W "init clone status checkout branch diff where whereis history" mepo +# SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +SCRIPT_DIR=$(dirname $(realpath $0)) + _get_mepo_commands() { local mepo_cmd_list="" - if [[ "$OSTYPE" == "darwin"* ]] - then - local mepodir=$(dirname $(readlink $(which mepo))) - else - local mepodir=$(dirname $(readlink -f $(which mepo))) - fi - for mydir in $(ls -d ${mepodir}/mepo.d/command/*/); do - if [[ $mydir != *"__pycache__"* ]]; then - mepo_cmd_list+=" $(basename $mydir)" - fi + local mepo_dir=$(python3 $SCRIPT_DIR/mepo-path.py) + for pyfile in $(ls ${mepo_dir}/command/*.py*); do + command=${pyfile##*/} # remove path + command=${command%.*} # remove extension + command=$(echo $command | cut -d _ -f 1) + mepo_cmd_list+=" $command" done echo ${mepo_cmd_list} } diff --git a/etc/mepo-path.py b/etc/mepo-path.py new file mode 100644 index 00000000..04488203 --- /dev/null +++ b/etc/mepo-path.py @@ -0,0 +1,3 @@ +import os, mepo + +print(os.path.dirname(mepo.__file__)) diff --git a/pyproject.toml b/pyproject.toml index 308021b8..1ca07a02 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mepo" -version = "2.0.0" +version = "2.1.0" description = "A tool for managing (m)ultiple r(epo)s" authors = [{name="GMAO SI Team", email="siteam@gmao.gsfc.nasa.gov"}] dependencies = [ @@ -23,6 +23,7 @@ dev-dependencies = [ "flake8>=7.0.0", "pre-commit>=3.7.1", "mdutils>=1.6.0", + "pytest>=8.2.1", ] [build-system] diff --git a/requirements-dev.lock b/requirements-dev.lock deleted file mode 100644 index 15f19a75..00000000 --- a/requirements-dev.lock +++ /dev/null @@ -1,68 +0,0 @@ -# generated by rye -# use `rye lock` or `rye sync` to update this lockfile -# -# last locked with the following flags: -# pre: false -# features: [] -# all-features: false -# with-sources: false - --e file:. -astroid==3.2.1 - # via pylint -black==24.4.2 -cfgv==3.4.0 - # via pre-commit -click==8.1.7 - # via black -colorama==0.4.6 - # via mepo -dill==0.3.8 - # via pylint -distlib==0.3.8 - # via virtualenv -filelock==3.14.0 - # via virtualenv -flake8==7.0.0 -identify==2.5.36 - # via pre-commit -isort==5.13.2 - # via pylint -mccabe==0.7.0 - # via flake8 - # via pylint -mdutils==1.6.0 -mypy-extensions==1.0.0 - # via black -nodeenv==1.8.0 - # via pre-commit -packaging==24.0 - # via black -pathspec==0.12.1 - # via black -platformdirs==4.2.2 - # via black - # via pylint - # via virtualenv -pre-commit==3.7.1 -pycodestyle==2.11.1 - # via flake8 -pyflakes==3.2.0 - # via flake8 -pylint==3.2.0 -pyyaml==6.0.1 - # via mepo - # via pre-commit -setuptools==70.0.0 - # via nodeenv -tomli==2.0.1 - # via black - # via pylint -tomlkit==0.12.5 - # via pylint -typing-extensions==4.11.0 - # via astroid - # via black - # via pylint -virtualenv==20.26.2 - # via pre-commit diff --git a/requirements.lock b/requirements.lock deleted file mode 100644 index 7cecfb7e..00000000 --- a/requirements.lock +++ /dev/null @@ -1,14 +0,0 @@ -# generated by rye -# use `rye lock` or `rye sync` to update this lockfile -# -# last locked with the following flags: -# pre: false -# features: [] -# all-features: false -# with-sources: false - --e file:. -colorama==0.4.6 - # via mepo -pyyaml==6.0.1 - # via mepo diff --git a/src/mepo/cmdline/parser.py b/src/mepo/cmdline/parser.py index cddd3f2d..4cee79d0 100644 --- a/src/mepo/cmdline/parser.py +++ b/src/mepo/cmdline/parser.py @@ -8,6 +8,12 @@ from ..utilities import mepoconfig +def get_version(): + from importlib import metadata + + return metadata.version("mepo") + + class MepoArgParser: __slots__ = ["parser", "subparsers"] @@ -16,6 +22,7 @@ def __init__(self): self.parser = argparse.ArgumentParser( description="Tool to manage (m)ultiple r(epo)s" ) + self.parser.add_argument("--version", action="version", version=get_version()) self.subparsers = self.parser.add_subparsers() self.subparsers.title = "mepo commands" self.subparsers.required = True diff --git a/src/mepo/command/update-state.py b/src/mepo/command/update-state.py index 58fcc000..94bc7c7e 100644 --- a/src/mepo/command/update-state.py +++ b/src/mepo/command/update-state.py @@ -1,6 +1,9 @@ """Permanently convert mepo1 state to mepo2 state""" +from urllib.parse import urljoin + from ..state import MepoState +from ..git import get_current_remote_url def run(_): @@ -9,6 +12,9 @@ def run(_): # mepo2 style does not exist allcomps = MepoState.read_state() MepoState.mepo1_patch_undo() + for comp in allcomps: + if comp.remote.startswith("../"): + comp.remote = urljoin(get_current_remote_url() + "/", comp.remote) # Write new state MepoState.write_state(allcomps) print("\nConverted mepo1 state to mepo2\n") diff --git a/src/mepo/component.py b/src/mepo/component.py index ef732ba1..7d9753b8 100644 --- a/src/mepo/component.py +++ b/src/mepo/component.py @@ -1,15 +1,18 @@ import os import shlex -from urllib.parse import urlparse +from dataclasses import dataclass +from urllib.parse import urljoin +from .git import get_current_remote_url from .utilities import shellcmd from .utilities.version import MepoVersion -# This will be used to store the "final nodes" from each subrepo -original_final_node_list = [] +# This will be used to store the "last nodes" from each subrepo +last_node_list = [] +@dataclass(eq=True) class MepoComponent(object): __slots__ = [ @@ -24,16 +27,27 @@ class MepoComponent(object): "ignore_submodules", ] - def __init__(self): - self.name = None - self.local = None - self.remote = None - self.version = None - self.sparse = None - self.develop = None - self.recurse_submodules = None - self.fixture = None - self.ignore_submodules = None + def __init__( + self, + name=None, + local=None, + remote=None, + version=None, + sparse=None, + develop=None, + recurse_submodules=None, + fixture=None, + ignore_submodules=None, + ): + self.name = name + self.local = local + self.remote = remote + self.version = version + self.sparse = sparse + self.develop = develop + self.recurse_submodules = recurse_submodules + self.fixture = fixture + self.ignore_submodules = ignore_submodules def __repr__(self): # Older mepo clones will not have ignore_submodules in comp, so @@ -103,54 +117,23 @@ def __set_original_version(self, comp_details): def registry_to_component(self, comp_name, comp_details, comp_style): self.name = comp_name self.fixture = comp_details.get("fixture", False) - # local/remote - start + # local/remote if self.fixture: self.local = "." - repo_url = get_current_remote_url() - p = urlparse(repo_url) - last_url_node = p.path.rsplit("/")[-1] - self.remote = "../" + last_url_node + self.remote = get_current_remote_url() else: - # Assume the flag for repostories is commercial-at - repo_flag = "@" - - # To make it easier to loop over the local path, split into a list - local_list = splitall(comp_details["local"]) - - # The last node of the path is what we will decorate - last_node = local_list[-1] - - # Add that final node to a list - original_final_node_list.append(last_node) - - # Now we need to decorate all the final nodes since we can have - # nested repos with mepo - for item in original_final_node_list: - try: - # Find the index of every "final node" in a local path - # for nesting - index = local_list.index(item) - - # Decorate all final nodes - local_list[index] = decorate_node(item, repo_flag, comp_style) - except ValueError: - pass - - # Now pull the list of nodes back into a path - self.local = os.path.join(*local_list) - # print(f'final self.local: {self.local}') - + self.local = stylize_local_path(comp_details["local"], comp_style) self.remote = comp_details["remote"] - # local/remote - end - self.sparse = comp_details.get("sparse", None) # sparse is optional - self.develop = comp_details.get("develop", None) # develop is optional - self.recurse_submodules = comp_details.get( - "recurse_submodules", None - ) # recurse_submodules is optional - self.ignore_submodules = comp_details.get( - "ignore_submodules", None - ) # ignore_submodules is optional + if self.remote.startswith("../"): + self.remote = urljoin(get_current_remote_url() + "/", self.remote) + # Optionals (None, if missing) + self.sparse = comp_details.get("sparse", None) + self.develop = comp_details.get("develop", None) + self.recurse_submodules = comp_details.get("recurse_submodules", None) + self.ignore_submodules = comp_details.get("ignore_submodules", None) + # version self.__set_original_version(comp_details) + return self def to_registry_format(self): @@ -204,27 +187,34 @@ def serialize(self): return d -def get_current_remote_url(): - cmd = "git remote get-url origin" - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output +def stylize_local_path(local_path, style): + repo_flag = "@" # Assumed flag for repos + local_list = splitall(local_path) + last_node = local_list[-1] + last_node_list.append(last_node) # maintain a list of last nodes + # Decorate ALL last nodes since we can have nested repos + for item in last_node_list: + try: + index = local_list.index(item) + local_list[index] = decorate_node(item, repo_flag, style) + except ValueError: + pass + return os.path.join(*local_list) def decorate_node(item, flag, style): - # If we do not pass in a style... if not style: - # Just use what's in components.yaml - return item - # else use the style + return item # use what's in the registry file + item = item.replace(flag, "") # remove existing flag + if style == "naked": + output = item + elif style == "prefix": + output = flag + item + elif style == "postfix": + output = item + flag else: - item = item.replace(flag, "") - if style == "naked": - output = item - elif style == "prefix": - output = flag + item - elif style == "postfix": - output = item + flag - return output + raise Exception(f"Invalid style: {style}") + return output # From https://learning.oreilly.com/library/view/python-cookbook/0596001673/ch04s16.html diff --git a/src/mepo/git.py b/src/mepo/git.py index 4518a549..5975ab17 100644 --- a/src/mepo/git.py +++ b/src/mepo/git.py @@ -5,7 +5,6 @@ from urllib.parse import urljoin -from .state import MepoState from .utilities import shellcmd from .utilities import colors from .utilities.exceptions import RepoAlreadyClonedError @@ -21,33 +20,26 @@ def get_editor(): return result.stdout.rstrip().decode("utf-8") # byte to utf-8 +def get_current_remote_url(): + cmd = "git remote get-url origin" + output = shellcmd.run(shlex.split(cmd), output=True).strip() + return output + + class GitRepository: """ Class to consolidate git commands """ - __slots__ = ["__local", "__full_local_path", "__remote", "__git"] - - def __init__(self, remote_url, local_path): - self.__local = local_path - - if remote_url.startswith(".."): - rel_remote = os.path.basename(remote_url) - fixture_url = get_current_remote_url() - self.__remote = urljoin(fixture_url, rel_remote) - else: - self.__remote = remote_url + __slots__ = ["__local_path_abs", "__remote", "__git"] - root_dir = MepoState.get_root_dir() - full_local_path = os.path.normpath(os.path.join(root_dir, local_path)) - self.__full_local_path = full_local_path - self.__git = 'git -C "{}"'.format(self.__full_local_path) + def __init__(self, remote_url, local_path_abs): + self.__local_path_abs = local_path_abs + self.__remote = remote_url + self.__git = 'git -C "{}"'.format(self.__local_path_abs) def get_local_path(self): - return self.__local - - def get_full_local_path(self): - return self.__full_local_path + return self.__local_path_abs def get_remote_url(self): return self.__remote @@ -63,15 +55,15 @@ def clone(self, version, recurse, type, comp_name, partial=None): if recurse: cmd1 += "--recurse-submodules " - cmd1 += "--quiet {} {}".format(self.__remote, self.__full_local_path) + cmd1 += "--quiet {} {}".format(self.__remote, self.__local_path_abs) try: shellcmd.run(shlex.split(cmd1)) except sp.CalledProcessError: raise RepoAlreadyClonedError(f"Error! Repo [{comp_name}] already cloned") - cmd2 = "git -C {} checkout {}".format(self.__full_local_path, version) + cmd2 = "git -C {} checkout {}".format(self.__local_path_abs, version) shellcmd.run(shlex.split(cmd2)) - cmd3 = "git -C {} checkout --detach".format(self.__full_local_path) + cmd3 = "git -C {} checkout --detach".format(self.__local_path_abs) shellcmd.run(shlex.split(cmd3)) # NOTE: The above looks odd because of a quirk of git. You can't do @@ -88,7 +80,7 @@ def checkout(self, version, detach=False): shellcmd.run(shlex.split(cmd2)) def sparsify(self, sparse_config): - dst = os.path.join(self.__full_local_path, ".git", "info", "sparse-checkout") + dst = os.path.join(self.__local_path_abs, ".git", "info", "sparse-checkout") os.makedirs(os.path.dirname(dst), exist_ok=True) shutil.copy(sparse_config, dst) cmd1 = self.__git + " config core.sparseCheckout true" @@ -144,7 +136,7 @@ def show_stash(self, patch): return output.rstrip() def run_diff(self, args=None, ignore_submodules=False): - cmd = "git -C {}".format(self.__full_local_path) + cmd = "git -C {}".format(self.__local_path_abs) if args.ignore_permissions: cmd += " -c core.fileMode=false" cmd += " diff --color" @@ -183,7 +175,7 @@ def create_tag(self, tag_name, annotate, message, tf_file=None): cmd = [ "git", "-C", - self.__full_local_path, + self.__local_path_abs, "tag", "-a", "-F", @@ -194,7 +186,7 @@ def create_tag(self, tag_name, annotate, message, tf_file=None): cmd = [ "git", "-C", - self.__full_local_path, + self.__local_path_abs, "tag", "-a", "-m", @@ -204,7 +196,7 @@ def create_tag(self, tag_name, annotate, message, tf_file=None): else: raise Exception("This should not happen") else: - cmd = ["git", "-C", self.__full_local_path, "tag", tag_name] + cmd = ["git", "-C", self.__local_path_abs, "tag", tag_name] shellcmd.run(cmd) def delete_branch(self, branch_name, force): @@ -241,7 +233,7 @@ def verify_branch_or_tag(self, ref_name): return status, ref_type def check_status(self, ignore_permissions=False, ignore_submodules=False): - cmd = "git -C {}".format(self.__full_local_path) + cmd = "git -C {}".format(self.__local_path_abs) if ignore_permissions: cmd += " -c core.fileMode=false" cmd += " status --porcelain=v2" @@ -488,9 +480,9 @@ def unstage_file(self, myfile): def commit_files(self, message, tf_file=None): if tf_file: - cmd = ["git", "-C", self.__full_local_path, "commit", "-F", tf_file] + cmd = ["git", "-C", self.__local_path_abs, "commit", "-F", tf_file] elif message: - cmd = ["git", "-C", self.__full_local_path, "commit", "-m", message] + cmd = ["git", "-C", self.__local_path_abs, "commit", "-m", message] else: raise Exception("This should not happen") shellcmd.run(cmd) @@ -571,9 +563,3 @@ def get_version(self): name = hash_out.rstrip() tYpe = "h" return (name, tYpe, detached) - - -def get_current_remote_url(): - cmd = "git remote get-url origin" - output = shellcmd.run(shlex.split(cmd), output=True).strip() - return output diff --git a/tests/test_component.py b/tests/test_component.py new file mode 100644 index 00000000..bb5d7e60 --- /dev/null +++ b/tests/test_component.py @@ -0,0 +1,68 @@ +import os + +from mepo.component import stylize_local_path +from mepo.component import MepoComponent +from mepo.registry import Registry +from mepo.utilities.version import MepoVersion + +from _pytest.assertion import truncate + +truncate.DEFAULT_MAX_LINES = 9999 +truncate.DEFAULT_MAX_CHARS = 9999 + +TEST_DIR = os.path.dirname(os.path.realpath(__file__)) + + +def get_registry(): + registry = os.path.join(TEST_DIR, "input", "components.yaml") + return Registry(registry).read_file() + + +def get_fvdycore_component(): + return MepoComponent( + name="fvdycore", + local="./src/Components/@FVdycoreCubed_GridComp/@fvdycore", + remote="https://github.com/GEOS-ESM/GFDL_atmos_cubed_sphere.git", + version=MepoVersion(name="geos/v1.3.0", type="t", detached=True), + sparse=None, + develop="geos/develop", + recurse_submodules=None, + fixture=False, + ignore_submodules=None, + ) + + +def get_fvdycore_serialized(): + return { + "name": "fvdycore", + "local": "./src/Components/@FVdycoreCubed_GridComp/@fvdycore", + "remote": "https://github.com/GEOS-ESM/GFDL_atmos_cubed_sphere.git", + "version": ["geos/v1.3.0", "t", True], + "sparse": None, + "develop": "geos/develop", + "recurse_submodules": None, + "fixture": False, + "ignore_submodules": None, + } + + +def test_stylize_local_path(): + local_path = "./src/Shared/@GMAO_Shared/@GEOS_Util" + output = stylize_local_path(local_path, None) + assert output == local_path + output = stylize_local_path(local_path, "prefix") + assert output == local_path + output = stylize_local_path(local_path, "naked") + assert output == "./src/Shared/@GMAO_Shared/GEOS_Util" + output = stylize_local_path(local_path, "postfix") + assert output == "./src/Shared/@GMAO_Shared/GEOS_Util@" + + +def test_MepoComponent(): + registry = get_registry() + complist = list() + for name, comp in registry.items(): + if name == "fvdycore": + fvdycore = MepoComponent().registry_to_component(name, comp, None) + assert fvdycore == get_fvdycore_component() + assert fvdycore.serialize() == get_fvdycore_serialized() diff --git a/tests/test_mepo_commands.py b/tests/test_mepo_commands.py index b610f823..ac7e6bce 100644 --- a/tests/test_mepo_commands.py +++ b/tests/test_mepo_commands.py @@ -27,6 +27,7 @@ import mepo.command.diff as mepo_diff import mepo.command.whereis as mepo_whereis import mepo.command.reset as mepo_reset +from mepo.cmdline.parser import get_version as get_mepo_version # Import commands with dash in the name mepo_restore_state = importlib.import_module("mepo.command.restore-state") @@ -335,6 +336,9 @@ def test_reset(self): with contextlib.redirect_stdout(io.StringIO()) as output: self.__class__.__mepo_clone() + def test_mepo_version(self): + self.assertEqual(get_mepo_version(), "2.1.0") + def tearDown(self): pass diff --git a/tests/test_registry.py b/tests/test_registry.py new file mode 100644 index 00000000..f7c47639 --- /dev/null +++ b/tests/test_registry.py @@ -0,0 +1,19 @@ +import os + +from mepo.registry import Registry + +TEST_DIR = os.path.dirname(os.path.realpath(__file__)) + + +def get_ecbuild_details(): + return { + "local": "./@cmake/@ecbuild", + "remote": "../ecbuild.git", + "tag": "geos/v1.2.0", + } + + +def test_registry(): + registry = os.path.join(TEST_DIR, "input", "components.yaml") + a = Registry(registry).read_file() + assert a["ecbuild"] == get_ecbuild_details()