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
54 changes: 44 additions & 10 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,7 @@ def _push_version(
kind: str = "script",
app_type: Optional[str] = None,
git: Optional[Dict[str, Any]] = None,
entry_point: Optional[str] = None,
) -> None:
script = self.get_script(script_id)
if not script:
Expand All @@ -764,6 +765,8 @@ def _push_version(
entry["app_type"] = app_type
if git is not None:
entry["git"] = git
if entry_point is not None:
entry["entry"] = entry_point
versions.insert(0, entry)

# Trim versions list and delete old files
Expand Down Expand Up @@ -833,6 +836,7 @@ def snapshot_current_app_version(self, script_id: str) -> None:
kind="app",
app_type=script.get("type"),
git=script.get("git"),
entry_point=script.get("entry"),
)
logger.info("Snapshot app version for %s -> %s", script_id, version_file.name)
except Exception:
Expand Down Expand Up @@ -865,11 +869,19 @@ def upsert_script_from_text(self, name: str, content: str) -> str:
logger.info("Script %s saved from text", script_id)
return script_id

def upsert_script_from_file(self, original_name: str, content_path: Path) -> str:
stem = Path(original_name).stem
script_id = stem.strip().replace(" ", "_")
if not script_id:
script_id = f"script_{len(self.meta.get('scripts', {})) + 1}"
def upsert_script_from_file(
self,
original_name: str,
content_path: Path,
override_id: Optional[str] = None,
) -> str:
if override_id:
script_id = override_id
else:
stem = Path(original_name).stem
script_id = stem.strip().replace(" ", "_")
if not script_id:
script_id = f"script_{len(self.meta.get('scripts', {})) + 1}"

# Snapshot the current state before overwriting, then clean up old-type artifacts.
existing = self.get_script(script_id)
Expand Down Expand Up @@ -1144,7 +1156,7 @@ def get_versions(self, script_id: str) -> List[Dict[str, Any]]:
fp = str(v.get("file", "")).strip()
if ts and fp:
entry: Dict[str, Any] = {"ts": ts, "file": fp}
for extra_key in ("kind", "app_type", "git"):
for extra_key in ("kind", "app_type", "git", "entry"):
if extra_key in v:
entry[extra_key] = v[extra_key]
out.append(entry)
Expand Down Expand Up @@ -1313,7 +1325,14 @@ async def rollback_to(self, script_id: str, ts: str) -> str:
shutil.rmtree(tmp_extract, ignore_errors=True)

py_files = discover_python_files(dest_root)
entry, _ = pick_entry_from_candidates(py_files)
entry, ambiguous = pick_entry_from_candidates(py_files)
# If the snapshotted version recorded a confirmed entry point (set when the
# owner chose from an ambiguous list), honour it instead of re-running
# auto-detection — which can return None for multi-file apps and break
# subsequent start_script calls.
if chosen.get("entry"):
entry = chosen["entry"]
ambiguous = False

venv_dir = self.app_venv_dir(script_id)
venv_ok, venv_out = await create_venv(venv_dir)
Expand Down Expand Up @@ -1818,6 +1837,18 @@ async def _prepare_git_app(info: Dict[str, Any]) -> Dict[str, Any]:
repo_dir = GITREPOS_DIR / app_id
repo_url = f"https://github.com/{info['owner']}/{info['repo']}.git"

# When the existing clone points to a *different* remote (e.g. the user
# is replacing a GitHub source via "Change Source"), discard it so
# git_clone_or_pull performs a fresh clone instead of fetching the wrong
# origin.
git_config = repo_dir / ".git" / "config"
if git_config.exists():
try:
if repo_url not in git_config.read_text():
shutil.rmtree(repo_dir, ignore_errors=True)
except Exception:
logger.warning("_prepare_git_app: could not read %s — leaving repo_dir as-is", git_config)

ok, out = await git_clone_or_pull(repo_url, info.get("branch"), repo_dir)
if not ok:
return {"status": "error", "message": out}
Expand Down Expand Up @@ -2734,7 +2765,7 @@ async def on_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
outer = stash.get("staging")
if outer:
shutil.rmtree(outer, ignore_errors=True)
await query.delete()
await query.message.delete()
await present_setup_result(chat_id, context, script_id, result, f"✅ {script_id} converted to archive app.")

elif stash_action == "git":
Expand All @@ -2746,7 +2777,7 @@ async def on_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
await query.edit_message_text(f"⚙️ Converting {script_id} to GitHub app ...")
result = await script_mgr.setup_project_app(script_id, staged_root, "git", git_info)
result["app_id"] = script_id
await query.delete()
await query.message.delete()
await present_setup_result(chat_id, context, script_id, result, f"✅ {script_id} converted to GitHub app.")

elif stash_action == "script_text":
Expand All @@ -2764,7 +2795,10 @@ async def on_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non
tmp_conv.write_bytes(file_bytes)
try:
await query.edit_message_text(f"⚙️ Converting {script_id} to script ...")
full_id = script_mgr.upsert_script_from_file(original_name, tmp_conv)
# Pass override_id so the target app keeps its existing id regardless
# of what the uploaded filename was (fixes "Change Source" with a
# mismatched filename).
full_id = script_mgr.upsert_script_from_file(original_name, tmp_conv, override_id=script_id)
finally:
tmp_conv.unlink(missing_ok=True)
await query.edit_message_text(f"✅ {full_id} converted to script.")
Expand Down
Loading