Skip to content

Repo sync #39245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 10, 2025
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
64 changes: 42 additions & 22 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,44 @@
This documentation repository consists mainly of content written in Markdown format. These files are converted into HTML for displaying on a website. Most Markdown files become a single article on the documentation site. Other files contain reusable content which is inserted into multiple articles. The repository also contains YAML files (e.g. for variable text), image files, JavaScript/TypeScript files, etc.
This repository contains code to run the GitHub Docs site on docs.github.com, as well as the content that is displayed on the site. The code is written in JavaScript and TypeScript, and the content is primarily written in Markdown.

Changes to files in `src/*` or files with `.ts` or `.js` extensions are likely code-related changes. Please follow the engineering guidelines below when making changes to these files.

Changes to files in `content/*` and `data/*` are likely content-related changes. Content changes include updates to articles, reusable content, and data files that define variables used in articles. Please follow the content guidelines below when making changes to these files.

## Engineering guidelines

### Scripts

All scripts can be found in `package.json`.

To validate any code changes:
- `npm run tsc`
- `npm run build`
- `npm run prettier`
- `npm run lint`: you can include `-- --fix`

To validate specific changes,
- `npm run test`: For all unit tests
- You can pass specific paths, e.g. `npm run test -- src/search/tests/ai-search-proxy`
- You can add `--silent=false` to include `console.log` debugging.
- `npm run build && npm run playwright-test -- playwright-rendering`: You need to build for changes outside of the test to be picked up. We use playwright for all rendering and end-to-end tests
- You can add `--ui` to keep open `localhost:4000` which can be viewed in a simple browser for debugging UI state.
- `npm run dev` to start the development server on `localhost:4000`.

### Imports

We use absolute imports, relative to the `src` directory, using the `@` symbol.

For example, `getRedirect` which lives inn `src/redirects/lib/get-redirect.js` can be imported with `import getRedirect from '@/redirects/lib/get-redirect'`.

The same rule applies for TypeScript (`.ts`) imports, e.g. `import type { GeneralSearchHit } from '@/search/types'`

### Testing changes

We use `vitest` to write unit tests. Tests live in their own files in the `tests` subdirectory of a source (src) directory, e.g. `src/search/tests/api-ai-search.ts`.

For integration tests, we can use the mock server in `src/tests/mocks/start-mock-server.ts` to mock exteneral requests.

For UI rendering tests, we use `playwright` and write tests in `src/fixtures/tests/playwright-rendering.spec.ts`

## Content guidelines

Expand Down Expand Up @@ -90,31 +130,11 @@ Then, within a collapsed section, quote the original prompt from Copilot Chat:

This helps reviewers understand the context and intent behind the automated changes.

## Development and testing guidelines

### Content changes
### Testing Content changes

Before committing content changes, always:

1. **Use the content linter** to validate content: `npm run lint-content -- --paths <file-paths>`
2. **Check for proper variable usage** in your content
3. **Verify [AUTOTITLE] links** point to existing articles
4. **Run tests** on changed content: `npm run test -- src/content-render/tests/render-changed-and-deleted-files.js`

### Script and code changes

For TypeScript, JavaScript, and SCSS files:

1. **Run Prettier** to check formatting: `npm run prettier-check`
2. **Run the linter**: `npm run lint`
3. **Run TypeScript checks**: `npm run tsc`
4. **Run relevant tests**: `npm test`

### Environment setup

When testing changes in your development environment:

1. Install dependencies: `npm ci`
2. For content changes, ensure the content linter runs successfully
3. For script changes, ensure all formatting and linting checks pass
4. Always verify your changes don't break existing functionality
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# ---------------------------------------------------------------
# To update the sha:
# https://github.com/github/gh-base-image/pkgs/container/gh-base-image%2Fgh-base-noble
FROM ghcr.io/github/gh-base-image/gh-base-noble:20250707-185623-g8becf904e AS base
FROM ghcr.io/github/gh-base-image/gh-base-noble:20250709-201453-g6a417ef5f AS base

# Install curl for Node install and determining the early access branch
# Install git for cloning docs-early-access & translations repos
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: About continuous integration with GitHub Actions
title: Continuous integration
intro: 'You can create custom continuous integration (CI) workflows directly in your {% data variables.product.prodname_dotcom %} repository with {% data variables.product.prodname_actions %}.'
redirect_from:
- /articles/about-continuous-integration
Expand All @@ -10,6 +10,7 @@ redirect_from:
- /actions/automating-builds-and-tests/about-continuous-integration
- /actions/about-github-actions/about-continuous-integration
- /actions/about-github-actions/about-continuous-integration-with-github-actions
- /actions/concepts/overview/about-continuous-integration-with-github-actions
versions:
fpt: '*'
ghes: '*'
Expand Down Expand Up @@ -44,13 +45,6 @@ In addition to helping you set up CI workflows for your project, you can use {%

For a definition of common terms, see [AUTOTITLE](/actions/learn-github-actions/understanding-github-actions).

## Workflow templates
## Next steps

{% data variables.product.github %} offers CI workflow templates for a variety of languages and frameworks.

Browse the complete list of CI workflow templates offered by {% data variables.product.company_short %} in the {% ifversion fpt or ghec %}[actions/starter-workflows](https://github.com/actions/starter-workflows/tree/main/ci) repository{% else %} `actions/starter-workflows` repository on {% data variables.product.prodname_dotcom_the_website %}{% endif %}.

## Further reading

* [AUTOTITLE](/actions/use-cases-and-examples/building-and-testing){% ifversion fpt or ghec %}
* [AUTOTITLE](/billing/managing-billing-for-github-actions){% endif %}
{% data variables.product.github %} offers CI workflow templates for a variety of languages and frameworks. For tutorials on setting up continuous integration with these templates, see [AUTOTITLE](/actions/use-cases-and-examples/building-and-testing).
2 changes: 1 addition & 1 deletion content/actions/concepts/overview/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ versions:
ghec: '*'
children:
- /about-continuous-deployment-with-github-actions
- /about-continuous-integration-with-github-actions
- /continuous-integration
- /usage-limits-billing-and-administration
---
3 changes: 1 addition & 2 deletions content/actions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ featuredLinks:
startHere:
- /actions/how-tos/writing-workflows
- /actions/how-tos/use-cases-and-examples
- /actions/concepts/overview/about-continuous-integration-with-github-actions
- /actions/concepts/overview/continuous-integration
- /actions/concepts/use-cases/deploying-with-github-actions
- /actions/concepts/use-cases/about-packaging-with-github-actions
- /actions/how-tos/monitoring-and-troubleshooting-workflows
Expand Down Expand Up @@ -42,4 +42,3 @@ children:
- /reference
- /tutorials
---

2 changes: 1 addition & 1 deletion data/reusables/repositories/repo-name.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
1. Type a name for your repository, and an optional description.
1. Type a name for your repository (maximum 100 characters), and an optional description.

![Screenshot of a the first step in creating a repository. The "Repository name" field contains the text "hello-world" and is outlined in orange.](/assets/images/help/repository/create-repository-name.png)
3 changes: 2 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import fs from 'fs'
import path from 'path'

import frontmatter from 'gray-matter'
import { ROOT } from '#src/frame/lib/constants.js'
// Replace imports with hardcoded values
const ROOT = process.env.ROOT || '.'

// Hard-coded language keys to avoid TypeScript import in config file
const languageKeys = ['en', 'es', 'ja', 'pt', 'zh', 'ru', 'fr', 'ko', 'de']
Expand Down
4 changes: 2 additions & 2 deletions src/archives/lib/is-archived-version.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import patterns from '@/frame/lib/patterns.js'
import { deprecated } from '@/versions/lib/enterprise-server-releases.js'
import patterns from '@/frame/lib/patterns'
import { deprecated } from '@/versions/lib/enterprise-server-releases'
import type { ExtendedRequest } from '@/types'

type IsArchivedInfo = {
Expand All @@ -26,7 +26,7 @@
}

// extract enterprise version from path, e.g. 2.16
const requestedVersion = pathToCheck.includes('enterprise-server@')

Check failure

Code scanning / CodeQL

Type confusion through parameter tampering Critical

Potential type confusion as
this HTTP request parameter
may be either an array or a string.
? pathToCheck.match(patterns.getEnterpriseServerNumber)?.[1]
: pathToCheck.match(patterns.getEnterpriseVersionNumber)?.[1]

Expand Down
6 changes: 3 additions & 3 deletions src/archives/lib/old-versions-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path'
import { supported, latest } from '@/versions/lib/enterprise-server-releases.js'
import patterns from '@/frame/lib/patterns.js'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version.js'
import { supported, latest } from '@/versions/lib/enterprise-server-releases'
import patterns from '@/frame/lib/patterns'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'
import { allVersions } from '@/versions/lib/all-versions'
const latestNewVersion = `enterprise-server@${latest}`
const oldVersions = ['dotcom'].concat(supported)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import got from 'got'
import type { Response, NextFunction } from 'express'

import patterns from '@/frame/lib/patterns.js'
import patterns from '@/frame/lib/patterns'
import { isArchivedVersion } from '@/archives/lib/is-archived-version'
import {
setFastlySurrogateKey,
SURROGATE_ENUMS,
} from '@/frame/middleware/set-fastly-surrogate-key.js'
import { archivedCacheControl, defaultCacheControl } from '@/frame/middleware/cache-control.js'
import { setFastlySurrogateKey, SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
import { archivedCacheControl, defaultCacheControl } from '@/frame/middleware/cache-control'
import type { ExtendedRequest } from '@/types'

// This module handles requests for the CSS and JS assets for
Expand Down
23 changes: 10 additions & 13 deletions src/archives/middleware/archived-enterprise-versions.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import type { Response, NextFunction } from 'express'
import got from 'got'

import statsd from '@/observability/lib/statsd.js'
import statsd from '@/observability/lib/statsd'
import {
firstVersionDeprecatedOnNewSite,
lastVersionWithoutArchivedRedirectsFile,
deprecatedWithFunctionalRedirects,
firstReleaseStoredInBlobStorage,
} from '@/versions/lib/enterprise-server-releases.js'
import patterns from '@/frame/lib/patterns.js'
import versionSatisfiesRange from '@/versions/lib/version-satisfies-range.js'
} from '@/versions/lib/enterprise-server-releases'
import patterns from '@/frame/lib/patterns'
import versionSatisfiesRange from '@/versions/lib/version-satisfies-range'
import { isArchivedVersion } from '@/archives/lib/is-archived-version'
import {
setFastlySurrogateKey,
SURROGATE_ENUMS,
} from '@/frame/middleware/set-fastly-surrogate-key.js'
import { readCompressedJsonFileFallbackLazily } from '@/frame/lib/read-json-file.js'
import { archivedCacheControl, languageCacheControl } from '@/frame/middleware/cache-control.js'
import { setFastlySurrogateKey, SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
import { readCompressedJsonFileFallbackLazily } from '@/frame/lib/read-json-file'
import { archivedCacheControl, languageCacheControl } from '@/frame/middleware/cache-control'
import { pathLanguagePrefixed, languagePrefixPathRegex } from '@/languages/lib/languages'
import getRedirect, { splitPathByLanguage } from '@/redirects/lib/get-redirect.js'
import getRemoteJSON from '@/frame/lib/get-remote-json.js'
import getRedirect, { splitPathByLanguage } from '@/redirects/lib/get-redirect'
import getRemoteJSON from '@/frame/lib/get-remote-json'
import { ExtendedRequest } from '@/types'

const OLD_PUBLIC_AZURE_BLOB_URL = 'https://githubdocs.azureedge.net'
Expand Down Expand Up @@ -75,7 +72,7 @@ const cacheAggressively = (res: Response) => {
// 3. ~4000ms
//
// ...if the limit we set is 3.
// Our own timeout, in #src/frame/middleware/timeout.js defaults to 10 seconds.
// Our own timeout, in @/frame/middleware/timeout.js defaults to 10 seconds.
// So there's no point in trying more attempts than 3 because it would
// just timeout on the 10s. (i.e. 1000 + 2000 + 4000 + 8000 > 10,000)
const retryConfiguration = { limit: 3 }
Expand Down
4 changes: 2 additions & 2 deletions src/archives/scripts/warmup-remotejson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
import { program } from 'commander'
import semver, { SemVer } from 'semver'

import getRemoteJSON from '@/frame/lib/get-remote-json.js'
import getRemoteJSON from '@/frame/lib/get-remote-json'
import {
deprecated,
lastVersionWithoutArchivedRedirectsFile,
} from '@/versions/lib/enterprise-server-releases.js'
} from '@/versions/lib/enterprise-server-releases'

program
.description(
Expand Down
4 changes: 2 additions & 2 deletions src/archives/tests/deprecated-enterprise-versions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { describe, expect, test, vi } from 'vitest'

import enterpriseServerReleases from '@/versions/lib/enterprise-server-releases.js'
import enterpriseServerReleases from '@/versions/lib/enterprise-server-releases'
import { get, getDOM } from '@/tests/helpers/e2etest-ts'
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key.js'
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'

describe('enterprise deprecation', () => {
vi.setConfig({ testTimeout: 60 * 1000 })
Expand Down
4 changes: 2 additions & 2 deletions src/article-api/middleware/article-body.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Response } from 'express'

import { Context } from '@/types.js'
import { Context } from '@/types'
import { ExtendedRequestWithPageInfo } from '../types'
import contextualize from '@/frame/middleware/context/context.js'
import contextualize from '@/frame/middleware/context/context'

export async function getArticleBody(req: ExtendedRequestWithPageInfo) {
// req.pageinfo is set from pageValidationMiddleware and pathValidationMiddleware
Expand Down
10 changes: 5 additions & 5 deletions src/article-api/middleware/article-pageinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import type { Response } from 'express'
import type { ExtendedRequestWithPageInfo } from '../types'

import type { ExtendedRequest, Page, Context, Permalink } from '@/types'
import shortVersions from '@/versions/middleware/short-versions.js'
import shortVersions from '@/versions/middleware/short-versions'
import contextualize from '@/frame/middleware/context/context'
import features from '@/versions/middleware/features.js'
import breadcrumbs from '@/frame/middleware/context/breadcrumbs.js'
import currentProductTree from '@/frame/middleware/context/current-product-tree.js'
import { readCompressedJsonFile } from '@/frame/lib/read-json-file.js'
import features from '@/versions/middleware/features'
import breadcrumbs from '@/frame/middleware/context/breadcrumbs'
import currentProductTree from '@/frame/middleware/context/current-product-tree'
import { readCompressedJsonFile } from '@/frame/lib/read-json-file'

// If you have pre-computed page info into a JSON file on disk, this is
// where it would be expected to be found.
Expand Down
8 changes: 4 additions & 4 deletions src/article-api/middleware/article.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { RequestHandler, Response } from 'express'
import express from 'express'

import { defaultCacheControl } from '@/frame/middleware/cache-control.js'
import catchMiddlewareError from '@/observability/middleware/catch-middleware-error.js'
import { defaultCacheControl } from '@/frame/middleware/cache-control'
import catchMiddlewareError from '@/observability/middleware/catch-middleware-error'
import { ExtendedRequestWithPageInfo } from '../types'
import { pageValidationMiddleware, pathValidationMiddleware } from './validation'
import { getArticleBody } from './article-body'
Expand All @@ -11,8 +11,8 @@ import {
makeLanguageSurrogateKey,
setFastlySurrogateKey,
SURROGATE_ENUMS,
} from '@/frame/middleware/set-fastly-surrogate-key.js'
import statsd from '@/observability/lib/statsd.js'
} from '@/frame/middleware/set-fastly-surrogate-key'
import statsd from '@/observability/lib/statsd'

const router = express.Router()

Expand Down
10 changes: 5 additions & 5 deletions src/article-api/middleware/pagelist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import express from 'express'
import type { Response, RequestHandler } from 'express'

import type { ExtendedRequest } from '@/types'
import { defaultCacheControl } from '@/frame/middleware/cache-control.js'
import { getProductStringFromPath, getVersionStringFromPath } from '@/frame/lib/path-utils.js'
import { getLanguageCodeFromPath } from '@/languages/middleware/detect-language.js'
import { defaultCacheControl } from '@/frame/middleware/cache-control'
import { getProductStringFromPath, getVersionStringFromPath } from '@/frame/lib/path-utils'
import { getLanguageCodeFromPath } from '@/languages/middleware/detect-language'
import { pagelistValidationMiddleware } from './validation'
import catchMiddlewareError from '@/observability/middleware/catch-middleware-error.js'
import statsd from '@/observability/lib/statsd.js'
import catchMiddlewareError from '@/observability/middleware/catch-middleware-error'
import statsd from '@/observability/lib/statsd'

const router = express.Router()

Expand Down
8 changes: 4 additions & 4 deletions src/article-api/middleware/validation.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ExtendedRequestWithPageInfo } from '../types'
import type { NextFunction, Response } from 'express'

import { ExtendedRequest, Page } from '@/types.js'
import { ExtendedRequest, Page } from '@/types'
import { isArchivedVersionByPath } from '@/archives/lib/is-archived-version'
import getRedirect from '@/redirects/lib/get-redirect.js'
import { getVersionStringFromPath, getLangFromPath } from '@/frame/lib/path-utils.js'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version.js'
import getRedirect from '@/redirects/lib/get-redirect'
import { getVersionStringFromPath, getLangFromPath } from '@/frame/lib/path-utils'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'

// validates the path for pagelist endpoint
// specifically, defaults to `/en/free-pro-team@latest` when those values are missing
Expand Down Expand Up @@ -85,8 +85,8 @@

// Similar to how the `handle-redirects.js` middleware works, let's first
// check if the URL is just having a trailing slash.
while (pathname.endsWith('/') && pathname.length > 1) {

Check failure

Code scanning / CodeQL

Type confusion through parameter tampering Critical

Potential type confusion as
this HTTP request parameter
may be either an array or a string.
pathname = pathname.slice(0, -1)

Check failure

Code scanning / CodeQL

Type confusion through parameter tampering Critical

Potential type confusion as
this HTTP request parameter
may be either an array or a string.
}

// E.g. a request for `/` is handled as a redirect outside the
Expand Down
2 changes: 1 addition & 1 deletion src/article-api/scripts/precompute-pageinfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import chalk from 'chalk'
import { program, Option } from 'commander'

import { languageKeys } from '@/languages/lib/languages'
import { loadPages, loadUnversionedTree } from '@/frame/lib/page-data.js'
import { loadPages, loadUnversionedTree } from '@/frame/lib/page-data'
import { CACHE_FILE_PATH, getPageInfo } from '../middleware/article-pageinfo'

program
Expand Down
6 changes: 3 additions & 3 deletions src/article-api/tests/pageinfo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { beforeAll, describe, expect, test } from 'vitest'

import { get } from '@/tests/helpers/e2etest.js'
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key.js'
import { latest } from '@/versions/lib/enterprise-server-releases.js'
import { get } from '@/tests/helpers/e2etest'
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
import { latest } from '@/versions/lib/enterprise-server-releases'

const makeURL = (pathname: string): string =>
`/api/article/meta?${new URLSearchParams({ pathname })}`
Expand Down
4 changes: 2 additions & 2 deletions src/article-api/tests/pagelist.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { beforeAll, describe, expect, test } from 'vitest'

import { get } from '@/tests/helpers/e2etest.js'
import { get } from '@/tests/helpers/e2etest'

import { allVersionKeys } from '@/versions/lib/all-versions'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version.js'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'

describe.each(allVersionKeys)('pagelist api for %s', async (versionKey) => {
beforeAll(() => {
Expand Down
7 changes: 2 additions & 5 deletions src/assets/middleware/dynamic-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ import type { Response, NextFunction } from 'express'
import sharp from 'sharp'

import type { ExtendedRequest } from '@/types'
import { assetCacheControl, defaultCacheControl } from '@/frame/middleware/cache-control.js'
import {
setFastlySurrogateKey,
SURROGATE_ENUMS,
} from '@/frame/middleware/set-fastly-surrogate-key.js'
import { assetCacheControl, defaultCacheControl } from '@/frame/middleware/cache-control'
import { setFastlySurrogateKey, SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'

/**
* This is the indicator that is a virtual part of the URL.
Expand Down
Loading
Loading