mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
Merge branch 'feature/worktrees' of github.com:AutoMaker-Org/automaker into feature/worktrees
This commit is contained in:
@@ -53,6 +53,16 @@ export function CreatePRDialog({
|
||||
// Reset state when dialog opens or worktree changes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
// Only reset form fields, not the result states (prUrl, browserUrl, showBrowserFallback)
|
||||
// These are set by the API response and should persist until dialog closes
|
||||
setTitle("");
|
||||
setBody("");
|
||||
setCommitMessage("");
|
||||
setBaseBranch("main");
|
||||
setIsDraft(false);
|
||||
setError(null);
|
||||
} else {
|
||||
// Reset everything when dialog closes
|
||||
setTitle("");
|
||||
setBody("");
|
||||
setCommitMessage("");
|
||||
@@ -98,21 +108,22 @@ export function CreatePRDialog({
|
||||
onCreated();
|
||||
} else {
|
||||
// Branch was pushed successfully
|
||||
toast.success("Branch pushed", {
|
||||
description: result.result.committed
|
||||
? `Commit ${result.result.commitHash} pushed to ${result.result.branch}`
|
||||
: `Branch ${result.result.branch} pushed`,
|
||||
});
|
||||
const prError = result.result.prError;
|
||||
const hasBrowserUrl = !!result.result.browserUrl;
|
||||
|
||||
// Check if we should show browser fallback
|
||||
if (!result.result.prCreated && result.result.browserUrl) {
|
||||
const prError = result.result.prError;
|
||||
|
||||
if (!result.result.prCreated && hasBrowserUrl) {
|
||||
// If gh CLI is not available, show browser fallback UI
|
||||
if (prError === "gh_cli_not_available" || !result.result.ghCliAvailable) {
|
||||
setBrowserUrl(result.result.browserUrl);
|
||||
setShowBrowserFallback(true);
|
||||
onCreated();
|
||||
toast.success("Branch pushed", {
|
||||
description: result.result.committed
|
||||
? `Commit ${result.result.commitHash} pushed to ${result.result.branch}`
|
||||
: `Branch ${result.result.branch} pushed`,
|
||||
});
|
||||
// Don't call onCreated() here - we want to keep the dialog open to show the browser URL
|
||||
setIsLoading(false);
|
||||
return; // Don't close dialog, show browser fallback UI
|
||||
}
|
||||
|
||||
@@ -135,16 +146,27 @@ export function CreatePRDialog({
|
||||
description: errorMessage,
|
||||
duration: 8000,
|
||||
});
|
||||
onCreated();
|
||||
// Don't call onCreated() here - we want to keep the dialog open to show the browser URL
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Show success toast for push
|
||||
toast.success("Branch pushed", {
|
||||
description: result.result.committed
|
||||
? `Commit ${result.result.commitHash} pushed to ${result.result.branch}`
|
||||
: `Branch ${result.result.branch} pushed`,
|
||||
});
|
||||
|
||||
// No browser URL available, just close
|
||||
if (!result.result.prCreated) {
|
||||
toast.info("PR not created", {
|
||||
description: "GitHub CLI (gh) may not be installed or authenticated",
|
||||
});
|
||||
if (!hasBrowserUrl) {
|
||||
toast.info("PR not created", {
|
||||
description: "Could not determine repository URL. GitHub CLI (gh) may not be installed or authenticated.",
|
||||
duration: 8000,
|
||||
});
|
||||
}
|
||||
}
|
||||
onCreated();
|
||||
onOpenChange(false);
|
||||
@@ -177,6 +199,8 @@ export function CreatePRDialog({
|
||||
|
||||
if (!worktree) return null;
|
||||
|
||||
const shouldShowBrowserFallback = showBrowserFallback && browserUrl;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleClose}>
|
||||
<DialogContent className="sm:max-w-[550px]">
|
||||
@@ -212,7 +236,7 @@ export function CreatePRDialog({
|
||||
View Pull Request
|
||||
</Button>
|
||||
</div>
|
||||
) : showBrowserFallback && browserUrl ? (
|
||||
) : shouldShowBrowserFallback ? (
|
||||
<div className="py-6 text-center space-y-4">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-500/10">
|
||||
<GitPullRequest className="w-8 h-8 text-blue-500" />
|
||||
@@ -225,17 +249,30 @@ export function CreatePRDialog({
|
||||
Click below to create a pull request in your browser.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
onClick={() => window.open(browserUrl, "_blank")}
|
||||
onClick={() => {
|
||||
if (browserUrl) {
|
||||
window.open(browserUrl, "_blank");
|
||||
}
|
||||
}}
|
||||
className="gap-2 w-full"
|
||||
size="lg"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
Create PR in Browser
|
||||
</Button>
|
||||
<div className="p-2 bg-muted rounded text-xs break-all font-mono">
|
||||
{browserUrl}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Tip: Install the GitHub CLI (<code className="bg-muted px-1 rounded">gh</code>) to create PRs directly from the app
|
||||
</p>
|
||||
<DialogFooter className="mt-4">
|
||||
<Button variant="outline" onClick={handleClose}>
|
||||
Close
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -11,13 +11,34 @@ const execAsync = promisify(exec);
|
||||
|
||||
// Extended PATH to include common tool installation locations
|
||||
// This is needed because Electron apps don't inherit the user's shell PATH
|
||||
const pathSeparator = process.platform === "win32" ? ";" : ":";
|
||||
const additionalPaths: string[] = [];
|
||||
|
||||
if (process.platform === "win32") {
|
||||
// Windows paths
|
||||
if (process.env.LOCALAPPDATA) {
|
||||
additionalPaths.push(`${process.env.LOCALAPPDATA}\\Programs\\Git\\cmd`);
|
||||
}
|
||||
if (process.env.PROGRAMFILES) {
|
||||
additionalPaths.push(`${process.env.PROGRAMFILES}\\Git\\cmd`);
|
||||
}
|
||||
if (process.env["ProgramFiles(x86)"]) {
|
||||
additionalPaths.push(`${process.env["ProgramFiles(x86)"]}\\Git\\cmd`);
|
||||
}
|
||||
} else {
|
||||
// Unix/Mac paths
|
||||
additionalPaths.push(
|
||||
"/opt/homebrew/bin", // Homebrew on Apple Silicon
|
||||
"/usr/local/bin", // Homebrew on Intel Mac, common Linux location
|
||||
"/home/linuxbrew/.linuxbrew/bin", // Linuxbrew
|
||||
`${process.env.HOME}/.local/bin`, // pipx, other user installs
|
||||
);
|
||||
}
|
||||
|
||||
const extendedPath = [
|
||||
process.env.PATH,
|
||||
"/opt/homebrew/bin", // Homebrew on Apple Silicon
|
||||
"/usr/local/bin", // Homebrew on Intel Mac, common Linux location
|
||||
"/home/linuxbrew/.linuxbrew/bin", // Linuxbrew
|
||||
`${process.env.HOME}/.local/bin`, // pipx, other user installs
|
||||
].filter(Boolean).join(":");
|
||||
...additionalPaths.filter(Boolean),
|
||||
].filter(Boolean).join(pathSeparator);
|
||||
|
||||
const execEnv = {
|
||||
...process.env,
|
||||
@@ -122,9 +143,12 @@ export function createCreatePRHandler() {
|
||||
let browserUrl: string | null = null;
|
||||
let ghCliAvailable = false;
|
||||
|
||||
// Check if gh CLI is available
|
||||
// Check if gh CLI is available (cross-platform)
|
||||
try {
|
||||
await execAsync("command -v gh", { env: execEnv });
|
||||
const checkCommand = process.platform === "win32"
|
||||
? "where gh"
|
||||
: "command -v gh";
|
||||
await execAsync(checkCommand, { env: execEnv });
|
||||
ghCliAvailable = true;
|
||||
} catch {
|
||||
ghCliAvailable = false;
|
||||
@@ -141,9 +165,22 @@ export function createCreatePRHandler() {
|
||||
});
|
||||
|
||||
// Parse remotes to detect fork workflow and get repo URL
|
||||
const lines = remotes.split("\n");
|
||||
const lines = remotes.split(/\r?\n/); // Handle both Unix and Windows line endings
|
||||
for (const line of lines) {
|
||||
const match = line.match(/^(\w+)\s+.*[:/]([^/]+)\/([^/\s]+?)(?:\.git)?\s+\(fetch\)/);
|
||||
// Try multiple patterns to match different remote URL formats
|
||||
// Pattern 1: git@github.com:owner/repo.git (fetch)
|
||||
// Pattern 2: https://github.com/owner/repo.git (fetch)
|
||||
// Pattern 3: https://github.com/owner/repo (fetch)
|
||||
let match = line.match(/^(\w+)\s+.*[:/]([^/]+)\/([^/\s]+?)(?:\.git)?\s+\(fetch\)/);
|
||||
if (!match) {
|
||||
// Try SSH format: git@github.com:owner/repo.git
|
||||
match = line.match(/^(\w+)\s+git@[^:]+:([^/]+)\/([^\s]+?)(?:\.git)?\s+\(fetch\)/);
|
||||
}
|
||||
if (!match) {
|
||||
// Try HTTPS format: https://github.com/owner/repo.git
|
||||
match = line.match(/^(\w+)\s+https?:\/\/[^/]+\/([^/]+)\/([^\s]+?)(?:\.git)?\s+\(fetch\)/);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
const [, remoteName, owner, repo] = match;
|
||||
if (remoteName === "upstream") {
|
||||
@@ -157,8 +194,30 @@ export function createCreatePRHandler() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Couldn't parse remotes
|
||||
} catch (error) {
|
||||
// Couldn't parse remotes - will try fallback
|
||||
}
|
||||
|
||||
// Fallback: Try to get repo URL from git config if remote parsing failed
|
||||
if (!repoUrl) {
|
||||
try {
|
||||
const { stdout: originUrl } = await execAsync("git config --get remote.origin.url", {
|
||||
cwd: worktreePath,
|
||||
env: execEnv,
|
||||
});
|
||||
const url = originUrl.trim();
|
||||
|
||||
// Parse URL to extract owner/repo
|
||||
// Handle both SSH (git@github.com:owner/repo.git) and HTTPS (https://github.com/owner/repo.git)
|
||||
let match = url.match(/[:/]([^/]+)\/([^/\s]+?)(?:\.git)?$/);
|
||||
if (match) {
|
||||
const [, owner, repo] = match;
|
||||
originOwner = owner;
|
||||
repoUrl = `https://github.com/${owner}/${repo}`;
|
||||
}
|
||||
} catch (error) {
|
||||
// Failed to get repo URL from config
|
||||
}
|
||||
}
|
||||
|
||||
// Construct browser URL for PR creation
|
||||
@@ -192,7 +251,6 @@ export function createCreatePRHandler() {
|
||||
prCmd += ` --title "${title.replace(/"/g, '\\"')}" --body "${body.replace(/"/g, '\\"')}" ${draftFlag}`;
|
||||
prCmd = prCmd.trim();
|
||||
|
||||
console.log("[CreatePR] Running:", prCmd);
|
||||
const { stdout: prOutput } = await execAsync(prCmd, {
|
||||
cwd: worktreePath,
|
||||
env: execEnv,
|
||||
@@ -202,11 +260,9 @@ export function createCreatePRHandler() {
|
||||
// gh CLI failed
|
||||
const err = ghError as { stderr?: string; message?: string };
|
||||
prError = err.stderr || err.message || "PR creation failed";
|
||||
console.warn("[CreatePR] gh CLI error:", prError);
|
||||
}
|
||||
} else {
|
||||
prError = "gh_cli_not_available";
|
||||
console.log("[CreatePR] gh CLI not available, returning browser URL");
|
||||
}
|
||||
|
||||
// Return result with browser fallback URL
|
||||
|
||||
Reference in New Issue
Block a user