Skip to content

Commit 14c7232

Browse files
committed
feat: add Semaphore CI provider
1 parent 308e747 commit 14c7232

File tree

3 files changed

+318
-0
lines changed

3 files changed

+318
-0
lines changed

src/ci_providers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as providerGitLabci from './provider_gitlabci'
1414
import * as providerHerokuci from './provider_herokuci'
1515
import * as providerJenkinsci from './provider_jenkinsci'
1616
import * as providerLocal from './provider_local'
17+
import * as providerSemaphore from './provider_semaphore'
1718
import * as providerTeamCity from './provider_teamcity'
1819
import * as providerTravisci from './provider_travisci'
1920
import * as providerWercker from './provider_wercker'
@@ -34,6 +35,7 @@ const providerList: IProvider[] = [
3435
providerGitLabci,
3536
providerHerokuci,
3637
providerJenkinsci,
38+
providerSemaphore,
3739
providerTeamCity,
3840
providerTravisci,
3941
providerWercker,
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* https://docs.semaphoreci.com/ci-cd-environment/environment-variables/
3+
*/
4+
import { IServiceParams, UploaderEnvs, UploaderInputs } from '../types'
5+
6+
/**
7+
* Detects if this CI provider is being used
8+
*
9+
* @param {*} envs an object of environment variable key/value pairs
10+
* @returns boolean
11+
*/
12+
13+
export function detect(envs: UploaderEnvs): boolean {
14+
return Boolean(envs.CI) && Boolean(envs.SEMAPHORE)
15+
}
16+
17+
/**
18+
* Determine the build number, based on args and envs
19+
*
20+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
21+
* @returns {string}
22+
*/
23+
function _getBuild(inputs: UploaderInputs): string {
24+
const { args, envs } = inputs
25+
return args.build || envs.SEMAPHORE_WORKFLOW_NUMBER || ''
26+
}
27+
28+
/**
29+
* Determine the build URL for use in the Codecov UI
30+
*
31+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
32+
* @returns {string}
33+
*/
34+
function _getBuildURL(inputs: UploaderInputs): string {
35+
const { envs } = inputs
36+
if (envs.SEMAPHORE_ORGANIZATION_URL && envs.SEMAPHORE_WORKFLOW_ID) {
37+
return `${envs.SEMAPHORE_ORGANIZATION_URL}/workflows/${envs.SEMAPHORE_WORKFLOW_ID}`
38+
}
39+
return ''
40+
}
41+
42+
/**
43+
* Determine the branch of the repository, based on args and envs
44+
*
45+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
46+
* @returns {string}
47+
*/
48+
function _getBranch(inputs: UploaderInputs): string {
49+
const { args, envs } = inputs
50+
return args.branch || envs.SEMAPHORE_GIT_PR_BRANCH || envs.SEMAPHORE_GIT_BRANCH || ''
51+
}
52+
53+
/**
54+
* Determine the job number, based on args or envs
55+
*
56+
* @param {*} envs an object of environment variable key/value pairs
57+
* @returns {string}
58+
*/
59+
function _getJob(envs: UploaderEnvs): string {
60+
return envs.SEMAPHORE_WORKFLOW_ID || ''
61+
}
62+
63+
/**
64+
* Determine the PR number, based on args and envs
65+
*
66+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
67+
* @returns {string}
68+
*/
69+
function _getPR(inputs: UploaderInputs): string {
70+
const { args, envs } = inputs
71+
return args.pr || envs.SEMAPHORE_GIT_PR_NUMBER || ''
72+
}
73+
74+
/**
75+
* The CI service name that gets sent to the Codecov uploader as part of the query string
76+
*
77+
* @returns {string}
78+
*/
79+
function _getService(): string {
80+
return 'semaphore'
81+
}
82+
83+
/**
84+
* The CI Service name that gets displayed when running the uploader
85+
*
86+
* @returns
87+
*/
88+
export function getServiceName(): string {
89+
return 'Semaphore CI'
90+
}
91+
/**
92+
* Determine the commit SHA that is being uploaded, based on args or envs
93+
*
94+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
95+
* @returns {string}
96+
*/
97+
function _getSHA(inputs: UploaderInputs): string {
98+
const { args, envs } = inputs
99+
try {
100+
// when running on a PR, SEMAPHORE_GIT_SHA is the PR's merge commit
101+
return args.sha || envs.SEMAPHORE_GIT_PR_SHA || envs.SEMAPHORE_GIT_SHA || ''
102+
} catch (error) {
103+
throw new Error(
104+
`There was an error getting the commit SHA from git: ${error}`,
105+
)
106+
}
107+
}
108+
/**
109+
* Determine the slug (org/repo) based on args or envs
110+
*
111+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
112+
* @returns {string}
113+
*/
114+
function _getSlug(inputs: UploaderInputs): string {
115+
const { args, envs } = inputs
116+
try {
117+
return args.slug || envs.SEMAPHORE_GIT_PR_SLUG || envs.SEMAPHORE_GIT_REPO_SLUG || ''
118+
} catch (error) {
119+
throw new Error(`There was an error getting the slug from git: ${error}`)
120+
}
121+
}
122+
/**
123+
* Generates and return the serviceParams object
124+
*
125+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
126+
* @returns {{ branch: string, build: string, buildURL: string, commit: string, job: string, pr: string, service: string, slug: string }}
127+
*/
128+
export async function getServiceParams(inputs: UploaderInputs): Promise<IServiceParams> {
129+
return {
130+
branch: _getBranch(inputs),
131+
build: _getBuild(inputs),
132+
buildURL: _getBuildURL(inputs),
133+
commit: _getSHA(inputs),
134+
job: _getJob(inputs.envs),
135+
pr: _getPR(inputs),
136+
service: _getService(),
137+
slug: _getSlug(inputs),
138+
}
139+
}
140+
141+
/**
142+
* Returns all the environment variables used by the provider
143+
*
144+
* @returns [{string}]
145+
*/
146+
export function getEnvVarNames(): string[] {
147+
return [
148+
'CI',
149+
'SEMAPHORE',
150+
'SEMAPHORE_WORKFLOW_NUMBER',
151+
'SEMAPHORE_ORGANIZATION_URL',
152+
'SEMAPHORE_WORKFLOW_ID',
153+
'SEMAPHORE_GIT_PR_BRANCH',
154+
'SEMAPHORE_GIT_BRANCH',
155+
'SEMAPHORE_GIT_PR_NUMBER',
156+
]
157+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import td from 'testdouble'
2+
import * as providerSemaphore from '../../src/ci_providers/provider_semaphore'
3+
import { IServiceParams, UploaderInputs } from '../../src/types'
4+
import { createEmptyArgs } from '../test_helpers'
5+
6+
describe('Semaphore Params', () => {
7+
afterEach(() => {
8+
td.reset()
9+
})
10+
11+
describe('detect()', () => {
12+
it('does not run without Semaphore env variable', () => {
13+
const inputs: UploaderInputs = {
14+
args: { ...createEmptyArgs() },
15+
envs: {},
16+
}
17+
let detected = providerSemaphore.detect(inputs.envs)
18+
expect(detected).toBeFalsy()
19+
20+
inputs.envs['CI'] = 'true'
21+
detected = providerSemaphore.detect(inputs.envs)
22+
expect(detected).toBeFalsy()
23+
})
24+
25+
it('does run with Semaphore env variable', () => {
26+
const inputs: UploaderInputs = {
27+
args: { ...createEmptyArgs() },
28+
envs: {
29+
CI: 'true',
30+
SEMAPHORE: 'true',
31+
},
32+
}
33+
const detected = providerSemaphore.detect(inputs.envs)
34+
expect(detected).toBeTruthy()
35+
})
36+
})
37+
38+
// This should test that the provider outputs proper default values
39+
it('gets the correct params on no env variables', async () => {
40+
const inputs: UploaderInputs = {
41+
args: { ...createEmptyArgs() },
42+
envs: {
43+
CI: 'true',
44+
SEMAPHORE: 'true',
45+
},
46+
}
47+
const expected: IServiceParams = {
48+
branch: '',
49+
build: '',
50+
buildURL: '',
51+
commit: '',
52+
job: '',
53+
pr: '',
54+
service: 'semaphore',
55+
slug: '',
56+
}
57+
const params = await providerSemaphore.getServiceParams(inputs)
58+
expect(params).toMatchObject(expected)
59+
})
60+
61+
// This should test that the provider outputs proper parameters when a push event is created
62+
it('gets the correct params on push', async () => {
63+
const inputs: UploaderInputs = {
64+
args: { ...createEmptyArgs() },
65+
envs: {
66+
CI: 'true',
67+
SEMAPHORE: 'true',
68+
SEMAPHORE_GIT_BRANCH: 'master',
69+
SEMAPHORE_GIT_REPO_SLUG: 'org/user',
70+
SEMAPHORE_GIT_SHA: 'testingsha',
71+
SEMAPHORE_ORGANIZATION_URL: 'https://example.semaphoreci.com',
72+
SEMAPHORE_WORKFLOW_ID: '03d9de4c-c798-4df5-bbd5-786db9d4d309',
73+
SEMAPHORE_WORKFLOW_NUMBER: '1',
74+
},
75+
}
76+
const expected: IServiceParams = {
77+
branch: 'master',
78+
build: '1',
79+
buildURL: 'https://example.semaphoreci.com/workflows/03d9de4c-c798-4df5-bbd5-786db9d4d309',
80+
commit: 'testingsha',
81+
job: '03d9de4c-c798-4df5-bbd5-786db9d4d309',
82+
pr: '',
83+
service: 'semaphore',
84+
slug: 'org/user',
85+
}
86+
const params = await providerSemaphore.getServiceParams(inputs)
87+
expect(params).toMatchObject(expected)
88+
})
89+
//
90+
// This should test that the provider outputs proper parameters when a pull request event is created
91+
it('gets the correct params on pr', async () => {
92+
const inputs: UploaderInputs = {
93+
args: { ...createEmptyArgs() },
94+
envs: {
95+
CI: 'true',
96+
SEMAPHORE: 'true',
97+
SEMAPHORE_GIT_BRANCH: 'master',
98+
SEMAPHORE_GIT_PR_BRANCH: 'feature-branch',
99+
SEMAPHORE_GIT_PR_NUMBER: '1234',
100+
SEMAPHORE_GIT_PR_SHA: 'prsha',
101+
SEMAPHORE_GIT_PR_SLUG: 'org/user',
102+
SEMAPHORE_GIT_SHA: 'testingsha',
103+
SEMAPHORE_ORGANIZATION_URL: 'https://example.semaphoreci.com',
104+
SEMAPHORE_WORKFLOW_ID: '03d9de4c-c798-4df5-bbd5-786db9d4d309',
105+
SEMAPHORE_WORKFLOW_NUMBER: '1',
106+
},
107+
}
108+
const expected: IServiceParams = {
109+
branch: 'feature-branch',
110+
build: '1',
111+
buildURL: 'https://example.semaphoreci.com/workflows/03d9de4c-c798-4df5-bbd5-786db9d4d309',
112+
commit: 'prsha',
113+
job: '03d9de4c-c798-4df5-bbd5-786db9d4d309',
114+
pr: '1234',
115+
service: 'semaphore',
116+
slug: 'org/user',
117+
}
118+
const params = await providerSemaphore.getServiceParams(inputs)
119+
expect(expected).toBeTruthy()
120+
})
121+
122+
// This should test that the provider outputs proper parameters when given overrides
123+
it('gets the correct params on overrides', async () => {
124+
const inputs: UploaderInputs = {
125+
args: {...createEmptyArgs(), ...{
126+
branch: 'overwrite-feature-branch',
127+
build: '3',
128+
pr: '4',
129+
sha: 'overwriteSha',
130+
slug: 'overwriteOwner/overwriteRepo',
131+
}},
132+
envs: {
133+
CI: 'true',
134+
SEMAPHORE: 'true',
135+
SEMAPHORE_GIT_BRANCH: 'master',
136+
SEMAPHORE_GIT_PR_BRANCH: 'feature-branch',
137+
SEMAPHORE_GIT_PR_NUMBER: '1234',
138+
SEMAPHORE_GIT_PR_SHA: 'prsha',
139+
SEMAPHORE_GIT_PR_SLUG: 'org/user',
140+
SEMAPHORE_GIT_SHA: 'testingsha',
141+
SEMAPHORE_ORGANIZATION_URL: 'https://example.semaphoreci.com',
142+
SEMAPHORE_WORKFLOW_ID: '03d9de4c-c798-4df5-bbd5-786db9d4d309',
143+
SEMAPHORE_WORKFLOW_NUMBER: '1',
144+
},
145+
}
146+
const expected: IServiceParams = {
147+
branch: 'overwrite-feature-branch',
148+
build: '3',
149+
buildURL: 'https://example.semaphoreci.com/workflows/03d9de4c-c798-4df5-bbd5-786db9d4d309',
150+
commit: 'overwriteSha',
151+
job: '03d9de4c-c798-4df5-bbd5-786db9d4d309',
152+
pr: '4',
153+
service: 'semaphore',
154+
slug: 'overwriteOwner/overwriteRepo',
155+
}
156+
const params = await providerSemaphore.getServiceParams(inputs)
157+
expect(params).toMatchObject(expected)
158+
})
159+
})

0 commit comments

Comments
 (0)