await git()

in scan/src/utils.ts [214:537]


        await git(['checkout', currentBranch])
        await git(['cherry-pick', commitToCherryPick])
      }
      await git(['push', 'origin', currentBranch])
      core.info(`Pushed quick-fixes to branch ${currentBranch}`)
    } else if (mode === PULL_REQUEST) {
      const newBranch = `qodana/quick-fixes-${currentCommit.slice(0, 7)}`
      await git(['checkout', '-b', newBranch])
      await git(['push', 'origin', newBranch])
      await createPr(
        commitMessage,
        `${c.repo.owner}/${c.repo.repo}`,
        currentBranch,
        newBranch
      )
      core.info(
        `Pushed quick-fixes to branch ${newBranch} and created pull request`
      )
    }
  } catch (error) {
    core.warning(`Failed to push quick fixes – ${(error as Error).message}`)
  }
}

export async function prepareAgent(
  args: string[],
  useNightly = false
): Promise<void> {
  const arch = getProcessArchName()
  const platform = getProcessPlatformName()
  const temp = await tc.downloadTool(getQodanaUrl(arch, platform, useNightly))
  if (!useNightly) {
    const expectedChecksum = getQodanaSha256(arch, platform)
    const actualChecksum = sha256sum(temp)
    if (expectedChecksum !== actualChecksum) {
      core.setFailed(
        getQodanaSha256MismatchMessage(expectedChecksum, actualChecksum)
      )
    }
  }
  let extractRoot
  if (process.platform === 'win32') {
    extractRoot = await tc.extractZip(temp)
  } else {
    extractRoot = await tc.extractTar(temp)
  }
  core.addPath(
    await tc.cacheDir(extractRoot, EXECUTABLE, useNightly ? 'nightly' : VERSION)
  )
  if (!isNativeMode(args)) {
    const exitCode = await qodana(getInputs(), getQodanaPullArgs(args))
    if (exitCode !== 0) {
      core.setFailed(`qodana pull failed with exit code ${exitCode}`)
      return
    }
  }
}

/**
 * Uploads the Qodana report files from temp directory to GitHub job artifact.
 * @param resultsDir The path to upload report from.
 * @param artifactName Artifact upload name.
 * @param execute whether to execute promise or not.
 */
export async function uploadArtifacts(
  resultsDir: string,
  artifactName: string,
  execute: boolean
): Promise<void> {
  if (!execute) {
    return
  }
  try {
    const workingDir = path.dirname(resultsDir)
    const archivePath = path.join(workingDir, `${artifactName}.zip`)
    await compressFolder(resultsDir, archivePath)
    await artifact.uploadArtifact(artifactName, [archivePath], workingDir)
  } catch (error) {
    core.warning(`Failed to upload report – ${(error as Error).message}`)
  }
}

/**
 * Uploads the cache to GitHub Actions cache from the given path.
 * @param cacheDir The path to upload the cache from.
 * @param primaryKey Addition to the generated cache hash
 * @param reservedCacheKey The cache key to check if the cache already exists.
 * @param execute whether to execute promise or not.
 */
export async function uploadCaches(
  cacheDir: string,
  primaryKey: string,
  reservedCacheKey: string,
  execute: boolean
): Promise<void> {
  if (!execute) {
    return
  }
  if (primaryKey === reservedCacheKey) {
    core.info(
      `Cache with key ${primaryKey} already exists, skipping cache uploading...`
    )
    return
  }
  try {
    await cache.saveCache([cacheDir], primaryKey)
  } catch (error) {
    const errorMessage = (error as Error).message
    if (errorMessage.includes('Cache already exists.')) {
      core.info(
        `Cache with key ${primaryKey} already exists, skipping cache uploading...`
      )
    } else {
      core.warning(`Failed to upload caches – ${errorMessage}`)
    }
  }
}

/**
 * Restores the cache from GitHub Actions cache to the given path.
 * @param cacheDir The path to restore the cache to.
 * @param primaryKey The primary cache key.
 * @param additionalCacheKey The additional cache key.
 * @param execute whether to execute promise or not.
 */
export async function restoreCaches(
  cacheDir: string,
  primaryKey: string,
  additionalCacheKey: string,
  execute: boolean
): Promise<string> {
  if (!execute) {
    return ''
  }
  const restoreKeys = [additionalCacheKey].filter(k => k)
  try {
    const cacheKey = await cache.restoreCache(
      [cacheDir],
      primaryKey,
      restoreKeys
    )
    if (!cacheKey) {
      core.info(
        `No cache found for input keys: ${[primaryKey, ...restoreKeys].join(', ')}.
          With cache the pipeline would be faster.`
      )
      return ''
    }
    return cacheKey
  } catch (error) {
    core.warning(
      `Failed to restore cache with key ${primaryKey} – ${(error as Error).message}`
    )
  }
  return ''
}

/**
 * Check if need to upload the cache.
 */
export function isNeedToUploadCache(
  useCaches: boolean,
  cacheDefaultBranchOnly: boolean
): boolean {
  if (!useCaches && cacheDefaultBranchOnly) {
    core.warning(ENABLE_USE_CACHE_OPTION_WARNING)
  }

  if (useCaches && cacheDefaultBranchOnly) {
    const currentBranch = github.context.ref
    const defaultBranch = github.context.payload.repository
      ?.default_branch as string
    core.debug(
      `Current branch: ${currentBranch} | Default branch: ${defaultBranch}`
    )
    return currentBranch === `refs/heads/${defaultBranch}`
  }

  return useCaches
}

/**
 * Returns the URL to the current workflow run.
 */
export function getWorkflowRunUrl(): string {
  if (!process.env['GITHUB_REPOSITORY']) {
    return ''
  }

  const runId = github.context.runId
  const repo = github.context.repo
  const serverUrl = process.env['GITHUB_SERVER_URL'] || 'https://github.com'
  return `${serverUrl}/${repo.owner}/${repo.repo}/actions/runs/${runId}`
}

/**
 * Post a new comment to the pull request.
 * @param toolName The name of the tool to mention in comment.
 * @param content The comment to post.
 * @param sourceDir The analyzed directory inside project
 * @param postComment Whether to post a comment or not.
 */
export async function postResultsToPRComments(
  toolName: string,
  content: string,
  sourceDir: string,
  postComment: boolean
): Promise<void> {
  const pr = github.context.payload.pull_request as
    | PullRequestPayload
    | undefined
  if (!postComment || !pr) {
    return
  }
  // source dir needed in case of monorepo with projects analyzed by the same tool
  const comment_tag_pattern = getCommentTag(toolName, sourceDir)
  const body = `${content}\n${comment_tag_pattern}`
  const client = github.getOctokit(getInputs().githubToken)
  const comment_id = await findCommentByTag(client, comment_tag_pattern)
  if (comment_id !== -1) {
    await updateComment(client, comment_id, body)
  } else {
    await createComment(client, body)
  }
}

/**
 * Asynchronously finds a comment on the GitHub issue and returns its ID based on the provided tag. If the
 * comment is not found, returns -1. Utilizes GitHub's Octokit REST API client.
 *
 * @param client The Octokit REST API client to be used for searching for the comment.
 * @param tag The string to be searched for in the comments' body.
 * @returns A Promise resolving to the comment's ID if found, or -1 if not found or an error occurs.
 */
export async function findCommentByTag(
  client: InstanceType<typeof GitHub>,
  tag: string
): Promise<number> {
  try {
    const {data: comments} = await client.rest.issues.listComments({
      ...github.context.repo,
      issue_number: github.context.issue.number
    })
    const comment = comments.find(c => c?.body?.includes(tag))
    return comment ? comment.id : -1
  } catch (error) {
    core.debug(`Failed to find comment by tag – ${(error as Error).message}`)
    return -1
  }
}

/**
 * Asynchronously creates a comment on the current issue using the provided body text.
 * @param client The Octokit REST API client to be used for creating the comment.
 * @param body The text content of the comment to be created.
 * @returns A Promise that resolves when the comment is successfully created.
 */
export async function createComment(
  client: InstanceType<typeof GitHub>,
  body: string
): Promise<void> {
  try {
    await client.rest.issues.createComment({
      owner: github.context.repo.owner,
      repo: github.context.repo.repo,
      issue_number: github.context.issue.number,
      body
    })
  } catch (error) {
    core.debug(`Failed to post comment – ${(error as Error).message}`)
  }
}

/**
 * Asynchronously updates a GitHub comment with the provided `comment_id` and new content/`body`.
 * Handles any occurring errors
 * internally by debugging them.
 *
 * @param client The Octokit REST API client to be used for updating the comment.
 * @param comment_id The ID of the GitHub comment to be updated.
 * @param body The new content of the comment.
 * @returns A Promise that resolves to void after attempted comment update.
 */
export async function updateComment(
  client: InstanceType<typeof GitHub>,
  comment_id: number,
  body: string
): Promise<void> {
  try {
    await client.rest.issues.updateComment({
      owner: github.context.repo.owner,
      repo: github.context.repo.repo,
      comment_id,
      body
    })
  } catch (error) {
    core.debug(`Failed to update comment – ${(error as Error).message}`)
  }
}

/**
 * Updates the reaction of a pull request review comment to the given 'newReaction'.
 * Removes the previous reaction if 'oldReaction' is non-empty.
 *
 * @param newReaction The new reaction to be added.
 * @param oldReaction The old reaction to be removed (if non-empty).
 * @returns A Promise resolving to void.
 */
export async function putReaction(
  newReaction: Reaction,
  oldReaction: string
): Promise<void> {
  const pr = github.context.payload.pull_request as
    | PullRequestPayload
    | undefined
  if (!pr) {
    return
  }
  const client = github.getOctokit(getInputs().githubToken)
  const issue_number = pr.number

  if (oldReaction !== '') {
    try {
      const {data: reactions} = await client.rest.reactions.listForIssue({