Skip to content

Commit a713823

Browse files
authored
Merge pull request #210 from fairagro/github_use_https
featuring git via https
2 parents 1d43499 + 1dde467 commit a713823

4 files changed

Lines changed: 65 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 via 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: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from pathlib import Path, PurePosixPath
2525
import os
2626
import tempfile
27+
import weakref
2728

2829
import git
2930

@@ -33,7 +34,7 @@ class GitRepoConfig(NamedTuple):
3334
A class for the configuration of a GitRepo.
3435
"""
3536

36-
repo_url: Annotated[str, "The ssh URL of the git repository"]
37+
repo_url: Annotated[str, "The ssh or https URL of the git repository"]
3738
local_path: Annotated[str, "The local path of the git repository"]
3839
user_name: Annotated[str, "The name of git user"]
3940
user_email: Annotated[str, "The email address of git usere"]
@@ -72,11 +73,8 @@ class GitRepo:
7273

7374
def __init__(self, config: GitRepoConfig) -> None:
7475
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"))
76+
self._ssh_tempdir = None
77+
self._finalizer = weakref.finalize(self, self.cleanup)
8078
self._repo = self._setup()
8179

8280
# Make this class a context manager to reliably delete the temp dir
@@ -112,7 +110,15 @@ def __exit__(
112110
-------
113111
None
114112
"""
115-
self._ssh_tempdir.cleanup()
113+
self.cleanup()
114+
115+
def cleanup(self) -> None:
116+
"""
117+
delete temp dir, if used
118+
"""
119+
if self._ssh_tempdir:
120+
self._ssh_tempdir.cleanup()
121+
self._ssh_tempdir = None
116122

117123
@property
118124
def working_dir(self) -> Union[str, os.PathLike[str]]:
@@ -169,27 +175,26 @@ def push(self) -> Any:
169175
return self._repo.remotes.origin.push()
170176

171177
@staticmethod
172-
def _make_ssh_key_path(original_path):
178+
def _make_ssh_key_path(path: Path) -> PurePosixPath:
173179
# This is some ugly workaround for git on Windows. In this case git is based on MSYS, so the
174180
# ssh command requires POSIX compatible paths, whereas otherwise we deal with Windows paths
175181
# on Windows. Thus we need to convert the Windows path to the ssh key to MSYS-POSIX.
176182
# Be aware: this is brittle as it assumes that we always use MSYS ssh on Windows. Maybe
177183
# there are other ways to setup git.
178-
path = Path(original_path)
179184
parts = path.parts
180185
if parts[0].endswith(':\\'):
181186
parts = ['/', parts[0].rstrip(':\\'), *parts[1:]]
182187
return PurePosixPath(*parts)
183188

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

188193
# find the ssh key and use it. We need an absolute path for this so git can find it.
189194
# 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)
195+
temp_dir = Path(self._ssh_tempdir.name)
196+
ssh_key = GitRepo._make_ssh_key_path(temp_dir / "ssh_key")
197+
ssh_authorized_keys = GitRepo._make_ssh_key_path(temp_dir / "ssh_authorized_keys")
193198

194199
# Get key from env and write to file
195200
private_key = os.environ.get("SSH_PRIVATE_KEY")
@@ -214,14 +219,42 @@ def _setup(self):
214219
f'-o StrictHostKeyChecking=yes'
215220
)
216221

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

226259
# set git config
227260
config_writer = repo.config_writer()

0 commit comments

Comments
 (0)