Skip to content

Commit 25e5db7

Browse files
committed
Fixup
1 parent ea304d3 commit 25e5db7

File tree

3 files changed

+175
-42
lines changed

3 files changed

+175
-42
lines changed

badges/coverage.svg

Lines changed: 1 addition & 1 deletion
Loading

src/functions/lock.js

Lines changed: 170 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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
4647
async 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

Comments
 (0)