@@ -11,6 +11,7 @@ permissions:
1111jobs :
1212 propagate :
1313 strategy :
14+ fail-fast : false
1415 matrix :
1516 branch : [modal, hetzner]
1617 runs-on : ubuntu-latest
2829 fetch-depth : 0
2930 token : ${{ steps.create_token.outputs.token }}
3031
32+ - name : Install uv
33+ uses : astral-sh/setup-uv@v4
34+
3135 - name : Attempt merge and push, or open PR on conflict
3236 env :
3337 GH_TOKEN : ${{ steps.create_token.outputs.token }}
3640 git config user.name "github-actions[bot]"
3741 git config user.email "github-actions[bot]@users.noreply.github.com"
3842
39- git checkout "$BRANCH"
40-
41- if git merge origin/main --no-edit; then
42- echo "Merge succeeded, pushing to $BRANCH"
43- git push origin "$BRANCH"
44- else
45- echo "Merge conflict detected, opening PR"
46- git merge --abort
47-
48- # Create a temporary branch for the PR
43+ open_propagation_pr() {
4944 TEMP_BRANCH="auto-merge/main-to-${BRANCH}-$(date +%Y%m%d%H%M%S)"
5045 git checkout -b "$TEMP_BRANCH" origin/main
5146
@@ -63,4 +58,120 @@ jobs:
6358 🤖 Generated by the propagate workflow
6459 PREOF
6560 )"
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
66177 fi
0 commit comments