Merge pull request #315 from github/commit-time-checks

Commit Time Checks 🔒 🕐
This commit is contained in:
Grant Birkinbine 2024-10-21 15:30:04 -07:00 коммит произвёл GitHub
Родитель 535e75d568 9bfda32f6c
Коммит e9ac229a76
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 534 добавлений и 2 удалений

Просмотреть файл

@ -0,0 +1,57 @@
import {commitSafetyChecks} from '../../src/functions/commit-safety-checks'
import * as core from '@actions/core'
const debugMock = jest.spyOn(core, 'debug').mockImplementation(() => {})
var data
var context
beforeEach(() => {
jest.clearAllMocks()
jest.spyOn(core, 'debug').mockImplementation(() => {})
context = {
payload: {
comment: {
created_at: '2024-10-15T12:00:00Z'
}
}
}
data = {
commit: {
author: {
date: '2024-10-15T11:00:00Z'
}
}
}
})
test('checks a commit and finds that it is safe (date)', async () => {
expect(await commitSafetyChecks(context, data)).toStrictEqual({
message: 'success',
status: true
})
expect(debugMock).toHaveBeenCalledWith(
'2024-10-15T12:00:00Z is not older than 2024-10-15T11:00:00Z'
)
})
test('checks a commit and finds that it is not safe (date)', async () => {
data.commit.author.date = '2024-10-15T12:00:01Z'
expect(await commitSafetyChecks(context, data)).toStrictEqual({
message:
'### ⚠️ Cannot proceed with deployment\n\nThe latest commit is not safe for deployment. It was authored after the trigger comment was created.',
status: false
})
expect(debugMock).toHaveBeenCalledWith(
'2024-10-15T12:00:00Z is older than 2024-10-15T12:00:01Z'
)
})
test('raises an error if the date format is invalid', async () => {
data.commit.author.date = '2024-10-15T12:00:uhoh'
await expect(commitSafetyChecks(context, data)).rejects.toThrow(
'Invalid date format. Please ensure the dates are valid UTC timestamps.'
)
})

Просмотреть файл

@ -14,6 +14,7 @@ import * as core from '@actions/core'
import * as isDeprecated from '../src/functions/deprecated-checks'
import * as nakedCommandCheck from '../src/functions/naked-command-check'
import * as validDeploymentOrder from '../src/functions/valid-deployment-order'
import * as commitSafetyChecks from '../src/functions/commit-safety-checks'
import {COLORS} from '../src/functions/colors'
const setOutputMock = jest.spyOn(core, 'setOutput')
@ -76,7 +77,8 @@ beforeEach(() => {
id: 123,
user: {
login: 'monalisa'
}
},
created_at: '2024-10-21T19:11:18Z'
}
}
@ -96,6 +98,17 @@ beforeEach(() => {
}),
createDeploymentStatus: jest.fn().mockImplementation(() => {
return {data: {}}
}),
getCommit: jest.fn().mockImplementation(() => {
return {
data: {
commit: {
author: {
date: '2024-10-15T12:00:00Z'
}
}
}
}
})
},
pulls: {
@ -127,6 +140,14 @@ beforeEach(() => {
sha: null
}
})
jest
.spyOn(commitSafetyChecks, 'commitSafetyChecks')
.mockImplementation(() => {
return {
status: true,
message: 'success'
}
})
jest
.spyOn(validDeploymentOrder, 'validDeploymentOrder')
.mockImplementation(() => {
@ -809,6 +830,17 @@ test('detects an out of date branch and exits', async () => {
}),
createDeploymentStatus: jest.fn().mockImplementation(() => {
return {data: {}}
}),
getCommit: jest.fn().mockImplementation(() => {
return {
data: {
commit: {
author: {
date: '2024-10-15T12:00:00Z'
}
}
}
}
})
}
}
@ -877,6 +909,28 @@ test('fails prechecks', async () => {
expect(validDeploymentOrderMock).not.toHaveBeenCalled()
})
test('fails commitSafetyChecks', async () => {
jest
.spyOn(commitSafetyChecks, 'commitSafetyChecks')
.mockImplementation(() => {
return {
status: false,
message:
'### ⚠️ Cannot proceed with deployment... a scary commit was found'
}
})
jest.spyOn(actionStatus, 'actionStatus').mockImplementation(() => {
return undefined
})
expect(await run()).toBe('failure')
expect(saveStateMock).toHaveBeenCalledWith('bypass', 'true')
expect(setFailedMock).toHaveBeenCalledWith(
'### ⚠️ Cannot proceed with deployment... a scary commit was found'
)
expect(validDeploymentOrderMock).not.toHaveBeenCalled()
})
test('runs the .help command successfully', async () => {
github.context.payload.comment.body = '.help'
jest.spyOn(help, 'help').mockImplementation(() => {

84
dist/index.js сгенерированный поставляемый
Просмотреть файл

@ -44013,6 +44013,60 @@ async function validDeploymentOrder(
return {valid: false, results: results}
}
;// CONCATENATED MODULE: ./src/functions/commit-safety-checks.js
// A helper method to ensure that the commit being used is safe for deployment
// These safety checks are supplemental to the checks found in `src/functions/prechecks.js`
// :param context: The context of the event
// :param data: An object containing data such as the sha, the created_at time for the comment, and more
async function commitSafetyChecks(context, data) {
const comment_created_at = context.payload.comment.created_at
core.debug(`comment_created_at: ${comment_created_at}`)
// fetch the timestamp that the commit was authored (format: "2024-10-21T19:10:24Z" - String)
const commit_created_at = data.commit.author.date
core.debug(`commit_created_at: ${commit_created_at}`)
// check to ensure that the commit was authored before the comment was created
if (isTimestampOlder(comment_created_at, commit_created_at)) {
return {
message: `### ⚠️ Cannot proceed with deployment\n\nThe latest commit is not safe for deployment. It was authored after the trigger comment was created.`,
status: false
}
}
// if we make it through all the checks, we can return a success object
return {message: 'success', status: true}
}
// A helper method that checks if timestamp A is older than timestamp B
// :param timestampA: The first timestamp to compare (String - format: "2024-10-21T19:10:24Z")
// :param timestampB: The second timestamp to compare (String - format: "2024-10-21T19:10:24Z")
// :returns: true if timestampA is older than timestampB, false otherwise
function isTimestampOlder(timestampA, timestampB) {
// Parse the date strings into Date objects
const timestampADate = new Date(timestampA)
const timestampBDate = new Date(timestampB)
// Check if the parsed dates are valid
if (isNaN(timestampADate) || isNaN(timestampBDate)) {
throw new Error(
'Invalid date format. Please ensure the dates are valid UTC timestamps.'
)
}
const result = timestampADate < timestampBDate
if (result) {
core.debug(`${timestampA} is older than ${timestampB}`)
} else {
core.debug(`${timestampA} is not older than ${timestampB}`)
}
return result
}
;// CONCATENATED MODULE: ./src/main.js
@ -44041,11 +44095,14 @@ async function validDeploymentOrder(
// :returns: 'success', 'success - noop', 'success - merge deploy mode', 'failure', 'safe-exit', 'success - unlock on merge mode' or raises an error
async function run() {
try {
core.debug(`context: ${JSON.stringify(github.context)}`)
// Get the inputs for the branch-deploy Action
const token = core.getInput('github_token', {required: true})
@ -44452,6 +44509,33 @@ async function run() {
return 'failure'
}
// fetch commit data from the API
const commitData = await octokit.rest.repos.getCommit({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
ref: precheckResults.sha // exact SHAs can be used here in the ref parameter (which is what we want)
})
// Run commit safety checks
const commitSafetyCheckResults = await commitSafetyChecks(github.context, {
commit: commitData.data.commit
})
// If the commitSafetyCheckResults failed, run the actionStatus function and return
// note: if we don't pass in the 'success' bool, actionStatus will default to failure mode
if (!commitSafetyCheckResults.status) {
await actionStatus(
github.context,
octokit,
reactRes.data.id, // original reaction id
commitSafetyCheckResults.message // message
)
// Set the bypass state to true so that the post run logic will not run
core.saveState('bypass', 'true')
core.setFailed(commitSafetyCheckResults.message)
return 'failure'
}
// check for enforced deployment order if the input was provided and we are NOT deploying to the stable branch
if (
inputs.enforced_deployment_order.length > 0 &&

2
dist/index.js.map сгенерированный поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

255
events/context.json Normal file
Просмотреть файл

@ -0,0 +1,255 @@
{
"payload": {
"action": "created",
"comment": {
"author_association": "OWNER",
"body": ".deploy to staging",
"created_at": "2024-10-21T19:11:18Z",
"html_url": "https://github.com/GrantBirki/actions-sandbox/pull/119#issuecomment-2427512135",
"id": 2427512135,
"issue_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/119",
"node_id": "IC_kwDOID9x8M6QsOVH",
"performed_via_github_app": null,
"reactions": {
"+1": 0,
"-1": 0,
"confused": 0,
"eyes": 0,
"heart": 0,
"hooray": 0,
"laugh": 0,
"rocket": 0,
"total_count": 0,
"url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/comments/2427512135/reactions"
},
"updated_at": "2024-10-21T19:11:18Z",
"url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/comments/2427512135",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/23362539?v=4",
"events_url": "https://api.github.com/users/GrantBirki/events{/privacy}",
"followers_url": "https://api.github.com/users/GrantBirki/followers",
"following_url": "https://api.github.com/users/GrantBirki/following{/other_user}",
"gists_url": "https://api.github.com/users/GrantBirki/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/GrantBirki",
"id": 23362539,
"login": "GrantBirki",
"node_id": "MDQ6VXNlcjIzMzYyNTM5",
"organizations_url": "https://api.github.com/users/GrantBirki/orgs",
"received_events_url": "https://api.github.com/users/GrantBirki/received_events",
"repos_url": "https://api.github.com/users/GrantBirki/repos",
"site_admin": true,
"starred_url": "https://api.github.com/users/GrantBirki/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/GrantBirki/subscriptions",
"type": "User",
"url": "https://api.github.com/users/GrantBirki",
"user_view_type": "public"
}
},
"issue": {
"active_lock_reason": null,
"assignee": null,
"assignees": [],
"author_association": "OWNER",
"body": null,
"closed_at": null,
"comments": 3,
"comments_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/119/comments",
"created_at": "2024-10-21T19:10:27Z",
"draft": false,
"events_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/119/events",
"html_url": "https://github.com/GrantBirki/actions-sandbox/pull/119",
"id": 2603432374,
"labels": [],
"labels_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/119/labels{/name}",
"locked": false,
"milestone": null,
"node_id": "PR_kwDOID9x8M5_XW7D",
"number": 119,
"performed_via_github_app": null,
"pull_request": {
"diff_url": "https://github.com/GrantBirki/actions-sandbox/pull/119.diff",
"html_url": "https://github.com/GrantBirki/actions-sandbox/pull/119",
"merged_at": null,
"patch_url": "https://github.com/GrantBirki/actions-sandbox/pull/119.patch",
"url": "https://api.github.com/repos/GrantBirki/actions-sandbox/pulls/119"
},
"reactions": {
"+1": 0,
"-1": 0,
"confused": 0,
"eyes": 0,
"heart": 0,
"hooray": 0,
"laugh": 0,
"rocket": 0,
"total_count": 0,
"url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/119/reactions"
},
"repository_url": "https://api.github.com/repos/GrantBirki/actions-sandbox",
"state": "open",
"state_reason": null,
"timeline_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/119/timeline",
"title": "test",
"updated_at": "2024-10-21T19:11:20Z",
"url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/119",
"user": {
"avatar_url": "https://avatars.githubusercontent.com/u/23362539?v=4",
"events_url": "https://api.github.com/users/GrantBirki/events{/privacy}",
"followers_url": "https://api.github.com/users/GrantBirki/followers",
"following_url": "https://api.github.com/users/GrantBirki/following{/other_user}",
"gists_url": "https://api.github.com/users/GrantBirki/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/GrantBirki",
"id": 23362539,
"login": "GrantBirki",
"node_id": "MDQ6VXNlcjIzMzYyNTM5",
"organizations_url": "https://api.github.com/users/GrantBirki/orgs",
"received_events_url": "https://api.github.com/users/GrantBirki/received_events",
"repos_url": "https://api.github.com/users/GrantBirki/repos",
"site_admin": true,
"starred_url": "https://api.github.com/users/GrantBirki/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/GrantBirki/subscriptions",
"type": "User",
"url": "https://api.github.com/users/GrantBirki",
"user_view_type": "public"
}
},
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/assignees{/user}",
"blobs_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/branches{/branch}",
"clone_url": "https://github.com/GrantBirki/actions-sandbox.git",
"collaborators_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/comments{/number}",
"commits_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/commits{/sha}",
"compare_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/contents/{+path}",
"contributors_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/contributors",
"created_at": "2022-09-25T02:35:17Z",
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/deployments",
"description": "A sandbox repo for testing GitHub Actions",
"disabled": false,
"downloads_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/downloads",
"events_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/events",
"fork": false,
"forks": 1,
"forks_count": 1,
"forks_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/forks",
"full_name": "GrantBirki/actions-sandbox",
"git_commits_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/git/tags{/sha}",
"git_url": "git://github.com/GrantBirki/actions-sandbox.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": true,
"has_pages": false,
"has_projects": true,
"has_wiki": false,
"homepage": "",
"hooks_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/hooks",
"html_url": "https://github.com/GrantBirki/actions-sandbox",
"id": 541028848,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues/events{/number}",
"issues_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/issues{/number}",
"keys_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/keys{/key_id}",
"labels_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/labels{/name}",
"language": null,
"languages_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/languages",
"license": null,
"merges_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/merges",
"milestones_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/milestones{/number}",
"mirror_url": null,
"name": "actions-sandbox",
"node_id": "R_kgDOID9x8A",
"notifications_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/notifications{?since,all,participating}",
"open_issues": 1,
"open_issues_count": 1,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/23362539?v=4",
"events_url": "https://api.github.com/users/GrantBirki/events{/privacy}",
"followers_url": "https://api.github.com/users/GrantBirki/followers",
"following_url": "https://api.github.com/users/GrantBirki/following{/other_user}",
"gists_url": "https://api.github.com/users/GrantBirki/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/GrantBirki",
"id": 23362539,
"login": "GrantBirki",
"node_id": "MDQ6VXNlcjIzMzYyNTM5",
"organizations_url": "https://api.github.com/users/GrantBirki/orgs",
"received_events_url": "https://api.github.com/users/GrantBirki/received_events",
"repos_url": "https://api.github.com/users/GrantBirki/repos",
"site_admin": true,
"starred_url": "https://api.github.com/users/GrantBirki/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/GrantBirki/subscriptions",
"type": "User",
"url": "https://api.github.com/users/GrantBirki",
"user_view_type": "public"
},
"private": false,
"pulls_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/pulls{/number}",
"pushed_at": "2024-10-21T19:10:24Z",
"releases_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/releases{/id}",
"size": 380,
"ssh_url": "git@github.com:GrantBirki/actions-sandbox.git",
"stargazers_count": 0,
"stargazers_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/stargazers",
"statuses_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/subscribers",
"subscription_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/subscription",
"svn_url": "https://github.com/GrantBirki/actions-sandbox",
"tags_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/tags",
"teams_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/teams",
"topics": [
"testing"
],
"trees_url": "https://api.github.com/repos/GrantBirki/actions-sandbox/git/trees{/sha}",
"updated_at": "2024-10-21T19:10:19Z",
"url": "https://api.github.com/repos/GrantBirki/actions-sandbox",
"visibility": "public",
"watchers": 0,
"watchers_count": 0,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/23362539?v=4",
"events_url": "https://api.github.com/users/GrantBirki/events{/privacy}",
"followers_url": "https://api.github.com/users/GrantBirki/followers",
"following_url": "https://api.github.com/users/GrantBirki/following{/other_user}",
"gists_url": "https://api.github.com/users/GrantBirki/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/GrantBirki",
"id": 23362539,
"login": "GrantBirki",
"node_id": "MDQ6VXNlcjIzMzYyNTM5",
"organizations_url": "https://api.github.com/users/GrantBirki/orgs",
"received_events_url": "https://api.github.com/users/GrantBirki/received_events",
"repos_url": "https://api.github.com/users/GrantBirki/repos",
"site_admin": true,
"starred_url": "https://api.github.com/users/GrantBirki/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/GrantBirki/subscriptions",
"type": "User",
"url": "https://api.github.com/users/GrantBirki",
"user_view_type": "public"
}
},
"eventName": "issue_comment",
"sha": "09e517ae87b70ef328cc825abff511df43382046",
"ref": "refs/heads/main",
"workflow": "deploy",
"action": "branch-deploy",
"actor": "GrantBirki",
"job": "trigger",
"runNumber": 761,
"runId": 11446979021,
"apiUrl": "https://api.github.com",
"serverUrl": "https://github.com",
"graphqlUrl": "https://api.github.com/graphql"
}

Просмотреть файл

@ -0,0 +1,52 @@
import * as core from '@actions/core'
// A helper method to ensure that the commit being used is safe for deployment
// These safety checks are supplemental to the checks found in `src/functions/prechecks.js`
// :param context: The context of the event
// :param data: An object containing data such as the sha, the created_at time for the comment, and more
export async function commitSafetyChecks(context, data) {
const comment_created_at = context.payload.comment.created_at
core.debug(`comment_created_at: ${comment_created_at}`)
// fetch the timestamp that the commit was authored (format: "2024-10-21T19:10:24Z" - String)
const commit_created_at = data.commit.author.date
core.debug(`commit_created_at: ${commit_created_at}`)
// check to ensure that the commit was authored before the comment was created
if (isTimestampOlder(comment_created_at, commit_created_at)) {
return {
message: `### ⚠️ Cannot proceed with deployment\n\nThe latest commit is not safe for deployment. It was authored after the trigger comment was created.`,
status: false
}
}
// if we make it through all the checks, we can return a success object
return {message: 'success', status: true}
}
// A helper method that checks if timestamp A is older than timestamp B
// :param timestampA: The first timestamp to compare (String - format: "2024-10-21T19:10:24Z")
// :param timestampB: The second timestamp to compare (String - format: "2024-10-21T19:10:24Z")
// :returns: true if timestampA is older than timestampB, false otherwise
function isTimestampOlder(timestampA, timestampB) {
// Parse the date strings into Date objects
const timestampADate = new Date(timestampA)
const timestampBDate = new Date(timestampB)
// Check if the parsed dates are valid
if (isNaN(timestampADate) || isNaN(timestampBDate)) {
throw new Error(
'Invalid date format. Please ensure the dates are valid UTC timestamps.'
)
}
const result = timestampADate < timestampBDate
if (result) {
core.debug(`${timestampA} is older than ${timestampB}`)
} else {
core.debug(`${timestampA} is not older than ${timestampB}`)
}
return result
}

Просмотреть файл

@ -26,10 +26,13 @@ import {COLORS} from './functions/colors'
import {getInputs} from './functions/inputs'
import {constructValidBranchName} from './functions/valid-branch-name'
import {validDeploymentOrder} from './functions/valid-deployment-order'
import {commitSafetyChecks} from './functions/commit-safety-checks'
// :returns: 'success', 'success - noop', 'success - merge deploy mode', 'failure', 'safe-exit', 'success - unlock on merge mode' or raises an error
export async function run() {
try {
core.debug(`context: ${JSON.stringify(context)}`)
// Get the inputs for the branch-deploy Action
const token = core.getInput('github_token', {required: true})
@ -436,6 +439,33 @@ export async function run() {
return 'failure'
}
// fetch commit data from the API
const commitData = await octokit.rest.repos.getCommit({
owner: context.repo.owner,
repo: context.repo.repo,
ref: precheckResults.sha // exact SHAs can be used here in the ref parameter (which is what we want)
})
// Run commit safety checks
const commitSafetyCheckResults = await commitSafetyChecks(context, {
commit: commitData.data.commit
})
// If the commitSafetyCheckResults failed, run the actionStatus function and return
// note: if we don't pass in the 'success' bool, actionStatus will default to failure mode
if (!commitSafetyCheckResults.status) {
await actionStatus(
context,
octokit,
reactRes.data.id, // original reaction id
commitSafetyCheckResults.message // message
)
// Set the bypass state to true so that the post run logic will not run
core.saveState('bypass', 'true')
core.setFailed(commitSafetyCheckResults.message)
return 'failure'
}
// check for enforced deployment order if the input was provided and we are NOT deploying to the stable branch
if (
inputs.enforced_deployment_order.length > 0 &&