Skip to content

Commit bf20f2d

Browse files
authored
feat: add GitHub-specific request headers (#443)
* refactor: simplify header append logic This felt a bit redundant, and this pattern will make it easier for us to add more headers more easily * refactor: add GHA check into separate function We'll be using this in our fetch wrapper so let's extract it! * feat: move source header to all requests Also the header is now different depending on if it's in a GHA environment or not Refactoring all fetch tests to check for multiple headers * feat: add additional GitHub metadata
1 parent 9f86fa0 commit bf20f2d

File tree

3 files changed

+65
-21
lines changed

3 files changed

+65
-21
lines changed

__tests__/lib/fetch.test.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,63 +6,93 @@ const pkg = require('../../package.json');
66

77
describe('#fetch()', () => {
88
describe('GitHub Actions environment', () => {
9+
// List of all GitHub Actions env variables:
10+
// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
911
beforeEach(() => {
1012
process.env.GITHUB_ACTIONS = 'true';
13+
process.env.GITHUB_REPOSITORY = 'octocat/Hello-World';
14+
process.env.GITHUB_RUN_ATTEMPT = '3';
15+
process.env.GITHUB_RUN_ID = '1658821493';
16+
process.env.GITHUB_RUN_NUMBER = '3';
17+
process.env.GITHUB_SHA = 'ffac537e6cbbf934b08745a378932722df287a53';
1118
});
1219

1320
afterEach(() => {
1421
delete process.env.GITHUB_ACTIONS;
22+
delete process.env.GITHUB_REPOSITORY;
23+
delete process.env.GITHUB_RUN_ATTEMPT;
24+
delete process.env.GITHUB_RUN_ID;
25+
delete process.env.GITHUB_RUN_NUMBER;
26+
delete process.env.GITHUB_SHA;
1527
});
1628

17-
it('should use correct user-agent for requests in GitHub Action env', async () => {
29+
it('should have correct headers for requests in GitHub Action env', async () => {
1830
const key = 'API_KEY';
1931

2032
const mock = getApiNock()
2133
.get('/api/v1')
2234
.basicAuth({ user: key })
2335
.reply(200, function () {
24-
return this.req.headers['user-agent'];
36+
return this.req.headers;
2537
});
2638

27-
const userAgent = await fetch(`${config.get('host')}/api/v1`, {
39+
const headers = await fetch(`${config.get('host')}/api/v1`, {
2840
method: 'get',
2941
headers: cleanHeaders(key),
3042
}).then(handleRes);
3143

32-
expect(userAgent.shift()).toBe(`rdme-github/${pkg.version}`);
44+
expect(headers['user-agent'].shift()).toBe(`rdme-github/${pkg.version}`);
45+
expect(headers['x-readme-source'].shift()).toBe('cli-gh');
46+
expect(headers['x-github-repository'].shift()).toBe('octocat/Hello-World');
47+
expect(headers['x-github-run-attempt'].shift()).toBe('3');
48+
expect(headers['x-github-run-id'].shift()).toBe('1658821493');
49+
expect(headers['x-github-run-number'].shift()).toBe('3');
50+
expect(headers['x-github-sha'].shift()).toBe('ffac537e6cbbf934b08745a378932722df287a53');
3351
mock.done();
3452
});
3553
});
3654

37-
it('should wrap all requests with a rdme User-Agent', async () => {
55+
it('should wrap all requests with standard user-agent and source headers', async () => {
3856
const key = 'API_KEY';
3957

4058
const mock = getApiNock()
4159
.get('/api/v1')
4260
.basicAuth({ user: key })
4361
.reply(200, function () {
44-
return this.req.headers['user-agent'];
62+
return this.req.headers;
4563
});
4664

47-
const userAgent = await fetch(`${config.get('host')}/api/v1`, {
65+
const headers = await fetch(`${config.get('host')}/api/v1`, {
4866
method: 'get',
4967
headers: cleanHeaders(key),
5068
}).then(handleRes);
5169

52-
expect(userAgent.shift()).toBe(`rdme/${pkg.version}`);
70+
expect(headers['user-agent'].shift()).toBe(`rdme/${pkg.version}`);
71+
expect(headers['x-readme-source'].shift()).toBe('cli');
72+
expect(headers['x-github-repository']).toBeUndefined();
73+
expect(headers['x-github-run-attempt']).toBeUndefined();
74+
expect(headers['x-github-run-id']).toBeUndefined();
75+
expect(headers['x-github-run-number']).toBeUndefined();
76+
expect(headers['x-github-sha']).toBeUndefined();
5377
mock.done();
5478
});
5579

5680
it('should support if we dont supply any other options with the request', async () => {
5781
const mock = getApiNock()
5882
.get('/api/v1/doesnt-need-auth')
5983
.reply(200, function () {
60-
return this.req.headers['user-agent'];
84+
return this.req.headers;
6185
});
6286

63-
const userAgent = await fetch(`${config.get('host')}/api/v1/doesnt-need-auth`).then(handleRes);
87+
const headers = await fetch(`${config.get('host')}/api/v1/doesnt-need-auth`).then(handleRes);
6488

65-
expect(userAgent.shift()).toBe(`rdme/${pkg.version}`);
89+
expect(headers['user-agent'].shift()).toBe(`rdme/${pkg.version}`);
90+
expect(headers['x-readme-source'].shift()).toBe('cli');
91+
expect(headers['x-github-repository']).toBeUndefined();
92+
expect(headers['x-github-run-attempt']).toBeUndefined();
93+
expect(headers['x-github-run-id']).toBeUndefined();
94+
expect(headers['x-github-run-number']).toBeUndefined();
95+
expect(headers['x-github-sha']).toBeUndefined();
6696
mock.done();
6797
});
6898
});

src/cmds/openapi.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ module.exports = class OpenAPICommand {
138138
const options = {
139139
headers: cleanHeaders(key, {
140140
'x-readme-version': versionCleaned,
141-
'x-readme-source': 'cli',
142141
Accept: 'application/json',
143142
}),
144143
body: formData,

src/lib/fetch.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,33 @@ const pkg = require('../../package.json');
44
const APIError = require('./apiError');
55

66
/**
7-
* Wrapper for the `fetch` API so we can add an rdme user agent to all API requests.
7+
* Small env check to determine if we're in a GitHub Actions environment
8+
* @link https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
9+
*/
10+
function isGHA() {
11+
return process.env.GITHUB_ACTIONS === 'true';
12+
}
13+
14+
/**
15+
* Wrapper for the `fetch` API so we can add rdme-specific headers to all API requests.
816
*
917
*/
10-
module.exports = (url, options = {}) => {
11-
if (!options.headers) {
12-
options.headers = {
13-
'User-Agent': module.exports.getUserAgent(),
14-
};
15-
} else {
16-
options.headers['User-Agent'] = module.exports.getUserAgent();
18+
module.exports = (url, options = { headers: {} }) => {
19+
let source = 'cli';
20+
21+
options.headers['User-Agent'] = module.exports.getUserAgent();
22+
23+
if (isGHA()) {
24+
source = 'cli-gh';
25+
options.headers['x-github-repository'] = process.env.GITHUB_REPOSITORY;
26+
options.headers['x-github-run-attempt'] = process.env.GITHUB_RUN_ATTEMPT;
27+
options.headers['x-github-run-id'] = process.env.GITHUB_RUN_ID;
28+
options.headers['x-github-run-number'] = process.env.GITHUB_RUN_NUMBER;
29+
options.headers['x-github-sha'] = process.env.GITHUB_SHA;
1730
}
1831

32+
options.headers['x-readme-source'] = source;
33+
1934
return fetch(url, options);
2035
};
2136

@@ -25,7 +40,7 @@ module.exports = (url, options = {}) => {
2540
*
2641
*/
2742
module.exports.getUserAgent = function getUserAgent() {
28-
const gh = process.env.GITHUB_ACTIONS === 'true' ? '-github' : '';
43+
const gh = isGHA() ? '-github' : '';
2944
return `rdme${gh}/${pkg.version}`;
3045
};
3146

0 commit comments

Comments
 (0)