---
name: Backported Dependency Changes

on:
  # The pull_request_target trigger event allows PRs raised from forks to have write permissions and access secrets.
  # We uses it in this workflow to enable writing comments to the PR.
  pull_request_target:
    types:
      - opened
      - synchronize
      - labeled
      - unlabeled
  pull_request:
    types:
      - opened
      - synchronize
      - labeled
      - unlabeled

# This workflow runs for not-yet-reviewed external contributions.
# Following a pull_request_target trigger the workflow would have write permissions,
# so we intentionally restrict the permissions to only include write access on pull-requests.
permissions:
    contents: read
    pull-requests: write

jobs:
  deps-change-comment:
    runs-on: ubuntu-latest
    steps:
      - name: "Identify if go.mod files have changed"
        uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
        id: changedfiles
        with:
            filters: |
                root-go-mod:
                    - 'go.mod'
                nested-go-mod:
                    - '**/*/go.mod'
            list-files: json

      # This step will create or delete an existing comment; responds to changes in the PR.
      - name: "Comment on PR if necessary"
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
        with:
            script: |
              // SETUP - values needed for function definitions below.
              const commentStart = "## Backported dependency change";
              
              const { number: issue_number } = context.issue;
              const { owner, repo } = context.repo;
              
              // List all comments
              const allComments = (await github.rest.issues.listComments({
                  issue_number,
                  owner,
                  repo,
              })).data;
              const existingComment = allComments.find(c => c.body.startsWith(commentStart));
              const comment_id = existingComment?.id;

              async function createOrUpdateComment(commentDetails) {
                const body = commentStart + "\n\n" + commentDetails;
                let resp
                if (existingComment) {
                    resp = await github.rest.issues.updateComment({
                        owner,
                        repo,
                        comment_id,
                        body,
                    });
                } else {
                    resp = await github.rest.issues.createComment({
                        owner,
                        repo,
                        issue_number,
                        body,
                    });
                }
                if (resp.status != 200){
                  console.error("creating/updating comment failed, here's the response:", resp )
                  core.setFailed("creating/updating comment failed with status code " + resp.status)
                }
              }

              async function deleteCommentIfExists() {
                if (existingComment) {
                  const resp = await github.rest.issues.deleteComment({
                      owner,
                      repo,
                      comment_id,
                  });
                  if (resp.status >= 300 ){
                    // Allow all status codes in 2XX range; deleting a non-existing comment is 204
                    console.error("deleting comment failed, here's the response:", resp )
                    core.setFailed("deleting comment failed with status code " + resp.status)
                  }
                }
              }

              async function getPrLabels() {
                const labelsResp = await github.rest.issues.listLabelsOnIssue({
                          owner,
                          repo,
                          issue_number,
                      });
                if (labelsResp.status != 200){
                  console.error("getting the PR's labels failed, here's the response:", resp )
                  core.setFailed("getting the PR's labels failed with status code " + resp.status)
                }
                return labelsResp
              }

              // INSPECT PR & UPDATE COMMENT

              const labels = await getPrLabels()
              const filteredLabels = labels.data.filter( label => {
                return label.name.includes("backport")
              })
              const hasBackportLabel = filteredLabels.length > 0
              const changedRootGoMod = ${{steps.changedfiles.outputs.root-go-mod}};
              const changedNestedGoMod = ${{steps.changedfiles.outputs.nested-go-mod}};
              const changesPresent = changedRootGoMod || changedNestedGoMod
              if (!changesPresent){
                console.log("This PR isn't attempting to change dependencies. No comment needed.")
                await deleteCommentIfExists()
              } else if (!hasBackportLabel) {
                console.log(`This PR contains changes to dependency-related files but doesn't have a backport label. No comment needed.` + 
                  `\nChanged root go.mod? = ${changedRootGoMod}`+ 
                  `\nChanged a nested go.mod? = ${changedNestedGoMod}`)
                await deleteCommentIfExists()
              } else {
                console.log("This PR contains changes to dependency-related files and is labelled for backport. Making sure comment is present.")
                const comment = "This PR makes changes to dependencies in go.mod file(s) and is labelled for backport.\n\n" +
                                "Notice to the maintainer: Before merging the backport of this PR please follow our security scanning processes."
                await createOrUpdateComment(comment)
              }