Skip to content

Commit 8af0d46

Browse files
authored
fix: compare BASE with merged state instead of HEAD directly
2 parents 183b7e5 + 19dfe78 commit 8af0d46

File tree

4 files changed

+101
-22
lines changed

4 files changed

+101
-22
lines changed

Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
FROM alpine:3.21.2
1+
FROM alpine:3.21.3
22

33
RUN apk update && apk --no-cache add bash curl git
44

55
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
66

7-
ARG KUSTOMIZE=5.4.3
7+
ARG KUSTOMIZE=5.6.0
88
RUN curl -sL https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE}/kustomize_v${KUSTOMIZE}_linux_amd64.tar.gz | \
99
tar xz && mv kustomize /usr/local/bin/kustomize
1010

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ build:
1616
docker buildx build --build-arg BUILDPLATFORM=$(BUILDPLATFORM) --build-arg TARGETARCH=$(GOARCH) -t local/$(PROJNAME) .
1717

1818
scan: build
19-
trivy --light -s "UNKNOWN,MEDIUM,HIGH,CRITICAL" --exit-code 1 local/$(PROJNAME)
19+
trivy image -s "UNKNOWN,MEDIUM,HIGH,CRITICAL" --exit-code 1 local/$(PROJNAME)

README.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ Managing Kubernetes configurations across multiple environments and PRs can be c
1313
## Features
1414

1515
- Automatically builds Kustomize configurations from both PR branches
16-
- Generates a diff between base and head configurations
16+
- Generates a diff showing what would actually change upon merge (not just differences between branches)
17+
- Intelligently handles parallel changes to base and feature branches
1718
- Configurable root directory and search depth for Kustomize files
1819
- Commits must meet [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
1920
- Automated with GitHub Actions ([commit-lint](https://github.com/conventional-changelog/commitlint/#what-is-commitlint))
@@ -22,6 +23,16 @@ Managing Kubernetes configurations across multiple environments and PRs can be c
2223
- Commits must be signed with [Developer Certificate of Origin (DCO)](https://developercertificate.org/)
2324
- Automated with GitHub App ([DCO](https://github.com/apps/dco))
2425

26+
## How It Works
27+
Unlike simple diff tools, this action:
28+
29+
1. Builds Kustomize output from the base branch
30+
1. Creates a temporary merge of the head branch into base (simulating the actual PR merge)
31+
1. Builds Kustomize output from the merged state
32+
1. Compares these outputs to show exactly what would change after merging
33+
34+
This approach correctly handles cases where changes have been made to the base branch in parallel to the feature branch, avoiding misleading diffs that incorrectly suggest changes would be reverted.
35+
2536
## Inputs
2637

2738
| Name | Description | Required | Default |

kustdiff

+86-18
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,22 @@ function safe_filename() {
3939
echo "$1" | sed 's/[^a-zA-Z0-9.]/_/g'
4040
}
4141

42-
function build {
42+
function build_ref {
4343
local ref="$1"
44-
local safe_ref=$(safe_filename "$ref")
44+
local output_dir="$2"
4545
echo "Checking out ref: $ref"
4646
git checkout "$ref" --quiet
47-
mkdir -p "$TMP_DIR/$safe_ref"
47+
mkdir -p "$output_dir"
4848
for envpath in $(get_targets); do
4949
local relative_path="${envpath#$ROOT_DIR/}"
5050
local safe_path=$(safe_filename "$relative_path")
51-
local output_file="$TMP_DIR/$safe_ref/${safe_path}.yaml"
51+
local output_file="$output_dir/${safe_path}.yaml"
5252
echo "Running kustomize for $envpath"
5353
kustomize build "$envpath" -o "$output_file"
5454

55-
# debug_log "Contents of $output_file:"
56-
# debug_log "$(cat "$output_file")"
57-
# debug_log "End of $output_file"
58-
# debug_log "------------------------------------"
55+
if [ "$DEBUG" = "true" ]; then
56+
debug_log "Built kustomize for $envpath to $output_file"
57+
fi
5958
done
6059
}
6160

@@ -64,30 +63,99 @@ function main {
6463
validate_root_dir
6564
validate_max_depth
6665

67-
git config --global --add safe.directory "$GITHUB_WORKSPACE"
68-
local diff escaped_output output
69-
build "$INPUT_HEAD_REF"
70-
build "$INPUT_BASE_REF"
66+
# Set up git identity for merge operations
67+
git config --global user.email "github-actions@github.com" || true
68+
git config --global user.name "GitHub Actions" || true
69+
70+
git config --global --add safe.directory "$GITHUB_WORKSPACE" || true
71+
72+
# Save current state to restore later
73+
local current_branch
74+
current_branch=$(git rev-parse --abbrev-ref HEAD || echo "detached")
75+
76+
# Check if the branch exists locally or as a remote reference
77+
function resolve_branch_ref() {
78+
local branch_name="$1"
79+
80+
# First check if it's a local branch
81+
if git show-ref --verify --quiet "refs/heads/$branch_name"; then
82+
echo "$branch_name"
83+
return 0
84+
fi
85+
86+
# Next check if it's a remote branch
87+
if git show-ref --verify --quiet "refs/remotes/origin/$branch_name"; then
88+
echo "origin/$branch_name"
89+
return 0
90+
fi
91+
92+
# Finally check if it's a valid commit SHA
93+
if git cat-file -e "$branch_name^{commit}" 2>/dev/null; then
94+
echo "$branch_name"
95+
return 0
96+
fi
97+
98+
# If we get here, we couldn't resolve the reference
99+
echo "Error: Could not resolve reference: $branch_name"
100+
return 1
101+
}
102+
103+
# Resolve the BASE and HEAD references
104+
local base_ref_resolved
105+
base_ref_resolved=$(resolve_branch_ref "$INPUT_BASE_REF") || exit 1
106+
debug_log "Resolved base reference: $base_ref_resolved (from $INPUT_BASE_REF)"
107+
108+
# Build BASE output
109+
local safe_base_ref=$(safe_filename "$INPUT_BASE_REF")
110+
local base_output_dir="$TMP_DIR/base"
111+
build_ref "$base_ref_resolved" "$base_output_dir"
112+
113+
# Resolve HEAD reference
114+
local head_ref_resolved
115+
head_ref_resolved=$(resolve_branch_ref "$INPUT_HEAD_REF") || exit 1
116+
debug_log "Resolved head reference: $head_ref_resolved (from $INPUT_HEAD_REF)"
117+
118+
# Create a temporary merge branch
119+
local merge_branch="temp-merge-$RANDOM"
120+
git checkout -b "$merge_branch" "$base_ref_resolved" --quiet
121+
122+
debug_log "Creating temporary merge of $head_ref_resolved into $base_ref_resolved (via $merge_branch)"
123+
124+
# Attempt to merge HEAD into BASE
125+
if ! git merge "$head_ref_resolved" --quiet; then
126+
echo "Merge conflict detected. Cannot automatically merge $INPUT_HEAD_REF into $INPUT_BASE_REF."
127+
git merge --abort || true
128+
git checkout "$current_branch" --quiet || git checkout "$base_ref_resolved" --quiet || true
129+
exit 1
130+
fi
71131

72-
local safe_head_ref=$(safe_dirname "$INPUT_HEAD_REF")
73-
local safe_base_ref=$(safe_dirname "$INPUT_BASE_REF")
132+
# Build merged output
133+
local merged_output_dir="$TMP_DIR/merged"
134+
build_ref "$merge_branch" "$merged_output_dir"
74135

136+
# Compare outputs
75137
set +e
76-
diff=$(git diff --no-index "$TMP_DIR/$safe_base_ref" "$TMP_DIR/$safe_head_ref")
138+
diff=$(git diff --no-index "$base_output_dir" "$merged_output_dir" 2>&1)
139+
local diff_exit_code=$?
77140

141+
debug_log "Git diff exit code: $diff_exit_code"
78142
debug_log "Git diff output:"
79143
debug_log "$diff"
80144
debug_log "End of git diff output"
81145
debug_log "------------------------------------"
82146

83-
if [[ -z "$diff" ]]; then
84-
output="No differences found between $INPUT_BASE_REF and $INPUT_HEAD_REF"
147+
# Clean up temporary branches
148+
git checkout "$current_branch" --quiet || git checkout "$base_ref_resolved" --quiet || true
149+
git branch -D "$merge_branch" --quiet || true
150+
151+
if [[ $diff_exit_code -eq 0 ]]; then
152+
output="No differences found in kustomize output after merging $INPUT_HEAD_REF into $INPUT_BASE_REF"
85153
else
86154
# Just pass through the raw git diff output
87155
output="$diff"
88156
fi
89157

90-
escaped_output=${output//$'\n'/'%0A'}
158+
local escaped_output=${output//$'\n'/'%0A'}
91159

92160
if [ ${#escaped_output} -gt 65000 ]; then
93161
escaped_output="Output is greater than 65000 characters, and therefore too large to print as a github comment."

0 commit comments

Comments
 (0)