diff --git a/publish/package.json b/publish/package.json index 98de35c..f01e6b8 100644 --- a/publish/package.json +++ b/publish/package.json @@ -1,6 +1,6 @@ { "name": "publish", - "version": "1.0.18", + "version": "1.0.19", "description": "An action that decides if a new release should be published", "scripts": { "build": "tsup", diff --git a/version-metadata/README.md b/version-metadata/README.md index 306c549..ff28ca1 100644 --- a/version-metadata/README.md +++ b/version-metadata/README.md @@ -8,6 +8,12 @@ Using this you can easily automate publishing to a package registry such as NPM This action only computes metadata and doesn't push git tags, publishes a package, creates github releases, etc. +This action has explicit support for the following event types: +- `push` +- `pull_request` +- `issue_comment` (on pull requests) +- `workflow_dispatch` (given ref must be associated with exactly one pull request) +- `merge_group` ## Usage diff --git a/version-metadata/package.json b/version-metadata/package.json index 3d08403..f6166f5 100644 --- a/version-metadata/package.json +++ b/version-metadata/package.json @@ -1,6 +1,6 @@ { "name": "version-metadata", - "version": "1.0.18", + "version": "1.0.19", "description": "An action that checks wether your package.json version has been updated with a bit of additional metadata", "scripts": { "build": "tsup", diff --git a/version-metadata/src/index.ts b/version-metadata/src/index.ts index c55adc3..88f82d0 100644 --- a/version-metadata/src/index.ts +++ b/version-metadata/src/index.ts @@ -98,7 +98,7 @@ async function run(): Promise { // all resolved correctly, just not "the bigger picture" (meaning all useful types in one place). const octokit = getOctokit(token) - let { base: maybeBase, head } = determineBaseAndHead(context) + let { base: maybeBase, head } = await determineBaseAndHead(octokit, context) let baseCommitIsIncludedInRange = true diff --git a/version-metadata/src/utils.ts b/version-metadata/src/utils.ts index 1155344..a9a7b43 100644 --- a/version-metadata/src/utils.ts +++ b/version-metadata/src/utils.ts @@ -2,10 +2,12 @@ import { spawnSync } from 'child_process' import type { Context } from '@actions/github/lib/context' import type { getOctokit } from '@actions/github' +type Octokit = ReturnType + export type VersionDiffType = 'major' | 'minor' | 'patch' | 'pre-release' export type Unpromise = T extends Promise ? U : never -export type Commit = Unpromise['rest']['repos']['getCommit']>>['data'] +export type Commit = Unpromise>['data'] type File = { status: 'added' | 'modified' | 'removed' | 'renamed' | 'copied' | 'changed' | 'unchanged' @@ -177,6 +179,56 @@ const computeResponseFromChanges = ( } } +const getPullRequestFromIssueComment = (octokit: Octokit, context: Context) => { + const issue = context.payload.issue + + if (!issue) { + throw new Error('issue_comment event payload does not contain issue information.') + } + + if (!issue.pull_request || !issue.pull_request.url) { + throw new Error('The issue comment is not made on a pull request.') + } + + return octokit.rest.pulls + .get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: issue.number + }) + .then((res) => res.data) +} + +const getPullRequestFromWorkflowDispatch = async (octokit: Octokit, context: Context) => { + const ref = context.payload.ref + + if (!ref) { + throw new Error('workflow_dispatch event does not contain a ref.') + } + + if (!ref.startsWith('refs/heads/')) { + throw new Error('workflow_dispatch only works refs pointing to branches associated with a pull request.') + } + + const branchName = ref.replace('refs/heads/', '') + + const prs = await octokit.rest.pulls + .list({ + owner: context.repo.owner, + repo: context.repo.repo, + head: `${context.repo.owner}:${branchName}` + }) + .then((res) => res.data) + + if (prs.length !== 1) { + throw new Error( + `workflow_dispatch event on branch '${branchName}' is associated with ${prs.length} pull requests, expected exactly one.` + ) + } + + return prs[0] +} + /** * Determines the base and head commits from the payload * @@ -196,7 +248,7 @@ const computeResponseFromChanges = ( * * (*): For pushes which create a new branch, context.payload.before is all zeroes (40 to be exact), in this case base is returned as undefined */ -const determineBaseAndHead = (context: Context) => { +const determineBaseAndHead = async (octokit: Octokit, context: Context) => { // Define the base and head commits to be extracted from the payload. let base: string | undefined let head: string | undefined @@ -206,6 +258,20 @@ const determineBaseAndHead = (context: Context) => { base = context.payload.pull_request?.base?.sha head = context.payload.pull_request?.head?.sha break + case 'issue_comment': + { + const pullRequest = await getPullRequestFromIssueComment(octokit, context) + base = pullRequest.base?.sha + head = pullRequest.head?.sha + } + break + case 'workflow_dispatch': + { + const pullRequest = await getPullRequestFromWorkflowDispatch(octokit, context) + base = pullRequest.base?.sha + head = pullRequest.head?.sha + } + break case 'push': base = context.payload.before head = context.payload.after @@ -225,7 +291,7 @@ const determineBaseAndHead = (context: Context) => { break default: throw new Error( - `This action only supports pull requests, pushes and merge_groups. ${context.eventName} events are not supported. ` + + `This action only supports pull_request, push, issue_comment, workflow_dispatch and merge_group as event types. ${context.eventName} events are not supported. ` + "Please submit an issue on this action's GitHub repo if you believe this in correct." ) } @@ -241,7 +307,7 @@ const determineBaseAndHead = (context: Context) => { return { base, head } } -const getCommit = (octokit: ReturnType, context: Context, ref: string) => +const getCommit = (octokit: Octokit, context: Context, ref: string) => octokit.rest.repos .getCommit({ owner: context.repo.owner, @@ -256,7 +322,7 @@ const getCommit = (octokit: ReturnType, context: Context, ref * * @returns the found commit and whether it is an initial commit or not (type: 'initial' | 'normal') */ -const backtrackToFirstBranchRef = async (octokit: ReturnType, context: Context, headSha: string) => { +const backtrackToFirstBranchRef = async (octokit: Octokit, context: Context, headSha: string) => { const { data: allBranches } = await octokit.rest.repos.listBranches({ owner: context.repo.owner, repo: context.repo.repo @@ -344,7 +410,7 @@ const prependCommitToCommitComparison = (commit: Commit, comparison: CompareComm } const compareCommits = async ( - octokit: ReturnType, + octokit: Octokit, context: Context, base: string, head: string, diff --git a/version-metadata/test/index.mjs b/version-metadata/test/index.mjs index 852e183..7b909fe 100755 --- a/version-metadata/test/index.mjs +++ b/version-metadata/test/index.mjs @@ -87,6 +87,38 @@ const tests = [ oldVersion: '1.0.0', newVersion: '1.0.0' } + }, + { + description: + 'Triggers on a pull request comment (`issue_comment`), looks up the corresponding PR, and then continues same as with a `pull_request` event', + pr: '17', + branch: 'testcase-dont-delete-pr', + base: '', + head: '', + repo: 'Quantco/ui-actions', + event: 'issue_comment', + file: 'version-metadata/package.json', + expected: { + changed: false, + oldVersion: '1.0.18', + newVersion: '1.0.18' + } + }, + { + description: + 'Triggers on `workflow_dispatch` event, uses the provided `ref` to look up associated pull requests, then continues same as with a `pull_request` event', + pr: '17', + branch: 'testcase-dont-delete-pr', + base: '', + head: '', + repo: 'Quantco/ui-actions', + event: 'workflow_dispatch', + file: 'version-metadata/package.json', + expected: { + changed: false, + oldVersion: '1.0.18', + newVersion: '1.0.18' + } } ] diff --git a/version-metadata/test/testrunner.mjs b/version-metadata/test/testrunner.mjs index e173259..2b478e1 100644 --- a/version-metadata/test/testrunner.mjs +++ b/version-metadata/test/testrunner.mjs @@ -32,6 +32,13 @@ const runTest = (test) => { sha: test.head } }, + issue: { + number: test.pr, + pull_request: { + url: `https://api.github.com/repos/${test.repo}/pulls/${test.pr}` + } + }, + ref: `refs/heads/${test.branch}`, before: test.base, after: test.head }