@actions/github#getOctokit TypeScript Examples

The following examples show how to use @actions/github#getOctokit. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
getContextIssueComments = (): Promise<IssueComments> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  return Github.issues
    .listComments({
      owner: context.repo.owner,
      repo: context.repo.repo,
      issue_number: context.issue.number
    })
    .then((res) => res.data);
}
Example #2
Source File: get-changed-files.ts    From action-eslint with MIT License 6 votes vote down vote up
getChangedFiles = async (token: string): Promise<FileNamesList> => {
  const octokit = getOctokit(token);
  const pullRequest = context.payload.pull_request;

  let filenames: FileNamesList = [];

  if (!pullRequest?.number) {
    const getCommitEndpointOptions = octokit.rest.repos.getCommit.endpoint.merge({
      owner: context.repo.owner,
      repo: context.repo.repo,
      ref: context.sha,
    });

    type ReposGetCommitResponse = GetResponseDataTypeFromEndpointMethod<typeof octokit.rest.repos.getCommit>;
    const response: ReposGetCommitResponse[] = await octokit.paginate(getCommitEndpointOptions);
    const filesArr = response.map((data) => data.files);

    const filesChangedInCommit = filesArr.reduce((acc, val) => acc?.concat(val || []), []);

    filenames = getFileNames(filesChangedInCommit as File[]);
  } else {
    const listFilesEndpointOptions = octokit.rest.pulls.listFiles.endpoint.merge({
      owner: context.repo.owner,
      repo: context.repo.repo,
      pull_number: pullRequest.number,
    });

    type PullsListFilesResponse = GetResponseDataTypeFromEndpointMethod<typeof octokit.rest.pulls.listFiles>;
    const filesChangedInPR: PullsListFilesResponse = await octokit.paginate(listFilesEndpointOptions);

    filenames = getFileNames(filesChangedInPR as File[]);
  }

  return filenames;
}
Example #3
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
removeLabels = async (labels: string[]) => {
  const Github = getOctokit(GITHUB_TOKEN).rest;

  // makes it easy to maintain the integration tests and the
  // responses from this are not used
  if (isMock() || isTest()) return;

  await Promise.all(
    // this will submit a max of three requests which is not enough to
    // rate limit
    labels.map((label) =>
      Github.issues.removeLabel({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
        name: label
      })
    )
  );
}
Example #4
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
addLabels = async (labels: string[]): Promise<void> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;

  // makes it easy to maintain the integration tests and the
  // responses from this are not used
  if (isMock() || isTest()) return;

  // because of a weird type issue
  const { addLabels: _addLabels } = Github.issues;

  await _addLabels({
    owner: context.repo.owner,
    repo: context.repo.repo,
    issue_number: context.issue.number,
    labels
  });
}
Example #5
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
setLabels = async (labels: string[]): Promise<void> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  await Github.issues
    .setLabels({
      owner: context.repo.owner,
      repo: context.repo.repo,
      issue_number: context.issue.number,
      // @ts-expect-error the expected type is (string[] & {name: string}[]) | undefined
      // but string[] and {name: string}[] cannot simultaneously coincide
      labels
    })
    .then((res) => res);
}
Example #6
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
getContextLabels = async (): Promise<ChangeTypes[]> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  const { data: issue } = await Github.issues.get({
    owner: context.repo.owner,
    repo: context.repo.repo,
    issue_number: context.issue.number
  });

  const labels = issue.labels;

  return labels
    .map((label) => {
      if (typeof label === "string") {
        return label;
      }
      return label.name;
      // this will make it so that the only labels considered are ChangeTypes
    })
    .filter(isDefined)
    .filter(isChangeType);
}
Example #7
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
createCommentOnContext = (message: string): Promise<any> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  return Github.issues.createComment({
    owner: context.repo.owner,
    repo: context.repo.repo,
    issue_number: context.issue.number,
    body: message
  });
}
Example #8
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
updateComment = (commentId: number, message: string): Promise<any> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  return Github.issues
    .updateComment({
      owner: context.repo.owner,
      repo: context.repo.repo,
      comment_id: commentId,
      body: message
    })
    .catch((err) => {
      if (err?.request?.body) {
        err.request.body = JSON.parse(err.request.body).body;
      }
      throw err;
    });
}
Example #9
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
resolveUserByEmail = async (email: string) => {
  const Github = getOctokit(GITHUB_TOKEN).rest;

  // @ts-ignore
  const { data: rawEmailSearch } = await Github.search.users({
    q: email
  });

  if (rawEmailSearch.total_count > 0 && rawEmailSearch.items[0] !== undefined) {
    return "@" + rawEmailSearch.items[0].login;
  }

  const { data: emailSearch } = await Github.search.users({
    q: `${email} in:email`
  });

  if (emailSearch.total_count === 1 && isDefined(emailSearch.items[0])) {
    return "@" + emailSearch.items[0].login;
  }

  const local = email.split("@")[0];
  if (!local) return;
  const firstName = local.split(".")[0];
  const lastName = local.split(".")[1];
  if (!firstName || !lastName) return;

  const { data: nameSearch } = await Github.search.users({
    q: `fullname:${firstName} ${lastName} type:users`
  });

  if (nameSearch.total_count === 1 && isDefined(nameSearch.items[0])) {
    return "@" + nameSearch.items[0].login;
  }

  return;
}
Example #10
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
requestReview = (pr: PR, reviewer: string) => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  return (
    Github.pulls
      .requestReviewers({
        owner: context.repo.owner,
        repo: context.repo.repo,
        pull_number: pr.number,
        reviewers: [reviewer]
      })
      // if an error occurs return undefined
      .catch((err) => {})
  );
}
Example #11
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
getRepoFilenameContent = (
  filename: string,
  sha: string
): Promise<ContentData> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  return Github.repos
    .getContent({
      owner: context.repo.owner,
      repo: context.repo.repo,
      path: filename,
      ref: sha
    })
    .then((res) => res.data);
}
Example #12
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
getPullRequestFiles = (pullNumber: number) => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  return Github.pulls
    .listFiles({
      pull_number: pullNumber,
      repo: context.repo.repo,
      owner: context.repo.owner
    })
    .then((res) => res.data);
}
Example #13
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
getPullRequestReviews = async (
  pullNumber: number,
  page = 1
): Promise<Review[]> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  const { data: reviews }: { data: Review[] } = await Github.pulls.listReviews({
    owner: context.repo.owner,
    repo: context.repo.repo,
    pull_number: pullNumber,
    per_page: 100,
    page
  });
  if (_.isEmpty(reviews)) {
    return reviews;
  }
  return getPullRequestReviews(pullNumber, page + 1).then((res) =>
    reviews.concat(res)
  );
}
Example #14
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 6 votes vote down vote up
getPullRequestFromNumber = (pullNumber: number) => {
  const github = getOctokit(GITHUB_TOKEN).rest;

  return github.pulls
    .get({
      repo: context.repo.repo,
      owner: context.repo.owner,
      pull_number: pullNumber
    })
    .then((res) => {
      return res.data;
    });
}
Example #15
Source File: github.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
getSelf = (): Promise<GithubSelf> => {
  const Github = getOctokit(GITHUB_TOKEN).rest;
  return Github.users.getAuthenticated().then((res) => {
    return res.data;
  });
}
Example #16
Source File: octokit.ts    From github-action with Apache License 2.0 5 votes vote down vote up
octokitUsingPAT = isPersonalAccessTokenPresent() ? getOctokit(personalAcessToken as string) : octokit
Example #17
Source File: octokit.ts    From github-action with Apache License 2.0 5 votes vote down vote up
octokit = getOctokit(githubActionsDefaultToken as string)
Example #18
Source File: index.ts    From auto-changelog with MIT License 5 votes vote down vote up
async function run() {
  const inputs = await getInputs();

  const octokit = getOctokit(getToken());

  const {
    repo: { owner, repo },
    sha,
  } = context;

  let semver: SemVer.SemVer | null = null;

  if (inputs.semver) {
    semver = SemVer.parse(inputs.releaseName, { includePrerelease: true });

    if (semver == null)
      return setFailed(
        `Expected a semver compatible releaseName, got "${inputs.releaseName}" instead.`,
      );
  }

  let prerelease = false;

  if (semver != null) prerelease = semver.prerelease.length > 0;

  const { sha: tagRef, name: tagName } = await getTagSha({
    octokit,
    owner,
    repo,
    sha,
    semver,
    prerelease,
  });

  let changelog = await generate({
    octokit,
    owner,
    repo,
    sha,
    tagRef,
    inputs,
  });

  if (inputs.mentionNewContributors) {
    const { data } = await octokit.rest.repos.generateReleaseNotes({
      owner,
      repo,
      tag_name: inputs.releaseName,
      previous_tag_name: tagName,
    });

    const tokens = marked.lexer(data.body);

    const index = tokens.findIndex(
      (token) => token.type === "heading" && token.text === "New Contributors",
    );

    const token = tokens[index + 1];

    if (token.type === "list")
      changelog += `\n\n## New Contributors\n${token.raw}\n`;
  }

  if (inputs.includeCompare && tagName != null) {
    changelog += `\n\n**Full Changelog**: https://github.com/${owner}/${repo}/compare/${tagName}...${inputs.releaseName}`;
  }

  info(`-> prerelease: ${prerelease}`);

  setOutput("prerelease", prerelease);

  info(`-> changelog: "${changelog}"`);

  setOutput("changelog", changelog);
}
Example #19
Source File: mockPR.ts    From EIP-Bot with Creative Commons Zero v1.0 Universal 5 votes vote down vote up
fetchAndCreateRecord = async (
  url: string,
  method: MockMethods,
  body?: string
) => {
  console.error("failed request", method, url, "\nmocking request...");

  const isMock = process.env.NODE_ENV === NodeEnvs.mock;

  if (!isMock) return;

  nock.cleanAll();
  nock.enableNetConnect();
  const github = getOctokit(GITHUB_TOKEN).request;
  const res = await github({
    method,
    url,
    ...JSON.parse(body || "{}")
  }).catch((err) => {
    nock.disableNetConnect();
    return err;
  });
  console.log("successfully fetched data");
  nock.disableNetConnect();

  const fileName = `records/${process.env.PULL_NUMBER?.replace("_", "/")}.json`;
  const mockedRecord: MockRecord[] = (await import("./" + fileName)).default;

  requireMockMethod(method);
  const handleResData = (res) => {
    const status = res.status;
    if ([HttpStatus.OK, HttpStatus.CREATED].includes(status)) {
      // when successful it returns the response in a res.data format
      return res.data;
    }
    if ([HttpStatus.NOT_FOUND].includes(status)) {
      // when it returns a not found or other types of failures
      return res.response.data;
    }
    throw new UnexpectedError(`status code ${status} is not a handled status`);
  };
  mockedRecord.push({
    req: {
      url,
      method
    },
    res: {
      status: res.status,
      data: handleResData(res)
    }
  });

  console.log(process.cwd() + "/src/tests/assets/" + fileName);
  fs.writeFile(
    process.cwd() + "/src/tests/assets/" + fileName,
    JSON.stringify(mockedRecord, null, 2),
    () => {
      console.log(mockedRecord);
      console.log("wrote file");
    }
  );
}
Example #20
Source File: main.ts    From checkstyle-github-action with MIT License 5 votes vote down vote up
async function createCheck(
  name: string,
  title: string,
  annotations: Annotation[],
  numErrors: number,
  conclusion: 'success' | 'failure' | 'neutral'
): Promise<void> {
  core.info(
    `Uploading ${annotations.length} / ${numErrors} annotations to GitHub as ${name} with conclusion ${conclusion}`
  )
  const octokit = getOctokit(core.getInput(Inputs.Token))
  let sha = context.sha

  if (context.payload.pull_request) {
    sha = context.payload.pull_request.head.sha
  }

  const req = {
    ...context.repo,
    ref: sha
  }

  const res = await octokit.checks.listForRef(req)
  const existingCheckRun = res.data.check_runs.find(
    check => check.name === name
  )

  if (!existingCheckRun) {
    const createRequest = {
      ...context.repo,
      head_sha: sha,
      conclusion,
      name,
      status: <const>'completed',
      output: {
        title,
        summary: `${numErrors} violation(s) found`,
        annotations
      }
    }

    await octokit.checks.create(createRequest)
  } else {
    const check_run_id = existingCheckRun.id

    const update_req = {
      ...context.repo,
      conclusion,
      check_run_id,
      status: <const>'completed',
      output: {
        title,
        summary: `${numErrors} violation(s) found`,
        annotations
      }
    }

    await octokit.checks.update(update_req)
  }
}
Example #21
Source File: main.ts    From spotbugs-github-action with MIT License 5 votes vote down vote up
async function createCheck(
  name: string,
  title: string,
  annotations: Annotation[],
  numErrors: number
): Promise<void> {
  const octokit = getOctokit(core.getInput(Inputs.Token))
  let sha = context.sha

  if (context.payload.pull_request) {
    sha = context.payload.pull_request.head.sha
  }

  const req = {
    ...context.repo,
    ref: sha
  }

  const res = await octokit.checks.listForRef(req)
  const existingCheckRun = res.data.check_runs.find(
    check => check.name === name
  )

  if (!existingCheckRun) {
    const createRequest = {
      ...context.repo,
      head_sha: sha,
      name,
      status: <const>'completed',
      conclusion: numErrors === 0 ? <const>'success' : <const>'neutral',
      output: {
        title,
        summary: `${numErrors} violation(s) found`,
        annotations
      }
    }

    await octokit.checks.create(createRequest)
  } else {
    const check_run_id = existingCheckRun.id

    const update_req = {
      ...context.repo,
      check_run_id,
      status: <const>'completed',
      conclusion: <const>'neutral',
      output: {
        title,
        summary: `${numErrors} violation(s) found`,
        annotations
      }
    }

    await octokit.checks.update(update_req)
  }
}
Example #22
Source File: mappingConfig.ts    From actions-mention-to-slack with MIT License 5 votes vote down vote up
MappingConfigRepositoryImpl = {
  downloadFromUrl: async (url: string) => {
    const response = await axios.get<string>(url);
    return response.data;
  },

  loadYaml: (data: string) => {
    const configObject = load(data);

    if (configObject === undefined) {
      throw new Error(
        ["failed to load yaml", JSON.stringify({ data }, null, 2)].join("\n")
      );
    }

    return configObject as MappingFile;
  },

  loadFromUrl: async (url: string) => {
    const data = await MappingConfigRepositoryImpl.downloadFromUrl(url);
    return MappingConfigRepositoryImpl.loadYaml(data);
  },

  loadFromGithubPath: async (
    repoToken: string,
    owner: string,
    repo: string,
    configurationPath: string,
    sha: string
  ) => {
    const githubClient = getOctokit(repoToken);
    const response = await githubClient.rest.repos.getContent({
      owner,
      repo,
      path: configurationPath,
      ref: sha,
    });

    if (!("content" in response.data)) {
      throw new Error(
        ["Unexpected response", JSON.stringify({ response }, null, 2)].join(
          "\n"
        )
      );
    }

    const data = Buffer.from(response.data.content, "base64").toString();

    return MappingConfigRepositoryImpl.loadYaml(data);
  },
}
Example #23
Source File: main.ts    From slack-workflow-status with MIT License 4 votes vote down vote up
// eslint-disable-line github/no-then

// Action entrypoint
async function main(): Promise<void> {
  // Collect Action Inputs
  const webhook_url = core.getInput('slack_webhook_url', {
    required: true
  })
  const github_token = core.getInput('repo_token', {required: true})
  const include_jobs = core.getInput('include_jobs', {
    required: true
  }) as IncludeJobs
  const include_commit_message =
    core.getInput('include_commit_message', {
      required: true
    }) === 'true'
  const slack_channel = core.getInput('channel')
  const slack_name = core.getInput('name')
  const slack_icon = core.getInput('icon_url')
  const slack_emoji = core.getInput('icon_emoji') // https://www.webfx.com/tools/emoji-cheat-sheet/
  // Force as secret, forces *** when trying to print or log values
  core.setSecret(github_token)
  core.setSecret(webhook_url)
  // Auth github with octokit module
  const octokit = getOctokit(github_token)
  // Fetch workflow run data
  const {data: workflow_run} = await octokit.actions.getWorkflowRun({
    owner: context.repo.owner,
    repo: context.repo.repo,
    run_id: context.runId
  })

  // Fetch workflow job information
  const {data: jobs_response} = await octokit.actions.listJobsForWorkflowRun({
    owner: context.repo.owner,
    repo: context.repo.repo,
    run_id: context.runId
  })

  const completed_jobs = jobs_response.jobs.filter(
    job => job.status === 'completed'
  )

  // Configure slack attachment styling
  let workflow_color // can be good, danger, warning or a HEX colour (#00FF00)
  let workflow_msg

  let job_fields: SlackMessageAttachementFields

  if (
    completed_jobs.every(job => ['success', 'skipped'].includes(job.conclusion))
  ) {
    workflow_color = 'good'
    workflow_msg = 'Success:'
    if (include_jobs === 'on-failure') {
      job_fields = []
    }
  } else if (completed_jobs.some(job => job.conclusion === 'cancelled')) {
    workflow_color = 'warning'
    workflow_msg = 'Cancelled:'
    if (include_jobs === 'on-failure') {
      job_fields = []
    }
  } else {
    // (jobs_response.jobs.some(job => job.conclusion === 'failed')
    workflow_color = 'danger'
    workflow_msg = 'Failed:'
  }

  if (include_jobs === 'false') {
    job_fields = []
  }

  // Build Job Data Fields
  job_fields ??= completed_jobs.map(job => {
    let job_status_icon

    switch (job.conclusion) {
      case 'success':
        job_status_icon = '✓'
        break
      case 'cancelled':
      case 'skipped':
        job_status_icon = '⃠'
        break
      default:
        // case 'failure'
        job_status_icon = '✗'
    }

    const job_duration = compute_duration({
      start: new Date(job.started_at),
      end: new Date(job.completed_at)
    })

    return {
      title: '', // FIXME: it's required in slack type, we should workaround that somehow
      short: true,
      value: `${job_status_icon} <${job.html_url}|${job.name}> (${job_duration})`
    }
  })

  // Payload Formatting Shortcuts
  const workflow_duration = compute_duration({
    start: new Date(workflow_run.created_at),
    end: new Date(workflow_run.updated_at)
  })
  const repo_url = `<${workflow_run.repository.html_url}|*${workflow_run.repository.full_name}*>`
  const branch_url = `<${workflow_run.repository.html_url}/tree/${workflow_run.head_branch}|*${workflow_run.head_branch}*>`
  const workflow_run_url = `<${workflow_run.html_url}|#${workflow_run.run_number}>`
  // Example: Success: AnthonyKinson's `push` on `master` for pull_request
  let status_string = `${workflow_msg} ${context.actor}'s \`${context.eventName}\` on \`${branch_url}\``
  // Example: Workflow: My Workflow #14 completed in `1m 30s`
  const details_string = `Workflow: ${context.workflow} ${workflow_run_url} completed in \`${workflow_duration}\``

  // Build Pull Request string if required
  const pull_requests = (workflow_run.pull_requests as PullRequest[])
    .map(
      pull_request =>
        `<${workflow_run.repository.html_url}/pull/${pull_request.number}|#${pull_request.number}> from \`${pull_request.head.ref}\` to \`${pull_request.base.ref}\``
    )
    .join(', ')

  if (pull_requests !== '') {
    status_string = `${workflow_msg} ${context.actor}'s \`pull_request\` ${pull_requests}`
  }

  const commit_message = `Commit: ${workflow_run.head_commit.message}`

  // We're using old style attachments rather than the new blocks because:
  // - Blocks don't allow colour indicators on messages
  // - Block are limited to 10 fields. >10 jobs in a workflow results in payload failure

  // Build our notification attachment
  const slack_attachment = {
    mrkdwn_in: ['text' as const],
    color: workflow_color,
    text: [status_string, details_string]
      .concat(include_commit_message ? [commit_message] : [])
      .join('\n'),
    footer: repo_url,
    footer_icon: 'https://github.githubassets.com/favicon.ico',
    fields: job_fields
  }
  // Build our notification payload
  const slack_payload_body = {
    attachments: [slack_attachment],
    ...(slack_name && {username: slack_name}),
    ...(slack_channel && {channel: slack_channel}),
    ...(slack_emoji && {icon_emoji: slack_emoji}),
    ...(slack_icon && {icon_url: slack_icon})
  }

  const slack_webhook = new IncomingWebhook(webhook_url)

  try {
    await slack_webhook.send(slack_payload_body)
  } catch (err) {
    if (err instanceof Error) {
      core.setFailed(err.message)
    }
  }
}
Example #24
Source File: index.ts    From ticket-check-action with MIT License 4 votes vote down vote up
async function run(): Promise<void> {
  try {
    // Provide complete context object right away if debugging
    debug('context', JSON.stringify(context));

    // Check for a ticket reference in the title
    const title: string = context?.payload?.pull_request?.title;
    const titleRegexBase = getInput('titleRegex', { required: true });
    const titleRegexFlags = getInput('titleRegexFlags', {
      required: true
    });
    const ticketLink = getInput('ticketLink', { required: false });
    const titleRegex = new RegExp(titleRegexBase, titleRegexFlags);
    const titleCheck = titleRegex.exec(title);

    // Instantiate a GitHub Client instance
    const token = getInput('token', { required: true });
    const client = getOctokit(token);
    const { owner, repo, number } = context.issue;
    const login = context.payload.pull_request?.user.login as string;
    const senderType = context.payload.pull_request?.user.type as string;
    const sender: string = senderType === 'Bot' ? login.replace('[bot]', '') : login;

    const linkTicket = async (matchArray: RegExpMatchArray): Promise<void> => {
      debug('match array for linkTicket', JSON.stringify(matchArray));
      debug('match array groups for linkTicket', JSON.stringify(matchArray.groups));

      if (!ticketLink) {
        return;
      }

      const ticketNumber = matchArray.groups?.ticketNumber;

      if (!ticketNumber) {
        debug('ticketNumber not found', 'ticketNumber group not found in match array.');

        return;
      }

      if (!ticketLink.includes('%ticketNumber%')) {
        debug('invalid ticketLink', 'ticketLink must include "%ticketNumber%" variable to post ticket link.');

        return;
      }

      const linkToTicket = ticketLink.replace('%ticketNumber%', ticketNumber);

      const currentReviews = await client.pulls.listReviews({
        owner,
        repo,
        pull_number: number
      });

      debug('current reviews', JSON.stringify(currentReviews));

      if (
        currentReviews?.data?.length &&
        currentReviews?.data.some((review: { body?: string }) => review?.body?.includes(linkToTicket))
      ) {
        debug('already posted ticketLink', 'found an existing review that contains the ticket link');

        return;
      }

      client.pulls.createReview({
        owner,
        repo,
        pull_number: number,
        body: `See the ticket for this pull request: ${linkToTicket}`,
        event: 'COMMENT'
      });
    };

    debug('title', title);

    // Return and approve if the title includes a Ticket ID
    if (titleCheck !== null) {
      debug('success', 'Title includes a ticket ID');
      await linkTicket(titleCheck);

      return;
    }

    const quiet = getInput('quiet', { required: false }) === 'true';

    // Exempt Users
    const exemptUsers = getInput('exemptUsers', { required: false })
      .split(',')
      .map(user => user.trim());

    // Debugging Entries
    debug('sender', sender);
    debug('sender type', senderType);
    debug('quiet mode', quiet.toString());
    debug('exempt users', exemptUsers.join(','));
    debug('ticket link', ticketLink);

    if (sender && exemptUsers.includes(sender)) {
      debug('success', 'User is listed as exempt');

      return;
    }

    // get the title format and ticket prefix
    const ticketPrefix = getInput('ticketPrefix');
    const titleFormat = getInput('titleFormat', { required: true });

    // Check for a ticket reference in the branch
    const branch: string = context.payload.pull_request?.head.ref;
    const branchRegexBase = getInput('branchRegex', { required: true });
    const branchRegexFlags = getInput('branchRegexFlags', {
      required: true
    });
    const branchRegex = new RegExp(branchRegexBase, branchRegexFlags);
    const branchCheck = branchRegex.exec(branch);

    if (branchCheck !== null) {
      debug('success', 'Branch name contains a reference to a ticket, updating title');

      const id = extractId(branch);

      if (id === null) {
        setFailed('Could not extract a ticket ID reference from the branch');

        return;
      }

      client.pulls.update({
        owner,
        repo,
        pull_number: number,
        title: titleFormat
          .replace('%prefix%', ticketPrefix)
          .replace('%id%', id)
          .replace('%title%', title)
      });

      if (!quiet) {
        client.pulls.createReview({
          owner,
          repo,
          pull_number: number,
          body:
            "Hey! I noticed that your PR contained a reference to the ticket in the branch name but not in the title. I went ahead and updated that for you. Hope you don't mind! ☺️",
          event: 'COMMENT'
        });
      }

      await linkTicket(branchCheck);

      return;
    }

    // Retrieve the pull request body and verify it's not empty
    const body = context?.payload?.pull_request?.body;

    if (body === undefined) {
      debug('failure', 'Body is undefined');
      setFailed('Could not retrieve the Pull Request body');

      return;
    }

    debug('body contents', body);

    // Check for a ticket reference number in the body
    const bodyRegexBase = getInput('bodyRegex', { required: true });
    const bodyRegexFlags = getInput('bodyRegexFlags', { required: true });
    const bodyRegex = new RegExp(bodyRegexBase, bodyRegexFlags);
    const bodyCheck = bodyRegex.exec(body);

    if (bodyCheck !== null) {
      debug('success', 'Body contains a reference to a ticket, updating title');

      const id = extractId(bodyCheck[0]);

      if (id === null) {
        setFailed('Could not extract a ticket shorthand reference from the body');

        return;
      }

      client.pulls.update({
        owner,
        repo,
        pull_number: number,
        title: titleFormat
          .replace('%prefix%', ticketPrefix)
          .replace('%id%', id)
          .replace('%title%', title)
      });

      if (!quiet) {
        client.pulls.createReview({
          owner,
          repo,
          pull_number: number,
          body:
            "Hey! I noticed that your PR contained a reference to the ticket in the body but not in the title. I went ahead and updated that for you. Hope you don't mind! ☺️",
          event: 'COMMENT'
        });
      }

      await linkTicket(bodyCheck);

      return;
    }

    // Last ditch effort, check for a ticket reference URL in the body
    const bodyURLRegexBase = getInput('bodyURLRegex', { required: false });

    if (!bodyURLRegexBase) {
      debug('failure', 'Title, branch, and body do not contain a reference to a ticket, and no body URL regex was set');
      setFailed('No ticket was referenced in this pull request');

      return;
    }

    const bodyURLRegexFlags = getInput('bodyURLRegexFlags', {
      required: true
    });
    const bodyURLRegex = new RegExp(bodyURLRegexBase, bodyURLRegexFlags);
    const bodyURLCheck = bodyURLRegex.exec(body);

    if (bodyURLCheck !== null) {
      debug('success', 'Body contains a ticket URL, updating title');

      const id = extractId(bodyURLCheck[0]);

      if (id === null) {
        setFailed('Could not extract a ticket URL from the body');

        return;
      }

      client.pulls.update({
        owner,
        repo,
        pull_number: number,
        title: titleFormat
          .replace('%prefix%', ticketPrefix)
          .replace('%id%', id)
          .replace('%title%', title)
      });

      if (!quiet) {
        client.pulls.createReview({
          owner,
          repo,
          pull_number: number,
          body:
            "Hey! I noticed that your PR contained a reference to the ticket URL in the body but not in the title. I went ahead and updated that for you. Hope you don't mind! ☺️",
          event: 'COMMENT'
        });
      }
    }

    if (titleCheck === null && branchCheck === null && bodyCheck === null && bodyURLCheck === null) {
      debug('failure', 'Title, branch, and body do not contain a reference to a ticket');
      setFailed('No ticket was referenced in this pull request');

      return;
    }
  } catch (error) {
    setFailed(error.message);
  }
}