Skip to content

Commit 7549c0a

Browse files
committed
✨ initial prototype
1 parent 0089785 commit 7549c0a

File tree

13 files changed

+376
-6
lines changed

13 files changed

+376
-6
lines changed

.moban.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
requires:
2-
- pypi-mobans-pkg==0.0.7
31
configuration:
42
template_dir:
5-
- "pypi://pypi-mobans-pkg/resources/templates"
3+
- "git://github.com/moremoban/pypi-mobans.git?submodule=true!/templates"
64
- ".moban.d"
75
configuration: gitfs2.yml
86
targets:

Pipfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ name = 'pypi'
77
python_version= '3.6'
88

99
[packages]
10+
fs = "*"
11+
crayons = "*"
12+
GitPython = "*"
1013

1114
[dev-packages]
1215
nose = "*"

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@
5959
# Add any paths that contain custom static files (such as style sheets) here,
6060
# relative to this directory. They are copied after the builtin static files,
6161
# so a file named "default.css" will overwrite the builtin "default.css".
62-
html_static_path = ['static']
62+
html_static_path = ['static']

gitfs2.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ current_version: "0.0.1"
88
release: "0.0.0"
99
copyright_year: 2019
1010
license: MIT
11-
dependencies: []
11+
dependencies:
12+
- fs
13+
- crayons
14+
- GitPython
15+
entry_points:
16+
fs.opener:
17+
- "git = gitfs2:GitFSOpener"
1218
description: "Python file system 2 over GitPython"
1319
moban_command: make format git-diff-check

gitfs2/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# flake8: noqa
22
from gitfs2._version import __version__
33
from gitfs2._version import __author__
4+
from gitfs2.opener import GitFSOpener

gitfs2/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PROGRAM_NAME = "fs.gitpython"
2+
REPOS_DIR_NAME = "repos"
3+
4+
MESSAGE_INVALID_GIT_URL = 'An invalid git url: "%s" in mobanfile'
5+
DEFAULT_CLONE_DEPTH = 2

gitfs2/opener.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from gitfs2 import repo
2+
from fs.osfs import OSFS
3+
from fs.opener import Opener
4+
5+
6+
class GitFSOpener(Opener):
7+
protocols = ["git"]
8+
update = True
9+
10+
def open_fs(self, fs_url, parse_result, writeable, create, cwd):
11+
repo_name, _, dir_path = parse_result.resource.partition("/")
12+
git_url = "{protocol}://{resource}".format(
13+
protocol=parse_result.protocol,
14+
resource=parse_result.resource
15+
)
16+
require = repo.GitRequire(
17+
git_url=git_url,
18+
branch=parse_result.params.get('branch'),
19+
submodule=parse_result.params.get('submodule'),
20+
reference=parse_result.params.get('reference')
21+
)
22+
local_folder = repo.git_clone(
23+
require,
24+
action_required=GitFSOpener.update)
25+
if GitFSOpener.update:
26+
GitFSOpener.update = False
27+
if parse_result.path:
28+
local_folder = local_folder+parse_result.path
29+
osfs = OSFS(root_path=local_folder)
30+
return osfs

gitfs2/repo.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import os
2+
import sys
3+
import errno
4+
import subprocess
5+
6+
import fs
7+
import fs.path
8+
from gitfs2 import constants, reporter
9+
10+
11+
class GitRequire(object):
12+
def __init__(
13+
self, git_url=None, branch=None, submodule="False", reference=None
14+
):
15+
self.git_url = git_url
16+
self.submodule = convert_submodule(submodule)
17+
self.branch = branch
18+
self.reference = reference
19+
20+
def clone_params(self):
21+
clone_params = {
22+
"single_branch": True,
23+
"depth": constants.DEFAULT_CLONE_DEPTH,
24+
}
25+
if self.branch is not None:
26+
clone_params["branch"] = self.branch
27+
elif self.reference is not None:
28+
clone_params["reference"] = self.reference
29+
return clone_params
30+
31+
def __eq__(self, other):
32+
return (
33+
self.git_url == other.git_url
34+
and self.submodule == other.submodule
35+
and self.branch == other.branch
36+
and self.reference == other.reference
37+
)
38+
39+
def __repr__(self):
40+
return "%s,%s,%s" % (self.git_url, self.branch, self.submodule)
41+
42+
43+
def convert_submodule(submodule_string):
44+
if submodule_string in ['1', 'True', 'true']:
45+
return True
46+
return False
47+
48+
49+
def git_clone(require, action_required=True):
50+
from git import Repo
51+
52+
if sys.platform != "win32":
53+
# Unfortunately for windows user, the following function
54+
# needs shell=True, which expose security risk. I would
55+
# rather not to trade it with its marginal benefit
56+
make_sure_git_is_available()
57+
58+
app_home = get_app_home()
59+
mkdir_p(app_home)
60+
61+
repo_name = get_repo_name(require.git_url)
62+
local_repo_folder = os.path.join(app_home, repo_name)
63+
64+
if action_required:
65+
if os.path.exists(local_repo_folder):
66+
reporter.info("Found repo in %s" % local_repo_folder)
67+
repo = Repo(local_repo_folder)
68+
repo.git.pull()
69+
if require.reference:
70+
repo.git.checkout(require.reference)
71+
elif require.branch:
72+
repo.git.checkout(require.branch)
73+
if require.submodule:
74+
reporter.info("updating submodule")
75+
repo.git.submodule("update")
76+
else:
77+
reporter.info("git clone %s" % require.git_url)
78+
repo = Repo.clone_from(
79+
require.git_url, local_repo_folder, **require.clone_params()
80+
)
81+
if require.submodule:
82+
reporter.info("checking out submodule")
83+
repo.git.submodule("update", "--init")
84+
85+
return local_repo_folder
86+
87+
88+
def get_repo_name(repo_url):
89+
import giturlparse
90+
from giturlparse.parser import ParserError
91+
92+
try:
93+
repo = giturlparse.parse(repo_url.rstrip("/"))
94+
return repo.name
95+
except ParserError:
96+
reporter.error(
97+
constants.MESSAGE_INVALID_GIT_URL % repo_url
98+
)
99+
raise
100+
101+
102+
def get_app_home():
103+
from appdirs import user_cache_dir
104+
105+
home_dir = user_cache_dir(appname=constants.PROGRAM_NAME)
106+
return fs.path.join(home_dir, constants.REPOS_DIR_NAME)
107+
108+
109+
def make_sure_git_is_available():
110+
try:
111+
subprocess.check_output(["git", "--help"])
112+
except Exception:
113+
raise Exception("Please install git command")
114+
115+
116+
def mkdir_p(path):
117+
try:
118+
os.makedirs(path)
119+
except OSError as exc: # Python >2.5
120+
if exc.errno == errno.EEXIST and os.path.isdir(path):
121+
pass
122+
else:
123+
raise

gitfs2/reporter.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import crayons
2+
3+
4+
def error(message):
5+
print(crayons.white("Error: ", bold=True) + crayons.red(message))
6+
7+
8+
def warn(message):
9+
print(crayons.white("Warning: ", bold=True) + crayons.yellow(message))
10+
11+
12+
def info(message):
13+
print(crayons.white("Info: ") + crayons.green(message))

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fs
2+
crayons
3+
GitPython

0 commit comments

Comments
 (0)