Skip to content

Commit c411702

Browse files
feat: enhance git command handling with improved error and success messages
- Added detailed error and success embeds for the `!git clone` and `!git pull` commands to improve user feedback. - Implemented a method to convert verbose template messages into a git-style diff format for clearer change representation. - Enhanced the handling of warnings and errors during template application, ensuring users receive comprehensive updates on repository status.
1 parent f3e5c2d commit c411702

File tree

1 file changed

+181
-15
lines changed

1 file changed

+181
-15
lines changed

src/gitcord/cogs/admin.py

Lines changed: 181 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
clean_webpage_text,
2323
parse_category_config_from_str,
2424
parse_channel_config_from_str,
25+
create_error_embed,
26+
create_success_embed,
2527
)
2628
from ..views.base_views import DeleteExtraObjectsView
2729
from ..utils import template_metadata
@@ -88,70 +90,234 @@ async def synccommands_prefix(self, ctx: commands.Context) -> None:
8890
ctx, "❌ Sync Failed", f"Failed to sync commands: {e}"
8991
)
9092

93+
def _convert_to_git_style_diff(self, result_msgs: list) -> str:
94+
"""Convert verbose template messages to git-style diff format."""
95+
diff_lines = []
96+
97+
for msg in result_msgs:
98+
# Skip summary lines and info messages
99+
if "**" in msg or msg.startswith("ℹ️") or "already exists" in msg:
100+
continue
101+
102+
# Parse different message types
103+
if "🔄 Updated channel:" in msg or "🔄 Updated category:" in msg:
104+
# Extract name: "🔄 Updated channel: moderator-only in Boilerplate" -> "M moderator-only"
105+
parts = msg.split(": ", 1)[1].split(" in ")
106+
name = parts[0] if parts else "unknown"
107+
diff_lines.append(f"M {name}")
108+
elif "Created channel:" in msg or "Created category:" in msg:
109+
# Extract name: "Created channel: new-channel in Category" -> "A new-channel"
110+
parts = msg.split(": ", 1)[1].split(" in ")
111+
name = parts[0] if parts else "unknown"
112+
diff_lines.append(f"A {name}")
113+
elif "Deleted channel:" in msg or "Deleted category:" in msg:
114+
# Extract name for deletions
115+
parts = msg.split(": ", 1)[1].split(" in ")
116+
name = parts[0] if parts else "unknown"
117+
diff_lines.append(f"D {name}")
118+
# Skip "Skipped" messages as they indicate no changes
119+
120+
return "\n".join(diff_lines) if diff_lines else "No changes"
121+
91122
@commands.command(name="git")
92123
@commands.has_permissions(administrator=True)
93124
async def git_command(self, ctx: commands.Context, *args):
94125
"""Handle !git clone <url> [-b branch], !git pull, and warn on others."""
95126
if not args:
96-
await ctx.send("❌ Usage: !git clone <url> [-b branch] or !git pull")
127+
embed = create_error_embed(
128+
"❌ Invalid Usage",
129+
"Usage: `!git clone <url> [-b branch]` or `!git pull`"
130+
)
131+
await ctx.send(embed=embed)
97132
return
133+
98134
cmd = args[0]
99135
guild_id = ctx.guild.id
100136
repo_dir = get_template_repo_dir(guild_id)
137+
101138
if cmd == "clone":
102139
if len(args) < 2:
103-
await ctx.send("❌ Usage: !git clone <url> [-b branch]")
140+
embed = create_error_embed(
141+
"❌ Missing Repository URL",
142+
"Usage: `!git clone <url> [-b branch]`"
143+
)
144+
await ctx.send(embed=embed)
104145
return
146+
105147
url = args[1]
106148
branch = "main"
107149
if len(args) >= 4 and args[2] == "-b":
108150
branch = args[3]
151+
109152
# Remove existing repo if present
110153
if os.path.exists(repo_dir):
111154
shutil.rmtree(repo_dir)
112155
os.makedirs(repo_dir, exist_ok=True)
156+
113157
try:
114158
result = subprocess.run([
115159
"git", "clone", "-b", branch, url, repo_dir
116160
], capture_output=True, text=True, timeout=60)
161+
117162
if result.returncode != 0:
118-
await ctx.send(f"❌ git clone failed: {result.stderr}")
163+
error_embed = create_error_embed(
164+
"❌ Git Clone Failed",
165+
f"```\n{result.stderr}\n```"
166+
)
167+
await ctx.send(embed=error_embed)
119168
return
169+
120170
# Save metadata
121171
template_metadata.save_metadata(guild_id, {
122172
"url": url,
123173
"branch": branch,
124174
"local_path": repo_dir
125175
})
126-
await ctx.send(f"✅ Cloned template repo from {url} (branch: {branch}) for this server.")
176+
177+
# Always show success
178+
success_embed = create_success_embed(
179+
"✅ Repository Cloned",
180+
f"Template repository cloned successfully\n`{url}` (branch: `{branch}`)"
181+
)
182+
await ctx.send(embed=success_embed)
183+
184+
# Show warnings if any
185+
if result.stderr.strip():
186+
warning_embed = create_embed(
187+
title="⚠️ Clone Warnings",
188+
description=f"```\n{result.stderr}\n```",
189+
color=discord.Color.orange()
190+
)
191+
await ctx.send(embed=warning_embed)
192+
193+
except subprocess.TimeoutExpired:
194+
timeout_embed = create_error_embed(
195+
"⏰ Clone Timeout",
196+
"Git clone operation timed out after 60 seconds."
197+
)
198+
await ctx.send(embed=timeout_embed)
127199
except Exception as e:
128-
await ctx.send(f"❌ git clone error: {e}")
200+
error_embed = create_error_embed(
201+
"❌ Clone Error",
202+
f"```\n{str(e)}\n```"
203+
)
204+
await ctx.send(embed=error_embed)
205+
129206
elif cmd == "pull":
130207
meta = template_metadata.load_metadata(guild_id)
131208
if not meta or not os.path.exists(meta.get("local_path", "")):
132-
await ctx.send("❌ No template repo found for this server. Run !git clone first.")
209+
embed = create_error_embed(
210+
"❌ No Template Repository",
211+
"Run `!git clone <url>` first to set up a template repository."
212+
)
213+
await ctx.send(embed=embed)
133214
return
215+
134216
try:
135217
result = subprocess.run([
136218
"git", "pull"
137219
], cwd=meta["local_path"], capture_output=True, text=True, timeout=60)
220+
138221
if result.returncode != 0:
139-
await ctx.send(f"❌ git pull failed: {result.stderr}")
222+
error_embed = create_error_embed(
223+
"❌ Git Pull Failed",
224+
f"```\n{result.stderr}\n```"
225+
)
226+
await ctx.send(embed=error_embed)
140227
return
141-
pull_msg = f"✅ git pull successful: {result.stdout}"
142-
await ctx.send("🔄 Applying template from local repo after pull...")
228+
229+
# Always show basic success
230+
git_output = result.stdout.strip()
231+
if git_output and git_output != "Already up to date.":
232+
# Show changes
233+
success_embed = create_success_embed(
234+
"✅ Repository Updated",
235+
f"```\n{git_output}\n```"
236+
)
237+
else:
238+
# Show "already up to date"
239+
success_embed = create_success_embed(
240+
"✅ Repository Up To Date",
241+
"No changes found in remote repository"
242+
)
243+
await ctx.send(embed=success_embed)
244+
245+
# Show git warnings if any
246+
if result.stderr.strip():
247+
warning_embed = create_embed(
248+
title="⚠️ Git Warnings",
249+
description=f"```\n{result.stderr}\n```",
250+
color=discord.Color.orange()
251+
)
252+
await ctx.send(embed=warning_embed)
253+
143254
try:
144255
result_msgs = await self._apply_template_from_dir(ctx.guild, meta["local_path"], ctx=ctx)
145-
for msg in result_msgs:
146-
self.logger.info(f"[git pull apply] {msg}")
147-
await ctx.send(pull_msg + "\n" + "\n".join(result_msgs))
256+
257+
# Convert to git-style diff
258+
if result_msgs:
259+
# Check for warnings/errors first
260+
warnings = [msg for msg in result_msgs if "warning" in msg.lower() or "error" in msg.lower() or "failed" in msg.lower()]
261+
262+
# Show warnings if any
263+
if warnings:
264+
template_warning_embed = create_embed(
265+
title="⚠️ Template Warnings",
266+
description=f"```\n{chr(10).join(warnings)}\n```",
267+
color=discord.Color.orange()
268+
)
269+
await ctx.send(embed=template_warning_embed)
270+
271+
# Convert to git-style diff
272+
git_diff = self._convert_to_git_style_diff(result_msgs)
273+
274+
if git_diff and git_diff != "No changes":
275+
template_changes_embed = create_success_embed(
276+
"✅ Template Applied",
277+
f"```\n{git_diff}\n```"
278+
)
279+
await ctx.send(embed=template_changes_embed)
280+
else:
281+
# No changes
282+
template_success_embed = create_success_embed(
283+
"✅ Template Applied",
284+
"No changes needed"
285+
)
286+
await ctx.send(embed=template_success_embed)
287+
else:
288+
# No template results
289+
template_success_embed = create_success_embed(
290+
"✅ Template Applied",
291+
"No output from template processing"
292+
)
293+
await ctx.send(embed=template_success_embed)
294+
148295
except Exception as e:
149296
self.logger.error(f"[git pull apply] Error: {e}", exc_info=True)
150-
await self.send_error(ctx, "❌ Template Error after pull", str(e))
297+
error_embed = create_error_embed(
298+
"❌ Template Application Failed",
299+
f"```\n{str(e)}\n```"
300+
)
301+
await ctx.send(embed=error_embed)
302+
303+
except subprocess.TimeoutExpired:
304+
timeout_embed = create_error_embed(
305+
"⏰ Pull Timeout",
306+
"Git pull operation timed out after 60 seconds."
307+
)
308+
await ctx.send(embed=timeout_embed)
151309
except Exception as e:
152-
await ctx.send(f"❌ git pull error: {e}")
310+
error_embed = create_error_embed(
311+
"❌ Pull Error",
312+
f"```\n{str(e)}\n```"
313+
)
314+
await ctx.send(embed=error_embed)
153315
else:
154-
await ctx.send("⚠️ Only 'git clone' and 'git pull' are supported for templates.")
316+
embed = create_error_embed(
317+
"⚠️ Unsupported Git Command",
318+
f"Only `git clone` and `git pull` are supported. You tried: `!git {cmd}`"
319+
)
320+
await ctx.send(embed=embed)
155321

156322
# Patch applytemplate to use local repo if present
157323
def _get_template_dir(self, folder=None, guild_id=None):

0 commit comments

Comments
 (0)