Skip to content

Commit

Permalink
Store complete remote url in state file (#316)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
pchakraborty authored Oct 2, 2024
1 parent b5539b7 commit 159862a
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 123 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/run-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ jobs:
- name: Run tests
run: |
source .venv/bin/activate
python tests/test_mepo_commands.py -v
rye test -v
timeout-minutes: 5
19 changes: 9 additions & 10 deletions etc/mepo-completion.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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}
}
Expand Down
3 changes: 3 additions & 0 deletions etc/mepo-path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import os, mepo

print(os.path.dirname(mepo.__file__))
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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="[email protected]"}]
dependencies = [
Expand All @@ -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]
Expand Down
6 changes: 6 additions & 0 deletions src/mepo/command/update-state.py
Original file line number Diff line number Diff line change
@@ -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(_):
Expand All @@ -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")
Expand Down
134 changes: 62 additions & 72 deletions src/mepo/component.py
Original file line number Diff line number Diff line change
@@ -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__ = [
Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 159862a

Please sign in to comment.