Skip to content

Commit b117ece

Browse files
Auto-resolve modal dependency overlay conflicts
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent dc2e5eb commit b117ece

1 file changed

Lines changed: 120 additions & 10 deletions

File tree

.github/workflows/propagate.yml

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ jobs:
2929
fetch-depth: 0
3030
token: ${{ steps.create_token.outputs.token }}
3131

32+
- name: Install uv
33+
uses: astral-sh/setup-uv@v4
34+
3235
- name: Attempt merge and push, or open PR on conflict
3336
env:
3437
GH_TOKEN: ${{ steps.create_token.outputs.token }}
@@ -37,16 +40,7 @@ jobs:
3740
git config user.name "github-actions[bot]"
3841
git config user.email "github-actions[bot]@users.noreply.github.com"
3942
40-
git checkout "$BRANCH"
41-
42-
if git merge origin/main --no-edit; then
43-
echo "Merge succeeded, pushing to $BRANCH"
44-
git push origin "$BRANCH"
45-
else
46-
echo "Merge conflict detected, opening PR"
47-
git merge --abort
48-
49-
# Create a temporary branch for the PR
43+
open_propagation_pr() {
5044
TEMP_BRANCH="auto-merge/main-to-${BRANCH}-$(date +%Y%m%d%H%M%S)"
5145
git checkout -b "$TEMP_BRANCH" origin/main
5246
@@ -64,4 +58,120 @@ jobs:
6458
🤖 Generated by the propagate workflow
6559
PREOF
6660
)"
61+
}
62+
63+
unresolved_conflicts() {
64+
git diff --name-only --diff-filter=U | sort
65+
}
66+
67+
conflicts_are_modal_overlay() {
68+
[ "$BRANCH" = "modal" ] || return 1
69+
70+
conflicts="$(unresolved_conflicts)"
71+
[ -n "$conflicts" ] || return 1
72+
73+
while IFS= read -r file; do
74+
case "$file" in
75+
pyproject.toml|uv.lock) ;;
76+
*) return 1 ;;
77+
esac
78+
done <<< "$conflicts"
79+
}
80+
81+
add_modal_dependency() {
82+
python3 <<'PY'
83+
from pathlib import Path
84+
import subprocess
85+
import tomllib
86+
87+
def is_modal_dependency(dependency: str) -> bool:
88+
return dependency.split(";", 1)[0].strip().lower().startswith("modal")
89+
90+
91+
def modal_dependency_from_branch() -> str:
92+
try:
93+
result = subprocess.run(
94+
["git", "show", ":2:pyproject.toml"],
95+
check=True,
96+
capture_output=True,
97+
text=True,
98+
)
99+
except subprocess.CalledProcessError:
100+
return "modal>=0.73.162"
101+
102+
dependencies = tomllib.loads(result.stdout).get("project", {}).get("dependencies", [])
103+
return next((dependency for dependency in dependencies if is_modal_dependency(dependency)), "modal>=0.73.162")
104+
105+
106+
path = Path("pyproject.toml")
107+
text = path.read_text()
108+
data = tomllib.loads(text)
109+
110+
dependencies = data.get("project", {}).get("dependencies", [])
111+
if any(is_modal_dependency(dependency) for dependency in dependencies):
112+
raise SystemExit(0)
113+
114+
lines = text.splitlines()
115+
start = next(
116+
(index for index, line in enumerate(lines) if line.strip() == "dependencies = ["),
117+
None,
118+
)
119+
if start is None:
120+
raise SystemExit("Could not find project dependencies in pyproject.toml")
121+
122+
end = next(
123+
(index for index in range(start + 1, len(lines)) if lines[index].strip() == "]"),
124+
None,
125+
)
126+
if end is None:
127+
raise SystemExit("Could not find end of project dependencies in pyproject.toml")
128+
129+
insert_at = end
130+
for index in range(start + 1, end):
131+
if lines[index].strip().startswith('"psycopg2-binary'):
132+
insert_at = index
133+
break
134+
135+
lines.insert(insert_at, f' "{modal_dependency_from_branch()}",')
136+
path.write_text("\n".join(lines) + "\n")
137+
PY
138+
}
139+
140+
resolve_modal_overlay() {
141+
if unresolved_conflicts | grep -qx "pyproject.toml"; then
142+
git checkout --theirs pyproject.toml
143+
fi
144+
145+
add_modal_dependency
146+
147+
if unresolved_conflicts | grep -qx "uv.lock"; then
148+
git checkout --ours uv.lock
149+
fi
150+
151+
uv lock
152+
git add pyproject.toml uv.lock
153+
154+
remaining_conflicts="$(unresolved_conflicts)"
155+
if [ -n "$remaining_conflicts" ]; then
156+
echo "Conflicts remain after modal overlay resolution:"
157+
echo "$remaining_conflicts"
158+
return 1
159+
fi
160+
161+
git commit --no-edit
162+
git push origin "$BRANCH"
163+
}
164+
165+
git checkout "$BRANCH"
166+
167+
if git merge origin/main --no-edit; then
168+
echo "Merge succeeded, pushing to $BRANCH"
169+
git push origin "$BRANCH"
170+
elif conflicts_are_modal_overlay; then
171+
echo "Merge conflict only affects the modal dependency overlay; resolving automatically"
172+
resolve_modal_overlay
173+
else
174+
echo "Merge conflict detected, opening PR"
175+
git merge --abort
176+
open_propagation_pr
67177
fi

0 commit comments

Comments
 (0)