Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions GitForWindowsHelper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ module.exports = async function (context, req) {
return withStatus(500, undefined, e.message || JSON.stringify(e, null, 2))
}

try {
const {addIssueToCurrentMilestone} = require('./update-milestones')
if (req.headers['x-github-event'] === 'pull_request'
&& ['git-for-windows/build-extra', 'git-for-windows/MINGW-packages', 'git-for-windows/MSYS2-packages'].includes(req.body.repository.full_name)
&& req.body.action === 'closed'
&& req.body.pull_request.merged === 'true') return ok(await addIssueToCurrentMilestone(context, req))
} catch (e) {
context.log(e)
return withStatus(500, undefined, e.message || JSON.stringify(e, null, 2))
}

try {
const { cascadingRuns, handlePush } = require('./cascading-runs.js')
if (req.headers['x-github-event'] === 'check_run'
Expand Down
62 changes: 62 additions & 0 deletions GitForWindowsHelper/update-milestones.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const addIssueToCurrentMilestone= async (context, req) => {
if (req.body.action !== 'closed') return "Nothing to do here: PR has not been closed"
if (req.body.pull_request.merged !== 'true') return "Nothing to do here: PR has been closed, but not by merging"

const owner = 'git-for-windows'
const repo = 'git'
const sender = req.body.sender.login

const getToken = (() => {
let token

const get = async () => {
const getInstallationIdForRepo = require('./get-installation-id-for-repo')
const installationId = await getInstallationIdForRepo(context, owner, repo)
const getInstallationAccessToken = require('./get-installation-access-token')
return await getInstallationAccessToken(context, installationId)
}

return async () => token || (token = await get())
})()

const isAllowed = async (login) => {
if (login === 'gitforwindowshelper[bot]') return true
const getCollaboratorPermissions = require('./get-collaborator-permissions')
const token = await getToken()
const permission = await getCollaboratorPermissions(context, token, owner, repo, login)
return ['ADMIN', 'MAINTAIN', 'WRITE'].includes(permission.toString())
}

if (!await isAllowed(sender)) throw new Error(`${sender} is not allowed to do that`)
Comment on lines +9 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it is about high time to move this (duplicated) logic into a central place instead, and maybe now would be as good a time as any to do that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That thought has crossed my mind as well.


const githubApiRequest = require('./github-api-request')

const candidates = req.body.pull_request.body.match(/(?:[Cc]loses|[Ff]ixes) (?:https:\/\/github\.com\/git-for-windows\/git\/issues\/|#)(\d+)/)

if (candidates.length !== 1) throw new Error(`Expected 1 candidate issue, got ${candidates.length}`)
Comment on lines +34 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking for something more robust, and while there does not seem anything useful in GitHub's REST API, there is something in the GraphQL API. Try this, for example:

gh api graphql -f query='{
  repository(owner: "git-for-windows", name: "MSYS2-packages") {
    pullRequest(number: 236) {
      closingIssuesReferences(first: 10) {
        nodes {
          number
          repository {
            owner {
              login
            }
            name
          }
          title
          url
        }
      }
    }
  }
}'

Over here, this returns this highly useful answer:

{
  "data": {                                                                                                                 "repository": {
      "pullRequest": {
        "closingIssuesReferences": {
          "nodes": [
            {
              "number": 5656,
              "repository": {
                "owner": {
                  "login": "git-for-windows"
                },
                "name": "git"
              },
              "title": "[New curl version] 8.14.1",
              "url": "https://github.com/git-for-windows/git/issues/5656"                                                           }
          ]
        }
      }
    }
  }
}

We already have a precedent of a GraphQL call when looking for the "collaborator permission", so we could something similar here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does look immensely more helpful than the REST API


const { getCurrentMilestone } = require('./GitForWindowsHelper/milestones')
const current = await getCurrentMilestone(console, await getToken(), owner, repo)

const issueNumber = candidates[0]
const issue = await githubApiRequest(context, await getToken(), 'GET', `/repos/${owner}/${repo}/issues/${issueNumber}`)

if (issue.labels.length>0){
for (const label of issue.labels) {
if (label.name === "component-update"){
await githubApiRequest(context, await getToken(), 'PATCH', `/repos/${owner}/${repo}/issues/${issueNumber}`, {
milestone: current.id
})

return `Added issue ${issueNumber} to milestone "Next release"`
}
}
}

throw new Error(`Issue ${issueNumber} isn't a component update`)
}


module.exports = {
addIssueToCurrentMilestone,
}