Skip to content

Commit 9f97451

Browse files
committed
Add utilities to create/init FederatedCode data repo
Signed-off-by: Keshav Priyadarshi <[email protected]>
1 parent 23b94ad commit 9f97451

File tree

3 files changed

+106
-21
lines changed

3 files changed

+106
-21
lines changed

scanpipe/pipelines/publish_to_federatedcode.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
# Visit https://github.com/aboutcode-org/scancode.io for support and download.
2222

2323

24+
import shutil
25+
2426
from scanpipe.pipelines import Pipeline
2527
from scanpipe.pipes import federatedcode
2628

@@ -41,11 +43,12 @@ class PublishToFederatedCode(Pipeline):
4143
def steps(cls):
4244
return (
4345
cls.check_federatedcode_eligibility,
46+
cls.create_federatedcode_working_dir,
4447
cls.get_package_repository,
4548
cls.clone_repository,
4649
cls.add_scan_result,
4750
cls.commit_and_push_changes,
48-
cls.delete_local_clone,
51+
cls.delete_working_dir,
4952
)
5053

5154
def check_federatedcode_eligibility(self):
@@ -55,9 +58,12 @@ def check_federatedcode_eligibility(self):
5558
"""
5659
federatedcode.check_federatedcode_eligibility(project=self.project)
5760

61+
def create_federatedcode_working_dir(self):
62+
self.working_path = federatedcode.create_federatedcode_working_dir()
63+
5864
def get_package_repository(self):
5965
"""Get the Git repository URL and scan path for a given package."""
60-
self.package_git_repo, self.package_scan_file = (
66+
self.package_repo_name, self.package_git_repo, self.package_scan_file = (
6167
federatedcode.get_package_repository(
6268
project_purl=self.project.purl, logger=self.log
6369
)
@@ -67,6 +73,7 @@ def clone_repository(self):
6773
"""Clone repository to local_path."""
6874
self.repo = federatedcode.clone_repository(
6975
repo_url=self.package_git_repo,
76+
clone_path=self.working_path / self.package_repo_name,
7077
logger=self.log,
7178
)
7279

@@ -91,6 +98,6 @@ def commit_and_push_changes(self):
9198
f"Scan result for '{self.project.purl}' pushed to '{self.package_git_repo}'"
9299
)
93100

94-
def delete_local_clone(self):
95-
"""Remove local clone."""
96-
federatedcode.delete_local_clone(repo=self.repo)
101+
def delete_working_dir(self):
102+
"""Remove temporary working dir."""
103+
shutil.rmtree(self.working_dir)

scanpipe/pipes/federatedcode.py

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import textwrap
2828
from pathlib import Path
2929
from urllib.parse import urljoin
30+
from urllib.parse import urlparse
3031

3132
from django.conf import settings
3233

@@ -43,6 +44,17 @@
4344
logger = logging.getLogger(__name__)
4445

4546

47+
def url_exists(url, timeout=5):
48+
try:
49+
response = requests.head(url, timeout=timeout)
50+
response.raise_for_status()
51+
except requests.exceptions.RequestException as request_exception:
52+
logger.debug(f"Error while checking {url}: {request_exception}")
53+
return False
54+
55+
return response.status_code == requests.codes.ok
56+
57+
4658
def is_configured():
4759
"""Return True if the required FederatedCode settings have been set."""
4860
if all(
@@ -57,19 +69,17 @@ def is_configured():
5769
return False
5870

5971

72+
def create_federatedcode_working_dir():
73+
"""Create temporary working dir for cloning federatedcode repositories."""
74+
return Path(tempfile.mkdtemp())
75+
76+
6077
def is_available():
6178
"""Return True if the configured Git account is available."""
6279
if not is_configured():
6380
return False
6481

65-
try:
66-
response = requests.head(settings.FEDERATEDCODE_GIT_ACCOUNT_URL, timeout=5)
67-
response.raise_for_status()
68-
except requests.exceptions.RequestException as request_exception:
69-
logger.debug(f"FederatedCode is_available() error: {request_exception}")
70-
return False
71-
72-
return response.status_code == requests.codes.ok
82+
return url_exists(settings.FEDERATEDCODE_GIT_ACCOUNT_URL)
7383

7484

7585
def get_package_repository(project_purl, logger=None):
@@ -85,7 +95,7 @@ def get_package_repository(project_purl, logger=None):
8595
)
8696
package_git_repo_url = urljoin(git_account_url, f"{package_repo_name}.git")
8797

88-
return package_git_repo_url, package_scan_path
98+
return package_repo_name, package_git_repo_url, package_scan_path
8999

90100

91101
def check_federatedcode_eligibility(project):
@@ -146,27 +156,93 @@ def check_federatedcode_configured_and_available(logger=None):
146156
logger("Federatedcode repositories are configured and available.")
147157

148158

149-
def clone_repository(repo_url, logger=None):
150-
"""Clone repository to local_path."""
151-
local_dir = tempfile.mkdtemp()
159+
def clone_repository(repo_url, clone_path, logger, shallow_clone=True):
160+
"""Clone repository to clone_path."""
161+
logger(f"Cloning repository {repo_url}")
152162

153163
authenticated_repo_url = repo_url.replace(
154164
"https://",
155165
f"https://{settings.FEDERATEDCODE_GIT_SERVICE_TOKEN}@",
156166
)
157-
repo = Repo.clone_from(url=authenticated_repo_url, to_path=local_dir, depth=1)
158-
167+
clone_args = {
168+
"url": authenticated_repo_url,
169+
"to_path": clone_path,
170+
}
171+
if shallow_clone:
172+
clone_args["depth"] = 1
173+
174+
repo = Repo.clone_from(**clone_args)
159175
repo.config_writer(config_level="repository").set_value(
160176
"user", "name", settings.FEDERATEDCODE_GIT_SERVICE_NAME
161177
).release()
162-
163178
repo.config_writer(config_level="repository").set_value(
164179
"user", "email", settings.FEDERATEDCODE_GIT_SERVICE_EMAIL
165180
).release()
166181

167182
return repo
168183

169184

185+
def get_github_org(url):
186+
"""Return Org username from GitHub account URL."""
187+
github_account_url = urlparse(url)
188+
path_after_domain = github_account_url.path.lstrip("/")
189+
org_name = path_after_domain.split("/")[0]
190+
return org_name
191+
192+
193+
def create_repository(repo_name, clone_path, logger, shallow_clone=True):
194+
account_url = settings.FEDERATEDCODE_GIT_ACCOUNT_URL
195+
repo_url = urljoin(account_url, repo_name)
196+
headers = {
197+
"Authorization": f"token {settings.FEDERATEDCODE_GIT_SERVICE_TOKEN}",
198+
"Accept": "application/vnd.github+json",
199+
}
200+
201+
data = {
202+
"name": repo_name,
203+
"private": False,
204+
"auto_init": True,
205+
"CC-BY-4.0": "cc-by-4.0",
206+
}
207+
org_name = get_github_org(account_url)
208+
create_repo_api = f"https://api.github.com/orgs/{org_name}/repos"
209+
response = requests.post(
210+
create_repo_api,
211+
headers=headers,
212+
json=data,
213+
timeout=5,
214+
)
215+
response.raise_for_status()
216+
return clone_repository(
217+
repo_url=repo_url,
218+
clone_path=clone_path,
219+
shallow_clone=shallow_clone,
220+
logger=logger,
221+
)
222+
223+
224+
def get_or_create_repository(repo_name, working_path, logger, shallow_clone=True):
225+
repo_url = urljoin(settings.FEDERATEDCODE_GIT_ACCOUNT_URL, repo_name)
226+
clone_path = working_path / repo_name
227+
228+
if clone_path.exists():
229+
return False, Repo(clone_path)
230+
if url_exists(repo_url):
231+
return False, clone_repository(
232+
repo_url=repo_url,
233+
clone_path=clone_path,
234+
logger=logger,
235+
shallow_clone=shallow_clone,
236+
)
237+
238+
return True, create_repository(
239+
repo_name=repo_name,
240+
clone_path=clone_path,
241+
logger=logger,
242+
shallow_clone=shallow_clone,
243+
)
244+
245+
170246
def add_scan_result(project, repo, package_scan_file, logger=None):
171247
"""Add package scan result to the local Git repository."""
172248
relative_scan_file_path = Path(*package_scan_file.parts[1:])

scanpipe/tests/pipes/test_federatedcode.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,16 @@ def test_scanpipe_pipes_federatedcode_get_package_repository(self):
5050
version="v.1.2.3",
5151
)
5252
project_purl = "pkg:npm/[email protected]"
53+
expected_repo_name = "aboutcode-packages-npm-3f1"
5354
expected_git_repo = "https://github.com/test/aboutcode-packages-npm-3f1.git"
5455
expected_scan_path = (
5556
"aboutcode-packages-npm-3f1/npm/foobar/v1.2.3/scancodeio.json"
5657
)
57-
git_repo, scan_path = federatedcode.get_package_repository(
58+
repo_name, git_repo, scan_path = federatedcode.get_package_repository(
5859
project_purl=project_purl
5960
)
6061

62+
self.assertEqual(expected_repo_name, repo_name)
6163
self.assertEqual(expected_git_repo, git_repo)
6264
self.assertEqual(expected_scan_path, str(scan_path))
6365

0 commit comments

Comments
 (0)