Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ You can pay for metered usage of {% data variables.product.github %} features th

* You must know your Azure subscription ID. See [Get subscription and tenant IDs in the Azure portal](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id) in the Microsoft Docs.

* You must be logged into Azure as a user who is able to provide tenant-wide admin consent or arrange to work with an Azure AD global administrator to configure an admin consent workflow. See [AUTOTITLE](/billing/concepts/azure-subscriptions).
* You must be logged into Azure as a user who is able to provide tenant-wide admin consent or arrange to work with a Microsoft Entra Global Administrator to configure an admin consent workflow. See [AUTOTITLE](/billing/concepts/azure-subscriptions).

## Connecting your Azure subscription to an organization or enterprise account

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,6 @@ Anthropic models include a cache write cost in addition to cached input.
[^5]: {% data variables.copilot.copilot_gemini_25_pro %} and {% data variables.copilot.copilot_gemini_31_pro %} pricing applies to prompts with ≤200K tokens.
[^6]: {% data variables.copilot.copilot_gemini_3_flash %} has no long-context surcharge.

### xAI

| Model | Release status | Category | Input | Cached input | Output |
| --- | --- | --- | ---: | ---: | ---: |
| {% for entry in tables.copilot.models-and-pricing %}{% if entry.provider == "xai" %} |
| {{ entry.model }} | {{ entry.release_status }} | {{ entry.category }} | {{ entry.input }} | {{ entry.cached_input }} | {{ entry.output }} |
| {% endif %}{% endfor %} |

### Fine-tuned ({% data variables.product.github %})

| Model | Release status | Category | Input | Cached input | Output |
Expand Down
4 changes: 0 additions & 4 deletions data/reusables/copilot/github-app-preview-note.md

This file was deleted.

2 changes: 1 addition & 1 deletion data/reusables/webhooks/watch_properties.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Key | Type | Description
----|------|-------------
`action`|`string` | The action that was performed. Currently, can only be `started`.
`action`|`string` | Can only be `started` to indicate a user starred a repository.
2 changes: 1 addition & 1 deletion data/reusables/webhooks/watch_short_desc.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
When someone stars a repository. {% data reusables.webhooks.action_type_desc %} For more information, see [AUTOTITLE](/rest/activity#starring).
A user stars a repository (historically referred to as "watching"). {% data reusables.webhooks.action_type_desc %} For more information, see [AUTOTITLE](/rest/activity#starring).
4 changes: 1 addition & 3 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,9 @@ export default [
'src/article-api/**/*.{ts,js}',
'src/audit-logs/**/*.{ts,js}',
'src/color-schemes/**/*.{ts,js}',
'src/data-directory/**/*.{ts,js}',
'src/dev-toc/**/*.{ts,js}',
'src/events/**/*.{ts,js}',
'src/events/components/**/*.{ts,js}',
'src/fixtures/**/*.{ts,js}',
'src/frame/**/*.{ts,js}',
'src/github-apps/**/*.{ts,js}',
'src/journeys/**/*.{ts,js}',
'src/languages/**/*.{ts,js}',
Expand Down
2,205 changes: 1,864 additions & 341 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
"@opentelemetry/instrumentation-express": "^0.63.0",
"@opentelemetry/instrumentation-http": "^0.215.0",
"@opentelemetry/instrumentation-undici": "^0.25.0",
"@opentelemetry/sdk-node": "^0.215.0",
"@opentelemetry/sdk-node": "^0.218.0",
"@primer/behaviors": "^1.10.2",
"@primer/css": "^21.3.1",
"@primer/live-region-element": "^0.7.2",
Expand Down Expand Up @@ -237,7 +237,7 @@
"mdast-util-to-markdown": "2.1.2",
"mdast-util-to-string": "^4.0.0",
"micromark-extension-gfm": "^3.0.0",
"next": "^16.2.3",
"next": "^16.2.6",
"ora": "^9.3.0",
"parse5": "8.0.1",
"quick-lru": "7.0.1",
Expand Down
9 changes: 6 additions & 3 deletions src/data-directory/lib/get-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { merge, get } from 'lodash-es'

import languages from '@/languages/lib/languages-server'
import { correctTranslatedContentStrings } from '@/languages/lib/correct-translation-content'
import { createLogger } from '@/observability/logger'

const logger = createLogger(import.meta.url)

interface YAMLException extends Error {
mark?: any
Expand Down Expand Up @@ -128,7 +131,7 @@ export const getDataByLanguage = memoize((dottedPath: string, langCode: string):
// would have caused a YAMLException
if (langCode !== 'en') {
if (DEBUG_JIT_DATA_READS) {
console.warn(`Unable to parse Yaml in (${langCode}) '${dottedPath}': ${error.message}`)
logger.warn('Unable to parse Yaml in translation', { langCode, dottedPath, error })
}
// Give it one more chance, but use English this time
return getDataByDir(dottedPath, languages.en.dir)
Expand Down Expand Up @@ -208,7 +211,7 @@ function getDataByDir(
return content
}
} else {
console.warn(`Unable to find variables Yaml file ${fullPath.join(path.sep)}`)
logger.warn('Unable to find variables Yaml file', { filePath: fullPath.join(path.sep) })
}
return undefined
}
Expand Down Expand Up @@ -347,7 +350,7 @@ const getFileContent = (
englishRoot?: string,
): string => {
const filePath = root ? path.join(root, relPath) : relPath
if (DEBUG_JIT_DATA_READS) console.log('READ', filePath)
if (DEBUG_JIT_DATA_READS) logger.info('READ', { filePath })
try {
return fs.readFileSync(filePath, 'utf-8')
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion src/events/lib/hydro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const { NODE_ENV, HYDRO_SECRET, HYDRO_ENDPOINT } = process.env
const inProd = NODE_ENV === 'production'

if (inProd && (isNil(HYDRO_SECRET) || isNil(HYDRO_ENDPOINT))) {
console.warn(
logger.warn(
'Running in production but HYDRO_SECRET and HYDRO_ENDPOINT environment variables are not set.',
)
}
Expand Down
5 changes: 4 additions & 1 deletion src/events/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createLogger } from '@/observability/logger'
import express from 'express'
import { omit, without, mapValues } from 'lodash-es'
import QuickLRU from 'quick-lru'
Expand All @@ -17,6 +18,8 @@ import { DOTCOM_USER_COOKIE_NAME, STAFFONLY_COOKIE_NAME } from '@/frame/lib/cons
import { analyzeComment, getGuessedLanguage } from './lib/analyze-comment'
import { EventType, EventProps, EventPropsByType } from './types'

const logger = createLogger(import.meta.url)

const router = express.Router()
const OMIT_FIELDS = ['type']
const allowedTypes = new Set(without(Object.keys(schemas), 'validation'))
Expand Down Expand Up @@ -103,7 +106,7 @@ router.post(
value: omit(body, OMIT_FIELDS),
})
} catch (eventError) {
console.error('Error validating event:', eventError)
logger.error('Error validating event', { error: eventError })
}
}
if (validEvents.length > 0) {
Expand Down
10 changes: 7 additions & 3 deletions src/frame/lib/check-node-version.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import fs from 'fs'
import semver from 'semver'

import { createLogger } from '@/observability/logger'
const logger = createLogger(import.meta.url)

export function checkNodeVersion() {
const packageFile = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
const { engines } = packageFile

if (!semver.satisfies(process.version, engines.node)) {
console.error(
`\n\nYou're using Node.js ${process.version.replace(/^v/, '')} but this project requires ${
logger.error(
`You're using Node.js ${process.version.replace(/^v/, '')} but this project requires ${
engines.node
}`,
{ currentVersion: process.version, requiredVersion: engines.node },
)
console.error('Visit nodejs.org to download an installer that meets these requirements.\n\n')
logger.error('Visit nodejs.org to download an installer that meets these requirements.')
process.exit(1)
}
}
4 changes: 3 additions & 1 deletion src/frame/lib/create-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import fs from 'fs/promises'

import PageClass from './page'
import type { UnversionedTree, Page } from '@/types'
import { createLogger } from '@/observability/logger'
const logger = createLogger(import.meta.url)

const isProduction = process.env.NODE_ENV === 'production'

Expand Down Expand Up @@ -50,7 +52,7 @@ export default async function createTree(
originalPath === 'content/early-access' ||
originalPath.startsWith('content/early-access/')
) {
console.warn(`Warning: ${msg}`)
logger.warn(msg, { path: originalPath })
return
}
throw new Error(msg)
Expand Down
13 changes: 8 additions & 5 deletions src/frame/lib/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { deprecated, supported } from '@/versions/lib/enterprise-server-releases
import { allPlatforms } from '@/tools/lib/all-platforms'
import type { Context, FrontmatterVersions, FeaturedLinksExpanded } from '@/types'
import type { Product } from '@/products/lib/all-products'
import { createLogger } from '@/observability/logger'
const logger = createLogger(import.meta.url)

const isProduction = process.env.NODE_ENV === 'production'

Expand Down Expand Up @@ -192,17 +194,18 @@ class Page {
} as PageReadResult
} catch (err) {
if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'ENOENT') return false
console.error(err)
logger.error('Failed to read page file', { error: err, fullPath })
return false
}
}

constructor(opts: PageReadResult) {
if (opts.frontmatterErrors && opts.frontmatterErrors.length) {
console.error(
`${opts.frontmatterErrors.length} frontmatter errors trying to load ${opts.fullPath}:`,
)
console.error(opts.frontmatterErrors)
logger.error('Frontmatter errors loading page', {
errorCount: opts.frontmatterErrors.length,
fullPath: opts.fullPath,
frontmatterErrors: opts.frontmatterErrors,
})
throw new FrontmatterErrorsError(
`${opts.frontmatterErrors.length} frontmatter errors in ${opts.fullPath}`,
opts.frontmatterErrors,
Expand Down
10 changes: 6 additions & 4 deletions src/frame/lib/read-json-file.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import fs from 'fs'
import { brotliDecompressSync } from 'zlib'

import { createLogger } from '@/observability/logger'
const logger = createLogger(import.meta.url)

export default function readJsonFile(xpath: string): unknown {
return JSON.parse(fs.readFileSync(xpath, 'utf8'))
}
Expand Down Expand Up @@ -68,10 +71,9 @@ export function readCompressedJsonFileFallbackLazily(xpath: string): () => unkno
if (!cache.has(xpath)) {
cache.set(xpath, readCompressedJsonFileFallback(xpath))
if (globalCacheCounter[xpath]) {
console.warn(
"If this happens it's because the readCompressedJsonFileFallbackLazily " +
'function has been called non-globally. Only use ' +
'readCompressedJsonFileFallback once at module-level.',
logger.warn(
'readCompressedJsonFileFallbackLazily called non-globally. Only use readCompressedJsonFileFallback once at module-level.',
{ xpath },
)
throw new Error(`Globally reading the same file more than once (${xpath})`)
}
Expand Down
4 changes: 3 additions & 1 deletion src/frame/middleware/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'

import { createLogger } from '@/observability/logger'
import events from '@/events/middleware'
import anchorRedirect from '@/rest/api/anchor-redirect'
import aiSearch from '@/search/middleware/ai-search'
Expand All @@ -13,6 +14,7 @@ import { ExtendedRequest } from '@/types'
import { noCacheControl } from './cache-control'
import { STAFFONLY_COOKIE_NAME } from '@/frame/lib/constants'

const logger = createLogger(import.meta.url)
const router = express.Router()

router.use('/events', events)
Expand All @@ -30,7 +32,7 @@ router.use('/article', article)
if (process.env.CSE_COPILOT_ENDPOINT || process.env.NODE_ENV === 'test') {
router.use('/ai-search', aiSearch)
} else {
console.log(
logger.info(
'Proxying AI Search requests to docs.github.com. To use the cse-copilot endpoint, set the CSE_COPILOT_ENDPOINT environment variable.',
)
router.use(aiSearchLocalProxy)
Expand Down
5 changes: 4 additions & 1 deletion src/frame/middleware/cache-control.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { Response } from 'express'

import { createLogger } from '@/observability/logger'
const logger = createLogger(import.meta.url)

interface CacheControlOptions {
key?: string
public_?: boolean
Expand Down Expand Up @@ -42,7 +45,7 @@ function cacheControlFactory(
.join(', ')
return (res: Response) => {
if (process.env.NODE_ENV !== 'production' && res.hasHeader('set-cookie') && maxAge) {
console.warn(
logger.warn(
"You can't set a >0 cache-control header AND set-cookie or else the CDN will never respect the cache-control.",
)
}
Expand Down
8 changes: 5 additions & 3 deletions src/frame/middleware/render-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Response } from 'express'

import type { Failbot } from '@github/failbot'
import { get } from 'lodash-es'
import { createLogger } from '@/observability/logger'

import { buildMiniTocFromCollected, type CollectedHeading } from '@/frame/lib/get-mini-toc-items'
import patterns from '@/frame/lib/patterns'
Expand All @@ -15,6 +16,7 @@ import { contentTypeCacheControl, defaultCacheControl } from './cache-control'
import { isConnectionDropped } from './halt-on-dropped-connection'
import { nextHandleRequest } from './next'

const logger = createLogger(import.meta.url)
const STATSD_KEY_RENDER = 'middleware.render_page'

async function buildRenderedPage(req: ExtendedRequest): Promise<string> {
Expand Down Expand Up @@ -71,9 +73,9 @@ export default async function renderPage(req: ExtendedRequest, res: Response) {
// render a 404 page
if (!page) {
if (process.env.NODE_ENV !== 'test' && context.redirectNotFound) {
console.error(
`\nTried to redirect to ${context.redirectNotFound}, but that page was not found.\n`,
)
logger.error('Tried to redirect to a page that was not found', {
redirectNotFound: context.redirectNotFound,
})
}

// send minimal 404 at this point since we ran into hydration issues trying to pass
Expand Down
8 changes: 4 additions & 4 deletions src/frame/start-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ async function checkPortAvailability() {
// Check that the development server is not already running
const portInUse = await tcpPortUsed.check(port)
if (portInUse) {
console.log(`\n\n\nPort ${port} is not available. You may already have a server running.`)
console.log(
`Try running \`npx kill-port ${port}\` to shut down all your running node processes.\n\n\n`,
logger.error('Port is not available. You may already have a server running.', { port })
logger.error(
`Try running \`npx kill-port ${port}\` to shut down all your running node processes.`,
)
console.log('\x07') // system 'beep' sound
logger.info('\x07') // system 'beep' sound
process.exit(1)
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/rest/scripts/utils/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ export default class Operation {
delete param.example
delete param.examples
delete param['x-multi-segment']
// Strip unused parameter schema sub-fields; only type, default, and
// enum are consumed by renderers
if (param.schema && typeof param.schema === 'object') {
const { type, default: defaultVal, enum: enumVal } = param.schema
param.schema = { type }
if (defaultVal !== undefined) param.schema.default = defaultVal
if (enumVal !== undefined) param.schema.enum = enumVal
}
return param
}),
)
Expand Down Expand Up @@ -220,5 +228,8 @@ export default class Operation {
// Programmatic access data structure varies by operation and is not strongly typed
programmaticAccess(progAccessData: any): void {
this.progAccess = progAccessData[this.#operation.operationId]
if (this.progAccess) {
delete this.progAccess.disabledForPatV2
}
}
}