Skip to content

fix: robust changed file detection for PRs/MRs in GitHub, GitLab, and… #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 29, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "hatchling.build"

[project]
name = "socketsecurity"
version = "2.1.28"
version = "2.1.32"
requires-python = ">= 3.10"
license = {"file" = "LICENSE"}
dependencies = [
Expand Down
2 changes: 1 addition & 1 deletion socketsecurity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__author__ = 'socket.dev'
__version__ = '2.1.28'
__version__ = '2.1.32'
96 changes: 93 additions & 3 deletions socketsecurity/core/git_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ def __init__(self, path: str):
self.repo = Repo(path)
assert self.repo
self.head = self.repo.head

# Always fetch all remote refs to ensure branches exist for diffing
try:
self.repo.git.fetch('--all')
log.debug("Fetched all remote refs for diffing.")
except Exception as fetch_error:
log.debug(f"Failed to fetch all remote refs: {fetch_error}")

# Use CI environment SHA if available, otherwise fall back to current HEAD commit
github_sha = os.getenv('GITHUB_SHA')
Expand Down Expand Up @@ -128,12 +135,95 @@ def __init__(self, path: str):
self.commit_sha = self.commit.binsha
self.commit_message = self.commit.message
self.committer = self.commit.committer
self.show_files = self.repo.git.show(self.commit, name_only=True, format="%n").splitlines()
# Detect changed files in PR/MR context for GitHub, GitLab, Bitbucket; fallback to git show
self.show_files = []
detected = False
# GitHub Actions PR context
github_base_ref = os.getenv('GITHUB_BASE_REF')
github_head_ref = os.getenv('GITHUB_HEAD_REF')
github_event_name = os.getenv('GITHUB_EVENT_NAME')
github_before_sha = os.getenv('GITHUB_EVENT_BEFORE') # previous commit for push
github_sha = os.getenv('GITHUB_SHA') # current commit
if github_event_name == 'pull_request' and github_base_ref and github_head_ref:
try:
# Fetch both branches individually
self.repo.git.fetch('origin', github_base_ref)
self.repo.git.fetch('origin', github_head_ref)
# Try remote diff first
diff_range = f"origin/{github_base_ref}...origin/{github_head_ref}"
try:
diff_files = self.repo.git.diff('--name-only', diff_range)
self.show_files = diff_files.splitlines()
log.debug(f"Changed files detected via git diff (GitHub PR remote): {self.show_files}")
detected = True
except Exception as remote_error:
log.debug(f"Remote diff failed: {remote_error}")
# Try local branch diff
local_diff_range = f"{github_base_ref}...{github_head_ref}"
try:
diff_files = self.repo.git.diff('--name-only', local_diff_range)
self.show_files = diff_files.splitlines()
log.debug(f"Changed files detected via git diff (GitHub PR local): {self.show_files}")
detected = True
except Exception as local_error:
log.debug(f"Local diff failed: {local_error}")
except Exception as error:
log.debug(f"Failed to fetch branches or diff for GitHub PR: {error}")
# Commits to default branch (push events)
elif github_event_name == 'push' and github_before_sha and github_sha:
try:
diff_files = self.repo.git.diff('--name-only', f'{github_before_sha}..{github_sha}')
self.show_files = diff_files.splitlines()
log.debug(f"Changed files detected via git diff (GitHub push): {self.show_files}")
detected = True
except Exception as error:
log.debug(f"Failed to get changed files via git diff (GitHub push): {error}")
elif github_event_name == 'push':
try:
self.show_files = self.repo.git.show(self.commit, name_only=True, format="%n").splitlines()
log.debug(f"Changed files detected via git show (GitHub push fallback): {self.show_files}")
detected = True
except Exception as error:
log.debug(f"Failed to get changed files via git show (GitHub push fallback): {error}")
# GitLab CI Merge Request context
if not detected:
gitlab_target = os.getenv('CI_MERGE_REQUEST_TARGET_BRANCH_NAME')
gitlab_source = os.getenv('CI_MERGE_REQUEST_SOURCE_BRANCH_NAME')
if gitlab_target and gitlab_source:
try:
self.repo.git.fetch('origin', gitlab_target, gitlab_source)
diff_range = f"origin/{gitlab_target}...origin/{gitlab_source}"
diff_files = self.repo.git.diff('--name-only', diff_range)
self.show_files = diff_files.splitlines()
log.debug(f"Changed files detected via git diff (GitLab): {self.show_files}")
detected = True
except Exception as error:
log.debug(f"Failed to get changed files via git diff (GitLab): {error}")
# Bitbucket Pipelines PR context
if not detected:
bitbucket_pr_id = os.getenv('BITBUCKET_PR_ID')
bitbucket_source = os.getenv('BITBUCKET_BRANCH')
bitbucket_dest = os.getenv('BITBUCKET_PR_DESTINATION_BRANCH')
# BITBUCKET_BRANCH is the source branch in PR builds
if bitbucket_pr_id and bitbucket_source and bitbucket_dest:
try:
self.repo.git.fetch('origin', bitbucket_dest, bitbucket_source)
diff_range = f"origin/{bitbucket_dest}...origin/{bitbucket_source}"
diff_files = self.repo.git.diff('--name-only', diff_range)
self.show_files = diff_files.splitlines()
log.debug(f"Changed files detected via git diff (Bitbucket): {self.show_files}")
detected = True
except Exception as error:
log.debug(f"Failed to get changed files via git diff (Bitbucket): {error}")
# Fallback to git show for single commit
if not detected:
self.show_files = self.repo.git.show(self.commit, name_only=True, format="%n").splitlines()
log.debug(f"Changed files detected via git show: {self.show_files}")
self.changed_files = []
for item in self.show_files:
if item != "":
full_path = f"{self.path}/{item}"
self.changed_files.append(full_path)
# Use relative path for glob matching
self.changed_files.append(item)

# Determine if this commit is on the default branch
# This considers both GitHub Actions detached HEAD and regular branch situations
Expand Down
2 changes: 1 addition & 1 deletion workflows/github-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- uses: actions/checkout@v4
with:
# For PRs, fetch one additional commit for proper diff analysis
fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 0 }}
fetch-depth: 0

- name: Run Socket Security Scan
env:
Expand Down
Loading