Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance configuration (#12, #15) #20

Merged
merged 11 commits into from Oct 27, 2019
6 changes: 4 additions & 2 deletions .vscode/settings.json
@@ -1,3 +1,5 @@
{
"editor.formatOnSave": true
}
"editor.formatOnSave": true,
"createTests.defaultLocationForTestFiles": "project root",
"createTests.testDirectoryName": "__tests__"
}
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -20,6 +20,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: TimonVS/pr-labeler-action@v3
with:
configuration-path: .github/pr-labeler.yml # optional, .github/pr-labeler.yml is the default value
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
Expand All @@ -43,6 +45,16 @@ Then if a pull request is opened with the branch name `feature/218-add-emoji-sup

You can use `*` as a wildcard for matching multiple branch names. See https://www.npmjs.com/package/matcher for more information about wildcard options.

### Default configuration

When no configuration is provided, the following defaults will be used:

```yml
feature: ['feature/*', 'feat/*'],
fix: 'fix/*',
chore: 'chore/*'
```

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down
26 changes: 25 additions & 1 deletion __tests__/action.test.ts
Expand Up @@ -6,6 +6,12 @@ import action from '../src/action'
nock.disableNetConnect()

describe('pr-labeler-action', () => {
beforeEach(() => {
// configuration-path parameter is required
// parameters are exposed as environment variables: https://help.github.com/en/github/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepswith
process.env['INPUT_CONFIGURATION-PATH'] = '.github/pr-labeler.yml'
})

it('adds the "fix" label for "fix/510-logging" branch', async () => {
nock('https://api.github.com')
.get('/repos/Codertocat/Hello-World/contents/.github/pr-labeler.yml?ref=fix%2F510-logging')
Expand All @@ -30,7 +36,7 @@ describe('pr-labeler-action', () => {
.reply(200, configFixture())
.post('/repos/Codertocat/Hello-World/issues/1/labels', body => {
expect(body).toMatchObject({
labels: ['feature']
labels: ['🎉 feature']
})
return true
})
Expand All @@ -42,6 +48,24 @@ describe('pr-labeler-action', () => {
expect.assertions(1)
})

it('adds the "release" label for "release/2.0" branch', async () => {
nock('https://api.github.com')
.get('/repos/Codertocat/Hello-World/contents/.github/pr-labeler.yml?ref=release%2F2.0')
.reply(200, configFixture())
.post('/repos/Codertocat/Hello-World/issues/1/labels', body => {
expect(body).toMatchObject({
labels: ['release']
})
return true
})
.reply(200)

await action({
payload: pullRequestOpenedFixture({ ref: 'release/2.0' })
})
expect.assertions(1)
})

it('uses the default config when no config was provided', async () => {
nock('https://api.github.com')
.get('/repos/Codertocat/Hello-World/contents/.github/pr-labeler.yml?ref=fix%2F510-logging')
Expand Down
3 changes: 2 additions & 1 deletion __tests__/fixtures/config.yml
@@ -1,3 +1,4 @@
feature: ['feature/*', 'feat/*']
'🎉 feature': ['feature/*', 'feat/*']
fix: fix/*
chore: chore/*
release: release/*
31 changes: 31 additions & 0 deletions __tests__/utils/config.test.ts
@@ -0,0 +1,31 @@
import getConfig from '../../src/utils/config'

describe('getConfig', () => {
it('returns default config when GitHub returns a 404 for given path', async () => {
const defaultConfig = {
foo: 'bar'
}

const githubMock = {
repos: {
getContents() {
throw new HTTPError(404)
}
}
}

const config = await getConfig(
githubMock as any,
'path/to/config',
{ owner: 'repo-owner', repo: 'repo-name' },
'ref',
defaultConfig
)

expect(config).toBe(defaultConfig)
})
})

class HTTPError {
constructor(public status: number) {}
}
4 changes: 4 additions & 0 deletions action.yml
@@ -1,6 +1,10 @@
name: 'PR Labeler'
description: 'Automatically labels your PRs based on branch name patterns like feature/* or fix/*.'
author: 'Timon van Spronsen'
inputs:
configuration-path:
description: 'The path for the label configurations'
default: '.github/pr-labeler.yml'
branding:
icon: 'tag'
color: 'white'
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 16 additions & 16 deletions src/action.ts
Expand Up @@ -5,8 +5,7 @@ import matcher from 'matcher'
import getConfig from './utils/config'
import { RepoInfo } from './utils/config'

const CONFIG_FILENAME = 'pr-labeler.yml'
const defaults = {
const defaultConfig = {
feature: ['feature/*', 'feat/*'],
fix: 'fix/*',
chore: 'chore/*'
Expand All @@ -20,6 +19,7 @@ async function action(context: Pick<Context, 'payload'> = github.context) {
owner: context.payload.repository!.owner.login,
repo: context.payload.repository!.name
}
const configPath = core.getInput('configuration-path', { required: true })

if (!context.payload.pull_request) {
throw new Error(
Expand All @@ -28,22 +28,22 @@ async function action(context: Pick<Context, 'payload'> = github.context) {
}

const ref: string = context.payload.pull_request.head.ref
const config = {
...defaults,
...(await getConfig(octokit, CONFIG_FILENAME, repoInfo, ref))
}
const config = await getConfig(octokit, configPath, repoInfo, ref, defaultConfig)

const labelsToAdd = Object.entries(config).reduce((labels: string[], [label, patterns]) => {
if (
Array.isArray(patterns)
? patterns.some(pattern => matcher.isMatch(ref, pattern))
: matcher.isMatch(ref, patterns)
) {
labels.push(label)
}
const labelsToAdd = Object.entries(config).reduce(
(labels, [label, patterns]) => {
if (
Array.isArray(patterns)
? patterns.some(pattern => matcher.isMatch(ref, pattern))
: matcher.isMatch(ref, patterns)
) {
labels.push(label)
}

return labels
}, [])
return labels
},
[] as string[]
)

if (labelsToAdd.length > 0) {
await octokit.issues.addLabels({
Expand Down
18 changes: 13 additions & 5 deletions src/utils/config.ts
@@ -1,26 +1,34 @@
import path from 'path'
import yaml from 'js-yaml'
import { GitHub } from '@actions/github'
const CONFIG_PATH = '.github'

export interface RepoInfo {
owner: string
repo: string
}

export default async function getConfig(github: GitHub, fileName: string, { owner, repo }: RepoInfo, ref: string) {
interface Config {
[k: string]: string | string[]
}

export default async function getConfig(
github: GitHub,
path: string,
{ owner, repo }: RepoInfo,
ref: string,
defaultConfig: Config
): Promise<Config> {
try {
const response = await github.repos.getContents({
owner,
repo,
path: path.posix.join(CONFIG_PATH, fileName),
path,
ref
})

return parseConfig(response.data.content)
} catch (error) {
if (error.status === 404) {
return null
return defaultConfig
}

throw error
Expand Down