From 190bee61c6c69cd2d8780b20962abce9b4261972 Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Thu, 24 Feb 2022 15:43:41 -0800 Subject: [PATCH 1/4] Fix regenerate-fixtures after #1065 ref was renamed --- bin/generate-fixtures.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/generate-fixtures.js b/bin/generate-fixtures.js index e179cfb31..482276192 100755 --- a/bin/generate-fixtures.js +++ b/bin/generate-fixtures.js @@ -50,7 +50,7 @@ for (const repo of repos) { variables: { owner: repo.owner, name: REPO_NAME, - ref: repo.branch, + targetCommitish: repo.branch, withPullRequestBody: true, withPullRequestURL: true, withBaseRefName: true, From 474458ed218d287dede318aeada55a5b62440ad6 Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Thu, 24 Feb 2022 15:47:57 -0800 Subject: [PATCH 2/4] Break out runQuery --- bin/generate-fixtures.js | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/bin/generate-fixtures.js b/bin/generate-fixtures.js index 482276192..f4ce8c6f7 100755 --- a/bin/generate-fixtures.js +++ b/bin/generate-fixtures.js @@ -38,25 +38,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, - targetCommitish: repo.branch, - withPullRequestBody: true, - withPullRequestURL: true, - withBaseRefName: true, - withHeadRefName: true, - }, - }), } fetch(GITHUB_GRAPHQL_API_ENDPOINT, options) @@ -72,10 +61,29 @@ 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, + }, + }) + ) +} From 998acd26137bc1ea6fa524de91aa82571deb4646 Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Thu, 3 Mar 2022 09:18:06 -0800 Subject: [PATCH 3/4] include-paths: Only consider PRs that modify these paths for release notes --- bin/generate-fixtures.js | 41 ++++++- lib/commits.js | 72 +++++++++++- lib/default-config.js | 1 + lib/schema.js | 4 + test/fixtures/__generated__/README.md | 1 - .../graphql-include-null-path-forking.json | 58 ++++++++++ ...raphql-include-null-path-merge-commit.json | 64 +++++++++++ ...l-include-null-path-overlapping-label.json | 37 ++++++ ...phql-include-null-path-rebase-merging.json | 49 ++++++++ ...phql-include-null-path-squash-merging.json | 37 ++++++ ...graphql-include-path-src-5.md-forking.json | 22 ++++ ...ql-include-path-src-5.md-merge-commit.json | 22 ++++ ...clude-path-src-5.md-overlapping-label.json | 19 +++ ...-include-path-src-5.md-rebase-merging.json | 22 ++++ ...-include-path-src-5.md-squash-merging.json | 19 +++ .../config/config-with-include-paths.yml | 8 ++ test/index.test.js | 108 ++++++++++++++++++ 17 files changed, 579 insertions(+), 5 deletions(-) delete mode 100644 test/fixtures/__generated__/README.md create mode 100644 test/fixtures/__generated__/graphql-include-null-path-forking.json create mode 100644 test/fixtures/__generated__/graphql-include-null-path-merge-commit.json create mode 100644 test/fixtures/__generated__/graphql-include-null-path-overlapping-label.json create mode 100644 test/fixtures/__generated__/graphql-include-null-path-rebase-merging.json create mode 100644 test/fixtures/__generated__/graphql-include-null-path-squash-merging.json create mode 100644 test/fixtures/__generated__/graphql-include-path-src-5.md-forking.json create mode 100644 test/fixtures/__generated__/graphql-include-path-src-5.md-merge-commit.json create mode 100644 test/fixtures/__generated__/graphql-include-path-src-5.md-overlapping-label.json create mode 100644 test/fixtures/__generated__/graphql-include-path-src-5.md-rebase-merging.json create mode 100644 test/fixtures/__generated__/graphql-include-path-src-5.md-squash-merging.json create mode 100644 test/fixtures/config/config-with-include-paths.yml diff --git a/bin/generate-fixtures.js b/bin/generate-fixtures.js index f4ce8c6f7..5d45e6fad 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' @@ -86,4 +89,40 @@ for (const repo of repos) { }, }) ) + + 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/lib/commits.js b/lib/commits.js index b42fc30e8..cfbe8b9c4 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 69b9a00f3..e3d8495e6 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/schema.js b/lib/schema.js index a4368cb79..521ba1b1a 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']), diff --git a/test/fixtures/__generated__/README.md b/test/fixtures/__generated__/README.md deleted file mode 100644 index 049e74c10..000000000 --- a/test/fixtures/__generated__/README.md +++ /dev/null @@ -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 000000000..dfc21c6bb --- /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 000000000..6bec31f1a --- /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 000000000..e2e4ae9cd --- /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 000000000..5c2bdf1ef --- /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 000000000..78e79c9a6 --- /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 000000000..9cf9a661f --- /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 000000000..dc8e24a9f --- /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 000000000..1fe9af696 --- /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 000000000..0af6b000c --- /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 000000000..cac694863 --- /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 000000000..882f96fe7 --- /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 3f2671444..78da5ca41 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') From b64b7eb07b16df1fac2fb91a6c0606fcd6742730 Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Wed, 2 Mar 2022 21:44:34 -0800 Subject: [PATCH 4/4] dist --- dist/index.js | 77 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/dist/index.js b/dist/index.js index da8b64ac7..7d6a58b26 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: [], @@ -129503,6 +129570,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']),