Skip to content

Commit 74e8af3

Browse files
authored
Merge pull request #43654 from github/repo-sync
Repo sync
2 parents d2de374 + 83d5c69 commit 74e8af3

File tree

9 files changed

+182
-15
lines changed

9 files changed

+182
-15
lines changed

content/billing/concepts/product-billing/github-actions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ If you use 3 GB of artifact storage for 10 days of March and 12 GB for 21 days o
165165
At the end of the month, {% data variables.product.github %} rounds your artifact storage to the nearest MB. Therefore, your artifact storage usage for March would be 9.097 GB.
166166

167167
> [!NOTE]
168-
> {% data variables.product.github %} updates your artifact storage space within a 6 to 12-hour window. If you delete artifacts, the available space will be reflected in your account during the next scheduled update.
168+
> {% data variables.product.github %} updates your artifact storage usage within 6 to 12 hours. Deleting artifacts frees up space for current storage, but does not reduce your accrued storage usage, which is used to calculate your storage billing for the current billing cycle.
169169
170170
### Example cache storage cost calculation
171171

content/copilot/reference/ai-models/model-hosting.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
2-
title: Hosting of models for GitHub Copilot Chat
2+
title: Hosting of models for GitHub Copilot
33
shortTitle: Model hosting
44
allowTitleToDifferFromFilename: true
5-
intro: 'Learn how different AI models are hosted for {% data variables.copilot.copilot_chat_short %}.'
5+
intro: 'Learn how different AI models are hosted for {% data variables.product.prodname_copilot %}.'
66
versions:
77
feature: copilot
88
category:
@@ -99,3 +99,7 @@ Will **only**:
9999
When using xAI, input prompts and output completions continue to run through {% data variables.product.prodname_copilot %}'s content filters for public code matching, when applied, along with those for harmful or offensive content.
100100

101101
For more information, see [xAI's enterprise terms of service](https://x.ai/legal/terms-of-service-enterprise) on the xAI website.
102+
103+
## Inline suggestions
104+
105+
Inline suggestions, including ghost text and next edit suggestions, are powered by models hosted on Azure for {% data variables.copilot.copilot_business_short %} and {% data variables.copilot.copilot_enterprise_short %} plans. {% data variables.copilot.copilot_free_short %} and {% data variables.copilot.copilot_student_short %} user models are hosted on Fireworks AI.

src/frame/components/ClientSideHashFocus.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export function ClientSideHashFocus() {
1717
}
1818
}
1919

20+
// Handle initial page load with a hash (e.g. direct link to
21+
// docs.github.com/en/discussions#guides-2)
22+
handleHashChange()
23+
2024
window.addEventListener('hashchange', handleHashChange)
2125
return () => window.removeEventListener('hashchange', handleHashChange)
2226
}, [])

src/landings/components/TocLanding.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const TocLanding = () => {
5454
<div className="border-bottom border-xl-0 pb-4 mb-5 pb-xl-2 mb-xl-2" />
5555

5656
<div className={variant === 'expanded' ? 'mt-5' : 'mt-2'}>
57-
{featuredLinks.gettingStarted && featuredLinks.popular && (
57+
{featuredLinks?.gettingStarted && featuredLinks?.popular && (
5858
<div className="pb-8 container-xl">
5959
<div className="gutter gutter-xl-spacious clearfix">
6060
<div className="col-12 col-lg-6 mb-md-4 mb-lg-0 float-left">

src/observability/lib/handle-exceptions.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
11
import FailBot from './failbot'
2+
import { toError } from '@/observability/lib/to-error'
23
import { createLogger } from '@/observability/logger'
34

45
const logger = createLogger(import.meta.url)
56

6-
// Safely convert an unknown thrown value to an Error, avoiding JSON.stringify
7-
// which can throw on circular references.
8-
function toError(value: Error | unknown): Error {
9-
if (value instanceof Error) return value
10-
try {
11-
return new Error(JSON.stringify(value))
12-
} catch {
13-
return new Error(String(value))
14-
}
15-
}
16-
177
process.on('uncaughtException', async (err: Error | unknown) => {
188
const error = toError(err)
199
logger.error('uncaughtException', { error })

src/observability/lib/to-error.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Safely convert an unknown thrown value to an Error, avoiding JSON.stringify
2+
// which can throw on circular references.
3+
export function toError(value: Error | unknown): Error {
4+
if (value instanceof Error) return value
5+
try {
6+
return new Error(JSON.stringify(value))
7+
} catch {
8+
return new Error(String(value))
9+
}
10+
}

src/observability/tests/logger.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,4 +431,97 @@ describe('createLogger', () => {
431431
expect(logOutput).not.toContain('nodeHostname=')
432432
})
433433
})
434+
435+
describe('BUILD_SHA in production logs', () => {
436+
it('should include build_sha in logfmt output when BUILD_SHA env var is set', async () => {
437+
vi.stubEnv('BUILD_SHA', 'abc123def456')
438+
vi.stubEnv('LOG_LIKE_PRODUCTION', 'true')
439+
440+
vi.resetModules()
441+
const { createLogger: freshCreateLogger } = await import('@/observability/logger')
442+
443+
const logger = freshCreateLogger('file:///path/to/test.js')
444+
logger.info('Build SHA test')
445+
446+
expect(consoleLogs).toHaveLength(1)
447+
const logOutput = consoleLogs[0]
448+
expect(logOutput).toContain('build_sha=abc123def456')
449+
})
450+
451+
it('should not include build_sha in logfmt output when BUILD_SHA env var is absent', async () => {
452+
vi.stubEnv('LOG_LIKE_PRODUCTION', 'true')
453+
delete process.env.BUILD_SHA
454+
455+
vi.resetModules()
456+
const { createLogger: freshCreateLogger } = await import('@/observability/logger')
457+
458+
const logger = freshCreateLogger('file:///path/to/test.js')
459+
logger.info('No build SHA test')
460+
461+
expect(consoleLogs).toHaveLength(1)
462+
const logOutput = consoleLogs[0]
463+
expect(logOutput).not.toContain('build_sha=')
464+
})
465+
})
466+
467+
describe('error serialization in production logs', () => {
468+
it('should include error_code and error_name when Error has a .code property', async () => {
469+
vi.stubEnv('LOG_LIKE_PRODUCTION', 'true')
470+
471+
vi.resetModules()
472+
const { createLogger: freshCreateLogger } = await import('@/observability/logger')
473+
474+
const logger = freshCreateLogger('file:///path/to/test.js')
475+
const error = new Error('Connection reset') as NodeJS.ErrnoException
476+
error.code = 'ECONNRESET'
477+
logger.error('Network failure', error)
478+
479+
expect(consoleLogs).toHaveLength(1)
480+
const logOutput = consoleLogs[0]
481+
expect(logOutput).toContain('included.error="Connection reset"')
482+
expect(logOutput).toContain('included.error_code=ECONNRESET')
483+
expect(logOutput).toContain('included.error_name=Error')
484+
expect(logOutput).toContain('included.error_stack=')
485+
})
486+
487+
it('should include error_name even when .code is undefined', async () => {
488+
vi.stubEnv('LOG_LIKE_PRODUCTION', 'true')
489+
490+
vi.resetModules()
491+
const { createLogger: freshCreateLogger } = await import('@/observability/logger')
492+
493+
const logger = freshCreateLogger('file:///path/to/test.js')
494+
const error = new TypeError('Cannot read property')
495+
logger.error('Type error occurred', error)
496+
497+
expect(consoleLogs).toHaveLength(1)
498+
const logOutput = consoleLogs[0]
499+
expect(logOutput).toContain('included.error="Cannot read property"')
500+
expect(logOutput).toContain('included.error_name=TypeError')
501+
// When .code is undefined, error_code is present but empty
502+
expect(logOutput).toMatch(/included\.error_code= /)
503+
expect(logOutput).toContain('included.error_stack=')
504+
})
505+
506+
it('should serialize multiple errors with indexed keys', async () => {
507+
vi.stubEnv('LOG_LIKE_PRODUCTION', 'true')
508+
509+
vi.resetModules()
510+
const { createLogger: freshCreateLogger } = await import('@/observability/logger')
511+
512+
const logger = freshCreateLogger('file:///path/to/test.js')
513+
const error1 = new Error('First') as NodeJS.ErrnoException
514+
error1.code = 'ERR_FIRST'
515+
const error2 = new Error('Second')
516+
logger.error('Multiple errors', error1, error2)
517+
518+
expect(consoleLogs).toHaveLength(1)
519+
const logOutput = consoleLogs[0]
520+
expect(logOutput).toContain('included.error_1=First')
521+
expect(logOutput).toContain('included.error_1_code=ERR_FIRST')
522+
expect(logOutput).toContain('included.error_1_name=Error')
523+
expect(logOutput).toContain('included.error_2=Second')
524+
expect(logOutput).toContain('included.error_2_name=Error')
525+
})
526+
})
434527
})
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import { toError } from '@/observability/lib/to-error'
4+
5+
describe('toError', () => {
6+
it('should return Error instances as-is', () => {
7+
const error = new Error('test error')
8+
const result = toError(error)
9+
expect(result).toBe(error)
10+
expect(result.message).toBe('test error')
11+
})
12+
13+
it('should return subclassed Error instances as-is', () => {
14+
const error = new TypeError('type error')
15+
const result = toError(error)
16+
expect(result).toBe(error)
17+
expect(result).toBeInstanceOf(TypeError)
18+
})
19+
20+
it('should convert a plain string to an Error via JSON.stringify', () => {
21+
const result = toError('something went wrong')
22+
expect(result).toBeInstanceOf(Error)
23+
expect(result.message).toBe('"something went wrong"')
24+
})
25+
26+
it('should convert a number to an Error', () => {
27+
const result = toError(42)
28+
expect(result).toBeInstanceOf(Error)
29+
expect(result.message).toBe('42')
30+
})
31+
32+
it('should convert null to an Error', () => {
33+
const result = toError(null)
34+
expect(result).toBeInstanceOf(Error)
35+
expect(result.message).toBe('null')
36+
})
37+
38+
it('should convert undefined to an Error via JSON.stringify', () => {
39+
const result = toError(undefined)
40+
expect(result).toBeInstanceOf(Error)
41+
// JSON.stringify(undefined) returns undefined (not a string),
42+
// so new Error(undefined) has an empty message
43+
expect(result.message).toBe('')
44+
})
45+
46+
it('should convert a plain object to an Error via JSON.stringify', () => {
47+
const result = toError({ code: 'ERR_TIMEOUT', detail: 'took too long' })
48+
expect(result).toBeInstanceOf(Error)
49+
expect(result.message).toBe('{"code":"ERR_TIMEOUT","detail":"took too long"}')
50+
})
51+
52+
it('should fall back to String() for circular references', () => {
53+
const circular: Record<string, unknown> = { name: 'loop' }
54+
circular.self = circular
55+
const result = toError(circular)
56+
expect(result).toBeInstanceOf(Error)
57+
// String() on an object returns '[object Object]'
58+
expect(result.message).toBe('[object Object]')
59+
})
60+
})

src/tools/components/InArticlePicker.module.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
.container {
2+
// Override Primer's selected tab indicator color to meet WCAG 1.4.11
3+
// non-text contrast minimum of 3:1. The default --color-primer-border-active
4+
// (#FD8C73) has only 2.3:1 contrast. Using a Primer theme variable
5+
// that meets contrast in both light and dark color modes.
6+
--underlineNav-borderColor-active: var(--color-severe-emphasis);
7+
28
// target the ActionList dropdown that appears when UnderlineNav overflows
39
ul[class*="prc-ActionList-ActionList"] {
410
background-color: var(

0 commit comments

Comments
 (0)