Skip to content

Commit f6a9aad

Browse files
reggiCopilot
andauthored
feat: differentiate GitHub Actions environments in user-agent (#9517)
Updates the `user-agent` to specify github-actions runner. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fc6268a commit f6a9aad

2 files changed

Lines changed: 127 additions & 1 deletion

File tree

workspaces/config/lib/definitions/definitions.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2477,6 +2477,36 @@ const definitions = {
24772477
flatten (key, obj, flatOptions) {
24782478
const value = obj[key]
24792479
const ciName = ciInfo.name?.toLowerCase().split(' ').join('-') || null
2480+
// A more specific sub-category for the detected CI, appended to the
2481+
// ci token as `ci/{ci-name}/{sub-ci-name}` when present.
2482+
let subCiName = null
2483+
if (ciInfo.GITHUB_ACTIONS) {
2484+
// Env vars can be absent, empty, or whitespace; normalize before use.
2485+
const serverUrl = (process.env.GITHUB_SERVER_URL || '').trim()
2486+
const runnerEnv = (process.env.RUNNER_ENVIRONMENT || '').trim()
2487+
let serverHost = ''
2488+
try {
2489+
serverHost = new URL(serverUrl).hostname.toLowerCase()
2490+
} catch {
2491+
serverHost = ''
2492+
}
2493+
if (serverHost === 'github.com') {
2494+
if (runnerEnv === 'github-hosted') {
2495+
subCiName = 'dotcom-hosted'
2496+
} else if (runnerEnv === 'self-hosted') {
2497+
subCiName = 'dotcom-selfhosted'
2498+
} else {
2499+
subCiName = 'dotcom'
2500+
}
2501+
} else if (serverHost === 'ghe.com' || serverHost.endsWith('.ghe.com')) {
2502+
subCiName = 'ghecom'
2503+
} else if (serverHost) {
2504+
subCiName = 'ghes'
2505+
}
2506+
}
2507+
const ci = ciName
2508+
? `ci/${ciName}${subCiName ? `/${subCiName}` : ''}`
2509+
: ''
24802510
let inWorkspaces = false
24812511
if (obj.workspaces || obj.workspace && obj.workspace.length) {
24822512
inWorkspaces = true
@@ -2487,7 +2517,7 @@ const definitions = {
24872517
.replace(/\{platform\}/gi, process.platform)
24882518
.replace(/\{arch\}/gi, process.arch)
24892519
.replace(/\{workspaces\}/gi, inWorkspaces)
2490-
.replace(/\{ci\}/gi, ciName ? `ci/${ciName}` : '')
2520+
.replace(/\{ci\}/gi, ci)
24912521
.trim()
24922522

24932523
// We can't clobber the original or else subsequent flattening will fail

workspaces/config/test/definitions/definitions.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,102 @@ t.test('user-agent', t => {
860860
t.end()
861861
})
862862

863+
t.test('user-agent github actions ci variants', t => {
864+
const npmVersion = '1.2.3'
865+
const base = `npm/${npmVersion} node/${process.version} ` +
866+
`${process.platform} ${process.arch} workspaces/false`
867+
868+
const cases = [
869+
{
870+
name: 'dotcom + github-hosted runner',
871+
env: { GITHUB_SERVER_URL: 'https://github.com', RUNNER_ENVIRONMENT: 'github-hosted' },
872+
expect: `${base} ci/github-actions/dotcom-hosted`,
873+
},
874+
{
875+
name: 'dotcom + self-hosted runner',
876+
env: { GITHUB_SERVER_URL: 'https://github.com', RUNNER_ENVIRONMENT: 'self-hosted' },
877+
expect: `${base} ci/github-actions/dotcom-selfhosted`,
878+
},
879+
{
880+
name: 'dotcom + missing runner environment',
881+
env: { GITHUB_SERVER_URL: 'https://github.com', RUNNER_ENVIRONMENT: undefined },
882+
expect: `${base} ci/github-actions/dotcom`,
883+
},
884+
{
885+
name: 'dotcom + whitespace runner environment',
886+
env: { GITHUB_SERVER_URL: 'https://github.com', RUNNER_ENVIRONMENT: ' ' },
887+
expect: `${base} ci/github-actions/dotcom`,
888+
},
889+
{
890+
name: 'ghe.com tenant',
891+
env: { GITHUB_SERVER_URL: 'https://octocorp.ghe.com', RUNNER_ENVIRONMENT: 'github-hosted' },
892+
expect: `${base} ci/github-actions/ghecom`,
893+
},
894+
{
895+
name: 'bare ghe.com host',
896+
env: { GITHUB_SERVER_URL: 'https://ghe.com', RUNNER_ENVIRONMENT: 'github-hosted' },
897+
expect: `${base} ci/github-actions/ghecom`,
898+
},
899+
{
900+
name: 'ghe.com only in path is treated as ghes',
901+
env: { GITHUB_SERVER_URL: 'https://evil.example/x.ghe.com', RUNNER_ENVIRONMENT: 'self-hosted' },
902+
expect: `${base} ci/github-actions/ghes`,
903+
},
904+
{
905+
name: 'ghes (non-empty, non github.com, non ghe.com)',
906+
env: { GITHUB_SERVER_URL: 'https://github.example.com', RUNNER_ENVIRONMENT: 'self-hosted' },
907+
expect: `${base} ci/github-actions/ghes`,
908+
},
909+
{
910+
name: 'missing server url stays generic',
911+
env: { GITHUB_SERVER_URL: undefined, RUNNER_ENVIRONMENT: undefined },
912+
expect: `${base} ci/github-actions`,
913+
},
914+
{
915+
name: 'whitespace server url stays generic',
916+
env: { GITHUB_SERVER_URL: ' ', RUNNER_ENVIRONMENT: 'github-hosted' },
917+
expect: `${base} ci/github-actions`,
918+
},
919+
]
920+
921+
for (const { name, env, expect } of cases) {
922+
t.test(name, t => {
923+
mockGlobals(t, { 'process.env': env })
924+
const defs = mockDefs({
925+
'ci-info': { isCi: true, name: 'GitHub Actions', GITHUB_ACTIONS: true },
926+
})
927+
const obj = {
928+
'npm-version': npmVersion,
929+
'user-agent': defs['user-agent'].default,
930+
}
931+
const flat = {}
932+
defs['user-agent'].flatten('user-agent', obj, flat)
933+
t.equal(flat.userAgent, expect)
934+
t.equal(process.env.npm_config_user_agent, flat.userAgent, 'npm_user_config environment is set')
935+
t.end()
936+
})
937+
}
938+
939+
t.test('non github-actions ci is unchanged', t => {
940+
mockGlobals(t, {
941+
'process.env': { GITHUB_SERVER_URL: 'https://github.com', RUNNER_ENVIRONMENT: 'github-hosted' },
942+
})
943+
const defs = mockDefs({
944+
'ci-info': { isCi: true, name: 'Travis CI', GITHUB_ACTIONS: false },
945+
})
946+
const obj = {
947+
'npm-version': npmVersion,
948+
'user-agent': defs['user-agent'].default,
949+
}
950+
const flat = {}
951+
defs['user-agent'].flatten('user-agent', obj, flat)
952+
t.equal(flat.userAgent, `${base} ci/travis-ci`)
953+
t.end()
954+
})
955+
956+
t.end()
957+
})
958+
863959
t.test('save-prefix', t => {
864960
const obj = {
865961
'save-exact': true,

0 commit comments

Comments
 (0)