@@ -192,73 +192,69 @@ jobs:
192192 echo "✅ Successfully applied PR #$number without conflicts."
193193 applied_pr_numbers+=($number)
194194 else
195- echo "❌ Conflict found with PR #$number. Attempting 'merge and regenerate ' strategy."
195+ echo "❌ Conflict found with PR #$number. Attempting 'Smart Apply ' strategy."
196196
197197 manifest_files_conflicted=$(git diff --name-only --diff-filter=U | grep -E "package.json|requirements.txt|pyproject.toml" || true)
198198
199199 if [ -n "$manifest_files_conflicted" ]; then
200- resolution_failed=false
201- for manifest_file in $manifest_files_conflicted; do
202- echo "Attempting to resolve conflict in manifest file: $manifest_file"
203-
204- git show :1:"$manifest_file" > base.txt
205- git show :2:"$manifest_file" > ours.txt
206- git show :3:"$manifest_file" > theirs.txt
207-
208- # Use --theirs to automatically resolve line-level conflicts by taking the incoming change
209- if git merge-file --theirs -p ours.txt base.txt theirs.txt > "$manifest_file"; then
210- echo "Successfully merged $manifest_file using 'theirs' strategy."
211- git add "$manifest_file"
212- else
213- echo "❌ Even with --theirs, could not merge $manifest_file. Aborting."
214- resolution_failed=true
215- break
216- fi
217- done
218-
219- if [ "$resolution_failed" = true ]; then
220- git cherry-pick --abort
221- continue
222- fi
223-
200+ echo "Conflict is in a known manifest. Proceeding with resolution."
201+
202+ # First, reset all conflicted manifest files to our current version.
203+ # This resolves the content conflict and gives us a clean slate to apply changes to.
204+ git checkout --ours $manifest_files_conflicted
205+
206+ # Now, intelligently apply the dependency changes from the incoming PR.
224207 case "$group_name" in
225208 frontend)
226- # Find the package.json that was conflicted to determine the directory.
227- # We assume only one package.json gets conflicted per cherry-pick for a single project.
228209 pkg_json_path=$(echo "$manifest_files_conflicted" | grep "package.json" | head -n1)
229- if [ -n "$pkg_json_path" ]; then
230- project_dir=$(dirname "$pkg_json_path")
231- echo "Regenerating lockfile in directory: $project_dir"
232-
233- # If a yarn.lock file exists in that directory, we treat it as a yarn project.
210+ project_dir=$(dirname "$pkg_json_path")
211+
212+ echo "Extracting dependencies from incoming PR for $pkg_json_path"
213+ # Extract dependencies from the incoming commit's package.json into a temporary file.
214+ git show FETCH_HEAD:"$pkg_json_path" | jq -r '.dependencies + .devDependencies | to_entries[] | "\(.key)@\(.value)"' > deps_to_install.txt
215+
216+ if [ -s deps_to_install.txt ]; then
234217 if [ -f "$project_dir/yarn.lock" ]; then
235- echo "Found yarn.lock, running 'yarn install'."
236- # Run yarn install from the project's directory
237- (cd "$project_dir" && yarn install --ignore-scripts)
238- # Add the regenerated lockfile to resolve its conflict
239- git add "$project_dir/yarn.lock"
240- # Otherwise, we fall back to npm.
218+ echo "Using yarn to add/update dependencies in $project_dir..."
219+ # By running `yarn add` for each package, we let yarn handle adding or updating the dependency.
220+ (cd "$project_dir" && xargs -n 1 yarn add < ../deps_to_install.txt)
221+ git add "$project_dir/yarn.lock"
241222 else
242- echo "No yarn.lock found. Running 'npm install'."
243- # Run npm install from the project's directory
244- (cd "$project_dir" && npm install --ignore-scripts)
245- # Add the regenerated lockfile to resolve its conflict
246- git add "$project_dir/package-lock.json"
223+ echo "Using npm to install/update dependencies in $project_dir..."
224+ # By running `npm install` for each package, we let npm handle adding or updating the dependency.
225+ (cd "$project_dir" && xargs -n 1 npm install < ../deps_to_install.txt)
226+ git add "$project_dir/package-lock.json"
247227 fi
228+ # Stage the manifest file that npm/yarn updated.
229+ git add "$pkg_json_path"
230+ else
231+ echo "No dependencies found to install."
248232 fi
249233 ;;
250234 backend)
251- # For python, find the requirements file that was conflicted.
252- req_file_path=$(echo "$manifest_files_conflicted" | grep "requirements.txt" | head -n1)
253- if [ -n "$req_file_path" ]; then
254- echo "Running 'pip install' for '$req_file_path' to ensure consistency."
255- pip install -r "$req_file_path"
256- # The requirements file itself was already staged in the merge loop.
257- fi
235+ req_file_path=$(echo "$manifest_files_conflicted" | grep "requirements.txt" | head -n1)
236+ if [ -n "$req_file_path" ]; then
237+ echo "Smart-merging Python requirements for '$req_file_path'"
238+
239+ # Get the incoming version of the requirements file
240+ git show FETCH_HEAD:"$req_file_path" > incoming_reqs.txt
241+
242+ # Merge the two requirements files. The sort command with -u on the key (package name)
243+ # ensures that versions from the incoming file take precedence if a package is in both files.
244+ echo "Merging existing requirements with incoming ones..."
245+ cat incoming_reqs.txt "$req_file_path" | sort -t'=' -k1,1 -u > merged_reqs.txt
246+ mv merged_reqs.txt "$req_file_path"
247+
248+ # Now install from the newly merged file to update the environment
249+ pip install -r "$req_file_path"
250+
251+ # Stage the merged requirements file
252+ git add "$req_file_path"
253+ fi
258254 ;;
259255 esac
260-
261- # Use -c to reuse the original commit message from the PR
256+
257+ # All files should now be resolved and staged. We can commit.
262258 if git commit -C FETCH_HEAD; then
263259 echo "✅ Successfully resolved conflict and applied PR #$number."
264260 applied_pr_numbers+=($number)
0 commit comments