Skip to content

Commit

Permalink
fix(.github/actions/semanetic-pr-footer-v1): add a list of known gitu…
Browse files Browse the repository at this point in the history
…b actors to skip validation (#99)
  • Loading branch information
Zidious authored Dec 2, 2023
1 parent cbb3d09 commit 8abfe96
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 50 deletions.
6 changes: 6 additions & 0 deletions .github/actions/semantic-pr-footer-v1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

A GitHub Action to validate pull request footers against our team policy

## Inputs

| Name | Required | Description | Default |
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- |
| `ignore_additional_actors` | No | 'Comma delimited list of additional actors to ignore when validating pull request footer. List of actors already ignored: dependabot[bot], dependabot-preview[bot], github-actions[bot], axe-core, attest-team-ci' | NA |

## Example usage

```yaml
Expand Down
8 changes: 7 additions & 1 deletion .github/actions/semantic-pr-footer-v1/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
name: 'Semantic PR Footer'
description: 'Validate pull request footer against team policy'

inputs:
ignore_additional_actors:
description: 'Comma delimited list of additional actors to ignore when validating pull request footer. List of actors already ignored: dependabot[bot], dependabot-preview[bot], github-actions[bot], axe-core, attest-team-ci'
default: ''

runs:
using: 'node20'
main: 'dist/index.js'
main: 'dist/index.js'
23 changes: 21 additions & 2 deletions .github/actions/semantic-pr-footer-v1/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29719,7 +29719,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
const core = __importStar(__nccwpck_require__(2481));
const github = __importStar(__nccwpck_require__(707));
const run_1 = __importDefault(__nccwpck_require__(1738));
(0, run_1.default)(core, github.context.payload);
(0, run_1.default)(core, github);


/***/ }),
Expand Down Expand Up @@ -29753,9 +29753,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ignoredActors = void 0;
const isValidFooter_1 = __importDefault(__nccwpck_require__(6592));
function run(core, payload) {
exports.ignoredActors = [
'dependabot[bot]',
'dependabot-preview[bot]',
'github-actions[bot]',
'axe-core',
'attest-team-ci'
];
function run(core, github) {
try {
const { payload, actor } = github.context;
const ignoreActors = core
.getInput('ignore_additional_actors')
.split(',')
.map(actor => actor.trim().toLowerCase())
.filter(actor => actor.length > 0);
exports.ignoredActors.push(...ignoreActors);
if (exports.ignoredActors.includes(actor)) {
core.info(`Skipping PR footer validation for actor: ${actor}`);
return;
}
const body = payload && payload.pull_request && payload.pull_request.body;
if (!body) {
core.setFailed('PR does not have a body');
Expand Down
7 changes: 3 additions & 4 deletions .github/actions/semantic-pr-footer-v1/dist/run.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type core from '@actions/core';
import type github from '@actions/github';
export type Core = Pick<typeof core, 'setFailed' | 'info'>;
export default function run(core: Core, payload?: typeof github.context.payload): void;
import type { Core, GitHub } from './types';
export declare const ignoredActors: string[];
export default function run(core: Core, github: GitHub): void;
4 changes: 4 additions & 0 deletions .github/actions/semantic-pr-footer-v1/dist/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import github from '@actions/github';
import core from '@actions/core';
export type Core = Pick<typeof core, 'setFailed' | 'info' | 'getInput'>;
export type GitHub = Pick<typeof github, 'context'>;
2 changes: 1 addition & 1 deletion .github/actions/semantic-pr-footer-v1/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import * as core from '@actions/core'
import * as github from '@actions/github'
import run from './run'

run(core, github.context.payload)
run(core, github)
159 changes: 124 additions & 35 deletions .github/actions/semantic-pr-footer-v1/src/run.test.ts
Original file line number Diff line number Diff line change
@@ -1,98 +1,187 @@
import sinon from 'sinon';
import sinon from 'sinon'
import { assert } from 'chai'
import run, { type Core } from './run'
import run, { ignoredActors } from './run'
import type { Core, GitHub } from './types'

describe('run', () => {
afterEach(() => {
sinon.restore()
})

describe('github actors', () => {
describe('given no actors', () => {
it('uses default actor and skips validation if actor is in ignoredActors', () => {
const core = {
info: sinon.spy(),
getInput: sinon.stub().returns('')
}

const github = {
context: {
actor: ignoredActors[0]
}
}

run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.info.calledOnce)
assert.isTrue(
core.info.calledWith(
`Skipping PR footer validation for actor: ${ignoredActors[0]}`
)
)
})
})

describe('given additional actors', () => {
it('skips validation', () => {
const core = {
info: sinon.spy(),
// give additional actors in different ways
getInput: sinon.stub().returns('coffee[bot], , HaXor')
}

const github = {
context: {
actor: 'haxor'
}
}

run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.info.calledOnce)
assert.isTrue(
core.info.calledWith(`Skipping PR footer validation for actor: haxor`)
)
})
})
})

it('fails if pr does not have body', () => {
const core = {
setFailed: sinon.spy()
setFailed: sinon.spy(),
getInput: sinon.stub().returns('')
}
run(core as unknown as Core)

const github = {
context: {
payload: {}
}
}

run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.setFailed.calledOnce)
assert.isTrue(core.setFailed.calledWith('PR does not have a body'))
})

it('fails if pr has empty body', () => {
const core = {
setFailed: sinon.spy()
setFailed: sinon.spy(),
getInput: sinon.stub().returns('')
}
const payload = {
pull_request: {
number: 1,
body: ''
const github = {
context: {
payload: {
pull_request: {
number: 1,
body: ''
}
}
}
}
run(core as unknown as Core, payload)
run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.setFailed.calledOnce)
assert.isTrue(core.setFailed.calledWith('PR does not have a body'))
})

it('logs the pr footer', () => {
const core = {
info: sinon.spy()
info: sinon.spy(),
getInput: sinon.stub().returns('')
}
const payload = {
pull_request: {
number: 1,
body: 'This pr does some things.\n\ncloses: #1'
const github = {
context: {
payload: {
pull_request: {
number: 1,
body: 'This pr does some things.\n\ncloses: #1'
}
}
}
}
run(core as unknown as Core, payload)
run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.info.calledWith('Validating PR footer: "closes: #1"'))
})

it('passes if pr footer is valid', () => {
const core = {
info: sinon.spy()
info: sinon.spy(),
getInput: sinon.stub().returns('')
}
const payload = {
pull_request: {
number: 1,
body: 'closes: #1'
const github = {
context: {
payload: {
pull_request: {
number: 1,
body: 'closes: #1'
}
}
}
}
run(core as unknown as Core, payload)
run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.info.calledWith('Footer matches team policy'))
})

it('fails if pr footer is not valid', () => {
const core = {
setFailed: sinon.spy(),
getInput: sinon.stub().returns(''),
info: sinon.spy()
}
const payload = {
pull_request: {
number: 1,
body: 'nothing to close'
const github = {
context: {
payload: {
pull_request: {
number: 1,
body: 'nothing to close'
}
}
}
}
run(core, payload)
run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.setFailed.calledOnce)
assert.isTrue(core.setFailed.calledWith(sinon.match('PR footer does not close an issue')))
assert.isTrue(
core.setFailed.calledWith(
sinon.match('PR footer does not close an issue')
)
)
})

it('fails if anything throws', () => {
const core = {
setFailed: sinon.spy(),
info() { throw new Error('failure!') }
getInput: sinon.stub().returns(''),
info() {
throw new Error('failure!')
}
}
const payload = {
pull_request: {
number: 1,
body: 'nothing to close'
const github = {
context: {
payload: {
pull_request: {
number: 1,
body: 'nothing to close'
}
}
}
}
run(core, payload)
run(core as unknown as Core, github as unknown as GitHub)

assert.isTrue(core.setFailed.calledWith('failure!'))
})
})
})
31 changes: 24 additions & 7 deletions .github/actions/semantic-pr-footer-v1/src/run.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import type core from '@actions/core'
import type github from '@actions/github'
import isValidFooter from './isValidFooter'
import type { Core, GitHub } from './types'

export type Core = Pick<typeof core, 'setFailed' | 'info'>
export const ignoredActors = [
'dependabot[bot]',
'dependabot-preview[bot]',
'github-actions[bot]',
'axe-core',
'attest-team-ci'
]

export default function run(
core: Core,
payload?: typeof github.context.payload
) {
export default function run(core: Core, github: GitHub) {
try {
const { payload, actor } = github.context

const ignoreActors = core
.getInput('ignore_additional_actors')
.split(',')
.map(actor => actor.trim().toLowerCase())
.filter(actor => actor.length > 0)

ignoredActors.push(...ignoreActors)
if (ignoredActors.includes(actor)) {
core.info(`Skipping PR footer validation for actor: ${actor}`)

return
}

const body: string | undefined =
payload && payload.pull_request && payload.pull_request.body

Expand Down
5 changes: 5 additions & 0 deletions .github/actions/semantic-pr-footer-v1/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import github from '@actions/github'
import core from '@actions/core'

export type Core = Pick<typeof core, 'setFailed' | 'info' | 'getInput'>
export type GitHub = Pick<typeof github, 'context'>

0 comments on commit 8abfe96

Please sign in to comment.