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

feat: adding the ability to pass a regex to filter tickets #10

Merged
merged 13 commits into from
Aug 15, 2019
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ script:
# - shellcheck -x $(git ls-files | grep '[.]sh$')
- ./node_modules/.bin/madge -c .
- yarn audit
- yarn build
- yarn lint
- yarn test

before_deploy:
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,27 @@ The plugin should be added to your config
"projectId": "UH",
"releaseNameTemplate": "Test v${version}",
"jiraHost": "uphabit.atlassian.net",
"ticketPrefixes": [ "TEST", "UH"]
"ticketPrefixes": [ "TEST", "UH"],
"ticketRegex": "[a-zA-Z]{3,5}-\\d{3,5}"
}]
]
}

Please note that `ticketRegex` cannot be used together with `ticketPrefixes`.
```
```typescript
interface Config {

/// A domain of a jira instance ie: `uphabit.atlasian.net`
jiraHost: string;

// A list of prefixes to match when looking for tickets in commits
// A list of prefixes to match when looking for tickets in commits. Cannot be used together with ticketRegex.
// ie. ['TEST'] would match `TEST-123` and `TEST-456`
ticketPrefixes: string[];
ticketPrefixes?: string[];

// A unescaped regex to match tickets in commits (without slashes). Cannot be used together with ticketPrefixes.
// ie. [a-zA-Z]{4}-\d{3,5} would match any ticket with 3 letters a dash and 3 to 5 numbers, such as `TEST-456`, `TEST-5643` and `TEST-56432`
ticketRegex?: string;

// The id or key for the project releases will be created in
projectId: string;
Expand Down
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-typescript',
],
};
12 changes: 9 additions & 3 deletions lib/success.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ import { makeClient } from './jira';
import { GenerateNotesContext, PluginConfig } from './types';
import { escapeRegExp } from './util';

function getTickets(config: PluginConfig, context: GenerateNotesContext): string[] {
const patterns = config.ticketPrefixes!
export function getTickets(config: PluginConfig, context: GenerateNotesContext): string[] {
let patterns: RegExp[] = [];

if (config.ticketRegex !== undefined) {
patterns = [new RegExp(config.ticketRegex, 'giu')];
} else {
patterns = config.ticketPrefixes!
.map(prefix => new RegExp(`\\b${escapeRegExp(prefix)}-(\\d+)\\b`, 'giu'));
}

const tickets = new Set<string>();
for (const commit of context.commits) {
for (const pattern of patterns) {
const matches = pattern.exec(commit.message);
const matches = commit.message.match(pattern);
if (matches) {
tickets.add(matches[0]);
context.logger.info(`Found ticket ${matches[0]} in commit: ${commit.commit.short}`);
Expand Down
3 changes: 2 additions & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export interface BaseConfig {
}

export interface PluginConfig extends BaseConfig {
ticketPrefixes: string[];
ticketPrefixes?: string[];
ticketRegex?: string;
projectId: string;
releaseNameTemplate?: string;
jiraHost: string;
Expand Down
23 changes: 19 additions & 4 deletions lib/verifyConditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,28 @@ export async function verifyConditions(config: PluginConfig, context: PluginCont
if (typeof config.projectId !== 'string') {
throw new SemanticReleaseError(`config.projectId must be a string`);
}
if (!Array.isArray(config.ticketPrefixes)) {
throw new SemanticReleaseError(`config.ticketPrefixes must be an array of string`);

if (!config.ticketPrefixes && !config.ticketRegex) {
throw new SemanticReleaseError('Either config.ticketPrefixes or config.ticketRegex must be passed');
}

if (config.ticketPrefixes && config.ticketRegex) {
throw new SemanticReleaseError(`config.ticketPrefixes and config.ticketRegex cannot be passed at the same time`);
}
for (const prefix of config.ticketPrefixes) {
if (typeof prefix !== 'string') {

if (config.ticketPrefixes) {
if (!Array.isArray(config.ticketPrefixes)) {
throw new SemanticReleaseError(`config.ticketPrefixes must be an array of string`);
}
for (const prefix of config.ticketPrefixes) {
if (typeof prefix !== 'string') {
throw new SemanticReleaseError(`config.ticketPrefixes must be an array of string`);
}
}
}

if (config.ticketRegex && typeof config.ticketRegex !== 'string') {
throw new SemanticReleaseError(`config.ticketRegex must be an string`);
}

if (config.releaseNameTemplate) {
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"private": false,
"scripts": {
"prepublishOnly": "mkdir -p dist && rm -rf dist && tsc -p .",
"test": "tsc -p . && tslint -p . && true \n#\n# No tests 😱\n#"
"lint": "tslint -p .",
"build": "tsc -p .",
"test": " yarn jest"
},
"dependencies": {
"@semantic-release/error": "^2.2.0",
Expand All @@ -16,9 +18,13 @@
"tslib": "^1.9.2"
},
"devDependencies": {
"@babel/preset-env": "^7.5.5",
"@babel/preset-typescript": "^7.3.3",
"@types/jest": "^24.0.17",
"@types/lodash": "^4.14.120",
"@types/node": "^10.12.19",
"@types/signale": "^1.2.0",
"jest": "^24.8.0",
"madge": "^3.4.3",
"tslint": "^5.12.1",
"tslint-config-airbnb": "^5.11.1",
Expand Down
2 changes: 1 addition & 1 deletion test-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@semantic-release/commit-analyzer": "^6.1.0",
"@semantic-release/npm": "^5.1.4",
"@semantic-release/release-notes-generator": "^7.1.4",
"semantic-release": "^15.13.3",
"semantic-release": "^15.13.3 || ^16.0.0",
"semantic-release-jira-releases": "./.."
}
}
76 changes: 76 additions & 0 deletions test/fakedata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { BaseConfig, Person, Commit, PreviousRelease, UpcomingRelease, PluginConfig, PluginContext, GenerateNotesContext } from "../lib/types";

export const baseConfig: BaseConfig = {
$0: '',
branch: 'test',
debug: true,
dryRun: true,
}

export const date = new Date("2019-01-01T00:00:00.000Z")

export const author: Person = {
name: 'test',
email: 'email',
date,
}

export const commits: Commit[] = [
'chore: fixing whitespace',
'docs: adding regex unescaping',
'fix: escaping regex',
'docs: [FIX-321] editing readme',
'feat: [UH-1258] better logging ',
'feat: [UH-1258] Implement release creation',
'fix: [FIX-123] typescript config',
'fix: [TEST-123] test commit',
].map(m => ({
author,
committer: author,
commitDate: date,
body: '',
hash: '',
message: m,
subject: '',
commit: {
long: '',
short: ''
}
}))

export const previousRelease: PreviousRelease = {
gitHead: '',
gitTag: '',
version: ''
}

export const upcomingRelease: UpcomingRelease = {
...previousRelease,
notes: '',
type: ''
}

export const pluginConfig: Partial<PluginConfig> = {
...baseConfig,
projectId: 'TEST',
jiraHost: 'testjira.com'
}

export const logger = {
info: jest.fn()
}

export const pluginContext: PluginContext = {
cwd: '',
env: {},
logger: logger as any,
options: baseConfig,
stderr: null,
stdout: null,
}
export const context: GenerateNotesContext = {
...pluginContext,
commits,
lastRelease: previousRelease,
nextRelease: upcomingRelease
}
35 changes: 35 additions & 0 deletions test/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { getTickets } from "../lib/success";
import { pluginConfig, context } from "./fakedata";
import { PluginConfig } from "../lib/types";

describe('Success tests', () => {
describe('#getTickets', () => {
it('should analyze tickets with one ticketPrefix', () => {
const config = {
...pluginConfig,
ticketPrefixes: ['UH']
} as PluginConfig;
expect(getTickets(config, context)).toEqual(['UH-1258'])
})

it('should analyze tickets with many ticketPrefix', () => {
const config = {
...pluginConfig,
ticketPrefixes: ['UH', 'FIX']
} as PluginConfig
expect(getTickets(config, context)).toEqual(['FIX-321', 'UH-1258', 'FIX-123'])
})

it('should analyze tickets with ticketRegex', () => {
const ticketRegex = '[A-Za-z]+-\\d+';

const config: PluginConfig = {
...pluginConfig,
ticketRegex
} as PluginConfig;

expect(getTickets(config, context)).toEqual(['FIX-321', 'UH-1258', 'FIX-123', 'TEST-123'])
})
})
})