Skip to content
Merged
Changes from all 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
130 changes: 120 additions & 10 deletions .github/workflows/propagate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jobs:
fetch-depth: 0
token: ${{ steps.create_token.outputs.token }}

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Attempt merge and push, or open PR on conflict
env:
GH_TOKEN: ${{ steps.create_token.outputs.token }}
Expand All @@ -37,16 +40,7 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git checkout "$BRANCH"

if git merge origin/main --no-edit; then
echo "Merge succeeded, pushing to $BRANCH"
git push origin "$BRANCH"
else
echo "Merge conflict detected, opening PR"
git merge --abort

# Create a temporary branch for the PR
open_propagation_pr() {
TEMP_BRANCH="auto-merge/main-to-${BRANCH}-$(date +%Y%m%d%H%M%S)"
git checkout -b "$TEMP_BRANCH" origin/main

Expand All @@ -64,4 +58,120 @@ jobs:
🤖 Generated by the propagate workflow
PREOF
)"
}

unresolved_conflicts() {
git diff --name-only --diff-filter=U | sort
}

conflicts_are_modal_overlay() {
[ "$BRANCH" = "modal" ] || return 1

conflicts="$(unresolved_conflicts)"
[ -n "$conflicts" ] || return 1

while IFS= read -r file; do
case "$file" in
pyproject.toml|uv.lock) ;;
*) return 1 ;;
esac
done <<< "$conflicts"
}

add_modal_dependency() {
python3 <<'PY'
from pathlib import Path
import subprocess
import tomllib

def is_modal_dependency(dependency: str) -> bool:
return dependency.split(";", 1)[0].strip().lower().startswith("modal")


def modal_dependency_from_branch() -> str:
try:
result = subprocess.run(
["git", "show", ":2:pyproject.toml"],
check=True,
capture_output=True,
text=True,
)
except subprocess.CalledProcessError:
return "modal>=0.73.162"

dependencies = tomllib.loads(result.stdout).get("project", {}).get("dependencies", [])
return next((dependency for dependency in dependencies if is_modal_dependency(dependency)), "modal>=0.73.162")


path = Path("pyproject.toml")
text = path.read_text()
data = tomllib.loads(text)

dependencies = data.get("project", {}).get("dependencies", [])
if any(is_modal_dependency(dependency) for dependency in dependencies):
raise SystemExit(0)

lines = text.splitlines()
start = next(
(index for index, line in enumerate(lines) if line.strip() == "dependencies = ["),
None,
)
if start is None:
raise SystemExit("Could not find project dependencies in pyproject.toml")

end = next(
(index for index in range(start + 1, len(lines)) if lines[index].strip() == "]"),
None,
)
if end is None:
raise SystemExit("Could not find end of project dependencies in pyproject.toml")

insert_at = end
for index in range(start + 1, end):
if lines[index].strip().startswith('"psycopg2-binary'):
insert_at = index
break

lines.insert(insert_at, f' "{modal_dependency_from_branch()}",')
path.write_text("\n".join(lines) + "\n")
PY
}

resolve_modal_overlay() {
if unresolved_conflicts | grep -qx "pyproject.toml"; then
git checkout --theirs pyproject.toml
fi

add_modal_dependency

if unresolved_conflicts | grep -qx "uv.lock"; then
git checkout --ours uv.lock
fi

uv lock
git add pyproject.toml uv.lock

remaining_conflicts="$(unresolved_conflicts)"
if [ -n "$remaining_conflicts" ]; then
echo "Conflicts remain after modal overlay resolution:"
echo "$remaining_conflicts"
return 1
fi

git commit --no-edit
git push origin "$BRANCH"
}

git checkout "$BRANCH"

if git merge origin/main --no-edit; then
echo "Merge succeeded, pushing to $BRANCH"
git push origin "$BRANCH"
elif conflicts_are_modal_overlay; then
echo "Merge conflict only affects the modal dependency overlay; resolving automatically"
resolve_modal_overlay
else
echo "Merge conflict detected, opening PR"
git merge --abort
open_propagation_pr
fi
Loading