WiP
This commit is contained in:
parent
d006d8284b
commit
60088b679f
19 changed files with 613 additions and 16 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -31,3 +31,8 @@ dmypy.json
|
|||
|
||||
# PyCharm
|
||||
.idea/
|
||||
|
||||
# tests poetry install
|
||||
|
||||
.tox/
|
||||
.coverage
|
|
@ -1,29 +1,62 @@
|
|||
import argparse
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Sequence
|
||||
|
||||
from many_repos.config import load_config
|
||||
from many_repos import common
|
||||
from many_repos import source_type
|
||||
from many_repos.config import load_config, Config
|
||||
|
||||
|
||||
def _add_specific_args(parser: argparse.ArgumentParser):
|
||||
parser.add_argument("-s", "--source", action="append")
|
||||
parser.add_argument("-s", "--source", help="limit cloning repositores only to these sources", action="append")
|
||||
|
||||
|
||||
def _construct_path(repo: common.Repository, config: Config) -> Path:
|
||||
return (Path(config.output_dir) / repo.vcs / repo.namespace / repo.name).resolve()
|
||||
|
||||
|
||||
def _create_path(path: Path):
|
||||
print(f"Creating `{path}`...")
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def _get_repositories_to_clone(config: Config, sources: list[str] | None = None) -> list[common.Repository]:
|
||||
repos = []
|
||||
for name, source_config in config.sources_configs.items():
|
||||
if sources and name not in sources:
|
||||
continue
|
||||
print(f"Getting {name} repositories..")
|
||||
|
||||
source_cls = source_type.get(source_config.source_type)
|
||||
source = source_cls(source_config)
|
||||
|
||||
all_repos = source.get_repositories()
|
||||
filtered_repos = source.filter_repositories(all_repos)
|
||||
repos.extend(filtered_repos)
|
||||
return repos
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Clone all repositores into `output_dir` from every source, unless specified "
|
||||
"Clone all repositories into `output_dir` from every source, unless specified "
|
||||
"otherwise using one or multiple `--source SOURCE` / `-s SOURCE` ."
|
||||
)
|
||||
)
|
||||
|
||||
_add_specific_args(parser)
|
||||
common.add_common_args(parser)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
config = load_config(args.config_filename)
|
||||
repositories = _get_repositories_to_clone(config, args.source)
|
||||
|
||||
for repository in repositories:
|
||||
repo_path = _construct_path(repository, config)
|
||||
if not repo_path.exists():
|
||||
_create_path(repo_path)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
import argparse
|
||||
import os
|
||||
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
def add_common_args(parser: argparse.ArgumentParser):
|
||||
parser.add_argument(
|
||||
'-C', '--config-filename',
|
||||
default=os.getenv("MANY_REPOS_CONFIG_PATH") or "repos.toml",
|
||||
default=os.getenv("MANY_REPOS_CONFIG_FILE") or "repos.toml",
|
||||
help='use a non-default config file (default `%(default)s`).',
|
||||
)
|
||||
|
||||
|
||||
class Repository(NamedTuple):
|
||||
name: str
|
||||
namespace: str
|
||||
url: str
|
||||
fork: bool
|
||||
vcs: str
|
||||
|
|
|
@ -1,25 +1,48 @@
|
|||
import tomllib
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import NamedTuple
|
||||
from typing import Self
|
||||
|
||||
from many_repos import errors
|
||||
|
||||
|
||||
class Source(NamedTuple):
|
||||
source_type: str
|
||||
username: str
|
||||
token: str
|
||||
forks: bool
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, _dict: dict[str, Any]) -> Self:
|
||||
return cls(
|
||||
source_type=_dict.get("type"),
|
||||
username=_dict.get("username"),
|
||||
token=_dict.get("token"),
|
||||
forks=_dict.get("forks", False)
|
||||
)
|
||||
|
||||
|
||||
class Config(NamedTuple):
|
||||
output_dir: str | Path
|
||||
sources: dict[str, Source] | None
|
||||
sources_configs: dict[str, Source] | None
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, _dict: dict[str, Any]) -> Self:
|
||||
output_dir = _dict.get("config").get("output_dir", None)
|
||||
sources = {name: Source.from_dict(src) for name, src in _dict.get("sources").items()}
|
||||
if not output_dir:
|
||||
raise errors.InvalidConfig("`output_dir` needs to be specified!")
|
||||
cfg = cls(output_dir=output_dir, sources_configs=sources)
|
||||
return cfg
|
||||
|
||||
|
||||
def load_config(config_path: str | Path) -> Config:
|
||||
config_path = Path(config_path)
|
||||
if not config_path.exists():
|
||||
raise errors.ConfigNotFound(f"Config at path {config_path} not found!")
|
||||
|
||||
with config_path.open() as fp:
|
||||
with config_path.open("rb") as fp:
|
||||
parsed_config = tomllib.load(fp)
|
||||
|
||||
return Config(output_dir="~/repos/", sources={"gitlab": Source(source_type="gitlab", username="justscreamy", token="urgay")})
|
||||
return Config.from_dict(parsed_config)
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
class ConfigNotFound(Exception):
|
||||
"Raised when non-existing config was tried to be used"
|
||||
"""Raised when non-existing config was tried to be used"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidConfig(Exception):
|
||||
"""Raised when config is invalid"""
|
||||
pass
|
||||
|
||||
|
||||
class InvalidSource(Exception):
|
||||
"""Raised when invalid source is used"""
|
||||
pass
|
||||
|
|
13
many_repos/source_type/__init__.py
Normal file
13
many_repos/source_type/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
import importlib
|
||||
|
||||
from . import base
|
||||
from .. import errors
|
||||
|
||||
|
||||
def get(source_name: str) -> type[base.BaseSource]:
|
||||
try:
|
||||
module = importlib.import_module(f"many_repos.source_type.{source_name}")
|
||||
return module.Source
|
||||
except (ModuleNotFoundError, AttributeError):
|
||||
raise errors.InvalidSource(f"Source {source_name} not found!")
|
||||
|
52
many_repos/source_type/base.py
Normal file
52
many_repos/source_type/base.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
import abc
|
||||
import json
|
||||
from urllib import request
|
||||
|
||||
from many_repos import common
|
||||
from many_repos import config
|
||||
|
||||
|
||||
class BaseSource(abc.ABC):
|
||||
source_config: config.Source
|
||||
|
||||
def __init__(self, source_config: config.Source):
|
||||
self.source_config = source_config
|
||||
|
||||
# @abc.abstractmethod
|
||||
# def authenticate(self) -> bool:
|
||||
# ...
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_repositories(self) -> list[common.Repository]:
|
||||
...
|
||||
|
||||
def filter_repositories(self, repositories: list[common.Repository]) -> list[common.Repository]:
|
||||
filtered_repositories = []
|
||||
for repo in repositories:
|
||||
if not self.source_config.forks and repo.fork:
|
||||
continue
|
||||
filtered_repositories.append(repo)
|
||||
return filtered_repositories
|
||||
|
||||
@staticmethod
|
||||
def _make_request(
|
||||
url: str, method: str = "GET", *,
|
||||
body: dict | None = None, headers: dict[str, str] | None = None
|
||||
) -> list[dict]:
|
||||
"""
|
||||
Helper function to create http requests using urllib
|
||||
"""
|
||||
if body:
|
||||
headers["Content-Type"] = "application/json"
|
||||
data = json.dumps(body).encode('utf-8')
|
||||
else:
|
||||
data = None
|
||||
|
||||
req = request.Request(
|
||||
url=url,
|
||||
method=method,
|
||||
headers=headers,
|
||||
data=data
|
||||
)
|
||||
res = request.urlopen(req)
|
||||
return json.load(res)
|
31
many_repos/source_type/github.py
Normal file
31
many_repos/source_type/github.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
from many_repos import common
|
||||
from many_repos.source_type.base import BaseSource
|
||||
|
||||
|
||||
class Source(BaseSource):
|
||||
api_url = "https://api.github.com/user/repos?per_page=100?affiliation=owner"
|
||||
|
||||
def get_repositories(self) -> list[common.Repository]:
|
||||
repos_json = self._make_request(self.api_url, headers=self._headers)
|
||||
repositories = []
|
||||
for repo in repos_json:
|
||||
if not self.source_config.forks and repo.get("fork"):
|
||||
continue
|
||||
namespace, name = repo.get("full_name").split("/", 2)
|
||||
repositories.append(
|
||||
common.Repository(
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
url=repo.get("ssh_url"),
|
||||
fork=repo.get("fork"),
|
||||
vcs="github.com"
|
||||
)
|
||||
)
|
||||
return repositories
|
||||
|
||||
@property
|
||||
def _headers(self):
|
||||
return {
|
||||
"Accept": "application/vnd.github+json",
|
||||
"Authorization": f"Bearer {self.source_config.token}"
|
||||
}
|
30
many_repos/source_type/gitlab.py
Normal file
30
many_repos/source_type/gitlab.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from many_repos import common
|
||||
from many_repos.source_type.base import BaseSource
|
||||
|
||||
|
||||
class Source(BaseSource):
|
||||
def get_repositories(self) -> list[common.Repository]:
|
||||
repos_json = self._make_request(self._api_url, headers=self._headers)
|
||||
repositories = []
|
||||
for repo in repos_json:
|
||||
namespace, name = repo.get("path_with_namespace").split("/")
|
||||
repositories.append(
|
||||
common.Repository(
|
||||
name=name,
|
||||
namespace=namespace,
|
||||
url=repo.get("ssh_url_to_repo"),
|
||||
fork=bool(repo.get("forked_from_project")),
|
||||
vcs="gitlab.com"
|
||||
)
|
||||
)
|
||||
return repositories
|
||||
|
||||
@property
|
||||
def _api_url(self) -> str:
|
||||
return f"https://gitlab.com/api/v4/users/{self.source_config.username}/projects"
|
||||
|
||||
@property
|
||||
def _headers(self) -> dict[str, str]:
|
||||
return {
|
||||
"PRIVATE-TOKEN": self.source_config.token
|
||||
}
|
8
many_repos/source_type/gitlab_group.py
Normal file
8
many_repos/source_type/gitlab_group.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from many_repos.source_type.gitlab import Source as BaseSource
|
||||
|
||||
|
||||
class Source(BaseSource):
|
||||
@property
|
||||
def _api_url(self) -> str:
|
||||
return (f"https://gitlab.com/api/v4/groups/"
|
||||
f"{'%2F'.join(self.source_config.username.split('/'))}/projects")
|
0
many_repos/source_type/utils.py
Normal file
0
many_repos/source_type/utils.py
Normal file
326
poetry.lock
generated
326
poetry.lock
generated
|
@ -1,7 +1,327 @@
|
|||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
package = []
|
||||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "5.3.3"
|
||||
description = "Extensible memoizing collections and decorators"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"},
|
||||
{file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "5.2.0"
|
||||
description = "Universal encoding detector for Python 3"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
|
||||
{file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.8"
|
||||
description = "Distribution utilities"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
|
||||
{file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.1"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
|
||||
{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.14.0"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"},
|
||||
{file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
|
||||
typing = ["typing-extensions (>=4.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
description = "brain-dead simple config-ini parsing"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.10.0"
|
||||
description = "Optional static typing for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"},
|
||||
{file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"},
|
||||
{file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"},
|
||||
{file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"},
|
||||
{file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"},
|
||||
{file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
|
||||
{file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
|
||||
{file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
|
||||
{file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
|
||||
{file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
|
||||
{file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
|
||||
{file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
|
||||
{file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
|
||||
{file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
|
||||
{file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
|
||||
{file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"},
|
||||
{file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"},
|
||||
{file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"},
|
||||
{file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"},
|
||||
{file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"},
|
||||
{file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"},
|
||||
{file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"},
|
||||
{file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"},
|
||||
{file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"},
|
||||
{file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"},
|
||||
{file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
|
||||
{file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mypy-extensions = ">=1.0.0"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = ">=4.1.0"
|
||||
|
||||
[package.extras]
|
||||
dmypy = ["psutil (>=4.0)"]
|
||||
install-types = ["pip"]
|
||||
mypyc = ["setuptools (>=50)"]
|
||||
reports = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.2.2"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
|
||||
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||
type = ["mypy (>=1.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.5.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
||||
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pyproject-api"
|
||||
version = "1.6.1"
|
||||
description = "API to interact with the python pyproject.toml based projects"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"},
|
||||
{file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
packaging = ">=23.1"
|
||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.2.1"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"},
|
||||
{file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=1.5,<2.0"
|
||||
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.4.5"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.4.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8f58e615dec58b1a6b291769b559e12fdffb53cc4187160a2fc83250eaf54e96"},
|
||||
{file = "ruff-0.4.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:84dd157474e16e3a82745d2afa1016c17d27cb5d52b12e3d45d418bcc6d49264"},
|
||||
{file = "ruff-0.4.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f483ad9d50b00e7fd577f6d0305aa18494c6af139bce7319c68a17180087f4"},
|
||||
{file = "ruff-0.4.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63fde3bf6f3ad4e990357af1d30e8ba2730860a954ea9282c95fc0846f5f64af"},
|
||||
{file = "ruff-0.4.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e3ba4620dee27f76bbcad97067766026c918ba0f2d035c2fc25cbdd04d9c97"},
|
||||
{file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:441dab55c568e38d02bbda68a926a3d0b54f5510095c9de7f95e47a39e0168aa"},
|
||||
{file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1169e47e9c4136c997f08f9857ae889d614c5035d87d38fda9b44b4338909cdf"},
|
||||
{file = "ruff-0.4.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:755ac9ac2598a941512fc36a9070a13c88d72ff874a9781493eb237ab02d75df"},
|
||||
{file = "ruff-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4b02a65985be2b34b170025a8b92449088ce61e33e69956ce4d316c0fe7cce0"},
|
||||
{file = "ruff-0.4.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:75a426506a183d9201e7e5664de3f6b414ad3850d7625764106f7b6d0486f0a1"},
|
||||
{file = "ruff-0.4.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6e1b139b45e2911419044237d90b60e472f57285950e1492c757dfc88259bb06"},
|
||||
{file = "ruff-0.4.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a6f29a8221d2e3d85ff0c7b4371c0e37b39c87732c969b4d90f3dad2e721c5b1"},
|
||||
{file = "ruff-0.4.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d6ef817124d72b54cc923f3444828ba24fa45c3164bc9e8f1813db2f3d3a8a11"},
|
||||
{file = "ruff-0.4.5-py3-none-win32.whl", hash = "sha256:aed8166c18b1a169a5d3ec28a49b43340949e400665555b51ee06f22813ef062"},
|
||||
{file = "ruff-0.4.5-py3-none-win_amd64.whl", hash = "sha256:b0b03c619d2b4350b4a27e34fd2ac64d0dabe1afbf43de57d0f9d8a05ecffa45"},
|
||||
{file = "ruff-0.4.5-py3-none-win_arm64.whl", hash = "sha256:9d15de3425f53161b3f5a5658d4522e4eee5ea002bf2ac7aa380743dd9ad5fba"},
|
||||
{file = "ruff-0.4.5.tar.gz", hash = "sha256:286eabd47e7d4d521d199cab84deca135557e6d1e0f0d01c29e757c3cb151b54"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tox"
|
||||
version = "4.15.0"
|
||||
description = "tox is a generic virtualenv management and test command line tool"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "tox-4.15.0-py3-none-any.whl", hash = "sha256:300055f335d855b2ab1b12c5802de7f62a36d4fd53f30bd2835f6a201dda46ea"},
|
||||
{file = "tox-4.15.0.tar.gz", hash = "sha256:7a0beeef166fbe566f54f795b4906c31b428eddafc0102ac00d20998dd1933f6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cachetools = ">=5.3.2"
|
||||
chardet = ">=5.2"
|
||||
colorama = ">=0.4.6"
|
||||
filelock = ">=3.13.1"
|
||||
packaging = ">=23.2"
|
||||
platformdirs = ">=4.1"
|
||||
pluggy = ">=1.3"
|
||||
pyproject-api = ">=1.6.1"
|
||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||
virtualenv = ">=20.25"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"]
|
||||
testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"},
|
||||
{file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.26.2"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"},
|
||||
{file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
distlib = ">=0.3.7,<1"
|
||||
filelock = ">=3.12.2,<4"
|
||||
platformdirs = ">=3.9.1,<5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "34e39677d8527182346093002688d17a5d2fc204b9eb3e094b2e6ac519028228"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "d1b9e19425ac7172708e0c3145dd78bc672c6c47b7a02b6de4251073db7fa713"
|
||||
|
|
|
@ -14,9 +14,15 @@ packages = [
|
|||
many-repos-clone = "many_repos.clone:main"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.12"
|
||||
python = "^3.10"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
tox = "^4.15.0"
|
||||
pytest = "^8.2.1"
|
||||
ruff = "^0.4.5"
|
||||
mypy = "^1.10.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
3
tests/dummy_test.py
Normal file
3
tests/dummy_test.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
class TestDummy:
|
||||
def test_dummy(self):
|
||||
assert 1 == 1
|
0
tests/source_type/__init__.py
Normal file
0
tests/source_type/__init__.py
Normal file
13
tests/source_type/conftest.py
Normal file
13
tests/source_type/conftest.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
import pytest
|
||||
|
||||
from many_repos.config import Source
|
||||
|
||||
|
||||
@pytest.fixture(params=[True, False])
|
||||
def source_config(forks):
|
||||
return Source(
|
||||
source_type="github",
|
||||
username="JustScreaMy",
|
||||
token="testToken",
|
||||
forks=forks
|
||||
)
|
7
tests/source_type/github_test.py
Normal file
7
tests/source_type/github_test.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from many_repos.source_type.github import Source as GithubSource
|
||||
from unittest import mock
|
||||
|
||||
|
||||
class TestGithubSource:
|
||||
def test_api_url_generation(self, source_config, monkeypatch):
|
||||
src = GithubSource(source_config)
|
32
tox.ini
Normal file
32
tox.ini
Normal file
|
@ -0,0 +1,32 @@
|
|||
[tox]
|
||||
requires =
|
||||
tox>=4
|
||||
env_list =
|
||||
py{310,311,312}
|
||||
lint
|
||||
coverage
|
||||
type
|
||||
|
||||
[testenv]
|
||||
description = run the tests with pytest
|
||||
deps = pytest
|
||||
commands =
|
||||
pytest {posargs:tests}
|
||||
|
||||
[testenv:type]
|
||||
description = run type checks
|
||||
deps = mypy
|
||||
commands =
|
||||
mypy {posargs:many_repos tests}
|
||||
|
||||
[testenv:lint]
|
||||
description = run linter
|
||||
deps = ruff
|
||||
commands = ruff check {posargs:many_repos tests}
|
||||
|
||||
[testenv:coverage]
|
||||
description = run coverage report
|
||||
deps =
|
||||
pytest
|
||||
pytest-cov
|
||||
commands = pytest --cov=many_repos tests/
|
Loading…
Reference in a new issue