mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-04-25 16:39:48 +00:00
Update fix
more enhanced UI and more details what is fixed, ect
This commit is contained in:
parent
f091409f7e
commit
b5096735ef
@ -5,6 +5,7 @@ import { logStore } from '~/lib/stores/logs';
|
|||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { Dialog, DialogRoot, DialogTitle, DialogDescription, DialogButton } from '~/components/ui/Dialog';
|
import { Dialog, DialogRoot, DialogTitle, DialogDescription, DialogButton } from '~/components/ui/Dialog';
|
||||||
import { classNames } from '~/utils/classNames';
|
import { classNames } from '~/utils/classNames';
|
||||||
|
import { Markdown } from '~/components/chat/Markdown';
|
||||||
|
|
||||||
interface UpdateProgress {
|
interface UpdateProgress {
|
||||||
stage: 'fetch' | 'pull' | 'install' | 'build' | 'complete';
|
stage: 'fetch' | 'pull' | 'install' | 'build' | 'complete';
|
||||||
@ -20,6 +21,8 @@ interface UpdateProgress {
|
|||||||
currentCommit?: string;
|
currentCommit?: string;
|
||||||
remoteCommit?: string;
|
remoteCommit?: string;
|
||||||
updateReady?: boolean;
|
updateReady?: boolean;
|
||||||
|
changelog?: string;
|
||||||
|
compareUrl?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,15 +53,52 @@ const UpdateProgressDisplay = ({ progress }: { progress: UpdateProgress }) => (
|
|||||||
{progress.details && (
|
{progress.details && (
|
||||||
<div className="mt-2 text-sm text-gray-600">
|
<div className="mt-2 text-sm text-gray-600">
|
||||||
{progress.details.changedFiles && progress.details.changedFiles.length > 0 && (
|
{progress.details.changedFiles && progress.details.changedFiles.length > 0 && (
|
||||||
<div className="mt-2">
|
<div className="mt-4">
|
||||||
<div className="font-medium">Changed Files:</div>
|
<div className="font-medium mb-2">Changed Files:</div>
|
||||||
<ul className="list-disc list-inside">
|
<div className="space-y-2">
|
||||||
{progress.details.changedFiles.map((file, index) => (
|
{/* Group files by type */}
|
||||||
<li key={index} className="ml-2">
|
{['Modified', 'Added', 'Deleted'].map((type) => {
|
||||||
{file}
|
const filesOfType = progress.details?.changedFiles?.filter((file) => file.startsWith(type)) || [];
|
||||||
</li>
|
|
||||||
))}
|
if (filesOfType.length === 0) {
|
||||||
</ul>
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={type} className="space-y-1">
|
||||||
|
<div
|
||||||
|
className={classNames('text-sm font-medium', {
|
||||||
|
'text-blue-500': type === 'Modified',
|
||||||
|
'text-green-500': type === 'Added',
|
||||||
|
'text-red-500': type === 'Deleted',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{type} ({filesOfType.length})
|
||||||
|
</div>
|
||||||
|
<div className="pl-4 space-y-1">
|
||||||
|
{filesOfType.map((file, index) => {
|
||||||
|
const fileName = file.split(': ')[1];
|
||||||
|
return (
|
||||||
|
<div key={index} className="text-sm text-bolt-elements-textSecondary flex items-center gap-2">
|
||||||
|
<div
|
||||||
|
className={classNames('w-4 h-4', {
|
||||||
|
'i-ph:pencil-simple': type === 'Modified',
|
||||||
|
'i-ph:plus': type === 'Added',
|
||||||
|
'i-ph:trash': type === 'Deleted',
|
||||||
|
'text-blue-500': type === 'Modified',
|
||||||
|
'text-green-500': type === 'Added',
|
||||||
|
'text-red-500': type === 'Deleted',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<span className="font-mono text-xs">{fileName}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{progress.details.totalSize && <div className="mt-1">Total size: {progress.details.totalSize}</div>}
|
{progress.details.totalSize && <div className="mt-1">Total size: {progress.details.totalSize}</div>}
|
||||||
@ -410,19 +450,100 @@ const UpdateTab = () => {
|
|||||||
{error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
|
{error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
|
||||||
|
|
||||||
{/* Show update source information */}
|
{/* Show update source information */}
|
||||||
<div className="mt-4 text-sm text-bolt-elements-textSecondary">
|
{updateProgress?.details?.currentCommit && updateProgress?.details?.remoteCommit && (
|
||||||
<p>
|
<div className="mt-4 text-sm text-bolt-elements-textSecondary">
|
||||||
Updates are fetched from: <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
<div className="flex items-center justify-between">
|
||||||
{isLatestBranch ? 'main' : 'stable'} branch)
|
<div>
|
||||||
</p>
|
<p>
|
||||||
{updateProgress?.details?.currentCommit && updateProgress?.details?.remoteCommit && (
|
Updates are fetched from: <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
||||||
<p className="mt-1">
|
{isLatestBranch ? 'main' : 'stable'} branch)
|
||||||
Current version: <span className="font-mono">{updateProgress.details.currentCommit}</span>
|
</p>
|
||||||
<span className="mx-2">→</span>
|
<p className="mt-1">
|
||||||
Latest version: <span className="font-mono">{updateProgress.details.remoteCommit}</span>
|
Current version: <span className="font-mono">{updateProgress.details.currentCommit}</span>
|
||||||
</p>
|
<span className="mx-2">→</span>
|
||||||
)}
|
Latest version: <span className="font-mono">{updateProgress.details.remoteCommit}</span>
|
||||||
</div>
|
</p>
|
||||||
|
</div>
|
||||||
|
{updateProgress?.details?.compareUrl && (
|
||||||
|
<a
|
||||||
|
href={updateProgress.details.compareUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
||||||
|
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
||||||
|
'hover:bg-purple-500/10 hover:text-purple-500',
|
||||||
|
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
||||||
|
'text-bolt-elements-textPrimary',
|
||||||
|
'transition-colors duration-200',
|
||||||
|
'w-fit',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="i-ph:github-logo w-4 h-4" />
|
||||||
|
View Changes on GitHub
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{updateProgress?.details?.additions !== undefined && updateProgress?.details?.deletions !== undefined && (
|
||||||
|
<div className="mt-2 flex items-center gap-2">
|
||||||
|
<div className="i-ph:git-diff text-purple-500 w-4 h-4" />
|
||||||
|
Changes: <span className="text-green-600">+{updateProgress.details.additions}</span>{' '}
|
||||||
|
<span className="text-red-600">-{updateProgress.details.deletions}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Add this before the changed files section */}
|
||||||
|
{updateProgress?.details?.changelog && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<div className="i-ph:scroll text-purple-500 w-5 h-5" />
|
||||||
|
<p className="font-medium">Changelog</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-lg p-4 overflow-auto max-h-[300px]">
|
||||||
|
<div className="prose dark:prose-invert prose-sm max-w-none">
|
||||||
|
<Markdown>{updateProgress.details.changelog}</Markdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Add this in the update status card, after the commit info */}
|
||||||
|
{updateProgress?.details?.compareUrl && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<a
|
||||||
|
href={updateProgress.details.compareUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
||||||
|
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
||||||
|
'hover:bg-purple-500/10 hover:text-purple-500',
|
||||||
|
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
||||||
|
'text-bolt-elements-textPrimary',
|
||||||
|
'transition-colors duration-200',
|
||||||
|
'w-fit',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="i-ph:github-logo w-4 h-4" />
|
||||||
|
View Changes on GitHub
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<p className="font-medium mb-2">Changes in this Update:</p>
|
||||||
|
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-lg p-4 overflow-auto max-h-[400px]">
|
||||||
|
<div className="prose dark:prose-invert prose-sm max-w-none">
|
||||||
|
{updateProgress.details.commitMessages.map((section, index) => (
|
||||||
|
<Markdown key={index}>{section}</Markdown>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Update dialog */}
|
{/* Update dialog */}
|
||||||
@ -435,40 +556,58 @@ const UpdateTab = () => {
|
|||||||
A new version is available from <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
A new version is available from <span className="font-mono">stackblitz-labs/bolt.diy</span> (
|
||||||
{isLatestBranch ? 'main' : 'stable'} branch)
|
{isLatestBranch ? 'main' : 'stable'} branch)
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{updateProgress?.details?.compareUrl && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<a
|
||||||
|
href={updateProgress.details.compareUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
|
||||||
|
'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
|
||||||
|
'hover:bg-purple-500/10 hover:text-purple-500',
|
||||||
|
'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
|
||||||
|
'text-bolt-elements-textPrimary',
|
||||||
|
'transition-colors duration-200',
|
||||||
|
'w-fit',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="i-ph:github-logo w-4 h-4" />
|
||||||
|
View Changes on GitHub
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
|
{updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
|
||||||
<div className="mb-4">
|
<div className="mb-6">
|
||||||
<p className="font-medium mb-2">Commit Messages:</p>
|
<p className="font-medium mb-2">Commit Messages:</p>
|
||||||
<ul className="list-disc list-inside space-y-1">
|
<div className="bg-[#F5F5F5] dark:bg-[#1A1A1A] rounded-lg p-3 space-y-2">
|
||||||
{updateProgress.details.commitMessages.map((msg, index) => (
|
{updateProgress.details.commitMessages.map((msg, index) => (
|
||||||
<li key={index} className="text-sm text-bolt-elements-textSecondary">
|
<div key={index} className="text-sm text-bolt-elements-textSecondary flex items-start gap-2">
|
||||||
{msg}
|
<div className="i-ph:git-commit text-purple-500 w-4 h-4 mt-0.5 flex-shrink-0" />
|
||||||
</li>
|
<span>{msg}</span>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{updateProgress?.details?.changedFiles && (
|
|
||||||
<div>
|
|
||||||
<p className="font-medium mb-2">Changed Files:</p>
|
|
||||||
<ul className="list-disc list-inside space-y-1">
|
|
||||||
{updateProgress.details.changedFiles.map((file, index) => (
|
|
||||||
<li key={index} className="text-sm text-bolt-elements-textSecondary">
|
|
||||||
{file}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{updateProgress?.details?.totalSize && (
|
{updateProgress?.details?.totalSize && (
|
||||||
<p className="mt-4 text-sm text-bolt-elements-textSecondary">
|
<div className="flex items-center gap-4 text-sm text-bolt-elements-textSecondary">
|
||||||
Total size: {updateProgress.details.totalSize}
|
<div className="flex items-center gap-2">
|
||||||
</p>
|
<div className="i-ph:file text-purple-500 w-4 h-4" />
|
||||||
)}
|
Total size: {updateProgress.details.totalSize}
|
||||||
{updateProgress?.details?.additions !== undefined && updateProgress?.details?.deletions !== undefined && (
|
</div>
|
||||||
<p className="mt-2 text-sm text-bolt-elements-textSecondary">
|
{updateProgress?.details?.additions !== undefined &&
|
||||||
Changes: <span className="text-green-600">+{updateProgress.details.additions}</span>{' '}
|
updateProgress?.details?.deletions !== undefined && (
|
||||||
<span className="text-red-600">-{updateProgress.details.deletions}</span>
|
<div className="flex items-center gap-2">
|
||||||
</p>
|
<div className="i-ph:git-diff text-purple-500 w-4 h-4" />
|
||||||
|
Changes: <span className="text-green-600">+{updateProgress.details.additions}</span>{' '}
|
||||||
|
<span className="text-red-600">-{updateProgress.details.deletions}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
|
@ -24,6 +24,8 @@ interface UpdateProgress {
|
|||||||
currentCommit?: string;
|
currentCommit?: string;
|
||||||
remoteCommit?: string;
|
remoteCommit?: string;
|
||||||
updateReady?: boolean;
|
updateReady?: boolean;
|
||||||
|
changelog?: string;
|
||||||
|
compareUrl?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,9 +233,103 @@ export const action: ActionFunction = async ({ request }) => {
|
|||||||
// Get commit messages between current and remote
|
// Get commit messages between current and remote
|
||||||
try {
|
try {
|
||||||
const { stdout: logOutput } = await execAsync(
|
const { stdout: logOutput } = await execAsync(
|
||||||
`git log --oneline ${currentCommit.trim()}..${remoteCommit.trim()}`,
|
`git log --pretty=format:"%h|%s|%aI" ${currentCommit.trim()}..${remoteCommit.trim()}`,
|
||||||
);
|
);
|
||||||
commitMessages = logOutput.split('\n').filter(Boolean);
|
|
||||||
|
// Parse and group commits by type
|
||||||
|
const commits = logOutput
|
||||||
|
.split('\n')
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((line) => {
|
||||||
|
const [hash, subject, timestamp] = line.split('|');
|
||||||
|
let type = 'other';
|
||||||
|
let message = subject;
|
||||||
|
|
||||||
|
if (subject.startsWith('feat:') || subject.startsWith('feature:')) {
|
||||||
|
type = 'feature';
|
||||||
|
message = subject.replace(/^feat(?:ure)?:/, '').trim();
|
||||||
|
} else if (subject.startsWith('fix:')) {
|
||||||
|
type = 'fix';
|
||||||
|
message = subject.replace(/^fix:/, '').trim();
|
||||||
|
} else if (subject.startsWith('docs:')) {
|
||||||
|
type = 'docs';
|
||||||
|
message = subject.replace(/^docs:/, '').trim();
|
||||||
|
} else if (subject.startsWith('style:')) {
|
||||||
|
type = 'style';
|
||||||
|
message = subject.replace(/^style:/, '').trim();
|
||||||
|
} else if (subject.startsWith('refactor:')) {
|
||||||
|
type = 'refactor';
|
||||||
|
message = subject.replace(/^refactor:/, '').trim();
|
||||||
|
} else if (subject.startsWith('perf:')) {
|
||||||
|
type = 'perf';
|
||||||
|
message = subject.replace(/^perf:/, '').trim();
|
||||||
|
} else if (subject.startsWith('test:')) {
|
||||||
|
type = 'test';
|
||||||
|
message = subject.replace(/^test:/, '').trim();
|
||||||
|
} else if (subject.startsWith('build:')) {
|
||||||
|
type = 'build';
|
||||||
|
message = subject.replace(/^build:/, '').trim();
|
||||||
|
} else if (subject.startsWith('ci:')) {
|
||||||
|
type = 'ci';
|
||||||
|
message = subject.replace(/^ci:/, '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
hash,
|
||||||
|
type,
|
||||||
|
message,
|
||||||
|
timestamp: new Date(timestamp),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group commits by type
|
||||||
|
const groupedCommits = commits.reduce(
|
||||||
|
(acc, commit) => {
|
||||||
|
if (!acc[commit.type]) {
|
||||||
|
acc[commit.type] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[commit.type].push(commit);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, typeof commits>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Format commit messages with emojis and timestamps
|
||||||
|
const formattedMessages = Object.entries(groupedCommits).map(([type, commits]) => {
|
||||||
|
const emoji = {
|
||||||
|
feature: '✨',
|
||||||
|
fix: '🐛',
|
||||||
|
docs: '📚',
|
||||||
|
style: '💎',
|
||||||
|
refactor: '♻️',
|
||||||
|
perf: '⚡',
|
||||||
|
test: '🧪',
|
||||||
|
build: '🛠️',
|
||||||
|
ci: '⚙️',
|
||||||
|
other: '🔍',
|
||||||
|
}[type];
|
||||||
|
|
||||||
|
const title = {
|
||||||
|
feature: 'Features',
|
||||||
|
fix: 'Bug Fixes',
|
||||||
|
docs: 'Documentation',
|
||||||
|
style: 'Styles',
|
||||||
|
refactor: 'Code Refactoring',
|
||||||
|
perf: 'Performance',
|
||||||
|
test: 'Tests',
|
||||||
|
build: 'Build',
|
||||||
|
ci: 'CI',
|
||||||
|
other: 'Other Changes',
|
||||||
|
}[type];
|
||||||
|
|
||||||
|
return `### ${emoji} ${title}\n\n${commits
|
||||||
|
.map((c) => `* ${c.message} (${c.hash.substring(0, 7)}) - ${c.timestamp.toLocaleString()}`)
|
||||||
|
.join('\n')}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
commitMessages = formattedMessages;
|
||||||
} catch {
|
} catch {
|
||||||
// Handle silently - empty commitMessages array will be used
|
// Handle silently - empty commitMessages array will be used
|
||||||
}
|
}
|
||||||
@ -260,6 +356,15 @@ export const action: ActionFunction = async ({ request }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch changelog
|
||||||
|
sendProgress({
|
||||||
|
stage: 'fetch',
|
||||||
|
message: 'Fetching changelog...',
|
||||||
|
progress: 95,
|
||||||
|
});
|
||||||
|
|
||||||
|
const changelog = await fetchChangelog(currentCommit.trim(), remoteCommit.trim());
|
||||||
|
|
||||||
// We have changes, send the details
|
// We have changes, send the details
|
||||||
sendProgress({
|
sendProgress({
|
||||||
stage: 'fetch',
|
stage: 'fetch',
|
||||||
@ -274,6 +379,8 @@ export const action: ActionFunction = async ({ request }) => {
|
|||||||
currentCommit: currentCommit.trim().substring(0, 7),
|
currentCommit: currentCommit.trim().substring(0, 7),
|
||||||
remoteCommit: remoteCommit.trim().substring(0, 7),
|
remoteCommit: remoteCommit.trim().substring(0, 7),
|
||||||
updateReady: true,
|
updateReady: true,
|
||||||
|
changelog,
|
||||||
|
compareUrl: `https://github.com/stackblitz-labs/bolt.diy/compare/${currentCommit.trim().substring(0, 7)}...${remoteCommit.trim().substring(0, 7)}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -292,6 +399,8 @@ export const action: ActionFunction = async ({ request }) => {
|
|||||||
currentCommit: currentCommit.trim().substring(0, 7),
|
currentCommit: currentCommit.trim().substring(0, 7),
|
||||||
remoteCommit: remoteCommit.trim().substring(0, 7),
|
remoteCommit: remoteCommit.trim().substring(0, 7),
|
||||||
updateReady: true,
|
updateReady: true,
|
||||||
|
changelog,
|
||||||
|
compareUrl: `https://github.com/stackblitz-labs/bolt.diy/compare/${currentCommit.trim().substring(0, 7)}...${remoteCommit.trim().substring(0, 7)}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -378,3 +487,87 @@ export const action: ActionFunction = async ({ request }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add this function to fetch the changelog
|
||||||
|
async function fetchChangelog(currentCommit: string, remoteCommit: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
// First try to get the changelog.md content
|
||||||
|
const { stdout: changelogContent } = await execAsync('git show upstream/main:changelog.md');
|
||||||
|
|
||||||
|
// If we have a changelog, return it
|
||||||
|
if (changelogContent) {
|
||||||
|
return changelogContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no changelog.md, generate one in a similar format
|
||||||
|
let changelog = '# Changes in this Update\n\n';
|
||||||
|
|
||||||
|
// Get commit messages grouped by type
|
||||||
|
const { stdout: commitLog } = await execAsync(
|
||||||
|
`git log --pretty=format:"%h|%s|%b" ${currentCommit.trim()}..${remoteCommit.trim()}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const commits = commitLog.split('\n').filter(Boolean);
|
||||||
|
const categorizedCommits: Record<string, string[]> = {
|
||||||
|
'✨ Features': [],
|
||||||
|
'🐛 Bug Fixes': [],
|
||||||
|
'📚 Documentation': [],
|
||||||
|
'💎 Styles': [],
|
||||||
|
'♻️ Code Refactoring': [],
|
||||||
|
'⚡ Performance': [],
|
||||||
|
'🧪 Tests': [],
|
||||||
|
'🛠️ Build': [],
|
||||||
|
'⚙️ CI': [],
|
||||||
|
'🔍 Other Changes': [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Categorize commits
|
||||||
|
for (const commit of commits) {
|
||||||
|
const [hash, subject] = commit.split('|');
|
||||||
|
let category = '🔍 Other Changes';
|
||||||
|
|
||||||
|
if (subject.startsWith('feat:') || subject.startsWith('feature:')) {
|
||||||
|
category = '✨ Features';
|
||||||
|
} else if (subject.startsWith('fix:')) {
|
||||||
|
category = '🐛 Bug Fixes';
|
||||||
|
} else if (subject.startsWith('docs:')) {
|
||||||
|
category = '📚 Documentation';
|
||||||
|
} else if (subject.startsWith('style:')) {
|
||||||
|
category = '💎 Styles';
|
||||||
|
} else if (subject.startsWith('refactor:')) {
|
||||||
|
category = '♻️ Code Refactoring';
|
||||||
|
} else if (subject.startsWith('perf:')) {
|
||||||
|
category = '⚡ Performance';
|
||||||
|
} else if (subject.startsWith('test:')) {
|
||||||
|
category = '🧪 Tests';
|
||||||
|
} else if (subject.startsWith('build:')) {
|
||||||
|
category = '🛠️ Build';
|
||||||
|
} else if (subject.startsWith('ci:')) {
|
||||||
|
category = '⚙️ CI';
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = subject.includes(':') ? subject.split(':')[1].trim() : subject.trim();
|
||||||
|
categorizedCommits[category].push(`* ${message} (${hash.substring(0, 7)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build changelog content
|
||||||
|
for (const [category, commits] of Object.entries(categorizedCommits)) {
|
||||||
|
if (commits.length > 0) {
|
||||||
|
changelog += `\n## ${category}\n\n${commits.join('\n')}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add stats
|
||||||
|
const { stdout: stats } = await execAsync(`git diff --shortstat ${currentCommit.trim()}..${remoteCommit.trim()}`);
|
||||||
|
|
||||||
|
if (stats) {
|
||||||
|
changelog += '\n## 📊 Stats\n\n';
|
||||||
|
changelog += `${stats.trim()}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changelog;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching changelog:', error);
|
||||||
|
return 'Unable to fetch changelog';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user