@@ -42,6 +42,7 @@ async function constructBranchName(environment, global, task = null) {
42
42
// :param reactionId: The ID of the reaction that triggered the lock request
43
43
// :param leaveComment: A bool indicating whether to leave a comment or not (default: true)
44
44
// :param task: The task to include in the lock (optional for concurrent deployments)
45
+ // :param: issue_number: The issue/PR number associated with the comment triggering the action
45
46
// :returns: The result of the createOrUpdateFileContents API call
46
47
async function createLock (
47
48
octokit ,
@@ -53,7 +54,8 @@ async function createLock(
53
54
global ,
54
55
reactionId ,
55
56
leaveComment ,
56
- task = null
57
+ task = null ,
58
+ issue_number = null
57
59
) {
58
60
core . debug ( 'attempting to create lock...' )
59
61
@@ -63,7 +65,7 @@ async function createLock(
63
65
// Construct the file contents for the lock file
64
66
// Use the 'sticky' flag to determine whether the lock is sticky or not
65
67
// Sticky locks will persist forever unless the 'unlock on merge' mode is being utilized
66
- // non-sticky locks are tempory and only exist during the deployment process to prevent other deployments...
68
+ // non-sticky locks are temporary and only exist during the deployment process to prevent other deployments...
67
69
// ... to the same environment
68
70
const lockData = {
69
71
reason : reason ,
@@ -74,8 +76,9 @@ async function createLock(
74
76
environment : environment ,
75
77
global : global ,
76
78
task : task ,
79
+ pr_number : issue_number ,
77
80
unlock_command : await constructUnlockCommand ( environment , global , task ) ,
78
- link : `${ process . env . GITHUB_SERVER_URL } /${ owner } /${ repo } /pull/${ context . issue . number } #issuecomment-${ context . payload . comment . id } `
81
+ link : `${ process . env . GITHUB_SERVER_URL } /${ owner } /${ repo } /pull/${ issue_number } #issuecomment-${ context . payload . comment . id } `
79
82
}
80
83
81
84
// Create the lock file
@@ -111,7 +114,11 @@ async function createLock(
111
114
lockMsg = '**globally**'
112
115
core . setOutput ( 'global_lock_claimed' , 'true' )
113
116
} else {
114
- lockMsg = `to the \`${ environment } \` environment`
117
+ if ( task ) {
118
+ lockMsg = `to the \`${ environment } \` environment with task \`${ task } \``
119
+ } else {
120
+ lockMsg = `to the \`${ environment } \` environment`
121
+ }
115
122
}
116
123
117
124
const comment = dedent ( `
@@ -329,10 +336,109 @@ async function checkLockOwner(
329
336
leaveComment
330
337
) {
331
338
core . debug ( 'checking the owner of the lock...' )
332
- // If the requestor is the one who owns the lock, return 'owner'
333
- if ( lockData . created_by === context . actor ) {
339
+
340
+ // Check if the requestor is the same user
341
+ const sameUser = lockData . created_by === context . actor
342
+
343
+ // For backward compatibility, if lockData doesn't have task, only check user
344
+ if ( lockData . task === undefined ) {
345
+ core . debug (
346
+ 'Lock data has no task - using legacy ownership check (user only)'
347
+ )
348
+
349
+ if ( sameUser ) {
350
+ core . info (
351
+ `✅ ${ COLORS . highlight } ${ context . actor } ${ COLORS . reset } initiated this request and is also the owner of the current lock`
352
+ )
353
+
354
+ // If this is a '.lock' command (sticky) and not a sticky_locks deployment request, update with actionStatus as we are about to exit
355
+ if ( sticky === true && leaveComment === true ) {
356
+ // Find the total time since the lock was created
357
+ const totalTime = await timeDiff (
358
+ lockData . created_at ,
359
+ new Date ( ) . toISOString ( )
360
+ )
361
+
362
+ let lockMsg
363
+ if ( lockData . global === true ) {
364
+ lockMsg = 'global'
365
+ } else {
366
+ lockMsg = `\`${ lockData . environment } \` environment`
367
+ }
368
+
369
+ const youOwnItComment = dedent ( `
370
+ ### 🔒 Deployment Lock Information
371
+
372
+ __${ context . actor } __, you are already the owner of the current ${ lockMsg } deployment lock
373
+
374
+ The current lock has been active for \`${ totalTime } \`
375
+
376
+ > If you need to release the lock, please comment \`${ lockData . unlock_command } \`
377
+ ` )
378
+
379
+ await actionStatus (
380
+ context ,
381
+ octokit ,
382
+ reactionId ,
383
+ youOwnItComment ,
384
+ true ,
385
+ true
386
+ )
387
+ }
388
+
389
+ return true
390
+ } else {
391
+ // Different user owns the lock - provide standard lock message
392
+
393
+ // Determine the lock type for the message
394
+ var lockMsg
395
+ if ( lockData . global === true ) {
396
+ lockMsg = '`global`'
397
+ } else {
398
+ lockMsg = `\`${ lockData . environment } \` environment`
399
+ }
400
+
401
+ const lockUnavailableComment = `Sorry __${ context . actor } __, the ${ lockMsg } deployment lock is currently claimed by __${ lockData . created_by } __`
402
+
403
+ await actionStatus ( context , octokit , reactionId , lockUnavailableComment )
404
+ core . saveState ( 'bypass' , 'true' )
405
+ core . setFailed (
406
+ `Cannot claim deployment lock for ${ lockMsg } . ${ lockUnavailableComment } `
407
+ )
408
+
409
+ core . debug (
410
+ `the lock was not claimed as it is owned by ${ lockData . created_by } `
411
+ )
412
+ if ( lockData . reason === null ) {
413
+ core . debug ( 'no reason detected' )
414
+ } else {
415
+ core . debug ( `lock reason: ${ lockData . reason } ` )
416
+ }
417
+
418
+ return false
419
+ }
420
+ }
421
+
422
+ // Enhanced ownership check for newer locks that include task information
423
+ const currentBranch = context . payload . pull_request ?. head ?. ref
424
+ const sameBranch = lockData . branch === currentBranch
425
+
426
+ core . debug (
427
+ `Lock ownership check - sameUser: ${ sameUser } , sameBranch: ${ sameBranch } `
428
+ )
429
+ core . debug (
430
+ `Current actor: ${ context . actor } , Lock owner: ${ lockData . created_by } `
431
+ )
432
+ core . debug (
433
+ `Current PR: ${ context . issue . number } , Lock PR: ${ lockData . pr_number } `
434
+ )
435
+ core . debug (
436
+ `Current branch: ${ currentBranch } , Lock branch: ${ lockData . branch } `
437
+ )
438
+
439
+ if ( sameUser && sameBranch ) {
334
440
core . info (
335
- `✅ ${ COLORS . highlight } ${ context . actor } ${ COLORS . reset } initiated this request and is also the owner of the current lock `
441
+ `✅ ${ COLORS . highlight } ${ context . actor } ${ COLORS . reset } initiated this request and owns the current lock from the same PR and branch `
336
442
)
337
443
338
444
// If this is a '.lock' command (sticky) and not a sticky_locks deployment request, update with actionStatus as we are about to exit
@@ -345,15 +451,15 @@ async function checkLockOwner(
345
451
346
452
let lockMsg
347
453
if ( lockData . global === true ) {
348
- lockMsg = 'global'
454
+ lockMsg = '` global` deployment lock '
349
455
} else {
350
- lockMsg = `\`${ lockData . environment } \` environment`
456
+ lockMsg = `deployment lock on \`${ lockData . environment } \` environment for the task \` ${ lockData . task } \` `
351
457
}
352
458
353
459
const youOwnItComment = dedent ( `
354
460
### 🔒 Deployment Lock Information
355
461
356
- __${ context . actor } __, you are already the owner of the current ${ lockMsg } deployment lock
462
+ __${ context . actor } __, you are already the owner of the current ${ lockMsg }
357
463
358
464
The current lock has been active for \`${ totalTime } \`
359
465
@@ -373,8 +479,10 @@ async function checkLockOwner(
373
479
return true
374
480
}
375
481
376
- // Deconstruct the context to obtain the owner and repo
377
- const { owner, repo} = context . repo
482
+ // If same user but different PR or branch, provide specific messaging
483
+ if ( sameUser && ! sameBranch ) {
484
+ core . warning ( '⚠️ Same user but different branch - denying lock access' )
485
+ }
378
486
379
487
// Find the total time since the lock was created
380
488
const totalTime = await timeDiff (
@@ -411,11 +519,20 @@ async function checkLockOwner(
411
519
)
412
520
lockBranchForLink = GLOBAL_LOCK_BRANCH
413
521
} else {
414
- lockText = `the \`${ lockData . environment } \` environment deployment lock is currently claimed by __${ lockData . created_by } __`
522
+ const taskText = lockData . task ? ` (task: \`${ lockData . task } \`)` : ''
523
+ lockText = `the \`${ lockData . environment } \` environment deployment lock${ taskText } is currently claimed by __${ lockData . created_by } __`
524
+
415
525
environmentText = `- __Environment__: \`${ lockData . environment } \``
416
- lockBranchForLink = `${ lockData . environment } -${ LOCK_BRANCH_SUFFIX } `
526
+ lockBranchForLink = await constructBranchName (
527
+ lockData . environment ,
528
+ lockData . global ,
529
+ lockData . task
530
+ )
417
531
}
418
532
533
+ // Deconstruct the context to obtain the owner and repo
534
+ const { owner, repo} = context . repo
535
+
419
536
// Construct the comment to add to the issue, alerting that the lock is already claimed
420
537
const comment = dedent ( `
421
538
### ⚠️ Cannot ${ header }
@@ -427,6 +544,8 @@ async function checkLockOwner(
427
544
${ reasonText }
428
545
${ environmentText }
429
546
- __Branch__: \`${ lockData . branch } \`
547
+ - __PR Number__: \`#${ lockData . pr_number || 'N/A' } \`
548
+ - __Task__: \`${ lockData . task || 'N/A' } \`
430
549
- __Created At__: \`${ lockData . created_at } \`
431
550
- __Created By__: \`${ lockData . created_by } \`
432
551
- __Sticky__: \`${ lockData . sticky } \`
@@ -463,7 +582,9 @@ async function checkLockOwner(
463
582
// :param detailsOnly: A bool indicating whether to only return the details of the lock and not alter its state
464
583
// :param postDeployStep: A bool indicating whether this function is being called from the post-deploy step
465
584
// :param leaveComment: A bool indicating whether to leave a comment or not (default: true)
466
- // :returns: A lock repsponse object
585
+ // :param task: The deployment task to lock (if any)
586
+ // :param issue_number: The number of the issue being processed
587
+ // :returns: A lock response object
467
588
// Example:
468
589
// {
469
590
// status: 'owner' | false | true | null | 'details-only',
@@ -487,7 +608,8 @@ export async function lock(
487
608
detailsOnly = false ,
488
609
postDeployStep = false ,
489
610
leaveComment = true ,
490
- task = null
611
+ task = null ,
612
+ issue_number = null
491
613
) {
492
614
var global
493
615
@@ -496,6 +618,9 @@ export async function lock(
496
618
core . debug ( `lock() called with environment: ${ environment } ` )
497
619
core . debug ( `lock() called with detailsOnly: ${ detailsOnly } ` )
498
620
core . debug ( `lock() called with postDeployStep: ${ postDeployStep } ` )
621
+ core . debug ( `lock() called with leaveComment: ${ leaveComment } ` )
622
+ core . debug ( `lock() called with task: ${ task } ` )
623
+ core . debug ( `lock() called with issue_number: ${ issue_number } ` )
499
624
500
625
// find the global flag for returning
501
626
const globalFlag = core . getInput ( 'global_lock_flag' ) . trim ( )
@@ -514,12 +639,12 @@ export async function lock(
514
639
}
515
640
516
641
// construct the branch name for the lock
517
- const branchName = await constructBranchName ( environment , global , task )
642
+ const lockBranchName = await constructBranchName ( environment , global , task )
518
643
519
644
// lock debug info
520
645
core . debug ( `detected lock env: ${ environment } ` )
521
646
core . debug ( `detected lock global: ${ global } ` )
522
- core . debug ( `constructed lock branch name: ${ branchName } ` )
647
+ core . debug ( `constructed lock branch name: ${ lockBranchName } ` )
523
648
524
649
// Before we can process THIS lock request, we must first check for a global lock
525
650
// If there is a global lock, we must check if the requestor is the owner of the lock
@@ -545,7 +670,7 @@ export async function lock(
545
670
detailsOnly === true &&
546
671
postDeployStep === false
547
672
) {
548
- // If the lock file exists and this is a detailsOnly request for the global lock, return the lock data
673
+ // If the global lock file exists and this is a detailsOnly request for the global lock, return the lock data
549
674
return {
550
675
status : 'details-only' ,
551
676
lockData : globalLockData ,
@@ -555,7 +680,7 @@ export async function lock(
555
680
}
556
681
}
557
682
558
- // If the global lock exists, check if the requestor is the owner
683
+ // If the global lock exists, but it is NOT a detailsOnly request, check if the requestor is the owner
559
684
if ( globalLockData && postDeployStep === false ) {
560
685
core . debug ( 'global lock exists - checking if requestor is the owner' )
561
686
// Check if the requestor is the owner of the global lock
@@ -577,31 +702,35 @@ export async function lock(
577
702
)
578
703
}
579
704
}
705
+ // -- End of global lock
580
706
581
707
// Check if the lock branch exists
582
- const branchExists = await checkBranch ( octokit , context , branchName )
708
+ const lockBranchExists = await checkBranch ( octokit , context , lockBranchName )
583
709
584
- if ( branchExists === false && detailsOnly === true ) {
710
+ if ( lockBranchExists === false && detailsOnly === true ) {
585
711
// If the lock branch doesn't exist and this is a detailsOnly request, return null
586
712
core . debug ( 'lock branch does not exist and this is a detailsOnly request' )
587
713
return { status : null , lockData : null , globalFlag, environment, global}
588
714
}
589
715
590
- if ( branchExists ) {
716
+ if ( lockBranchExists ) {
591
717
// Check if the lock file exists
592
- const lockData = await checkLockFile ( octokit , context , branchName )
593
-
594
- if ( lockData === false && detailsOnly === true ) {
595
- // If the lock file doesn't exist and this is a detailsOnly request, return null
596
- return { status : null , lockData : null , globalFlag, environment, global}
597
- } else if ( lockData && detailsOnly ) {
598
- // If the lock file exists and this is a detailsOnly request, return the lock data
599
- return {
600
- status : 'details-only' ,
601
- lockData : lockData ,
602
- globalFlag,
603
- environment,
604
- global
718
+ const lockData = await checkLockFile ( octokit , context , lockBranchName )
719
+
720
+ // If detailsOnly request
721
+ if ( detailsOnly ) {
722
+ if ( lockData === false ) {
723
+ // If the lock file doesn't exist and this is a detailsOnly request, return null
724
+ return { status : null , lockData : null , globalFlag, environment, global}
725
+ } else {
726
+ // If the lock file exists and this is a detailsOnly request, return the lock data
727
+ return {
728
+ status : 'details-only' ,
729
+ lockData : lockData ,
730
+ globalFlag,
731
+ environment,
732
+ global
733
+ }
605
734
}
606
735
}
607
736
@@ -618,7 +747,8 @@ export async function lock(
618
747
global ,
619
748
reactionId ,
620
749
leaveComment ,
621
- task
750
+ task ,
751
+ issue_number
622
752
)
623
753
return { status : true , lockData : null , globalFlag, environment, global}
624
754
} else {
@@ -657,7 +787,7 @@ export async function lock(
657
787
// We can now safely create the lock branch and the lock file
658
788
659
789
// Create the lock branch if it doesn't exist
660
- await createBranch ( octokit , context , branchName )
790
+ await createBranch ( octokit , context , lockBranchName )
661
791
662
792
// Create the lock file
663
793
await createLock (
@@ -670,7 +800,8 @@ export async function lock(
670
800
global ,
671
801
reactionId ,
672
802
leaveComment ,
673
- task
803
+ task ,
804
+ issue_number
674
805
)
675
806
return { status : true , lockData : null , globalFlag, environment, global}
676
807
}
0 commit comments