@@ -42,6 +42,7 @@ async function constructBranchName(environment, global, task = null) {
4242// :param reactionId: The ID of the reaction that triggered the lock request
4343// :param leaveComment: A bool indicating whether to leave a comment or not (default: true)
4444// :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
4546// :returns: The result of the createOrUpdateFileContents API call
4647async function createLock (
4748 octokit ,
@@ -53,7 +54,8 @@ async function createLock(
5354 global ,
5455 reactionId ,
5556 leaveComment ,
56- task = null
57+ task = null ,
58+ issue_number = null
5759) {
5860 core . debug ( 'attempting to create lock...' )
5961
@@ -63,7 +65,7 @@ async function createLock(
6365 // Construct the file contents for the lock file
6466 // Use the 'sticky' flag to determine whether the lock is sticky or not
6567 // 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...
6769 // ... to the same environment
6870 const lockData = {
6971 reason : reason ,
@@ -74,8 +76,9 @@ async function createLock(
7476 environment : environment ,
7577 global : global ,
7678 task : task ,
79+ pr_number : issue_number ,
7780 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 } `
7982 }
8083
8184 // Create the lock file
@@ -111,7 +114,11 @@ async function createLock(
111114 lockMsg = '**globally**'
112115 core . setOutput ( 'global_lock_claimed' , 'true' )
113116 } 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+ }
115122 }
116123
117124 const comment = dedent ( `
@@ -329,10 +336,109 @@ async function checkLockOwner(
329336 leaveComment
330337) {
331338 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 ) {
334440 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 `
336442 )
337443
338444 // 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(
345451
346452 let lockMsg
347453 if ( lockData . global === true ) {
348- lockMsg = 'global'
454+ lockMsg = '` global` deployment lock '
349455 } else {
350- lockMsg = `\`${ lockData . environment } \` environment`
456+ lockMsg = `deployment lock on \`${ lockData . environment } \` environment for the task \` ${ lockData . task } \` `
351457 }
352458
353459 const youOwnItComment = dedent ( `
354460 ### 🔒 Deployment Lock Information
355461
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 }
357463
358464 The current lock has been active for \`${ totalTime } \`
359465
@@ -373,8 +479,10 @@ async function checkLockOwner(
373479 return true
374480 }
375481
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+ }
378486
379487 // Find the total time since the lock was created
380488 const totalTime = await timeDiff (
@@ -411,11 +519,20 @@ async function checkLockOwner(
411519 )
412520 lockBranchForLink = GLOBAL_LOCK_BRANCH
413521 } 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+
415525 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+ )
417531 }
418532
533+ // Deconstruct the context to obtain the owner and repo
534+ const { owner, repo} = context . repo
535+
419536 // Construct the comment to add to the issue, alerting that the lock is already claimed
420537 const comment = dedent ( `
421538 ### ⚠️ Cannot ${ header }
@@ -427,6 +544,8 @@ async function checkLockOwner(
427544 ${ reasonText }
428545 ${ environmentText }
429546 - __Branch__: \`${ lockData . branch } \`
547+ - __PR Number__: \`#${ lockData . pr_number || 'N/A' } \`
548+ - __Task__: \`${ lockData . task || 'N/A' } \`
430549 - __Created At__: \`${ lockData . created_at } \`
431550 - __Created By__: \`${ lockData . created_by } \`
432551 - __Sticky__: \`${ lockData . sticky } \`
@@ -463,7 +582,9 @@ async function checkLockOwner(
463582// :param detailsOnly: A bool indicating whether to only return the details of the lock and not alter its state
464583// :param postDeployStep: A bool indicating whether this function is being called from the post-deploy step
465584// :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
467588// Example:
468589// {
469590// status: 'owner' | false | true | null | 'details-only',
@@ -487,7 +608,8 @@ export async function lock(
487608 detailsOnly = false ,
488609 postDeployStep = false ,
489610 leaveComment = true ,
490- task = null
611+ task = null ,
612+ issue_number = null
491613) {
492614 var global
493615
@@ -496,6 +618,9 @@ export async function lock(
496618 core . debug ( `lock() called with environment: ${ environment } ` )
497619 core . debug ( `lock() called with detailsOnly: ${ detailsOnly } ` )
498620 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 } ` )
499624
500625 // find the global flag for returning
501626 const globalFlag = core . getInput ( 'global_lock_flag' ) . trim ( )
@@ -514,12 +639,12 @@ export async function lock(
514639 }
515640
516641 // construct the branch name for the lock
517- const branchName = await constructBranchName ( environment , global , task )
642+ const lockBranchName = await constructBranchName ( environment , global , task )
518643
519644 // lock debug info
520645 core . debug ( `detected lock env: ${ environment } ` )
521646 core . debug ( `detected lock global: ${ global } ` )
522- core . debug ( `constructed lock branch name: ${ branchName } ` )
647+ core . debug ( `constructed lock branch name: ${ lockBranchName } ` )
523648
524649 // Before we can process THIS lock request, we must first check for a global lock
525650 // 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(
545670 detailsOnly === true &&
546671 postDeployStep === false
547672 ) {
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
549674 return {
550675 status : 'details-only' ,
551676 lockData : globalLockData ,
@@ -555,7 +680,7 @@ export async function lock(
555680 }
556681 }
557682
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
559684 if ( globalLockData && postDeployStep === false ) {
560685 core . debug ( 'global lock exists - checking if requestor is the owner' )
561686 // Check if the requestor is the owner of the global lock
@@ -577,31 +702,35 @@ export async function lock(
577702 )
578703 }
579704 }
705+ // -- End of global lock
580706
581707 // Check if the lock branch exists
582- const branchExists = await checkBranch ( octokit , context , branchName )
708+ const lockBranchExists = await checkBranch ( octokit , context , lockBranchName )
583709
584- if ( branchExists === false && detailsOnly === true ) {
710+ if ( lockBranchExists === false && detailsOnly === true ) {
585711 // If the lock branch doesn't exist and this is a detailsOnly request, return null
586712 core . debug ( 'lock branch does not exist and this is a detailsOnly request' )
587713 return { status : null , lockData : null , globalFlag, environment, global}
588714 }
589715
590- if ( branchExists ) {
716+ if ( lockBranchExists ) {
591717 // 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+ }
605734 }
606735 }
607736
@@ -618,7 +747,8 @@ export async function lock(
618747 global ,
619748 reactionId ,
620749 leaveComment ,
621- task
750+ task ,
751+ issue_number
622752 )
623753 return { status : true , lockData : null , globalFlag, environment, global}
624754 } else {
@@ -657,7 +787,7 @@ export async function lock(
657787 // We can now safely create the lock branch and the lock file
658788
659789 // Create the lock branch if it doesn't exist
660- await createBranch ( octokit , context , branchName )
790+ await createBranch ( octokit , context , lockBranchName )
661791
662792 // Create the lock file
663793 await createLock (
@@ -670,7 +800,8 @@ export async function lock(
670800 global ,
671801 reactionId ,
672802 leaveComment ,
673- task
803+ task ,
804+ issue_number
674805 )
675806 return { status : true , lockData : null , globalFlag, environment, global}
676807}
0 commit comments