Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion snakedeploy/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def __init__(
tag: Optional[str] = None,
branch: Optional[str] = None,
force=False,
host: Optional[str] = None,
):
self.provider = get_provider(source)
self.env = Environment(loader=PackageLoader("snakedeploy"))
Expand All @@ -29,6 +30,7 @@ def __init__(
self._cloned = None
self.tag = tag
self.branch = branch
self.host = host

def __enter__(self):
return self
Expand Down Expand Up @@ -227,7 +229,7 @@ def deploy_snakefile(self, tmpdir: str, name: str):
module_deployment = template.render(
name=name,
snakefile=self.provider.get_source_file_declaration(
snakefile, self.tag, self.branch
snakefile, self.tag, self.branch, self.host
),
repo=self.provider.source_url,
config=config,
Expand All @@ -253,6 +255,7 @@ def deploy(
branch: Optional[str],
dest_path: Path,
force=False,
host: Optional[str] = None,
):
"""
Deploy a given workflow to the local machine, using the Snakemake module system.
Expand Down
43 changes: 33 additions & 10 deletions snakedeploy/providers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from abc import abstractmethod, ABC
from shutil import copytree
import os
import shutil
from snakedeploy.exceptions import UserError
import subprocess as sp
import os
from abc import ABC, abstractmethod
from shutil import copytree
from typing import Optional

from snakedeploy.exceptions import UserError


def get_provider(source_url):
Expand Down Expand Up @@ -31,16 +33,20 @@ def __init__(self, source_url):

@classmethod
@abstractmethod
def matches(cls, source_url: str): ...
def matches(cls, source_url: str):
...

@abstractmethod
def clone(self, path: str): ...
def clone(self, path: str):
...

@abstractmethod
def checkout(self, path: str, ref: str): ...
def checkout(self, path: str, ref: str):
...

@abstractmethod
def get_raw_file(self, path: str, tag: str): ...
def get_raw_file(self, path: str, tag: str):
...

def get_repo_name(self):
return self.source_url.split("/")[-1]
Expand Down Expand Up @@ -73,7 +79,9 @@ def get_raw_file(self, path: str, tag: str):
)
return f"{self.source_url}/{path}"

def get_source_file_declaration(self, path: str, tag: str, branch: str):
def get_source_file_declaration(
self, path: str, tag: str, branch: str, host: Optional[str] = None
):
relative_path = path.replace(self.source_url, "").strip(os.sep)
return f'"{relative_path}"'

Expand Down Expand Up @@ -105,7 +113,9 @@ def checkout(self, path: str, ref: str):
def get_raw_file(self, path: str, tag: str):
return f"{self.source_url}/raw/{tag}/{path}"

def get_source_file_declaration(self, path: str, tag: str, branch: str):
def get_source_file_declaration(
self, path: str, tag: str, branch: str, host: Optional[str] = None
):
owner_repo = "/".join(self.source_url.split("/")[-2:])
if not (tag or branch):
raise UserError("Either tag or branch has to be specified for deployment.")
Expand All @@ -117,5 +127,18 @@ class Gitlab(Github):
def get_raw_file(self, path: str, tag: str):
return f"{self.source_url}/-/raw/{tag}/{path}"

def get_source_file_declaration(
self, path: str, tag: str, branch: str, host: Optional[str] = None
):
owner_repo = "/".join(self.source_url.split("/")[-2:])
url_host = self.source_url.split("/")[2]
if host is None and url_host != "gitlab.com":
host = url_host
if not (tag or branch):
raise UserError("Either tag or branch has to be specified for deployment.")
ref_arg = f'tag="{tag}"' if tag is not None else f'branch="{branch}"'
host_arg = f'host="{host}"' if host is not None else ""
return f'{self.name}("{owner_repo}", path="{path}", {ref_arg}, {host_arg})'

Comment on lines +130 to +142
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix GitLab namespace parsing and avoid dangling comma in call.

  • owner_repo drops parent groups for nested namespaces (e.g., group/subgroup/repo).
  • Building the call with an empty host_arg yields a trailing comma in the argument list.

Proposed fix:

  • Use the full path after the host for owner_repo.
  • Build args dynamically to avoid the dangling comma.

Apply this diff:

 def get_source_file_declaration(
     self, path: str, tag: str, branch: str, host: Optional[str] = None
 ):
-    owner_repo = "/".join(self.source_url.split("/")[-2:])
-    url_host = self.source_url.split("/")[2]
+    # Use full namespace path (supports nested groups)
+    owner_repo = "/".join(self.source_url.split("/")[3:])
+    url_host = self.source_url.split("/")[2]
     if host is None and url_host != "gitlab.com":
         host = url_host
     if not (tag or branch):
         raise UserError("Either tag or branch has to be specified for deployment.")
     ref_arg = f'tag="{tag}"' if tag is not None else f'branch="{branch}"'
-    host_arg = f'host="{host}"' if host is not None else ""
-    return f'{self.name}("{owner_repo}", path="{path}", {ref_arg}, {host_arg})'
+    args = [f'"{owner_repo}"', f'path="{path}"', ref_arg]
+    if host is not None:
+        args.append(f'host="{host}"')
+    return f'{self.name}({", ".join(args)})'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def get_source_file_declaration(
self, path: str, tag: str, branch: str, host: Optional[str] = None
):
owner_repo = "/".join(self.source_url.split("/")[-2:])
url_host = self.source_url.split("/")[2]
if host is None and url_host != "gitlab.com":
host = url_host
if not (tag or branch):
raise UserError("Either tag or branch has to be specified for deployment.")
ref_arg = f'tag="{tag}"' if tag is not None else f'branch="{branch}"'
host_arg = f'host="{host}"' if host is not None else ""
return f'{self.name}("{owner_repo}", path="{path}", {ref_arg}, {host_arg})'
def get_source_file_declaration(
self, path: str, tag: str, branch: str, host: Optional[str] = None
):
# Use full namespace path (supports nested groups)
owner_repo = "/".join(self.source_url.split("/")[3:])
url_host = self.source_url.split("/")[2]
if host is None and url_host != "gitlab.com":
host = url_host
if not (tag or branch):
raise UserError("Either tag or branch has to be specified for deployment.")
ref_arg = f'tag="{tag}"' if tag is not None else f'branch="{branch}"'
- host_arg = f'host="{host}"' if host is not None else ""
args = [f'"{owner_repo}"', f'path="{path}"', ref_arg]
if host is not None:
args.append(f'host="{host}"')
return f'{self.name}({", ".join(args)})'
🤖 Prompt for AI Agents
In snakedeploy/providers.py around lines 130 to 142, owner_repo currently uses
only the last two path segments which loses parent groups for nested GitLab
namespaces and host handling can produce an empty host_arg that leaves a
dangling comma in the returned call string; to fix, derive owner_repo as the
full path portion after the host (i.e., join all segments after the domain),
determine url_host from the netloc/second segment as you already do, build a
list of argument strings starting with the required '"{owner_repo}"' and
'path="{path}"', add the ref argument as tag or branch depending on which is
set, and only append a host="..." argument if host is not None, then return the
call by joining the args with ", " so no trailing comma appears.


PROVIDERS = [Github, Gitlab, Local]
Loading