diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8bceb78..fb59aa7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: build: # make sure build/ci work properly runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # As of 2023-02-22, the ubuntu-latest image uses npm v8.3.1. This version # has a bug that prevents loading dependency-submission-toolkit in the # 'example/' project. npm v8.4.1 (same version in Codespace created on diff --git a/ownership.yaml b/ownership.yaml index 3524793..f31eae5 100644 --- a/ownership.yaml +++ b/ownership.yaml @@ -16,7 +16,6 @@ ownership: exec_sponsor: jacobdepriest product_manager: courtneycl team_slack: dependency-graph - ops_slack: dg-alerts qos: experimental tier: 3 sev1: diff --git a/package-lock.json b/package-lock.json index 7ee817c..5cbbc56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@actions/exec": "^1.1.1", "@actions/github": "^5.0.0", "@octokit/rest": "^18.12.0", + "@octokit/webhooks-types": "^6.10.0", "openapi-typescript": "^5.2.0", "packageurl-js": "0.0.6" }, @@ -1314,6 +1315,11 @@ "@octokit/openapi-types": "^12.11.0" } }, + "node_modules/@octokit/webhooks-types": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-6.10.0.tgz", + "integrity": "sha512-lDNv83BeEyxxukdQ0UttiUXawk9+6DkdjjFtm2GFED+24IQhTVaoSbwV9vWWKONyGLzRmCQqZmoEWkDhkEmPlw==" + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", diff --git a/package.json b/package.json index 4fc02d2..c66292e 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@actions/exec": "^1.1.1", "@actions/github": "^5.0.0", "@octokit/rest": "^18.12.0", + "@octokit/webhooks-types": "^6.10.0", "openapi-typescript": "^5.2.0", "packageurl-js": "0.0.6" }, diff --git a/src/snapshot.test.ts b/src/snapshot.test.ts index e42b104..ce4b233 100644 --- a/src/snapshot.test.ts +++ b/src/snapshot.test.ts @@ -2,7 +2,7 @@ import { context } from '@actions/github' import { Manifest } from './manifest' import { PackageCache } from './package-cache' -import { Snapshot } from './snapshot' +import { shaFromContext, Snapshot } from './snapshot' function roundTripJSON(obj: any): object { return JSON.parse(JSON.stringify(obj)) @@ -20,20 +20,17 @@ manifest.addDirectDependency( manifest.addIndirectDependency(cache.package('pkg:npm/%40actions/core@1.6.0')) // add bogus git data to the context -context.sha = '0000000000000000000000000000000000000000' +context.sha = '1000000000000000000000000000000000000000' context.ref = 'foo/bar/baz' +context.eventName = 'push' describe('Snapshot', () => { it('renders expected JSON', () => { const snapshot = new Snapshot( - { - name: 'test detector', - url: 'https://github.com/github/dependency-submission-toolkit', - version: '0.0.1' - }, + exampleDetector, context, - { id: '42', correlator: 'test' }, - new Date('2022-06-04T05:07:06.457Z') + exampleJob, + exampleDate ) snapshot.addManifest(manifest) expect(roundTripJSON(snapshot)).toEqual({ @@ -49,7 +46,7 @@ describe('Snapshot', () => { }, ref: 'foo/bar/baz', scanned: '2022-06-04T05:07:06.457Z', - sha: '0000000000000000000000000000000000000000', + sha: '1000000000000000000000000000000000000000', manifests: { test: { resolved: { @@ -73,4 +70,74 @@ describe('Snapshot', () => { } }) }) + + it('gets the correct sha from the context when given a pull request', () => { + const prContext = context + const expectedSha = 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2' + prContext.eventName = 'pull_request' + prContext.payload.pull_request = { + number: 1, + head: { + sha: expectedSha + } + } + + const snapshot = new Snapshot( + exampleDetector, + prContext, + exampleJob, + exampleDate + ) + + expect(snapshot.sha).toEqual(expectedSha) + }) }) + +describe('shaFromContext', () => { + it('gets the right sha from the context when given a pull_request event', () => { + const expectedSha = '1234567890123456789012345678901234567890' + const prContext = context + prContext.eventName = 'pull_request' + prContext.payload.pull_request = { + number: 1, + head: { + sha: expectedSha + } + } + expect(shaFromContext(prContext)).toEqual(expectedSha) + }) + + it('gets the right sha from the context when given a pull_request_review event', () => { + const expectedSha = 'abcdef1234567890123456789012345678901234' + const prReviewContext = context + prReviewContext.eventName = 'pull_request_review' + prReviewContext.payload.pull_request = { + number: 1, + head: { + sha: expectedSha + } + } + expect(shaFromContext(prReviewContext)).toEqual(expectedSha) + }) + + it('uses the primary sha from the context when given a push event', () => { + const expectedSha = 'def1234567890123456789012345678901234567' + const pushContext = context + pushContext.eventName = 'push' + pushContext.sha = expectedSha + expect(shaFromContext(pushContext)).toEqual(expectedSha) + }) +}) + +const exampleDetector = { + name: 'test detector', + url: 'https://github.com/github/dependency-submission-toolkit', + version: '0.0.1' +} + +const exampleJob = { + id: '42', + correlator: 'test' +} + +const exampleDate = new Date('2022-06-04T05:07:06.457Z') diff --git a/src/snapshot.ts b/src/snapshot.ts index 6fb2316..4e50a7c 100644 --- a/src/snapshot.ts +++ b/src/snapshot.ts @@ -3,6 +3,7 @@ import * as core from '@actions/core' import * as github from '@actions/github' import { Octokit } from '@octokit/rest' import { RequestError } from '@octokit/request-error' +import { PullRequestEvent } from '@octokit/webhooks-types' import { Manifest } from './manifest' @@ -33,6 +34,34 @@ export function jobFromContext(context: Context): Job { } } +/** + * shaFromContext returns the sha of the commit that triggered the action, or the head sha of the PR. + * + * See https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request for more details + * about why this function is necessary, but the short reason is that GITHUB_SHA is _not_ necessarily the head sha + * of the PR when the event is pull_request (or some other related event types). + * + * @param {Context} context + * @returns {string} + */ +export function shaFromContext(context: Context): string { + const pullRequestEvents = [ + 'pull_request', + 'pull_request_comment', + 'pull_request_review', + 'pull_request_review_comment' + // Note that pull_request_target is omitted here. + // That event runs in the context of the base commit of the PR, + // so the snapshot should not be associated with the head commit. + ] + if (pullRequestEvents.includes(context.eventName)) { + const pr = (context.payload as PullRequestEvent).pull_request + return pr.head.sha + } else { + return context.sha + } +} + /** * Detector provides metadata details about the detector used to generate the snapshot */ @@ -104,7 +133,7 @@ export class Snapshot { this.detector = detector this.version = version this.job = job || jobFromContext(context) - this.sha = context.sha + this.sha = shaFromContext(context) this.ref = context.ref this.scanned = date.toISOString() this.manifests = {}