Skip to content

Commit 2270741

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

File tree

3 files changed

+319
-0
lines changed

3 files changed

+319
-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: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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+
try {
51+
return args.branch || envs.SEMAPHORE_GIT_PR_BRANCH || envs.SEMAPHORE_GIT_BRANCH || ''
52+
} catch (error) {
53+
throw new Error(
54+
`There was an error getting the branch name from git: ${error}`,
55+
)
56+
}
57+
}
58+
59+
/**
60+
* Determine the job number, based on args or envs
61+
*
62+
* @param {*} envs an object of environment variable key/value pairs
63+
* @returns {string}
64+
*/
65+
function _getJob(envs: UploaderEnvs): string {
66+
return envs.SEMAPHORE_WORKFLOW_ID || ''
67+
}
68+
69+
/**
70+
* Determine the PR number, based on args and envs
71+
*
72+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
73+
* @returns {string}
74+
*/
75+
function _getPR(inputs: UploaderInputs): string {
76+
const { args, envs } = inputs
77+
try {
78+
return args.pr || envs.SEMAPHORE_GIT_PR_NUMBER || ''
79+
} catch (error) {
80+
throw new Error(`There was an error getting the pr number: ${error}`)
81+
}
82+
}
83+
84+
/**
85+
* The CI service name that gets sent to the Codecov uploader as part of the query string
86+
*
87+
* @returns {string}
88+
*/
89+
function _getService(): string {
90+
return 'semaphore'
91+
}
92+
93+
/**
94+
* The CI Service name that gets displayed when running the uploader
95+
*
96+
* @returns
97+
*/
98+
export function getServiceName(): string {
99+
return 'Semaphore CI'
100+
}
101+
/**
102+
* Determine the commit SHA that is being uploaded, based on args or envs
103+
*
104+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
105+
* @returns {string}
106+
*/
107+
function _getSHA(inputs: UploaderInputs): string {
108+
const { args, envs } = inputs
109+
try {
110+
// when running on a PR, SEMAPHORE_GIT_SHA is the PR's merge commit
111+
return args.sha || envs.SEMAPHORE_GIT_PR_SHA || envs.SEMAPHORE_GIT_SHA || ''
112+
} catch (error) {
113+
throw new Error(
114+
`There was an error getting the commit SHA from git: ${error}`,
115+
)
116+
}
117+
}
118+
/**
119+
* Determine the slug (org/repo) based on args or envs
120+
*
121+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
122+
* @returns {string}
123+
*/
124+
function _getSlug(inputs: UploaderInputs): string {
125+
const { args, envs } = inputs
126+
try {
127+
return args.slug || envs.SEMAPHORE_GIT_PR_SLUG || envs.SEMAPHORE_GIT_REPO_SLUG || ''
128+
} catch (error) {
129+
throw new Error(`There was an error getting the slug from git: ${error}`)
130+
}
131+
}
132+
/**
133+
* Generates and return the serviceParams object
134+
*
135+
* @param {args: {}, envs: {}} inputs an object of arguments and environment variable key/value pairs
136+
* @returns {{ branch: string, build: string, buildURL: string, commit: string, job: string, pr: string, service: string, slug: string }}
137+
*/
138+
export async function getServiceParams(inputs: UploaderInputs): Promise<IServiceParams> {
139+
return {
140+
branch: _getBranch(inputs),
141+
build: _getBuild(inputs),
142+
buildURL: _getBuildURL(inputs),
143+
commit: _getSHA(inputs),
144+
job: _getJob(inputs.envs),
145+
pr: _getPR(inputs),
146+
service: _getService(),
147+
slug: _getSlug(inputs),
148+
}
149+
}
150+
151+
/**
152+
* Returns all the environment variables used by the provider
153+
*
154+
* @returns [{string}]
155+
*/
156+
export function getEnvVarNames(): string[] {
157+
return []
158+
}
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)