From 1ce8b4abaa6ce6ba60e7b14d226927d8a83f1dc2 Mon Sep 17 00:00:00 2001 From: Devon Stewart Date: Thu, 24 Feb 2022 15:10:19 -0800 Subject: [PATCH] include-paths: Only consider PRs that modify these paths for release notes --- action.yml | 5 +++ index.js | 7 ++++ lib/commits.js | 75 +++++++++++++++++++++++++++++++++++++++++-- lib/default-config.js | 1 + lib/schema.js | 4 +++ 5 files changed, 89 insertions(+), 3 deletions(-) diff --git a/action.yml b/action.yml index 6d02f059fc..26724fcc9e 100644 --- a/action.yml +++ b/action.yml @@ -53,6 +53,11 @@ inputs: A boolean indicating whether the autolabeler mode is disabled. required: false default: '' + include-paths: + description: | + A list of paths for which associated PRs should be considered + required: false + default: '' outputs: id: description: The ID of therelease that was created or updated. diff --git a/index.js b/index.js index a4babd9d7c..68253ec70e 100644 --- a/index.js +++ b/index.js @@ -139,6 +139,7 @@ module.exports = (app, { getRouter }) => { name, disableReleaser, commitish, + includePaths, } = getInput() const config = await getConfig({ @@ -150,6 +151,8 @@ module.exports = (app, { getRouter }) => { if (config === null || disableReleaser) return + const includePaths = config['include-paths'] + // GitHub Actions merge payloads slightly differ, in that their ref points // to the PR branch instead of refs/heads/master const ref = process.env['GITHUB_REF'] || context.payload.ref @@ -173,6 +176,7 @@ module.exports = (app, { getRouter }) => { targetCommitish, lastRelease, config, + includePaths, }) const sortedMergedPullRequests = sortPullRequests( @@ -239,6 +243,9 @@ function getInput({ config } = {}) { disableAutolabeler: core.getInput('disable-autolabeler').toLowerCase() === 'true', commitish: core.getInput('commitish') || undefined, + includePaths: (core.getInput('include-paths') || '') + .split(',') + .filter((x) => Boolean(x)), } } diff --git a/lib/commits.js b/lib/commits.js index b42fc30e8f..ec2eca950a 100644 --- a/lib/commits.js +++ b/lib/commits.js @@ -2,6 +2,34 @@ const _ = require('lodash') const { log } = require('./log') const { paginate } = require('./pagination') +const findCommitsWithPathChangesQuery = ({ includePaths }) => /* GraphQL */ ` + query findCommitsWithPathChangesQuery( + $name: String! + $owner: String! + $targetCommitish: String! + $since: GitTimestamp + $after: String + ) { + repository(name: $name, owner: $owner) { + object(expression: $targetCommitish) { + ... on Commit { + ${includePaths + .map( + (path, idx) => `\ + path${idx}: history(path: "${path}", since: $since, after: $after) { + nodes { + id + } + } + ` + ) + .join('\n')} + } + } + } + } +` + const findCommitsWithAssociatedPullRequestsQuery = /* GraphQL */ ` query findCommitsWithAssociatedPullRequests( $name: String! @@ -70,6 +98,7 @@ const findCommitsWithAssociatedPullRequests = async ({ targetCommitish, lastRelease, config, + includePaths, }) => { const { owner, repo } = context.repo() const variables = { @@ -84,7 +113,38 @@ const findCommitsWithAssociatedPullRequests = async ({ const dataPath = ['repository', 'object', 'history'] const repoNameWithOwner = `${owner}/${repo}` - let data, commits + let data, + allCommits, + includedIds = {} + + if (includePaths.length > 0) { + const commitsWithPathChanges = await context.octokit.graphql( + findCommitsWithPathChangesQuery({ + includePaths, + }), + lastRelease ? { ...variables, since: lastRelease.created_at } : variables + ) + + var anyChanges = false + for (const [idx, path] of includePaths.entries()) { + const { nodes } = _.get(commitsWithPathChanges, [ + 'repository', + 'object', + `path${idx}`, + ]) + includedIds[path] = includedIds[path] || new Set([]) + for (const { id } of nodes) { + anyChanges = true + includedIds[path].add(id) + } + } + + if (!anyChanges) { + // Short circuit to avoid blowing GraphQL budget + return { commits: [], pullRequests: [] } + } + } + if (lastRelease) { log({ context, @@ -99,7 +159,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 +171,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 +194,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 69b9a00f3c..e3d8495e66 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 a4368cb79e..521ba1b1a5 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']),