Skip to content

Commit fc621c1

Browse files
committed
featuring git via https
1 parent d724475 commit fc621c1

4 files changed

Lines changed: 70 additions & 26 deletions

File tree

.vscode/launch.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
66
"configurations": [
7-
87
{
98
"name": "Python Debugger: container-structure-test",
109
"type": "debugpy",
1110
"request": "launch",
1211
"module": "middleware.main",
1312
"console": "integratedTerminal",
1413
"args": ["-c", "test/container-structure-test/image_test_config.yml", "--no-git"]
14+
},
15+
{
16+
"name": "Python Debugger: test run",
17+
"type": "debugpy",
18+
"request": "launch",
19+
"module": "middleware.main",
20+
"console": "integratedTerminal",
21+
"args": ["-c", "config.yml"]
1522
}
1623
]
1724
}

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,11 @@ docker run \
6767
A few notes on this docker run call:
6868

6969
- We run the container without root privileges as user `nonroot`. This user is defined in the Wolfi base image.
70-
- `config.yml` and `ssh_key.pem` specify the configuration. `config.yml` is mounted as volume, whereas the content of `ssh_key.epm`
70+
- `config.yml` and `ssh_key.pem` specify the configuration. `config.yml` is mounted as volume, whereas the content of `ssh_key.pem`
7171
is passed as environment variable `SSH_PRIVATE_KEY`. This is to prevent from permission issues. The ssh key is for git interaction with the
72-
remote repository specified in the config file. Of course it needs to be already known to the git repo.
73-
- The local git working folder would preferably also be mounted into the container so its contents could be cached between container runs.
74-
But this seems not to be possible -- at least not without administrational permission on the host machine. The issue is that mounted
75-
volumes always belong to root, but the container does not run with root permissions, so it has no write access to the folder.
72+
remote repository specified in the config file. Of course the key already needs to be registered with the git repo.
73+
- As an alternative to access git via `ssh`, it's also possible to use the `https` protocol. In this case you will need to create a
74+
personal access token in the github user interface to gain write access to the middleware repo. This access token is passed in terms of the environment variable `ACCESS_TOKEN`.
7675

7776
## Notes on the python version ##
7877

config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ http_client:
3838
# You can omit the ssh_key_path if you have a local git setup with your personal key.
3939
# If branch is omitted, the main branch is used
4040
git:
41-
repo_url: git@github.com:fairagro/middleware_repo.git
41+
repo_url: https://github.com/fairagro/middleware_repo.git
4242
branch: main
4343
local_path: output
4444
user_name: "FAIRagro middleware"

middleware/git_repo/__init__.py

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class GitRepoConfig(NamedTuple):
3333
A class for the configuration of a GitRepo.
3434
"""
3535

36-
repo_url: Annotated[str, "The ssh URL of the git repository"]
36+
repo_url: Annotated[str, "The ssh or https URL of the git repository"]
3737
local_path: Annotated[str, "The local path of the git repository"]
3838
user_name: Annotated[str, "The name of git user"]
3939
user_email: Annotated[str, "The email address of git usere"]
@@ -72,13 +72,17 @@ class GitRepo:
7272

7373
def __init__(self, config: GitRepoConfig) -> None:
7474
self._config = config
75-
self._ssh_tempdir = tempfile.TemporaryDirectory() # pylint: disable=R1732
76-
self._ssh_key = os.path.abspath(
77-
os.path.join(self._ssh_tempdir.name, "ssh_key"))
78-
self._ssh_authorized_keys = os.path.abspath(
79-
os.path.join(self._ssh_tempdir.name, "authorized_keys"))
75+
self._ssh_tempdir = None
8076
self._repo = self._setup()
8177

78+
def _cleanup(self) -> None:
79+
"""
80+
delete temp dir, if used
81+
"""
82+
if self._ssh_tempdir:
83+
self._ssh_tempdir.cleanup()
84+
self._ssh_tempdir = None
85+
8286
# Make this class a context manager to reliably delete the temp dir
8387
def __enter__(self) -> "GitRepo":
8488
"""
@@ -112,7 +116,14 @@ def __exit__(
112116
-------
113117
None
114118
"""
115-
self._ssh_tempdir.cleanup()
119+
self._cleanup()
120+
121+
def __del__(self):
122+
"""
123+
In case this class was not used as context manager, delete the temp
124+
dir, when it is garbage collected
125+
"""
126+
self._cleanup()
116127

117128
@property
118129
def working_dir(self) -> Union[str, os.PathLike[str]]:
@@ -169,27 +180,26 @@ def push(self) -> Any:
169180
return self._repo.remotes.origin.push()
170181

171182
@staticmethod
172-
def _make_ssh_key_path(original_path):
183+
def _make_ssh_key_path(path: Path) -> PurePosixPath:
173184
# This is some ugly workaround for git on Windows. In this case git is based on MSYS, so the
174185
# ssh command requires POSIX compatible paths, whereas otherwise we deal with Windows paths
175186
# on Windows. Thus we need to convert the Windows path to the ssh key to MSYS-POSIX.
176187
# Be aware: this is brittle as it assumes that we always use MSYS ssh on Windows. Maybe
177188
# there are other ways to setup git.
178-
path = Path(original_path)
179189
parts = path.parts
180190
if parts[0].endswith(':\\'):
181191
parts = ['/', parts[0].rstrip(':\\'), *parts[1:]]
182192
return PurePosixPath(*parts)
183193

184-
def _setup(self):
185-
# find out local repo path
186-
local_path = self._config.local_path
194+
def _setup_ssh_protocol(self) -> str:
195+
# We need a temp dir to store some ssh specific files
196+
self._ssh_tempdir = tempfile.TemporaryDirectory() # pylint: disable=consider-using-with
187197

188198
# find the ssh key and use it. We need an absolute path for this so git can find it.
189199
# no matter which is the current working directory.
190-
ssh_key = GitRepo._make_ssh_key_path(self._ssh_key)
191-
ssh_authorized_keys = GitRepo._make_ssh_key_path(
192-
self._ssh_authorized_keys)
200+
temp_dir = Path(self._ssh_tempdir.name)
201+
ssh_key = GitRepo._make_ssh_key_path(temp_dir / "ssh_key")
202+
ssh_authorized_keys = GitRepo._make_ssh_key_path(temp_dir / "ssh_authorized_keys")
193203

194204
# Get key from env and write to file
195205
private_key = os.environ.get("SSH_PRIVATE_KEY")
@@ -214,14 +224,42 @@ def _setup(self):
214224
f'-o StrictHostKeyChecking=yes'
215225
)
216226

227+
return self._config.repo_url
228+
229+
def _setup_https_protocol(self) -> str:
230+
access_token = os.environ.get("ACCESS_TOKEN")
231+
if access_token:
232+
_, rest = self._config.repo_url.split("https://", 1)
233+
repo_url = f"https://{access_token}@{rest}"
234+
else:
235+
repo_url = self._config.repo_url
236+
237+
return repo_url
238+
239+
240+
def _setup(self):
241+
# find out local repo path
242+
local_path = self._config.local_path
243+
244+
# detect git protocol and perform corrersponding setup
245+
if self._config.repo_url.startswith('git@'):
246+
repo_url = self._setup_ssh_protocol()
247+
elif self._config.repo_url.startswith('https://'):
248+
repo_url = self._setup_https_protocol()
249+
else:
250+
raise ValueError(
251+
f"The specified git repo URL '{self._config.repo_url}' "
252+
"does neither use the https nor the ssh protocol"
253+
)
254+
217255
# Initialize existing repo or clone it, if this hasn't been done yet
218256
try:
219257
repo = git.Repo(local_path)
220-
if repo.remotes.origin.url != self._config.repo_url:
221-
raise RuntimeError("Repository " + local_path + "already exists and is not a " +
222-
" clone of " + self._config.repo_url)
258+
if repo.remotes.origin.url != repo_url:
259+
raise RuntimeError(f"Repository '{local_path}' already exists and is not a "
260+
"clone of '{self._config.repo_url}'")
223261
except (git.NoSuchPathError, git.InvalidGitRepositoryError):
224-
repo = git.Repo.clone_from(self._config.repo_url, local_path)
262+
repo = git.Repo.clone_from(repo_url, local_path)
225263

226264
# set git config
227265
config_writer = repo.config_writer()

0 commit comments

Comments
 (0)