Skip to content

Commit

Permalink
feat(dashboard-reporter): add github actions ci provider (#1869)
Browse files Browse the repository at this point in the history
Add Github actions to the list of supported CI providers for the dashboard reporter. This makes automagic discovery of `dashboard.project` and `dashboard.version` possible for github actions.
  • Loading branch information
nicojs authored Nov 21, 2019
1 parent d7a6f88 commit b38b30d
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 29 deletions.
22 changes: 22 additions & 0 deletions packages/core/src/reporters/ci/GithubActionsProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getEnvironmentVariableOrThrow } from '../../utils/objectUtils';

import { CIProvider } from './Provider';

/**
* https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-environment-variables#default-environment-variables
*/
export default class GithubActionsCIProvider implements CIProvider {
public determineProject(): string {
return `github.com/${getEnvironmentVariableOrThrow('GITHUB_REPOSITORY')}`;
}
public determineVersion(): string {
const rawRef = getEnvironmentVariableOrThrow('GITHUB_REF');
// rawRef will be in the form "refs/pull/:prNumber/merge" or "refs/heads/feat/branch-1"
const [, type, ...name] = rawRef.split('/');
if (type === 'pull') {
return `PR-${name[0]}`;
} else {
return name.join('/');
}
}
}
5 changes: 4 additions & 1 deletion packages/core/src/reporters/ci/Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getEnvironmentVariable } from '../../utils/objectUtils';

import CircleProvider from './CircleProvider';
import TravisProvider from './TravisProvider';
import GithubActionsCIProvider from './GithubActionsProvider';

/**
* Represents an object that can provide information about a CI/CD provider.
Expand All @@ -28,8 +29,10 @@ export function determineCIProvider() {
return new TravisProvider();
} else if (getEnvironmentVariable('CIRCLECI')) {
return new CircleProvider();
} else if (getEnvironmentVariable('GITHUB_ACTION')) {
return new GithubActionsCIProvider();
}
// TODO: Add vsts, github actions and gitlab CI
// TODO: Add vsts and gitlab CI

return null;
}
4 changes: 3 additions & 1 deletion packages/core/src/utils/objectUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import treeKill = require('tree-kill');
import { StrykerError } from '@stryker-mutator/util';

export { serialize, deserialize } from 'surrial';

export function freezeRecursively<T extends { [prop: string]: any }>(target: T): T {
Expand Down Expand Up @@ -33,7 +35,7 @@ export function getEnvironmentVariable(nameEnvironmentVariable: string): string
export function getEnvironmentVariableOrThrow(name: string): string {
const value = getEnvironmentVariable(name);
if (value === undefined) {
throw new Error(`Missing environment variable "${name}"`);
throw new StrykerError(`Missing environment variable "${name}"`);
} else {
return value;
}
Expand Down
47 changes: 47 additions & 0 deletions packages/core/test/unit/reporters/ci/GithubActionsProvider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { expect } from 'chai';
import { StrykerError } from '@stryker-mutator/util';

import GithubActionsCIProvider from '../../../../src/reporters/ci/GithubActionsProvider';
import { EnvironmentVariableStore } from '../../../helpers/EnvironmentVariableStore';

describe(GithubActionsCIProvider.name, () => {
const env = new EnvironmentVariableStore();
let sut: GithubActionsCIProvider;
beforeEach(() => {
sut = new GithubActionsCIProvider();
});
afterEach(() => {
env.restore();
});

describe('determineProject()', () => {
it('should return the appropriate value', () => {
env.set('GITHUB_REPOSITORY', 'stryker/stryker');
const result = sut.determineProject();
expect(result).eq('github.com/stryker/stryker');
});

it('should throw if env variable is missing', () => {
env.unset('GITHUB_REPOSITORY');
expect(() => sut.determineProject()).throws(StrykerError);
});
});

describe('determineVersion()', () => {
it('should retrieve the PR name', () => {
//"refs/pull/:prNumber/merge" or "refs/heads/feat/branch-1"
env.set('GITHUB_REF', 'refs/pull/156/merge');
expect(sut.determineVersion()).eq('PR-156');
});

it('should retrieve the branch name', () => {
env.set('GITHUB_REF', 'refs/heads/feat/branch-1');
expect(sut.determineVersion()).eq('feat/branch-1');
});

it('should throw if env variable is missing', () => {
env.unset('GITHUB_REF');
expect(() => sut.determineVersion()).throws(StrykerError);
});
});
});
54 changes: 27 additions & 27 deletions packages/core/test/unit/reporters/ci/Provider.spec.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
import { expect } from 'chai';
import * as sinon from 'sinon';

import * as environmentVariables from '../../../../src/utils/objectUtils';
import { determineCIProvider } from '../../../../src/reporters/ci/Provider';
import TravisProvider from '../../../../src/reporters/ci/TravisProvider';
import CircleProvider from '../../../../src/reporters/ci/CircleProvider';
import { EnvironmentVariableStore } from '../../../helpers/EnvironmentVariableStore';
import GithubActionsProvider from '../../../../src/reporters/ci/GithubActionsProvider';

describe('determineCiProvider()', () => {
let getEnvironmentVariables: sinon.SinonStub;
const env = new EnvironmentVariableStore();

beforeEach(() => {
getEnvironmentVariables = sinon.stub(environmentVariables, 'getEnvironmentVariable');
env.unset('HAS_JOSH_K_SEAL_OF_APPROVAL');
env.unset('CIRCLECI');
env.unset('GITHUB_ACTION');
});

describe('Without CI environment', () => {
it('should not select a CI Provider', () => {
getEnvironmentVariables.withArgs('HAS_JOSH_K_SEAL_OF_APPROVAL').returns('');

const result = determineCIProvider();

expect(result).to.be.null;
});
afterEach(() => {
env.restore();
});

describe("When HAS_JOSH_K_SEAL_OF_APPROVAL is 'true'", () => {
it('should provide a CI Provider implementation', () => {
getEnvironmentVariables.withArgs('HAS_JOSH_K_SEAL_OF_APPROVAL').returns(true);

const result = determineCIProvider();

expect(result).to.be.not.undefined;
});
it('should not select a CI Provider when not in a CI environment', () => {
const result = determineCIProvider();
expect(result).to.be.null;
});

describe("When CIRCLECI is 'true'", () => {
it('should provide a CI Provider implementation', () => {
getEnvironmentVariables.withArgs('CIRCLECI').returns(true);
it('should provide Travis when running in the travis environment', () => {
env.set('HAS_JOSH_K_SEAL_OF_APPROVAL', 'true');
const result = determineCIProvider();
expect(result).instanceOf(TravisProvider);
});

const result = determineCIProvider();
it('should provide CircleCI when running in the circle CI environment', () => {
env.set('CIRCLECI', 'true');
const result = determineCIProvider();
expect(result).instanceOf(CircleProvider);
});

expect(result).to.be.not.undefined;
});
it('should provide Github when running in the github actions CI environment', () => {
env.set('GITHUB_ACTION', 'true');
const result = determineCIProvider();
expect(result).instanceOf(GithubActionsProvider);
});
});

0 comments on commit b38b30d

Please sign in to comment.