diff --git a/content/billing/managing-your-billing/charging-business-units.md b/content/billing/managing-your-billing/charging-business-units.md index c0f70cfd976e..c5848da34528 100644 --- a/content/billing/managing-your-billing/charging-business-units.md +++ b/content/billing/managing-your-billing/charging-business-units.md @@ -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). diff --git a/data/reusables/organizations/organization-rulesets-targeting-repositories-step.md b/data/reusables/organizations/organization-rulesets-targeting-repositories-step.md index 083a3558f311..ce62cc4b4058 100644 --- a/data/reusables/organizations/organization-rulesets-targeting-repositories-step.md +++ b/data/reusables/organizations/organization-rulesets-targeting-repositories-step.md @@ -4,13 +4,28 @@ 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). @@ -18,17 +33,3 @@ To target all repositories in your organization, in the "Target repositories" se > 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. diff --git a/next.config.js b/next.config.js index 8a67f3417ae5..d97371239620 100644 --- a/next.config.js +++ b/next.config.js @@ -24,6 +24,7 @@ export default { }, sassOptions: { quietDeps: true, + silenceDeprecations: ['import'], }, async rewrites() { const DEFAULT_VERSION = 'free-pro-team@latest' diff --git a/src/rest/scripts/utils/update-markdown.js b/src/rest/scripts/utils/update-markdown.js index ac4aaa45ab3e..d3664115b4ff 100644 --- a/src/rest/scripts/utils/update-markdown.js +++ b/src/rest/scripts/utils/update-markdown.js @@ -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) { @@ -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 = {} diff --git a/src/rest/tests/update-markdown.js b/src/rest/tests/update-markdown.js new file mode 100644 index 000000000000..8d63729ddb01 --- /dev/null +++ b/src/rest/tests/update-markdown.js @@ -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 + }) +})