@link-assistant/hive-mind 1.22.4 → 1.22.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/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/src/github-merge.lib.mjs +32 -0
- package/src/solve.auto-merge.lib.mjs +54 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.22.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fdd8eaa: Fix auto-merge failure in fork mode with permission pre-check (Issue #1226)
|
|
8
|
+
- Add fork-mode guard in `startAutoRestartUntilMergable()` to detect when `--auto-merge` cannot work
|
|
9
|
+
- Add `checkMergePermissions()` function to verify write/push/admin/maintain access before merge attempts
|
|
10
|
+
- Add permission pre-check in `attemptAutoMerge()` to fail fast when user lacks write access
|
|
11
|
+
- Post "Ready to merge" comment to PR when auto-merge cannot be performed due to permissions
|
|
12
|
+
- Prevent silent failures and infinite restart loops in fork mode scenarios
|
|
13
|
+
|
|
3
14
|
## 1.22.4
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/package.json
CHANGED
package/src/github-merge.lib.mjs
CHANGED
|
@@ -433,6 +433,37 @@ export async function checkPRMergeable(owner, repo, prNumber, verbose = false) {
|
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Check if the authenticated user has write/merge permissions on the repository
|
|
438
|
+
* @param {string} owner - Repository owner
|
|
439
|
+
* @param {string} repo - Repository name
|
|
440
|
+
* @param {boolean} verbose - Whether to log verbose output
|
|
441
|
+
* @returns {Promise<{canMerge: boolean, permission: string|null}>}
|
|
442
|
+
*/
|
|
443
|
+
export async function checkMergePermissions(owner, repo, verbose = false) {
|
|
444
|
+
try {
|
|
445
|
+
const { stdout } = await exec(`gh api repos/${owner}/${repo} --jq '.permissions'`);
|
|
446
|
+
const permissions = JSON.parse(stdout.trim());
|
|
447
|
+
|
|
448
|
+
const canMerge = permissions.admin === true || permissions.maintain === true || permissions.push === true;
|
|
449
|
+
|
|
450
|
+
if (verbose) {
|
|
451
|
+
console.log(`[VERBOSE] /merge: Merge permissions for ${owner}/${repo}: push=${permissions.push}, admin=${permissions.admin}, maintain=${permissions.maintain}`);
|
|
452
|
+
console.log(`[VERBOSE] /merge: Can merge: ${canMerge}`);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
canMerge,
|
|
457
|
+
permission: permissions.admin ? 'admin' : permissions.maintain ? 'maintain' : permissions.push ? 'push' : 'read',
|
|
458
|
+
};
|
|
459
|
+
} catch (error) {
|
|
460
|
+
if (verbose) {
|
|
461
|
+
console.log(`[VERBOSE] /merge: Error checking merge permissions: ${error.message}`);
|
|
462
|
+
}
|
|
463
|
+
return { canMerge: false, permission: null };
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
436
467
|
/**
|
|
437
468
|
* Merge a pull request
|
|
438
469
|
* @param {string} owner - Repository owner
|
|
@@ -600,6 +631,7 @@ export default {
|
|
|
600
631
|
getAllReadyPRs,
|
|
601
632
|
checkPRCIStatus,
|
|
602
633
|
checkPRMergeable,
|
|
634
|
+
checkMergePermissions,
|
|
603
635
|
mergePullRequest,
|
|
604
636
|
waitForCI,
|
|
605
637
|
parseRepositoryUrl,
|
|
@@ -33,7 +33,7 @@ const { reportError } = sentryLib;
|
|
|
33
33
|
|
|
34
34
|
// Import GitHub merge functions
|
|
35
35
|
const githubMergeLib = await import('./github-merge.lib.mjs');
|
|
36
|
-
const { checkPRCIStatus, checkPRMergeable, mergePullRequest, waitForCI } = githubMergeLib;
|
|
36
|
+
const { checkPRCIStatus, checkPRMergeable, checkMergePermissions, mergePullRequest, waitForCI } = githubMergeLib;
|
|
37
37
|
|
|
38
38
|
// Import GitHub functions for log attachment
|
|
39
39
|
const githubLib = await import('./github.lib.mjs');
|
|
@@ -506,6 +506,13 @@ export const attemptAutoMerge = async params => {
|
|
|
506
506
|
await log('');
|
|
507
507
|
await log(formatAligned('🔀', 'AUTO-MERGE:', 'Checking if PR can be merged...'));
|
|
508
508
|
|
|
509
|
+
// Issue #1226: Check merge permissions before attempting
|
|
510
|
+
const { canMerge, permission } = await checkMergePermissions(owner, repo, argv.verbose);
|
|
511
|
+
if (!canMerge) {
|
|
512
|
+
await log(formatAligned('⚠️', 'Cannot merge:', `Insufficient permissions (${permission || 'unknown'})`, 2));
|
|
513
|
+
return { success: false, reason: 'insufficient_permissions', error: `User has ${permission || 'unknown'} access, needs push/maintain/admin` };
|
|
514
|
+
}
|
|
515
|
+
|
|
509
516
|
// Wait for CI to complete (with timeout)
|
|
510
517
|
const ciWaitResult = await waitForCI(
|
|
511
518
|
owner,
|
|
@@ -564,7 +571,7 @@ export const attemptAutoMerge = async params => {
|
|
|
564
571
|
* Start auto-restart-until-mergable mode
|
|
565
572
|
*/
|
|
566
573
|
export const startAutoRestartUntilMergable = async params => {
|
|
567
|
-
const { argv } = params;
|
|
574
|
+
const { argv, owner, repo, prNumber } = params;
|
|
568
575
|
|
|
569
576
|
// Determine the mode
|
|
570
577
|
const isAutoMerge = argv.autoMerge || false;
|
|
@@ -574,13 +581,57 @@ export const startAutoRestartUntilMergable = async params => {
|
|
|
574
581
|
return null; // Neither mode enabled
|
|
575
582
|
}
|
|
576
583
|
|
|
577
|
-
if (!
|
|
584
|
+
if (!prNumber) {
|
|
578
585
|
await log('');
|
|
579
586
|
await log(formatAligned('⚠️', 'Auto-restart-until-mergable:', 'Requires a pull request'));
|
|
580
587
|
await log(formatAligned('', 'Note:', 'This mode only works with existing PRs', 2));
|
|
581
588
|
return null;
|
|
582
589
|
}
|
|
583
590
|
|
|
591
|
+
// Issue #1226: Check if running in fork mode — auto-merge cannot work without write access
|
|
592
|
+
if (argv.fork && isAutoMerge) {
|
|
593
|
+
await log('');
|
|
594
|
+
await log(formatAligned('⚠️', 'Auto-merge:', 'Cannot auto-merge fork PRs'));
|
|
595
|
+
await log(formatAligned('', 'Reason:', 'Fork contributors do not have write access to merge PRs to upstream repositories', 2));
|
|
596
|
+
await log(formatAligned('', 'Action:', 'PR is ready for manual merge by a repository maintainer', 2));
|
|
597
|
+
await log('');
|
|
598
|
+
|
|
599
|
+
// Post a comment to the PR notifying the maintainer
|
|
600
|
+
try {
|
|
601
|
+
const commentBody = `## ✅ Ready to merge\n\nThis pull request is ready to be merged. Auto-merge was requested (\`--auto-merge\`) but cannot be performed because this PR was created from a fork (no write access to the target repository).\n\nPlease merge manually.\n\n---\n*hive-mind with --auto-merge flag (fork mode)*`;
|
|
602
|
+
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
603
|
+
await log(formatAligned('', '💬 Posted merge readiness notification to PR', '', 2));
|
|
604
|
+
} catch {
|
|
605
|
+
// Don't fail if comment posting fails
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return { success: false, reason: 'fork_no_write_access' };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Issue #1226: Verify merge permissions before entering the auto-merge/restart loop
|
|
612
|
+
if (isAutoMerge && owner && repo) {
|
|
613
|
+
const { canMerge, permission } = await checkMergePermissions(owner, repo, argv.verbose);
|
|
614
|
+
if (!canMerge) {
|
|
615
|
+
await log('');
|
|
616
|
+
await log(formatAligned('⚠️', 'Auto-merge:', 'Insufficient permissions to merge'));
|
|
617
|
+
await log(formatAligned('', 'Permission level:', permission || 'unknown', 2));
|
|
618
|
+
await log(formatAligned('', 'Required:', 'push, maintain, or admin access', 2));
|
|
619
|
+
await log(formatAligned('', 'Action:', 'PR is ready for manual merge by a repository maintainer', 2));
|
|
620
|
+
await log('');
|
|
621
|
+
|
|
622
|
+
// Post a comment to the PR notifying the maintainer
|
|
623
|
+
try {
|
|
624
|
+
const commentBody = `## ✅ Ready to merge\n\nThis pull request is ready to be merged. Auto-merge was requested (\`--auto-merge\`) but cannot be performed because the authenticated user lacks write access to \`${owner}/${repo}\` (current permission: \`${permission || 'unknown'}\`).\n\nPlease merge manually.\n\n---\n*hive-mind with --auto-merge flag*`;
|
|
625
|
+
await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${commentBody}`;
|
|
626
|
+
await log(formatAligned('', '💬 Posted merge readiness notification to PR', '', 2));
|
|
627
|
+
} catch {
|
|
628
|
+
// Don't fail if comment posting fails
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return { success: false, reason: 'insufficient_permissions' };
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
584
635
|
// If --auto-merge implies --auto-restart-until-mergable
|
|
585
636
|
if (isAutoMerge) {
|
|
586
637
|
argv.autoRestartUntilMergable = true;
|