Repository: release-drafter/release-drafter
Pull request #1089 (2022-02-28 14:45:12)

Filter by `commitish` regardless of the `refs/heads` prefix
Fixes the problem from https://github.com/release-drafter/release-drafter/pull/1065#issuecomment-1049911908

@robbinjanssen appreciate if you could test this!

robbinjanssen: @mikeroll seems to work, tested it locally!  👍 

Patch:
diff --git a/bin/generate-fixtures.js b/bin/generate-fixtures.js
index e179cfb..5d45e6f 100755
--- a/bin/generate-fixtures.js
+++ b/bin/generate-fixtures.js
@@ -3,7 +3,10 @@
 const fs = require('fs')
 const path = require('path')
 const fetch = require('node-fetch')
-const { findCommitsWithAssociatedPullRequestsQuery } = require('../lib/commits')
+const {
+  findCommitsWithAssociatedPullRequestsQuery,
+  findCommitsWithPathChangesQuery,
+} = require('../lib/commits')
 
 const REPO_NAME = 'release-drafter-test-repo'
 const GITHUB_GRAPHQL_API_ENDPOINT = 'https://api.github.com/graphql'
@@ -38,25 +41,14 @@ const repos = [
   },
 ]
 
-for (const repo of repos) {
+const runQuery = (kind, repo, body) => {
   const options = {
+    body,
     method: 'POST',
     headers: {
       'Content-Type': 'application/json',
       Authorization: `bearer ${GITHUB_TOKEN}`,
     },
-    body: JSON.stringify({
-      query: findCommitsWithAssociatedPullRequestsQuery,
-      variables: {
-        owner: repo.owner,
-        name: REPO_NAME,
-        ref: repo.branch,
-        withPullRequestBody: true,
-        withPullRequestURL: true,
-        withBaseRefName: true,
-        withHeadRefName: true,
-      },
-    }),
   }
 
   fetch(GITHUB_GRAPHQL_API_ENDPOINT, options)
@@ -72,10 +64,65 @@ for (const repo of repos) {
         path.resolve(
           __dirname,
           '../test/fixtures/__generated__',
-          `graphql-commits-${repo.branch}.json`
+          `graphql-${kind}-${repo.branch}.json`
         ),
         string + '\n'
       )
     })
     .catch(console.error)
 }
+
+for (const repo of repos) {
+  runQuery(
+    'commits',
+    repo,
+    JSON.stringify({
+      query: findCommitsWithAssociatedPullRequestsQuery,
+      variables: {
+        owner: repo.owner,
+        name: REPO_NAME,
+        targetCommitish: repo.branch,
+        withPullRequestBody: true,
+        withPullRequestURL: true,
+        withBaseRefName: true,
+        withHeadRefName: true,
+      },
+    })
+  )
+
+  runQuery(
+    'include-null-path',
+    repo,
+    JSON.stringify({
+      query: findCommitsWithPathChangesQuery,
+      variables: {
+        owner: repo.owner,
+        name: REPO_NAME,
+        targetCommitish: repo.branch,
+        withPullRequestBody: true,
+        withPullRequestURL: true,
+        withBaseRefName: true,
+        withHeadRefName: true,
+        path: null,
+      },
+    })
+  )
+
+  runQuery(
+    'include-path-src-5.md',
+    repo,
+    JSON.stringify({
+      query: findCommitsWithPathChangesQuery,
+      variables: {
+        owner: repo.owner,
+        name: REPO_NAME,
+        targetCommitish: repo.branch,
+        withPullRequestBody: true,
+        withPullRequestURL: true,
+        withBaseRefName: true,
+        withHeadRefName: true,
+        path: 'src/5.md',
+      },
+    })
+  )
+}
diff --git a/dist/index.js b/dist/index.js
index eed174c..6e5a773 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -128725,6 +128725,33 @@ const _ = __nccwpck_require__(90250)
 const { log } = __nccwpck_require__(71911)
 const { paginate } = __nccwpck_require__(46418)
 
+const findCommitsWithPathChangesQuery = /* GraphQL */ `
+  query findCommitsWithPathChangesQuery(
+    $name: String!
+    $owner: String!
+    $targetCommitish: String!
+    $since: GitTimestamp
+    $after: String
+    $path: String
+  ) {
+    repository(name: $name, owner: $owner) {
+      object(expression: $targetCommitish) {
+        ... on Commit {
+          history(path: $path, since: $since, after: $after) {
+            pageInfo {
+              hasNextPage
+              endCursor
+            }
+            nodes {
+              id
+            }
+          }
+        }
+      }
+    }
+  }
+`
+
 const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
   query findCommitsWithAssociatedPullRequests(
     $name: String!
@@ -128804,10 +128831,40 @@ const findCommitsWithAssociatedPullRequests = async ({
     withBaseRefName: config['change-template'].includes('$BASE_REF_NAME'),
     withHeadRefName: config['change-template'].includes('$HEAD_REF_NAME'),
   }
+  const includePaths = config['include-paths']
   const dataPath = ['repository', 'object', 'history']
   const repoNameWithOwner = `${owner}/${repo}`
 
-  let data, commits
+  let data,
+    allCommits,
+    includedIds = {}
+
+  if (includePaths.length > 0) {
+    var anyChanges = false
+    for (const path of includePaths) {
+      const pathData = await paginate(
+        context.octokit.graphql,
+        findCommitsWithPathChangesQuery,
+        lastRelease
+          ? { ...variables, since: lastRelease.created_at, path }
+          : { ...variables, path },
+        dataPath
+      )
+      const commitsWithPathChanges = _.get(pathData, [...dataPath, 'nodes'])
+
+      includedIds[path] = includedIds[path] || new Set([])
+      for (const { id } of commitsWithPathChanges) {
+        anyChanges = true
+        includedIds[path].add(id)
+      }
+    }
+
+    if (!anyChanges) {
+      // Short circuit to avoid blowing GraphQL budget
+      return { commits: [], pullRequests: [] }
+    }
+  }
+
   if (lastRelease) {
     log({
       context,
@@ -128822,7 +128879,7 @@ const findCommitsWithAssociatedPullRequests = async ({
     )
     // GraphQL call is inclusive of commits from the specified dates.  This means the final
     // commit from the last tag is included, so we remove this here.
-    commits = _.get(data, [...dataPath, 'nodes']).filter(
+    allCommits = _.get(data, [...dataPath, 'nodes']).filter(
       (commit) => commit.committedDate != lastRelease.created_at
     )
   } else {
@@ -128834,9 +128891,16 @@ const findCommitsWithAssociatedPullRequests = async ({
       variables,
       dataPath
     )
-    commits = _.get(data, [...dataPath, 'nodes'])
+    allCommits = _.get(data, [...dataPath, 'nodes'])
   }
 
+  const commits =
+    includePaths.length > 0
+      ? allCommits.filter((commit) =>
+          includePaths.some((path) => includedIds[path].has(commit.id))
+        )
+      : allCommits
+
   const pullRequests = _.uniqBy(
     commits.flatMap((commit) => commit.associatedPullRequests.nodes),
     'number'
@@ -128850,6 +128914,8 @@ const findCommitsWithAssociatedPullRequests = async ({
 exports.findCommitsWithAssociatedPullRequestsQuery =
   findCommitsWithAssociatedPullRequestsQuery
 
+exports.findCommitsWithPathChangesQuery = findCommitsWithPathChangesQuery
+
 exports.findCommitsWithAssociatedPullRequests =
   findCommitsWithAssociatedPullRequests
 
@@ -128944,6 +129010,7 @@ const DEFAULT_CONFIG = Object.freeze({
   categories: [],
   'exclude-labels': [],
   'include-labels': [],
+  'include-paths': [],
   'exclude-contributors': [],
   'no-contributors-template': 'No contributors',
   replacers: [],
@@ -129086,8 +129153,14 @@ const findReleases = async ({
 
   log({ context, message: `Found ${releases.length} releases` })
 
+  // `refs/heads/branch` and `branch` are the same thing in this context
+  const headRefRegex = /^refs\/heads\//
+  const targetCommitishName = targetCommitish.replace(headRefRegex, '')
   const filteredReleases = filterByCommitish
-    ? releases.filter((r) => targetCommitish.match(`/${r.target_commitish}$`))
+    ? releases.filter(
+        (r) =>
+          targetCommitishName === r.target_commitish.replace(headRefRegex, '')
+      )
     : releases
   const sortedPublishedReleases = sortReleases(
     filteredReleases.filter((r) => !r.draft)
@@ -129514,6 +129587,10 @@ const schema = (context) => {
         .items(Joi.string())
         .default(DEFAULT_CONFIG['include-labels']),
 
+      'include-paths': Joi.array()
+        .items(Joi.string())
+        .default(DEFAULT_CONFIG['include-paths']),
+
       'exclude-contributors': Joi.array()
         .items(Joi.string())
         .default(DEFAULT_CONFIG['exclude-contributors']),
diff --git a/lib/commits.js b/lib/commits.js
index b42fc30..cfbe8b9 100644
--- a/lib/commits.js
+++ b/lib/commits.js
@@ -2,6 +2,33 @@ const _ = require('lodash')
 const { log } = require('./log')
 const { paginate } = require('./pagination')
 
+const findCommitsWithPathChangesQuery = /* GraphQL */ `
+  query findCommitsWithPathChangesQuery(
+    $name: String!
+    $owner: String!
+    $targetCommitish: String!
+    $since: GitTimestamp
+    $after: String
+    $path: String
+  ) {
+    repository(name: $name, owner: $owner) {
+      object(expression: $targetCommitish) {
+        ... on Commit {
+          history(path: $path, since: $since, after: $after) {
+            pageInfo {
+              hasNextPage
+              endCursor
+            }
+            nodes {
+              id
+            }
+          }
+        }
+      }
+    }
+  }
+`
+
 const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ `
   query findCommitsWithAssociatedPullRequests(
     $name: String!
@@ -81,10 +108,40 @@ const findCommitsWithAssociatedPullRequests = async ({
     withBaseRefName: config['change-template'].includes('$BASE_REF_NAME'),
     withHeadRefName: config['change-template'].includes('$HEAD_REF_NAME'),
   }
+  const includePaths = config['include-paths']
   const dataPath = ['repository', 'object', 'history']
   const repoNameWithOwner = `${owner}/${repo}`
 
-  let data, commits
+  let data,
+    allCommits,
+    includedIds = {}
+
+  if (includePaths.length > 0) {
+    var anyChanges = false
+    for (const path of includePaths) {
+      const pathData = await paginate(
+        context.octokit.graphql,
+        findCommitsWithPathChangesQuery,
+        lastRelease
+          ? { ...variables, since: lastRelease.created_at, path }
+          : { ...variables, path },
+        dataPath
+      )
+      const commitsWithPathChanges = _.get(pathData, [...dataPath, 'nodes'])
+
+      includedIds[path] = includedIds[path] || new Set([])
+      for (const { id } of commitsWithPathChanges) {
+        anyChanges = true
+        includedIds[path].add(id)
+      }
+    }
+
+    if (!anyChanges) {
+      // Short circuit to avoid blowing GraphQL budget
+      return { commits: [], pullRequests: [] }
+    }
+  }
+
   if (lastRelease) {
     log({
       context,
@@ -99,7 +156,7 @@ const findCommitsWithAssociatedPullRequests = async ({
     )
     // GraphQL call is inclusive of commits from the specified dates.  This means the final
     // commit from the last tag is included, so we remove this here.
-    commits = _.get(data, [...dataPath, 'nodes']).filter(
+    allCommits = _.get(data, [...dataPath, 'nodes']).filter(
       (commit) => commit.committedDate != lastRelease.created_at
     )
   } else {
@@ -111,9 +168,16 @@ const findCommitsWithAssociatedPullRequests = async ({
       variables,
       dataPath
     )
-    commits = _.get(data, [...dataPath, 'nodes'])
+    allCommits = _.get(data, [...dataPath, 'nodes'])
   }
 
+  const commits =
+    includePaths.length > 0
+      ? allCommits.filter((commit) =>
+          includePaths.some((path) => includedIds[path].has(commit.id))
+        )
+      : allCommits
+
   const pullRequests = _.uniqBy(
     commits.flatMap((commit) => commit.associatedPullRequests.nodes),
     'number'
@@ -127,5 +191,7 @@ const findCommitsWithAssociatedPullRequests = async ({
 exports.findCommitsWithAssociatedPullRequestsQuery =
   findCommitsWithAssociatedPullRequestsQuery
 
+exports.findCommitsWithPathChangesQuery = findCommitsWithPathChangesQuery
+
 exports.findCommitsWithAssociatedPullRequests =
   findCommitsWithAssociatedPullRequests
diff --git a/lib/default-config.js b/lib/default-config.js
index 69b9a00..e3d8495 100644
--- a/lib/default-config.js
+++ b/lib/default-config.js
@@ -16,6 +16,7 @@ const DEFAULT_CONFIG = Object.freeze({
   categories: [],
   'exclude-labels': [],
   'include-labels': [],
+  'include-paths': [],
   'exclude-contributors': [],
   'no-contributors-template': 'No contributors',
   replacers: [],
diff --git a/lib/releases.js b/lib/releases.js
index 009c274..2881311 100644
--- a/lib/releases.js
+++ b/lib/releases.js
@@ -43,8 +43,14 @@ const findReleases = async ({
 
   log({ context, message: `Found ${releases.length} releases` })
 
+  // `refs/heads/branch` and `branch` are the same thing in this context
+  const headRefRegex = /^refs\/heads\//
+  const targetCommitishName = targetCommitish.replace(headRefRegex, '')
   const filteredReleases = filterByCommitish
-    ? releases.filter((r) => targetCommitish.match(`/${r.target_commitish}$`))
+    ? releases.filter(
+        (r) =>
+          targetCommitishName === r.target_commitish.replace(headRefRegex, '')
+      )
     : releases
   const sortedPublishedReleases = sortReleases(
     filteredReleases.filter((r) => !r.draft)
diff --git a/lib/schema.js b/lib/schema.js
index a4368cb..521ba1b 100644
--- a/lib/schema.js
+++ b/lib/schema.js
@@ -51,6 +51,10 @@ const schema = (context) => {
         .items(Joi.string())
         .default(DEFAULT_CONFIG['include-labels']),
 
+      'include-paths': Joi.array()
+        .items(Joi.string())
+        .default(DEFAULT_CONFIG['include-paths']),
+
       'exclude-contributors': Joi.array()
         .items(Joi.string())
         .default(DEFAULT_CONFIG['exclude-contributors']),


Test patch:
diff --git a/test/fixtures/__generated__/README.md b/test/fixtures/__generated__/README.md
deleted file mode 100644
index 049e74c..0000000
--- a/test/fixtures/__generated__/README.md
+++ /dev/null
@@ -1,1 +0,0 @@
-These fixtures are generated using the `bin/generate-fixtures.js` script and should be regenerated when the query in `lib/commits.js` changes. The fixtures are generated from https://github.com/TimonVS/release-drafter-test-repo.
diff --git a/test/fixtures/__generated__/graphql-include-null-path-forking.json b/test/fixtures/__generated__/graphql-include-null-path-forking.json
new file mode 100644
index 0000000..dfc21c6
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-null-path-forking.json
@@ -0,0 +1,58 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "96cf76bb3470913a449d505f2011544b661652f4 13"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjk2Y2Y3NmJiMzQ3MDkxM2E0NDlkNTA1ZjIwMTE1NDRiNjYxNjUyZjQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjlhZjJlZWMyYjY4NDA0MWY5ZDE3YmFlM2IwNTAwMjYwNGU5NGE4MzM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjYxMTAwMDY2OWJmZTRlYjQ2NjUzYTQ0YjU2MGUzNjFkYjRhZDc5MjQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjIyOWY2MWY0ODNmNDEzODFjODUwMThkNmZmMGIwZjMyYzk0NWNhMTI="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmMzYThhNDY4NmFlMDkwNzdmNDk0YzcyOGJhMzc5ZTdhMDU4ZWJiYzY="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmMwOWM2NmZkNWU2YjU2NTE3MzRlMTI1NGY4YjkzZTZkOWI1ZjI5ZmQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmNjNTliYjUxN2EwOGYxNDhkZThlM2JiMDIxOTkyNjQ1YTQwMGNlNzY="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjg1ODExZmJkYmE2OTViYjk3ZDc3YTVlMmE5NjNiMzFmNTM4YTgyMjM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmVkM2FmY2QzNmM4MDhlZWIxNWFhZjc0Zjg3MTNiODVkZTA2ZjBhMTk="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmQyMTM0MjNmOTBjZjU5Y2YxMmRiNmUwMTIxYjgwNzE1NTE4NGRiNmQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmQ5NTJjMjU1MDI5N2E4NTE1MmJlMWM4MjYwYjI1ZmI4OWYyODFkNDk="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjk0ZGViYjMwZTAyYjExNTc1YWQ0YzBmMDNmMWE1ZjdmOTJiMTFiMjg="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjNhYmJhNTkxOGZmN2QxMmZhYjMyMjA1N2ZiZGMyM2I1MzVlNzZkMDE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmI5YWI2MGVlNGI4ZjNmYTg0Mjg2NDQ0NjViZGI1YTg4ZDcyZTY1M2E="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-null-path-merge-commit.json b/test/fixtures/__generated__/graphql-include-null-path-merge-commit.json
new file mode 100644
index 0000000..6bec31f
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-null-path-merge-commit.json
@@ -0,0 +1,64 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "00de634cb5f3c0b854eb30b0afe5b1816e372ed3 15"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjAwZGU2MzRjYjVmM2MwYjg1NGViMzBiMGFmZTViMTgxNmUzNzJlZDM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmQ4NTg3ZmRiMjYzNjM5Zjc2NTYxMjQ2ZDUzN2UwNDE4YTJmODZhZWU="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjI1MzQ0MDhmZWI4MzZkYWNjNTFlYzdjMGFmNGYyNDdlN2JlMzlkNzQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjQ4ZTkwNTYwNWQ4ODkyNWI1MjJjMmIwMGY2NDZmNzQ0ZmQ5NzZkMjE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjVmNTViZThiOTA2M2IzOWQ5Y2VkYzllNWRkOGZkMDIyMjFiZDMxZjQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjg1ODExZmJkYmE2OTViYjk3ZDc3YTVlMmE5NjNiMzFmNTM4YTgyMjM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmVkM2FmY2QzNmM4MDhlZWIxNWFhZjc0Zjg3MTNiODVkZTA2ZjBhMTk="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmQyMTM0MjNmOTBjZjU5Y2YxMmRiNmUwMTIxYjgwNzE1NTE4NGRiNmQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmQ5NTJjMjU1MDI5N2E4NTE1MmJlMWM4MjYwYjI1ZmI4OWYyODFkNDk="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjFiYmJiM2FhYWFmMTk3MDYyYmRmMjg2ODhiMDk4NTI0NjcxM2NkMTI="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjNlOGE5YTc4ODA3MmRiZDcyMmMxZTkzYTU0NzEzYzI5NzEyNjI5MDY="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmI0YWZlNDc3YTZlMmU0OWFmYzdhYTU1ZWYwODMyOTU4MWJmODRiNDM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmQwMWJjZmNjNzczMWRlYzNiOTg2OWY5ZTFkYTYwMjlhYTY2NzcyNDA="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjk0ZGViYjMwZTAyYjExNTc1YWQ0YzBmMDNmMWE1ZjdmOTJiMTFiMjg="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjNhYmJhNTkxOGZmN2QxMmZhYjMyMjA1N2ZiZGMyM2I1MzVlNzZkMDE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmI5YWI2MGVlNGI4ZjNmYTg0Mjg2NDQ0NjViZGI1YTg4ZDcyZTY1M2E="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-null-path-overlapping-label.json b/test/fixtures/__generated__/graphql-include-null-path-overlapping-label.json
new file mode 100644
index 0000000..e2e4ae9
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-null-path-overlapping-label.json
@@ -0,0 +1,37 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "b2ed3f089aa1022e91e0a9d109ab876fde688c34 6"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmIyZWQzZjA4OWFhMTAyMmU5MWUwYTlkMTA5YWI4NzZmZGU2ODhjMzQ="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmI2NzdkYjY0MGQwYjljMjViYjVmYjE3NjhiMzQ3NGEyNTI5MzQ5NzM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjQ3N2FjNTBlNGNkMWMzOTRhYmJkNjcwZjFkNTkwY2I3OWJjOThhYzk="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjliYTBlNTQ4MmMwY2I1OWU0MDBmYzhmNWUyNGU1OWMyMDE2N2U5MGE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmY5YmYyMmMzNTI0MTcyODg0NTJhYmZkMzlkMGIxY2JhMmViYjk2MWE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjNhYmJhNTkxOGZmN2QxMmZhYjMyMjA1N2ZiZGMyM2I1MzVlNzZkMDE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmI5YWI2MGVlNGI4ZjNmYTg0Mjg2NDQ0NjViZGI1YTg4ZDcyZTY1M2E="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-null-path-rebase-merging.json b/test/fixtures/__generated__/graphql-include-null-path-rebase-merging.json
new file mode 100644
index 0000000..5c2bdf1
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-null-path-rebase-merging.json
@@ -0,0 +1,49 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "ed0a2bec8c895bb908638795c9da64763be75120 10"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmVkMGEyYmVjOGM4OTViYjkwODYzODc5NWM5ZGE2NDc2M2JlNzUxMjA="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmZhNDQ2NGQ3MTIyNjg1OTY3MDNjNmI0OGNiNTI4NjkzNzE3NzRlYmY="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjkxZjkzZTk1YTBhZGRlNzE0MTk4OTc2OWJlMWJhZTVmOWU2ZDQ4MDA="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjk0NjY1ZWQyMWVjN2YwMTU1MzBkMmRhMWM0ODQzMTVlNTg2NmM2NzY="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmMyMmY2MjI0OGYzZDAzMWRmMDAxYWRiZWRkNmY0NGZkOGM3YzQyZDY="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmQ5MDRkYWFmMzFmOGZkN2M4NjI3MzlkYWNlMjM0ZGViNGQ5MTA0ZmE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjRmMmEwNjkxOTRlYWY5MDAwMWM2ZDg3Y2EwN2RhNjA0ZDM1Zjc0YTA="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjJjZjU4MTc2OTZiNTExODRlNzc5MmI4ODczYWI3MTk4ODIwYmU0ZGE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjIzMmM2MjQ5MTRmMzMyZmI2MmMwZDdiYmViYmYwYWE4NGIyYjJhZWU="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjNhYmJhNTkxOGZmN2QxMmZhYjMyMjA1N2ZiZGMyM2I1MzVlNzZkMDE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmI5YWI2MGVlNGI4ZjNmYTg0Mjg2NDQ0NjViZGI1YTg4ZDcyZTY1M2E="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-null-path-squash-merging.json b/test/fixtures/__generated__/graphql-include-null-path-squash-merging.json
new file mode 100644
index 0000000..78e79c9
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-null-path-squash-merging.json
@@ -0,0 +1,37 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "3f37d85d6f8bc9e456a93600d1648fe81316ac7c 6"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjNmMzdkODVkNmY4YmM5ZTQ1NmE5MzYwMGQxNjQ4ZmU4MTMxNmFjN2M="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmViMmVjZjc4YjNjZGI0Y2VlMzc2MmZkZmQ0MjY2MmMwNWM3MzQzZTM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjc5Zjc4ODBmMTU1ZWU3ZGZkZGUxNmVlMTFmYTMwZmFhMDljODhlMjU="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmUyMTZhODViOGNlMzU2MzRkN2IyMGNlMjU5YmRmZTA4ZjFhMGJiZGI="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmQzNmM3YmU4OTBlOGEwYzY3NWYyNzUzMDk0YzYyNmQyN2RiOWI5NmM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjNhYmJhNTkxOGZmN2QxMmZhYjMyMjA1N2ZiZGMyM2I1MzVlNzZkMDE="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmI5YWI2MGVlNGI4ZjNmYTg0Mjg2NDQ0NjViZGI1YTg4ZDcyZTY1M2E="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-path-src-5.md-forking.json b/test/fixtures/__generated__/graphql-include-path-src-5.md-forking.json
new file mode 100644
index 0000000..9cf9a66
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-path-src-5.md-forking.json
@@ -0,0 +1,22 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "96cf76bb3470913a449d505f2011544b661652f4 1"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOjg1ODExZmJkYmE2OTViYjk3ZDc3YTVlMmE5NjNiMzFmNTM4YTgyMjM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MjY3MTQyMTYxOmVkM2FmY2QzNmM4MDhlZWIxNWFhZjc0Zjg3MTNiODVkZTA2ZjBhMTk="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-path-src-5.md-merge-commit.json b/test/fixtures/__generated__/graphql-include-path-src-5.md-merge-commit.json
new file mode 100644
index 0000000..dc8e24a
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-path-src-5.md-merge-commit.json
@@ -0,0 +1,22 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "00de634cb5f3c0b854eb30b0afe5b1816e372ed3 1"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjg1ODExZmJkYmE2OTViYjk3ZDc3YTVlMmE5NjNiMzFmNTM4YTgyMjM="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmVkM2FmY2QzNmM4MDhlZWIxNWFhZjc0Zjg3MTNiODVkZTA2ZjBhMTk="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-path-src-5.md-overlapping-label.json b/test/fixtures/__generated__/graphql-include-path-src-5.md-overlapping-label.json
new file mode 100644
index 0000000..1fe9af6
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-path-src-5.md-overlapping-label.json
@@ -0,0 +1,19 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "b2ed3f089aa1022e91e0a9d109ab876fde688c34 0"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmIyZWQzZjA4OWFhMTAyMmU5MWUwYTlkMTA5YWI4NzZmZGU2ODhjMzQ="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-path-src-5.md-rebase-merging.json b/test/fixtures/__generated__/graphql-include-path-src-5.md-rebase-merging.json
new file mode 100644
index 0000000..0af6b00
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-path-src-5.md-rebase-merging.json
@@ -0,0 +1,22 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "ed0a2bec8c895bb908638795c9da64763be75120 1"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmVkMGEyYmVjOGM4OTViYjkwODYzODc5NWM5ZGE2NDc2M2JlNzUxMjA="
+            },
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOmZhNDQ2NGQ3MTIyNjg1OTY3MDNjNmI0OGNiNTI4NjkzNzE3NzRlYmY="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/__generated__/graphql-include-path-src-5.md-squash-merging.json b/test/fixtures/__generated__/graphql-include-path-src-5.md-squash-merging.json
new file mode 100644
index 0000000..cac6948
--- /dev/null
+++ b/test/fixtures/__generated__/graphql-include-path-src-5.md-squash-merging.json
@@ -0,0 +1,19 @@
+{
+  "data": {
+    "repository": {
+      "object": {
+        "history": {
+          "pageInfo": {
+            "hasNextPage": false,
+            "endCursor": "3f37d85d6f8bc9e456a93600d1648fe81316ac7c 0"
+          },
+          "nodes": [
+            {
+              "id": "MDY6Q29tbWl0MTgzNzY2OTUzOjNmMzdkODVkNmY4YmM5ZTQ1NmE5MzYwMGQxNjQ4ZmU4MTMxNmFjN2M="
+            }
+          ]
+        }
+      }
+    }
+  }
+}
diff --git a/test/fixtures/config/config-with-include-paths.yml b/test/fixtures/config/config-with-include-paths.yml
new file mode 100644
index 0000000..882f96f
--- /dev/null
+++ b/test/fixtures/config/config-with-include-paths.yml
@@ -0,0 +1,8 @@
+template: |
+  # What's Changed
+  $CHANGES
+name-template: 'v$INPUT_VERSION (Code name: Placeholder)'
+tag-template: v$INPUT_VERSION
+version-template: $MAJOR.$MINOR.$PATCH
+include-paths:
+  - src/5.md
diff --git a/test/index.test.js b/test/index.test.js
index 3f26714..78da5ca 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -13,6 +13,8 @@ const release3Payload = require('./fixtures/release-3.json')
 const pushNonMasterPayload = require('./fixtures/push-non-master-branch.json')
 const graphqlCommitsNoPRsPayload = require('./fixtures/graphql-commits-no-prs.json')
 const graphqlCommitsMergeCommit = require('./fixtures/__generated__/graphql-commits-merge-commit.json')
+const graphqlNullIncludePathMergeCommit = require('./fixtures/__generated__/graphql-include-null-path-merge-commit.json')
+const graphqlIncludePathMergeCommit = require('./fixtures/__generated__/graphql-include-path-src-5.md-merge-commit.json')
 const graphqlCommitsEmpty = require('./fixtures/graphql-commits-empty.json')
 const releaseDrafterFixture = require('./fixtures/release-draft.json')
 const graphqlCommitsOverlappingLabel = require('./fixtures/__generated__/graphql-commits-overlapping-label.json')
@@ -2118,6 +2120,112 @@ describe('release-drafter', () => {
     })
   })
 
+  describe('with include-paths config', () => {
+    it('returns all PRs when not path filtered', async () => {
+      getConfigMock('config-with-include-paths.yml')
+
+      nock('https://api.github.com')
+        .post('/graphql', (body) =>
+          body.query.includes('query findCommitsWithAssociatedPullRequests')
+        )
+        .reply(200, graphqlCommitsMergeCommit)
+        .post('/graphql', (body) =>
+          body.query.includes('query findCommitsWithPathChangesQuery')
+        )
+        .reply(200, graphqlNullIncludePathMergeCommit)
+
+      nock('https://api.github.com')
+        .get(
+          '/repos/toolmantim/release-drafter-test-project/releases?per_page=100'
+        )
+        .reply(200, [])
+
+      nock('https://api.github.com')
+        .post(
+          '/repos/toolmantim/release-drafter-test-project/releases',
+          (body) => {
+            expect(body).toMatchInlineSnapshot(`
+              Object {
+                "body": "# What's Changed
+              * Add documentation (#5) @TimonVS
+              * Update dependencies (#4) @TimonVS
+              * Bug fixes (#3) @TimonVS
+              * Add big feature (#2) @TimonVS
+              * 👽 Add alien technology (#1) @TimonVS
+              ",
+                "draft": true,
+                "name": "v$INPUT_VERSION (Code name: Placeholder)",
+                "prerelease": false,
+                "tag_name": "v$INPUT_VERSION",
+                "target_commitish": "refs/heads/master",
+              }
+            `)
+            return true
+          }
+        )
+        .reply(200, releasePayload)
+
+      const payload = pushPayload
+
+      await probot.receive({
+        name: 'push',
+        payload,
+      })
+
+      expect.assertions(1)
+    })
+
+    it('returns the modified paths', async () => {
+      getConfigMock('config-with-include-paths.yml')
+
+      nock('https://api.github.com')
+        .post('/graphql', (body) =>
+          body.query.includes('query findCommitsWithAssociatedPullRequests')
+        )
+        .reply(200, graphqlCommitsMergeCommit)
+        .post('/graphql', (body) =>
+          body.query.includes('query findCommitsWithPathChangesQuery')
+        )
+        .reply(200, graphqlIncludePathMergeCommit)
+
+      nock('https://api.github.com')
+        .get(
+          '/repos/toolmantim/release-drafter-test-project/releases?per_page=100'
+        )
+        .reply(200, [])
+
+      nock('https://api.github.com')
+        .post(
+          '/repos/toolmantim/release-drafter-test-project/releases',
+          (body) => {
+            expect(body).toMatchInlineSnapshot(`
+              Object {
+                "body": "# What's Changed
+              * Add documentation (#5) @TimonVS
+              ",
+                "draft": true,
+                "name": "v$INPUT_VERSION (Code name: Placeholder)",
+                "prerelease": false,
+                "tag_name": "v$INPUT_VERSION",
+                "target_commitish": "refs/heads/master",
+              }
+            `)
+            return true
+          }
+        )
+        .reply(200, releasePayload)
+
+      const payload = pushPayload
+
+      await probot.receive({
+        name: 'push',
+        payload,
+      })
+
+      expect.assertions(1)
+    })
+  })
+
   describe('config error handling', () => {
     it('schema error', async () => {
       getConfigMock('config-with-schema-error.yml')