Skip to content

Commit 1d00ed3

Browse files
authored
Merge pull request github#40421 from github/repo-sync
Repo sync
2 parents ae64114 + 72dea2b commit 1d00ed3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1487
-525
lines changed

.gitignore

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@
3737
# TypeScript incremental build info
3838
*.tsbuildinfo
3939

40-
# Early access images from docs-early-access repo
41-
assets/images/early-access/
42-
4340
# Accidentally committed file that should be ignored
4441
assets/images/help/writing/unordered-list-rendered (1).png
4542

@@ -52,15 +49,14 @@ blc_output_internal.log
5249
# Old broken links report
5350
broken_links.md
5451

55-
# Early access content from docs-early-access repo
56-
content/early-access/
52+
# Directories from the docs-early-access repo. Used for symlinks in local docs-internal checkouts. Don't add trailing slashes.
53+
content/early-access
54+
data/early-access
55+
assets/images/early-access
5756

5857
# Test coverage reports
5958
coverage/
6059

61-
# Early access data from docs-early-access repo
62-
data/early-access/
63-
6460
# Cloned for Elasticsearch indexing data
6561
docs-internal-data/
6662

content/billing/concepts/enterprise-billing/combined-enterprise-use.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ To use a {% data variables.product.prodname_ghe_server %} instance, you must upl
3232
There are two types of {% data variables.product.prodname_enterprise %} (GHE) licensing models, with different processes for enabling combined use of {% data variables.product.prodname_ghe_cloud %} and {% data variables.product.prodname_ghe_server %}.
3333

3434
* **GHE (Usage-based, also called metered)**: A cloud-first license where users must first be assigned to a {% data variables.product.prodname_ghe_cloud %} organization.
35-
* All Cloud users automatically receive a use right for {% data variables.product.prodname_ghe_server %}.
35+
* All Cloud users automatically receive a right to use {% data variables.product.prodname_ghe_server %}.
3636
* Billing is based on the number of active users each month.
3737
* Users can generate their own Server license, which covers the number of assigned Cloud seats at the time of generation and is valid for one year.
3838
* Server-only users will be added to GHE (Metered) billing. These users are de-duplicated with email matching to avoid double billing.

content/billing/how-tos/manage-plan-and-licenses/manage-user-licenses.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@ Enterprise **owners** or **billing managers** can add or remove user licenses.
5757

5858
1. Navigate to your enterprise account.
5959
{% data reusables.billing.enterprise-billing-menu %}
60-
1. In the left sidebar, click **Licensing**.
60+
1. In the left sidebar, click {% octicon "law" aria-hidden="true" aria-label="law" %} **Licensing**.
6161
1. Next to "Enterprise Cloud", click **{% octicon "kebab-horizontal" aria-hidden="true" aria-label="kebab-horizontal" %}**, then click **Manage licenses**.
6262
1. Choose your number of licenses, then click **Confirm licenses**.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{% ifversion enhanced-billing-platform %}
22

3-
> [!NOTE] If you currently pay for your {% data variables.product.prodname_enterprise %} licenses through a volume, subscription, or prepaid agreement, you will continue to be billed in this way until your agreement expires. At renewal, you have the option to switch to the metered billing model.
3+
> [!NOTE] If you currently pay for your {% data variables.product.prodname_enterprise %} licenses through a volume, subscription, or prepaid agreement, you will continue to be billed in this way until your agreement expires or you are invited to transition. At renewal, you have the option to switch to the metered billing model.
44
55
{% endif %}

src/frame/middleware/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import { MAX_REQUEST_TIMEOUT } from '@/frame/lib/constants'
6666
import { initLoggerContext } from '@/observability/logger/lib/logger-context'
6767
import { getAutomaticRequestLogger } from '@/observability/logger/middleware/get-automatic-request-logger'
6868
import appRouterGateway from './app-router-gateway'
69+
import urlDecode from './url-decode'
6970

7071
const { NODE_ENV } = process.env
7172
const isTest = NODE_ENV === 'test' || process.env.GITHUB_ACTIONS === 'true'
@@ -199,6 +200,7 @@ export default function (app: Express) {
199200
app.set('etag', false) // We will manage our own ETags if desired
200201

201202
// *** Config and context for redirects ***
203+
app.use(urlDecode) // Must come before detectLanguage to decode @ symbols in version segments
202204
app.use(detectLanguage) // Must come before context, breadcrumbs, find-page, handle-errors, homepages
203205
app.use(asyncMiddleware(reloadTree)) // Must come before context
204206
app.use(asyncMiddleware(context)) // Must come before early-access-*, handle-redirects

src/frame/middleware/url-decode.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { NextFunction, Response } from 'express'
2+
import type { ExtendedRequest } from '@/types'
3+
4+
/**
5+
* Middleware to decode URL-encoded @ symbols.
6+
*
7+
* SharePoint and other systems automatically encode @ symbols to %40,
8+
* which breaks our versioned URLs like /en/enterprise-cloud@latest.
9+
* This middleware decodes @ symbols anywhere in the URL.
10+
*/
11+
export default function urlDecode(req: ExtendedRequest, res: Response, next: NextFunction) {
12+
const originalUrl = req.url
13+
14+
// Only process URLs that contain %40 (encoded @)
15+
if (!originalUrl.includes('%40')) {
16+
return next()
17+
}
18+
19+
try {
20+
// Decode the entire URL, replacing %40 with @
21+
const decodedUrl = originalUrl.replace(/%40/g, '@')
22+
req.url = decodedUrl
23+
return next()
24+
} catch {
25+
// If decoding fails for any reason, continue with original URL
26+
return next()
27+
}
28+
}

src/frame/tests/url-encoding.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, expect, test } from 'vitest'
2+
import { get } from '@/tests/helpers/e2etest'
3+
4+
describe('URL encoding for version paths', () => {
5+
test('handles URL-encoded @ symbol in enterprise-cloud version', async () => {
6+
// SharePoint encodes @ as %40, so /en/enterprise-cloud@latest becomes /en/enterprise-cloud%40latest
7+
const encodedUrl = '/en/enterprise-cloud%40latest/copilot/concepts/chat'
8+
const res = await get(encodedUrl)
9+
10+
// Should either:
11+
// 1. Work directly (200) - the encoded URL should decode and work
12+
// 2. Redirect (301/302) to the proper decoded URL
13+
// Should NOT return 404
14+
expect([200, 301, 302]).toContain(res.statusCode)
15+
16+
if (res.statusCode === 301 || res.statusCode === 302) {
17+
// If it redirects, it should redirect to the decoded version
18+
expect(res.headers.location).toBe('/en/enterprise-cloud@latest/copilot/concepts/chat')
19+
}
20+
})
21+
22+
test('handles URL-encoded @ symbol in enterprise-server version', async () => {
23+
const encodedUrl =
24+
'/en/enterprise-server%403.17/admin/managing-github-actions-for-your-enterprise'
25+
const res = await get(encodedUrl)
26+
27+
expect([200, 301, 302]).toContain(res.statusCode)
28+
29+
if (res.statusCode === 301 || res.statusCode === 302) {
30+
expect(res.headers.location).toBe(
31+
'/en/[email protected]/admin/managing-github-actions-for-your-enterprise',
32+
)
33+
}
34+
})
35+
36+
test('handles URL-encoded @ symbol in second path segment', async () => {
37+
// When no language prefix is present
38+
const encodedUrl = '/enterprise-cloud%40latest/copilot/concepts/chat'
39+
const res = await get(encodedUrl)
40+
41+
// Should redirect to add language prefix and decode
42+
expect([301, 302]).toContain(res.statusCode)
43+
expect(res.headers.location).toBe('/en/enterprise-cloud@latest/copilot/concepts/chat')
44+
})
45+
46+
test('normal @ symbol paths continue to work', async () => {
47+
// Ensure we don't break existing functionality
48+
const normalUrl = '/en/enterprise-cloud@latest/copilot/concepts/chat'
49+
const res = await get(normalUrl)
50+
51+
expect(res.statusCode).toBe(200)
52+
})
53+
54+
test('URL encoding in other parts of URL is preserved', async () => {
55+
// Only @ symbols in version paths should be decoded, other encoding should be preserved
56+
const encodedUrl = '/en/enterprise-cloud@latest/copilot/concepts/some%20page'
57+
const res = await get(encodedUrl)
58+
59+
// This might 404 if the page doesn't exist, but shouldn't break due to encoding
60+
expect(res.statusCode).not.toBe(500)
61+
})
62+
63+
test('Express URL properties are correctly updated after decoding', async () => {
64+
// Test that req.path, req.query, etc. are properly updated when req.url is modified
65+
const encodedUrl = '/en/enterprise-cloud%40latest/copilot/concepts/chat?test=value'
66+
const res = await get(encodedUrl)
67+
68+
// Should work correctly (200 or redirect) - the middleware should properly update
69+
// req.path from '/en/enterprise-cloud%40latest/...' to '/en/enterprise-cloud@latest/...'
70+
expect([200, 301, 302]).toContain(res.statusCode)
71+
})
72+
})

src/github-apps/data/fpt-2022-11-28/fine-grained-pat-permissions.json

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -962,21 +962,48 @@
962962
"permissions": [
963963
{
964964
"category": "orgs",
965-
"slug": "list-custom-property-values-for-organization-repositories",
965+
"slug": "get-all-custom-properties-for-an-organization",
966966
"subcategory": "custom-properties",
967967
"verb": "get",
968-
"requestPath": "/orgs/{org}/properties/values",
968+
"requestPath": "/orgs/{org}/properties/schema",
969969
"additional-permissions": false,
970970
"access": "read"
971971
},
972972
{
973973
"category": "orgs",
974-
"slug": "create-or-update-custom-property-values-for-organization-repositories",
974+
"slug": "create-or-update-custom-properties-for-an-organization",
975975
"subcategory": "custom-properties",
976976
"verb": "patch",
977-
"requestPath": "/orgs/{org}/properties/values",
977+
"requestPath": "/orgs/{org}/properties/schema",
978978
"additional-permissions": false,
979-
"access": "write"
979+
"access": "admin"
980+
},
981+
{
982+
"category": "orgs",
983+
"slug": "get-a-custom-property-for-an-organization",
984+
"subcategory": "custom-properties",
985+
"verb": "get",
986+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}",
987+
"additional-permissions": false,
988+
"access": "read"
989+
},
990+
{
991+
"category": "orgs",
992+
"slug": "create-or-update-a-custom-property-for-an-organization",
993+
"subcategory": "custom-properties",
994+
"verb": "put",
995+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}",
996+
"additional-permissions": false,
997+
"access": "admin"
998+
},
999+
{
1000+
"category": "orgs",
1001+
"slug": "remove-a-custom-property-for-an-organization",
1002+
"subcategory": "custom-properties",
1003+
"verb": "delete",
1004+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}",
1005+
"additional-permissions": false,
1006+
"access": "admin"
9801007
}
9811008
]
9821009
},

src/github-apps/data/fpt-2022-11-28/fine-grained-pat.json

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3349,16 +3349,34 @@
33493349
"requestPath": "/orgs/{org}/outside_collaborators/{username}"
33503350
},
33513351
{
3352-
"slug": "list-custom-property-values-for-organization-repositories",
3352+
"slug": "get-all-custom-properties-for-an-organization",
33533353
"subcategory": "custom-properties",
33543354
"verb": "get",
3355-
"requestPath": "/orgs/{org}/properties/values"
3355+
"requestPath": "/orgs/{org}/properties/schema"
33563356
},
33573357
{
3358-
"slug": "create-or-update-custom-property-values-for-organization-repositories",
3358+
"slug": "create-or-update-custom-properties-for-an-organization",
33593359
"subcategory": "custom-properties",
33603360
"verb": "patch",
3361-
"requestPath": "/orgs/{org}/properties/values"
3361+
"requestPath": "/orgs/{org}/properties/schema"
3362+
},
3363+
{
3364+
"slug": "get-a-custom-property-for-an-organization",
3365+
"subcategory": "custom-properties",
3366+
"verb": "get",
3367+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}"
3368+
},
3369+
{
3370+
"slug": "create-or-update-a-custom-property-for-an-organization",
3371+
"subcategory": "custom-properties",
3372+
"verb": "put",
3373+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}"
3374+
},
3375+
{
3376+
"slug": "remove-a-custom-property-for-an-organization",
3377+
"subcategory": "custom-properties",
3378+
"verb": "delete",
3379+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}"
33623380
},
33633381
{
33643382
"slug": "list-public-organization-members",

src/github-apps/data/fpt-2022-11-28/server-to-server-permissions.json

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,22 +1168,55 @@
11681168
"permissions": [
11691169
{
11701170
"category": "orgs",
1171-
"slug": "list-custom-property-values-for-organization-repositories",
1171+
"slug": "get-all-custom-properties-for-an-organization",
11721172
"subcategory": "custom-properties",
11731173
"verb": "get",
1174-
"requestPath": "/orgs/{org}/properties/values",
1174+
"requestPath": "/orgs/{org}/properties/schema",
11751175
"access": "read",
11761176
"user-to-server": true,
11771177
"server-to-server": true,
11781178
"additional-permissions": false
11791179
},
11801180
{
11811181
"category": "orgs",
1182-
"slug": "create-or-update-custom-property-values-for-organization-repositories",
1182+
"slug": "create-or-update-custom-properties-for-an-organization",
11831183
"subcategory": "custom-properties",
11841184
"verb": "patch",
1185-
"requestPath": "/orgs/{org}/properties/values",
1186-
"access": "write",
1185+
"requestPath": "/orgs/{org}/properties/schema",
1186+
"access": "admin",
1187+
"user-to-server": true,
1188+
"server-to-server": true,
1189+
"additional-permissions": false
1190+
},
1191+
{
1192+
"category": "orgs",
1193+
"slug": "get-a-custom-property-for-an-organization",
1194+
"subcategory": "custom-properties",
1195+
"verb": "get",
1196+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}",
1197+
"access": "read",
1198+
"user-to-server": true,
1199+
"server-to-server": true,
1200+
"additional-permissions": false
1201+
},
1202+
{
1203+
"category": "orgs",
1204+
"slug": "create-or-update-a-custom-property-for-an-organization",
1205+
"subcategory": "custom-properties",
1206+
"verb": "put",
1207+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}",
1208+
"access": "admin",
1209+
"user-to-server": true,
1210+
"server-to-server": true,
1211+
"additional-permissions": false
1212+
},
1213+
{
1214+
"category": "orgs",
1215+
"slug": "remove-a-custom-property-for-an-organization",
1216+
"subcategory": "custom-properties",
1217+
"verb": "delete",
1218+
"requestPath": "/orgs/{org}/properties/schema/{custom_property_name}",
1219+
"access": "admin",
11871220
"user-to-server": true,
11881221
"server-to-server": true,
11891222
"additional-permissions": false

0 commit comments

Comments
 (0)