Skip to content

Commit d76b111

Browse files
authored
fix: update already present repositories (#949)
Signed-off-by: Ben Selwyn-Smith <[email protected]>
1 parent 7313899 commit d76b111

File tree

4 files changed

+82
-38
lines changed

4 files changed

+82
-38
lines changed

src/macaron/slsa_analyzer/git_url.py

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,6 @@ def check_out_repo_target(
124124
125125
This function assumes that a remote "origin" exist and checkout from that remote ONLY.
126126
127-
If ``offline_mode`` is False, this function will fetch new changes from origin remote. The fetching operation
128-
will prune and update all references (e.g. tags, branches) to make sure that the local repository is up-to-date
129-
with the repository specified by origin remote.
130-
131127
If ``offline_mode`` is True and neither ``branch_name`` nor commit are provided, this function will not do anything
132128
and the HEAD commit will be analyzed. If there are uncommitted local changes, the HEAD commit will
133129
appear in the report but the repo with local changes will be analyzed. We leave it up to the user to decide
@@ -166,36 +162,13 @@ def check_out_repo_target(
166162
bool
167163
True if succeed else False.
168164
"""
169-
if not offline_mode:
170-
# Fetch from remote origin by running ``git fetch origin --force --tags --prune --prune-tags`` inside the target
171-
# repository.
172-
# The flags `--force --tags --prune --prune-tags` are used to make sure we analyze the most up-to-date version
173-
# of the repo.
174-
# - Any modified tags in the remote repository is updated locally.
175-
# - Prune deleted branches and tags in the remote from the local repository.
176-
# References:
177-
# https://git-scm.com/docs/git-fetch
178-
# https://github.com/oracle/macaron/issues/547
165+
if not offline_mode and not branch_name and not digest:
179166
try:
180-
git_obj.repo.git.fetch(
181-
"origin",
182-
"--force",
183-
"--tags",
184-
"--prune",
185-
"--prune-tags",
186-
)
167+
git_obj.repo.git.checkout("--force", "origin/HEAD")
187168
except GitCommandError:
188-
logger.error("Unable to fetch from the origin remote of the repository.")
169+
logger.debug("Cannot checkout the default branch at origin/HEAD")
189170
return False
190171

191-
# By default check out the commit at origin/HEAD only when offline_mode is False.
192-
if not branch_name and not digest:
193-
try:
194-
git_obj.repo.git.checkout("--force", "origin/HEAD")
195-
except GitCommandError:
196-
logger.debug("Cannot checkout the default branch at origin/HEAD")
197-
return False
198-
199172
# The following checkout operations will be done whether offline_mode is False or not.
200173
if branch_name and not digest:
201174
try:
@@ -300,9 +273,9 @@ def clone_remote_repo(clone_dir: str, url: str) -> Repo | None:
300273
"""Clone the remote repository and return the `git.Repo` object for that repository.
301274
302275
If there is an existing non-empty ``clone_dir``, Macaron assumes the repository has
303-
been cloned already and cancels the clone.
304-
This could happen when multiple runs of Macaron use the same `<output_dir>`, leading
305-
to Macaron potentially trying to clone a repository multiple times.
276+
been cloned already and will attempt to fetch the latest changes. The fetching operation
277+
will prune and update all references (e.g. tags, branches) to make sure that the local
278+
repository is up-to-date with the repository specified by origin remote.
306279
307280
We use treeless partial clone to reduce clone time, by retrieving trees and blobs lazily.
308281
For more details, see the following:
@@ -337,11 +310,34 @@ def clone_remote_repo(clone_dir: str, url: str) -> Repo | None:
337310
os.rmdir(clone_dir)
338311
logger.debug("The clone dir %s is empty. It has been deleted for cloning the repo.", clone_dir)
339312
except OSError:
340-
logger.debug(
341-
"The clone dir %s is not empty. Cloning will not be proceeded.",
342-
clone_dir,
343-
)
344-
return None
313+
# Update the existing repository by running ``git fetch`` inside the existing directory.
314+
# The flags `--force --tags --prune --prune-tags` are used to make sure we analyze the most up-to-date
315+
# version of the repo.
316+
# - Any modified tags in the remote repository are updated locally.
317+
# - Deleted branches and tags in the remote repository are pruned from the local copy.
318+
# References:
319+
# https://git-scm.com/docs/git-fetch
320+
# https://github.com/oracle/macaron/issues/547
321+
try:
322+
git_env_patch = {
323+
# Setting the GIT_TERMINAL_PROMPT environment variable to ``0`` stops
324+
# ``git clone`` from prompting for login credentials.
325+
"GIT_TERMINAL_PROMPT": "0",
326+
}
327+
subprocess.run( # nosec B603
328+
args=["git", "fetch", "origin", "--force", "--tags", "--prune", "--prune-tags"],
329+
capture_output=True,
330+
cwd=clone_dir,
331+
# If `check=True` and return status code is not zero, subprocess.CalledProcessError is
332+
# raised, which we don't want. We want to check the return status code of the subprocess
333+
# later on.
334+
check=False,
335+
env=get_patched_env(git_env_patch),
336+
)
337+
return Repo(path=clone_dir)
338+
except (subprocess.CalledProcessError, OSError):
339+
logger.debug("The clone dir %s is not empty. An attempt to update it failed.")
340+
return None
345341

346342
# Ensure that the parent directory where the repo is cloned into exists.
347343
parent_dir = Path(clone_dir).parent
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
# Copyright (c) 2024 - 2024, Oracle and/or its affiliates. All rights reserved.
3+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
4+
5+
cd output/git_repos/github_com/avaje/avaje-prisms
6+
git tag --delete avaje-prisms-1.1
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* Copyright (c) 2024 - 2024, Oracle and/or its affiliates. All rights reserved. */
2+
/* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/. */
3+
4+
#include "prelude.dl"
5+
6+
Policy("test_policy", component_id, "") :-
7+
check_passed(component_id, "mcn_version_control_system_1"),
8+
is_repo_url(component_id, "https://github.com/avaje/avaje-prisms").
9+
10+
apply_policy_to("test_policy", component_id) :-
11+
is_component(component_id, "pkg:maven/io.avaje/[email protected]").
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) 2024 - 2024, Oracle and/or its affiliates. All rights reserved.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
3+
4+
description: |
5+
Ensuring previously cloned repositories are updated when newer changes are available at their remote origins.
6+
7+
tags:
8+
- macaron-python-package
9+
- macaron-docker-image
10+
11+
steps:
12+
- name: Analyze a repository
13+
kind: analyze
14+
options:
15+
command_args:
16+
- -purl
17+
- pkg:maven/io.avaje/[email protected]
18+
- name: Delete the chosen tag from the repository
19+
kind: shell
20+
options:
21+
cmd: ./modify_clone.sh
22+
- name: Analyze the repository again
23+
kind: analyze
24+
options:
25+
command_args:
26+
- -purl
27+
- pkg:maven/io.avaje/[email protected]
28+
- name: Run macaron verify-policy to verify version control check which will only pass if the tag is found
29+
kind: verify
30+
options:
31+
policy: policy.dl

0 commit comments

Comments
 (0)