Skip to content

Repo sync #39037

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 4 commits into from
Jun 24, 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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ To learn more about roles authorized to create and manage cost centers, see [AUT

## Creating a cost center

> [!NOTE]
> An enterprise can create up to 250 cost centers.

Create cost centers to monitor and manage expenses for specific organizations or repositories. Multiple organizations, repositories, and users can be assigned to one cost center.

When you create a cost center, you can add **organizations** or **repositories**—which track spending for usage-based products like {% data variables.product.prodname_actions %}—via the user interface. To track spending for license-based products like {% data variables.product.prodname_copilot %}, you will need to add **users** to the cost center via the API after the cost center has been created. For guidance by product, see [Allocating spending to a cost center](#allocating-spending-to-a-cost-center).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,32 @@ For more information about custom properties, see [AUTOTITLE](/organizations/man

If a repository is targeted by a ruleset created at the organization level, only owners of the organization can edit the ruleset. However, people with admin access to the repository, or with a custom role including the "edit repository rules" permission, can create additional rulesets at the repository level. The rules in these rulesets will be aggregated with the rules defined at the organization level. The result is that creating a new ruleset can make the rules targeting a branch or tag more restrictive, but never less restrictive. For more information on creating rulesets, see [AUTOTITLE](/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets).

#### Targeting repositories by properties in your organization

You can target repositories in your organization by custom properties. See [AUTOTITLE](/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization).

1. To target a dynamic list of repositories in your organization by properties, in the "Target repositories" section, next to "Repository targeting criteria" select **Repositories matching a filter**.
1. To add a target, in the filter section, **enter a query** for example, `visibility:private props.team:infra -language:java` or **Select by filter**.
1. In the modal dialog that appears, select custom or system properties from the dropdown menu, then select a value for each property.
1. Click **Apply**.

#### Targeting all repositories in your organization

To target all repositories in your organization, in the "Target repositories" section, select **{% octicon "goal" aria-hidden="true" aria-label="goal" %} Target: REPOSITORIES**, then click **All repositories**.
To target all repositories in your organization, in the "Target repositories" section, next to
"Repository targeting criteria", select **All repositories**.

#### Targeting select repositories in your organization

1. To target a static, manually selected list of repositories in your organization, in the "Target repositories" section, next to "Repository targeting criteria", select **Only selected repositories**.
1. To select repositories to target, in the "Targeting criteria" section, select **{% octicon "repo" aria-hidden="true" aria-label="repo" %} Select repositories**, then search for the name of each repository you would like to target. Select each repository from the search results.

#### Targeting repositories by naming convention in your organization

1. To target a dynamic list of repositories in your organization by naming convention, in the "Target repositories" section, select **{% octicon "goal" aria-hidden="true" aria-label="goal" %} Target: REPOSITORIES**, then click **Dynamic list of repositories**.
1. To target a dynamic list of repositories in your organization by naming convention, in the "Target repositories" section, next to "Repository targeting criteria", select **Repositories matching a name**.
1. To begin defining a targeting pattern, in the "Targeting criteria" section, select **Add a target** {% octicon "triangle-down" aria-hidden="true" aria-label="triangle-down" %}, then click **Include by pattern** or **Exclude by pattern**.
1. In the modal dialog that appears, enter a repository naming pattern using `fnmatch` syntax, then click **Add Inclusion pattern** or **Add Exclusion pattern**. For more information on `fnmatch` syntax, see [Using `fnmatch` syntax](#using-fnmatch-syntax).

> [!NOTE]
> You can add multiple targeting criteria to the same ruleset. For example, you could include any repositories matching the pattern `*cat*`, then specifically exclude a repository matching the pattern `not-a-cat`.

1. Optionally, on the ruleset configuration page, select **Prevent renaming of target repositories**.

#### Targeting repositories by properties in your organization

You can target repositories in your organization by custom properties. For more information, see [AUTOTITLE](/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization).

1. To target a dynamic list of repositories in your organization by properties, in the "Target repositories" section, select **{% octicon "goal" aria-hidden="true" aria-label="goal" %} Target: REPOSITORIES**, then click **Dynamic list by property**.
1. To add a target, in the "Targeting criteria" section, select **Add a target** {% octicon "triangle-down" aria-hidden="true" aria-label="triangle-down" %}, then click **Include by property** or **Exclude by property**.
1. In the modal dialog that appears, select a custom or system property from the dropdown menu, then select a value for the property.
1. Click **Add target**.

#### Targeting select repositories in your organization

1. To target a static, manually selected list of repositories in your organization, in the "Target repositories" section, select **{% octicon "goal" aria-hidden="true" aria-label="goal" %} Target: REPOSITORIES**, then click **Select repositories**.
1. To select repositories to target, in the "Targeting criteria" section, select **{% octicon "repo" aria-hidden="true" aria-label="repo" %} Select repositories**, then search for the name of each repository you would like to target. Select each repository from the search results.
1 change: 1 addition & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default {
},
sassOptions: {
quietDeps: true,
silenceDeprecations: ['import'],
},
async rewrites() {
const DEFAULT_VERSION = 'free-pro-team@latest'
Expand Down
39 changes: 29 additions & 10 deletions src/rest/scripts/utils/update-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,26 @@ export async function updateRestFiles() {

// Reads data files from the directory provided and returns a
// JSON object that lists the versions for each category/subcategory
/**
* Extract GHES version from a file path if it's a GHES directory
* @param {string} filePath - File path to parse
* @returns {string|null} - GHES version or null if not a GHES file
*/
export function getGHESVersionFromFilepath(filePath) {
// Normalize path separators to handle both Unix and Windows paths
const normalizedPath = filePath.replace(/\\/g, '/')
const pathParts = normalizedPath.split('/')
const ghesDir = pathParts.find((part) => part.startsWith('ghes-'))

if (!ghesDir) {
return null
}

// Extract version from ghes-X.Y or ghes-X.Y-YYYY-MM-DD format
const versionMatch = ghesDir.match(/^ghes-(\d+\.\d+)/)
return versionMatch ? versionMatch[1] : null
}

// The data files are split up by version, so all files must be
// read to get a complete list of versions.
async function getDataFrontmatter(dataDirectory, schemaFilename) {
Expand All @@ -36,16 +56,15 @@ async function getDataFrontmatter(dataDirectory, schemaFilename) {
// the most recent deprecated version but still allow data to exist.
// This makes the deprecation steps easier.
.filter((file) => {
return !deprecated.some((depVersion) =>
file.split(path.sep).find((pathSplit) => {
if (pathSplit.startsWith('ghes')) {
// An example version format is: ghes-3.6 or ghes-3.6-2022-01-01
const ghesVersion = pathSplit.split('-')[1]
return ghesVersion === depVersion
}
return false
}),
)
const ghesVersion = getGHESVersionFromFilepath(file)

// If it's not a GHES file, include it (e.g., ghae, fpt, ghec)
if (!ghesVersion) {
return true
}

// If it's a GHES file, exclude it only if the version is deprecated
return !deprecated.includes(ghesVersion)
})

const restVersions = {}
Expand Down
59 changes: 59 additions & 0 deletions src/rest/tests/update-markdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { describe, expect, test } from 'vitest'
import { getGHESVersionFromFilepath } from '../scripts/utils/update-markdown.js'

describe('GHES version extraction for update-markdown', () => {
test('extracts GHES version from file path with date suffix', () => {
const filePath = 'src/rest/data/ghes-3.10-2022-11-28/schema.json'
expect(getGHESVersionFromFilepath(filePath)).toBe('3.10')
})

test('extracts GHES version from file path without date suffix', () => {
const filePath = 'src/rest/data/ghes-3.6/schema.json'
expect(getGHESVersionFromFilepath(filePath)).toBe('3.6')
})

test('returns null for non-GHES file paths', () => {
expect(getGHESVersionFromFilepath('src/rest/data/ghae/schema.json')).toBeNull()
expect(getGHESVersionFromFilepath('src/rest/data/fpt-2022-11-28/schema.json')).toBeNull()
expect(getGHESVersionFromFilepath('src/rest/data/ghec-2022-11-28/schema.json')).toBeNull()
})

test('handles various GHES version formats', () => {
expect(getGHESVersionFromFilepath('src/rest/data/ghes-2.22/schema.json')).toBe('2.22')
expect(getGHESVersionFromFilepath('src/rest/data/ghes-3.0-2022-01-01/schema.json')).toBe('3.0')
expect(getGHESVersionFromFilepath('src/rest/data/ghes-3.15-2023-05-15/schema.json')).toBe(
'3.15',
)
})

test('returns null for malformed GHES paths', () => {
expect(getGHESVersionFromFilepath('src/rest/data/ghes-/schema.json')).toBeNull()
expect(getGHESVersionFromFilepath('src/rest/data/ghes-abc/schema.json')).toBeNull()
expect(getGHESVersionFromFilepath('src/rest/data/ghes/schema.json')).toBeNull()
})

test('works with different path separators and nested paths', () => {
const windowsPath = 'src\\rest\\data\\ghes-3.10-2022-11-28\\schema.json'
expect(getGHESVersionFromFilepath(windowsPath)).toBe('3.10')

const nestedPath = 'some/deep/path/src/rest/data/ghes-3.5-2021-12-31/nested/schema.json'
expect(getGHESVersionFromFilepath(nestedPath)).toBe('3.5')
})

test('demonstrates the original bug scenario', () => {
// This test demonstrates the bug described in the issue
// where ghes-3.10 would incorrectly match deprecated version 3.1
const filePath = 'src/rest/data/ghes-3.10-2022-11-28/schema.json'
const extractedVersion = getGHESVersionFromFilepath(filePath)

// Mock deprecated versions array like in the actual code
const deprecated = ['3.0', '3.1', '3.2', '2.22', '2.21']

expect(extractedVersion).toBe('3.10')
// This should be false - 3.10 is NOT in the deprecated list
expect(deprecated.includes(extractedVersion)).toBe(false)

// The old buggy logic would have incorrectly flagged this as deprecated
// because it would find '3.1' as a substring in the path
})
})
Loading