Merge pull request #2229 from actions/copilot/extend-user-agent-http-requests

Extend user-agent with orchestration ID and add default user-agent
This commit is contained in:
Tingluo Huang
2026-01-07 10:42:56 -05:00
committed by GitHub
2 changed files with 80 additions and 3 deletions
+67 -2
View File
@@ -21,7 +21,10 @@ describe('basics', () => {
_http = new httpm.HttpClient('http-client-tests')
})
afterEach(() => {})
afterEach(() => {
// Clean up environment variable to prevent test pollution
delete process.env['ACTIONS_ORCHESTRATION_ID']
})
it('constructs', () => {
const http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests')
@@ -60,7 +63,7 @@ describe('basics', () => {
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.url).toBe('https://postman-echo.com/get')
expect(obj.headers['user-agent']).toBeFalsy()
expect(obj.headers['user-agent']).toBe('actions/http-client')
})
/* TODO write a mock rather then relying on a third party
@@ -374,4 +377,66 @@ describe('basics', () => {
httpm.MediaTypes.ApplicationJson
)
})
it('appends orchestration ID to user-agent when ACTIONS_ORCHESTRATION_ID is set', async () => {
const orchId = 'test-orch-id-12345'
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests')
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.headers['user-agent']).toBe(
`http-client-tests actions_orchestration_id/${orchId}`
)
})
it('sanitizes invalid characters in orchestration ID', async () => {
const orchId = 'test (with) special/chars'
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests')
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
// Spaces, parentheses, and slashes should be replaced with underscores
expect(obj.headers['user-agent']).toBe(
'http-client-tests actions_orchestration_id/test__with__special_chars'
)
})
it('does not modify user-agent when ACTIONS_ORCHESTRATION_ID is not set', async () => {
delete process.env['ACTIONS_ORCHESTRATION_ID']
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests')
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.headers['user-agent']).toBe('http-client-tests')
})
it('uses default user-agent with orchestration ID when no custom user-agent provided', async () => {
const orchId = 'test-default-id-12345'
process.env['ACTIONS_ORCHESTRATION_ID'] = orchId
const http: httpm.HttpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await http.get(
'https://postman-echo.com/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
expect(obj.headers['user-agent']).toBe(
`actions/http-client actions_orchestration_id/${orchId}`
)
})
})
+13 -1
View File
@@ -147,7 +147,7 @@ export class HttpClient {
handlers?: ifm.RequestHandler[],
requestOptions?: ifm.RequestOptions
) {
this.userAgent = userAgent
this.userAgent = this._getUserAgentWithOrchestrationId(userAgent)
this.handlers = handlers || []
this.requestOptions = requestOptions
if (requestOptions) {
@@ -816,6 +816,18 @@ export class HttpClient {
return proxyAgent
}
private _getUserAgentWithOrchestrationId(userAgent?: string): string {
const baseUserAgent = userAgent || 'actions/http-client'
const orchId = process.env['ACTIONS_ORCHESTRATION_ID']
if (orchId) {
// Sanitize the orchestration ID to ensure it contains only valid characters
// Valid characters: 0-9, a-z, _, -, .
const sanitizedId = orchId.replace(/[^a-z0-9_.-]/gi, '_')
return `${baseUserAgent} actions_orchestration_id/${sanitizedId}`
}
return baseUserAgent
}
private async _performExponentialBackoff(retryNumber: number): Promise<void> {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber)
const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber)