gitpadi 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +46 -11
- package/dist/commands/contribute.js +28 -10
- package/dist/commands/issues.js +19 -1
- package/package.json +1 -1
- package/src/cli.ts +45 -11
- package/src/commands/contribute.ts +30 -10
- package/src/commands/issues.ts +18 -1
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import chalk from 'chalk';
|
|
|
8
8
|
import inquirer from 'inquirer';
|
|
9
9
|
import gradient from 'gradient-string';
|
|
10
10
|
import figlet from 'figlet';
|
|
11
|
+
import { execSync } from 'child_process';
|
|
11
12
|
import boxen from 'boxen';
|
|
12
13
|
import { createSpinner } from 'nanospinner';
|
|
13
14
|
import { Octokit } from '@octokit/rest';
|
|
@@ -257,16 +258,50 @@ async function contributorMenu() {
|
|
|
257
258
|
await contribute.viewLogs();
|
|
258
259
|
}
|
|
259
260
|
else if (action === 'submit') {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
262
|
+
const match = branch.match(/issue-(\d+)/);
|
|
263
|
+
const detectedIssue = match ? parseInt(match[1]) : undefined;
|
|
264
|
+
let detectedTitle = '';
|
|
265
|
+
if (detectedIssue) {
|
|
266
|
+
process.stdout.write(dim(` 🔍 Detected Issue #${detectedIssue} from branch... `));
|
|
267
|
+
try {
|
|
268
|
+
const { data: issueData } = await getOctokit().issues.get({
|
|
269
|
+
owner: getOwner(),
|
|
270
|
+
repo: getRepo(),
|
|
271
|
+
issue_number: detectedIssue,
|
|
272
|
+
});
|
|
273
|
+
detectedTitle = issueData.title;
|
|
274
|
+
console.log(cyan(`${detectedTitle}`));
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
console.log(dim(' (could not fetch title)'));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const { confirm } = await ask([
|
|
281
|
+
{
|
|
282
|
+
type: 'list',
|
|
283
|
+
name: 'confirm',
|
|
284
|
+
message: yellow('🚀 Ready to submit?'),
|
|
285
|
+
choices: [
|
|
286
|
+
{ name: `✅ Yes, use automated metadata`, value: 'yes' },
|
|
287
|
+
{ name: `✍️ Edit title/message manually`, value: 'edit' },
|
|
288
|
+
{ name: `⬅ Back`, value: 'back' }
|
|
289
|
+
]
|
|
290
|
+
}
|
|
264
291
|
]);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
292
|
+
if (confirm === 'back')
|
|
293
|
+
return;
|
|
294
|
+
let submissionOpts = {};
|
|
295
|
+
if (confirm === 'edit') {
|
|
296
|
+
submissionOpts = await ask([
|
|
297
|
+
{ type: 'input', name: 'title', message: 'PR Title:', default: detectedTitle ? `fix: ${detectedTitle}` : '' },
|
|
298
|
+
{ type: 'input', name: 'message', message: 'Commit Message:', default: detectedTitle ? `fix: ${detectedTitle} (#${detectedIssue})` : '' },
|
|
299
|
+
{ type: 'input', name: 'issue', message: 'Issue #:', default: detectedIssue ? detectedIssue.toString() : '' }
|
|
300
|
+
]);
|
|
301
|
+
if (submissionOpts.issue)
|
|
302
|
+
submissionOpts.issue = parseInt(submissionOpts.issue);
|
|
303
|
+
}
|
|
304
|
+
await contribute.submitPR(submissionOpts);
|
|
270
305
|
}
|
|
271
306
|
}
|
|
272
307
|
}
|
|
@@ -752,8 +787,8 @@ function setupCommander() {
|
|
|
752
787
|
.option('-i, --issue <n>', 'Issue number')
|
|
753
788
|
.action(async (o) => {
|
|
754
789
|
await contribute.submitPR({
|
|
755
|
-
title: o.title
|
|
756
|
-
message: o.message
|
|
790
|
+
title: o.title,
|
|
791
|
+
message: o.message,
|
|
757
792
|
issue: o.issue ? parseInt(o.issue) : undefined
|
|
758
793
|
});
|
|
759
794
|
});
|
|
@@ -235,9 +235,34 @@ export async function submitPR(opts) {
|
|
|
235
235
|
const owner = getOwner();
|
|
236
236
|
const repo = getRepo();
|
|
237
237
|
const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
238
|
+
// Auto-infer issue from branch name (e.g. fix/issue-303)
|
|
239
|
+
let linkedIssue = opts.issue;
|
|
240
|
+
if (!linkedIssue) {
|
|
241
|
+
const match = branch.match(/issue-(\d+)/);
|
|
242
|
+
if (match)
|
|
243
|
+
linkedIssue = parseInt(match[1]);
|
|
244
|
+
}
|
|
245
|
+
const octokit = getOctokit();
|
|
246
|
+
// 2. Fetch Issue Details for better PR Metadata
|
|
247
|
+
let issueTitle = '';
|
|
248
|
+
if (linkedIssue) {
|
|
249
|
+
spinner.text = `Fetching details for issue #${linkedIssue}...`;
|
|
250
|
+
try {
|
|
251
|
+
const { data: issueData } = await octokit.issues.get({
|
|
252
|
+
owner,
|
|
253
|
+
repo,
|
|
254
|
+
issue_number: linkedIssue,
|
|
255
|
+
});
|
|
256
|
+
issueTitle = issueData.title;
|
|
257
|
+
}
|
|
258
|
+
catch (e) {
|
|
259
|
+
// Silently fallback if issue doesn't exist or no access
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const prTitle = opts.title || (issueTitle ? `fix: ${issueTitle} (#${linkedIssue})` : 'Automated contribution via GitPadi');
|
|
263
|
+
const commitMsg = opts.message || prTitle;
|
|
238
264
|
// 1. Stage and Commit
|
|
239
265
|
spinner.text = 'Staging and committing changes...';
|
|
240
|
-
const commitMsg = opts.message || opts.title || 'Automated contribution via GitPadi';
|
|
241
266
|
try {
|
|
242
267
|
execSync('git add .', { stdio: 'pipe' });
|
|
243
268
|
// Check if there are changes to commit
|
|
@@ -250,13 +275,6 @@ export async function submitPR(opts) {
|
|
|
250
275
|
// If commit fails (e.g. no changes), we might still want to push if there are unpushed commits
|
|
251
276
|
dim(' (Note: No new changes to commit or commit failed)');
|
|
252
277
|
}
|
|
253
|
-
// Auto-infer issue from branch name (e.g. fix/issue-303)
|
|
254
|
-
let linkedIssue = opts.issue;
|
|
255
|
-
if (!linkedIssue) {
|
|
256
|
-
const match = branch.match(/issue-(\d+)/);
|
|
257
|
-
if (match)
|
|
258
|
-
linkedIssue = parseInt(match[1]);
|
|
259
|
-
}
|
|
260
278
|
spinner.text = 'Pushing to your fork...';
|
|
261
279
|
execSync(`git push origin ${branch}`, { stdio: 'pipe' });
|
|
262
280
|
spinner.text = 'Creating Pull Request...';
|
|
@@ -269,10 +287,10 @@ export async function submitPR(opts) {
|
|
|
269
287
|
catch {
|
|
270
288
|
baseBranch = 'master';
|
|
271
289
|
}
|
|
272
|
-
const { data: pr } = await
|
|
290
|
+
const { data: pr } = await octokit.pulls.create({
|
|
273
291
|
owner,
|
|
274
292
|
repo,
|
|
275
|
-
title:
|
|
293
|
+
title: prTitle,
|
|
276
294
|
body,
|
|
277
295
|
head: `${await getAuthenticatedUser()}:${branch}`,
|
|
278
296
|
base: baseBranch,
|
package/dist/commands/issues.js
CHANGED
|
@@ -94,7 +94,10 @@ function parseMarkdownIssues(content) {
|
|
|
94
94
|
for (let i = 1; i < lines.length; i++) {
|
|
95
95
|
const labelsMatch = lines[i].match(/^\*\*Labels?:\*\*\s*(.+)/i);
|
|
96
96
|
if (labelsMatch) {
|
|
97
|
-
|
|
97
|
+
// Strip backticks and trim
|
|
98
|
+
labels = labelsMatch[1].split(',')
|
|
99
|
+
.map(l => l.trim().replace(/^`+/, '').replace(/`+$/, ''))
|
|
100
|
+
.filter(Boolean);
|
|
98
101
|
}
|
|
99
102
|
else {
|
|
100
103
|
bodyLines.push(lines[i]);
|
|
@@ -196,6 +199,21 @@ export async function createIssuesFromFile(filePath, opts) {
|
|
|
196
199
|
await new Promise((r) => setTimeout(r, 1200));
|
|
197
200
|
}
|
|
198
201
|
catch (e) {
|
|
202
|
+
// If it's a 403 (unauthorized labels), try again without labels
|
|
203
|
+
if (e.status === 403 && issue.labels?.length > 0) {
|
|
204
|
+
try {
|
|
205
|
+
const { data } = await octokit.issues.create({
|
|
206
|
+
owner: getOwner(), repo: getRepo(), title: issue.title, body: issue.body,
|
|
207
|
+
});
|
|
208
|
+
created++;
|
|
209
|
+
console.log(` ${chalk.green('✅')} #${String(issue.number).padStart(2, '0')} → GitHub #${data.number} ${chalk.yellow('(labels skipped - permission)')}`);
|
|
210
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
catch (retryErr) {
|
|
214
|
+
e = retryErr;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
199
217
|
failed++;
|
|
200
218
|
console.log(` ${chalk.red('❌')} #${String(issue.number).padStart(2, '0')}: ${e.message}`);
|
|
201
219
|
}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -287,16 +287,50 @@ async function contributorMenu() {
|
|
|
287
287
|
} else if (action === 'logs') {
|
|
288
288
|
await contribute.viewLogs();
|
|
289
289
|
} else if (action === 'submit') {
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
290
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
291
|
+
const match = branch.match(/issue-(\d+)/);
|
|
292
|
+
const detectedIssue = match ? parseInt(match[1]) : undefined;
|
|
293
|
+
|
|
294
|
+
let detectedTitle = '';
|
|
295
|
+
if (detectedIssue) {
|
|
296
|
+
process.stdout.write(dim(` 🔍 Detected Issue #${detectedIssue} from branch... `));
|
|
297
|
+
try {
|
|
298
|
+
const { data: issueData } = await getOctokit().issues.get({
|
|
299
|
+
owner: getOwner(),
|
|
300
|
+
repo: getRepo(),
|
|
301
|
+
issue_number: detectedIssue,
|
|
302
|
+
});
|
|
303
|
+
detectedTitle = issueData.title;
|
|
304
|
+
console.log(cyan(`${detectedTitle}`));
|
|
305
|
+
} catch { console.log(dim(' (could not fetch title)')); }
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const { confirm } = await ask([
|
|
309
|
+
{
|
|
310
|
+
type: 'list',
|
|
311
|
+
name: 'confirm',
|
|
312
|
+
message: yellow('🚀 Ready to submit?'),
|
|
313
|
+
choices: [
|
|
314
|
+
{ name: `✅ Yes, use automated metadata`, value: 'yes' },
|
|
315
|
+
{ name: `✍️ Edit title/message manually`, value: 'edit' },
|
|
316
|
+
{ name: `⬅ Back`, value: 'back' }
|
|
317
|
+
]
|
|
318
|
+
}
|
|
294
319
|
]);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
320
|
+
|
|
321
|
+
if (confirm === 'back') return;
|
|
322
|
+
|
|
323
|
+
let submissionOpts: any = {};
|
|
324
|
+
if (confirm === 'edit') {
|
|
325
|
+
submissionOpts = await ask([
|
|
326
|
+
{ type: 'input', name: 'title', message: 'PR Title:', default: detectedTitle ? `fix: ${detectedTitle}` : '' },
|
|
327
|
+
{ type: 'input', name: 'message', message: 'Commit Message:', default: detectedTitle ? `fix: ${detectedTitle} (#${detectedIssue})` : '' },
|
|
328
|
+
{ type: 'input', name: 'issue', message: 'Issue #:', default: detectedIssue ? detectedIssue.toString() : '' }
|
|
329
|
+
]);
|
|
330
|
+
if (submissionOpts.issue) submissionOpts.issue = parseInt(submissionOpts.issue);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
await contribute.submitPR(submissionOpts);
|
|
300
334
|
}
|
|
301
335
|
}
|
|
302
336
|
}
|
|
@@ -784,8 +818,8 @@ function setupCommander(): Command {
|
|
|
784
818
|
.option('-i, --issue <n>', 'Issue number')
|
|
785
819
|
.action(async (o) => {
|
|
786
820
|
await contribute.submitPR({
|
|
787
|
-
title: o.title
|
|
788
|
-
message: o.message
|
|
821
|
+
title: o.title,
|
|
822
|
+
message: o.message,
|
|
789
823
|
issue: o.issue ? parseInt(o.issue) : undefined
|
|
790
824
|
});
|
|
791
825
|
});
|
|
@@ -268,9 +268,36 @@ export async function submitPR(opts: { title: string, body?: string, issue?: num
|
|
|
268
268
|
const repo = getRepo();
|
|
269
269
|
const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
270
270
|
|
|
271
|
+
// Auto-infer issue from branch name (e.g. fix/issue-303)
|
|
272
|
+
let linkedIssue = opts.issue;
|
|
273
|
+
if (!linkedIssue) {
|
|
274
|
+
const match = branch.match(/issue-(\d+)/);
|
|
275
|
+
if (match) linkedIssue = parseInt(match[1]);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const octokit = getOctokit();
|
|
279
|
+
|
|
280
|
+
// 2. Fetch Issue Details for better PR Metadata
|
|
281
|
+
let issueTitle = '';
|
|
282
|
+
if (linkedIssue) {
|
|
283
|
+
spinner.text = `Fetching details for issue #${linkedIssue}...`;
|
|
284
|
+
try {
|
|
285
|
+
const { data: issueData } = await octokit.issues.get({
|
|
286
|
+
owner,
|
|
287
|
+
repo,
|
|
288
|
+
issue_number: linkedIssue,
|
|
289
|
+
});
|
|
290
|
+
issueTitle = issueData.title;
|
|
291
|
+
} catch (e) {
|
|
292
|
+
// Silently fallback if issue doesn't exist or no access
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const prTitle = opts.title || (issueTitle ? `fix: ${issueTitle} (#${linkedIssue})` : 'Automated contribution via GitPadi');
|
|
297
|
+
const commitMsg = opts.message || prTitle;
|
|
298
|
+
|
|
271
299
|
// 1. Stage and Commit
|
|
272
300
|
spinner.text = 'Staging and committing changes...';
|
|
273
|
-
const commitMsg = opts.message || opts.title || 'Automated contribution via GitPadi';
|
|
274
301
|
|
|
275
302
|
try {
|
|
276
303
|
execSync('git add .', { stdio: 'pipe' });
|
|
@@ -284,13 +311,6 @@ export async function submitPR(opts: { title: string, body?: string, issue?: num
|
|
|
284
311
|
dim(' (Note: No new changes to commit or commit failed)');
|
|
285
312
|
}
|
|
286
313
|
|
|
287
|
-
// Auto-infer issue from branch name (e.g. fix/issue-303)
|
|
288
|
-
let linkedIssue = opts.issue;
|
|
289
|
-
if (!linkedIssue) {
|
|
290
|
-
const match = branch.match(/issue-(\d+)/);
|
|
291
|
-
if (match) linkedIssue = parseInt(match[1]);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
314
|
spinner.text = 'Pushing to your fork...';
|
|
295
315
|
execSync(`git push origin ${branch}`, { stdio: 'pipe' });
|
|
296
316
|
|
|
@@ -305,10 +325,10 @@ export async function submitPR(opts: { title: string, body?: string, issue?: num
|
|
|
305
325
|
baseBranch = 'master';
|
|
306
326
|
}
|
|
307
327
|
|
|
308
|
-
const { data: pr } = await
|
|
328
|
+
const { data: pr } = await octokit.pulls.create({
|
|
309
329
|
owner,
|
|
310
330
|
repo,
|
|
311
|
-
title:
|
|
331
|
+
title: prTitle,
|
|
312
332
|
body,
|
|
313
333
|
head: `${await getAuthenticatedUser()}:${branch}`,
|
|
314
334
|
base: baseBranch,
|
package/src/commands/issues.ts
CHANGED
|
@@ -109,7 +109,10 @@ function parseMarkdownIssues(content: string): { issues: any[]; labels: Record<s
|
|
|
109
109
|
for (let i = 1; i < lines.length; i++) {
|
|
110
110
|
const labelsMatch = lines[i].match(/^\*\*Labels?:\*\*\s*(.+)/i);
|
|
111
111
|
if (labelsMatch) {
|
|
112
|
-
|
|
112
|
+
// Strip backticks and trim
|
|
113
|
+
labels = labelsMatch[1].split(',')
|
|
114
|
+
.map(l => l.trim().replace(/^`+/, '').replace(/`+$/, ''))
|
|
115
|
+
.filter(Boolean);
|
|
113
116
|
} else {
|
|
114
117
|
bodyLines.push(lines[i]);
|
|
115
118
|
}
|
|
@@ -214,6 +217,20 @@ export async function createIssuesFromFile(filePath: string, opts: { dryRun?: bo
|
|
|
214
217
|
console.log(` ${chalk.green('✅')} #${String(issue.number).padStart(2, '0')} → GitHub #${data.number}`);
|
|
215
218
|
await new Promise((r) => setTimeout(r, 1200));
|
|
216
219
|
} catch (e: any) {
|
|
220
|
+
// If it's a 403 (unauthorized labels), try again without labels
|
|
221
|
+
if (e.status === 403 && issue.labels?.length > 0) {
|
|
222
|
+
try {
|
|
223
|
+
const { data } = await octokit.issues.create({
|
|
224
|
+
owner: getOwner(), repo: getRepo(), title: issue.title, body: issue.body,
|
|
225
|
+
});
|
|
226
|
+
created++;
|
|
227
|
+
console.log(` ${chalk.green('✅')} #${String(issue.number).padStart(2, '0')} → GitHub #${data.number} ${chalk.yellow('(labels skipped - permission)')}`);
|
|
228
|
+
await new Promise((r) => setTimeout(r, 1200));
|
|
229
|
+
continue;
|
|
230
|
+
} catch (retryErr: any) {
|
|
231
|
+
e = retryErr;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
217
234
|
failed++;
|
|
218
235
|
console.log(` ${chalk.red('❌')} #${String(issue.number).padStart(2, '0')}: ${e.message}`);
|
|
219
236
|
}
|