Skip to content

Commit 8eaa8eb

Browse files
authored
Merge pull request #39316 from github/repo-sync
Repo sync
2 parents fc1b486 + 97173c4 commit 8eaa8eb

File tree

15 files changed

+328
-69
lines changed

15 files changed

+328
-69
lines changed

data/release-notes/enterprise-server/3-13/16.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ sections:
1616
Users of GitHub Actions could not view or manage Actions artifacts and logs if the global AWS STS endpoint was unavailable, because Actions did not use the configured regional STS endpoint.
1717
- |
1818
If an Enterprise Managed User (EMU) pushed to their personal repository with both secret scanning and push protection enabled, the custom patterns defined at enterprise level were not being applied during the push protection scan.
19+
- |
20+
In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14]
1921
changes:
2022
- |
2123
Site administrators can now set rate limits for the WebSockets controller used for live updates, with `ghe-config app.github.web-sockets-rate-limit`. For more information, see [Controlling the rate for the live update service](/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits#controlling-the-rate-for-the-live-update-service).

data/release-notes/enterprise-server/3-14/13.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ sections:
2020
Organization owners had no audit log events to track organization announcements displayed on banners in the UI.
2121
- |
2222
If an Enterprise Managed User (EMU) pushed to their personal repository with both secret scanning and push protection enabled, the custom patterns defined at enterprise level were not being applied during the push protection scan.
23+
- |
24+
In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14]
2325
changes:
2426
- |
2527
Site administrators can now set rate limits for the WebSockets controller used for live updates, with `ghe-config app.github.web-sockets-rate-limit`. For more information, see [Controlling the rate for the live update service](/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits#controlling-the-rate-for-the-live-update-service).

data/release-notes/enterprise-server/3-15/8.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ sections:
2222
Organization owners had no audit log events to track organization announcements displayed on banners in the UI.
2323
- |
2424
If an Enterprise Managed User (EMU) pushed to their personal repository with both secret scanning and push protection enabled, the custom patterns defined at enterprise level were not being applied during the push protection scan.
25+
- |
26+
In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14]
2527
changes:
2628
- |
2729
To ensure critical integrations and automated systems have uninterrupted access, the `/repositories/:repository_id/collaborators` endpoints now honor the higher rate limits for exempt users set with `ghe-config app.github.rate-limiting-exempt-users "<USER>"`.

data/release-notes/enterprise-server/3-16/4.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ sections:
3030
When an administrator suspended a user from the site admin dashboard, the form required them to complete Digital Services Act (DSA) fields that are not relevant on GitHub Enterprise Server.
3131
- |
3232
After an appliance reboot, code scanning did not always trigger or process analyses.
33+
- |
34+
In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14]
3335
changes:
3436
- |
3537
Site administrators who test the Prometheus endpoint can now use 127.0.0.1 as a trusted IP address. Previously, only specific IPs were allowed for testing.

data/release-notes/enterprise-server/3-17/1.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ sections:
3030
On instances with dangling commit graph lock files, recompute checksum operations were unexpectedly triggered.
3131
- |
3232
After an appliance reboot, code scanning did not always trigger or process analyses.
33+
- |
34+
In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14]
3335
changes:
3436
- |
3537
Site administrators who test the Prometheus endpoint can now use 127.0.0.1 as a trusted IP address. Previously, only specific IPs were allowed for testing.

data/ui.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ parameter_table:
144144
audit_logs:
145145
action: Action
146146
description: Description
147+
reference: Reference
147148
graphql:
148149
reference:
149150
implements: <code>{{ GraphQLItemTitle }}</code> Implements

src/audit-logs/components/GroupedEvents.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,40 @@ export default function GroupedEvents({ auditLogEvents, category }: Props) {
2222
<tr>
2323
<th scope="col">{t('action')}</th>
2424
<th scope="col">{t('description')}</th>
25+
<th scope="col">{t('reference')}</th>
2526
</tr>
2627
</thead>
2728
<tbody>
2829
{auditLogEvents.map((event) => {
30+
const renderReferenceLinks = () => {
31+
if (!event.docs_reference_links || event.docs_reference_links === 'N/A') {
32+
return null
33+
}
34+
35+
const links = event.docs_reference_links
36+
.split(/[,\s]+/)
37+
.map((link) => link.trim())
38+
.filter((link) => link && link !== 'N/A')
39+
40+
const titles = event.docs_reference_titles
41+
? event.docs_reference_titles.split(', ')
42+
: links
43+
44+
return links.map((link, index) => (
45+
<span key={link}>
46+
<a href={link}>{titles[index] || link}</a>
47+
{index < links.length - 1 && ', '}
48+
</span>
49+
))
50+
}
51+
2952
return (
3053
<tr key={event.action}>
3154
<td>
3255
<code>{event.action}</code>
3356
</td>
3457
<td>{event.description}</td>
58+
<td>{renderReferenceLinks()}</td>
3559
</tr>
3660
)
3761
})}

src/audit-logs/lib/index.ts

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'path'
22

33
import { readCompressedJsonFileFallback } from '@/frame/lib/read-json-file'
44
import { getOpenApiVersion } from '@/versions/lib/all-versions'
5+
import findPage from '@/frame/lib/find-page'
56
import type {
67
AuditLogEventT,
78
CategorizedEvents,
@@ -20,6 +21,61 @@ type PipelineConfig = {
2021
appendedDescriptions: Record<string, string>
2122
}
2223

24+
type TitleResolutionContext = {
25+
pages: Record<string, any>
26+
redirects: Record<string, string>
27+
}
28+
29+
// Resolves docs_reference_links URLs to page titles
30+
async function resolveReferenceLinksToTitles(
31+
docsReferenceLinks: string,
32+
context: TitleResolutionContext,
33+
): Promise<string> {
34+
if (!docsReferenceLinks || docsReferenceLinks === 'N/A') {
35+
return ''
36+
}
37+
38+
// Handle multiple comma-separated or space-separated links
39+
const links = docsReferenceLinks
40+
.split(/[,\s]+/)
41+
.map((link) => link.trim())
42+
.filter((link) => link && link !== 'N/A')
43+
44+
const titles = []
45+
for (const link of links) {
46+
try {
47+
const page = findPage(link, context.pages, context.redirects)
48+
if (page) {
49+
// Create a minimal context for rendering the title
50+
const renderContext = {
51+
currentLanguage: 'en',
52+
currentVersion: 'free-pro-team@latest',
53+
pages: context.pages,
54+
redirects: context.redirects,
55+
}
56+
const title = await page.renderProp('title', renderContext, { textOnly: true })
57+
titles.push(title)
58+
} else {
59+
// If we can't resolve the link, use the original URL
60+
titles.push(link)
61+
}
62+
} catch (error) {
63+
// If resolution fails, use the original URL
64+
console.warn(
65+
`Failed to resolve title for link: ${link}`,
66+
error instanceof Error
67+
? error instanceof Error
68+
? error.message
69+
: String(error)
70+
: String(error),
71+
)
72+
titles.push(link)
73+
}
74+
}
75+
76+
return titles.join(', ')
77+
}
78+
2379
// get audit log event data for the requested page and version
2480
//
2581
// returns an array of event objects that look like this:
@@ -87,17 +143,19 @@ export function getCategorizedAuditLogEvents(page: string, version: string) {
87143
}
88144

89145
// Filters audit log events based on allowlist values.
90-
//
91-
// * eventsToCheck: events to consider
92-
// * allowListvalues: allowlist values to filter by
93-
// * currentEvents: events already collected
94-
// * pipelineConfig: audit log pipeline config data
95-
export function filterByAllowlistValues(
96-
eventsToCheck: RawAuditLogEventT[],
97-
allowListValues: string | string[],
98-
currentEvents: AuditLogEventT[],
99-
pipelineConfig: PipelineConfig,
100-
) {
146+
export async function filterByAllowlistValues({
147+
eventsToCheck,
148+
allowListValues,
149+
currentEvents = [],
150+
pipelineConfig,
151+
titleContext,
152+
}: {
153+
eventsToCheck: RawAuditLogEventT[]
154+
allowListValues: string | string[]
155+
currentEvents?: AuditLogEventT[]
156+
pipelineConfig: PipelineConfig
157+
titleContext?: TitleResolutionContext
158+
}) {
101159
if (!Array.isArray(allowListValues)) allowListValues = [allowListValues]
102160
if (!currentEvents) currentEvents = []
103161

@@ -112,12 +170,27 @@ export function filterByAllowlistValues(
112170
if (seen.has(event.action)) continue
113171
seen.add(event.action)
114172

115-
const minimal = {
173+
const minimal: AuditLogEventT = {
116174
action: event.action,
117175
description: processAndGetEventDescription(event, eventAllowlists, pipelineConfig),
118176
docs_reference_links: event.docs_reference_links,
119177
}
120178

179+
// Resolve reference link titles if context is provided
180+
if (titleContext && event.docs_reference_links && event.docs_reference_links !== 'N/A') {
181+
try {
182+
minimal.docs_reference_titles = await resolveReferenceLinksToTitles(
183+
event.docs_reference_links,
184+
titleContext,
185+
)
186+
} catch (error) {
187+
console.warn(
188+
`Failed to resolve titles for event ${event.action}:`,
189+
error instanceof Error ? error.message : String(error),
190+
)
191+
}
192+
}
193+
121194
minimalEvents.push(minimal)
122195
}
123196
}
@@ -132,6 +205,7 @@ export function filterByAllowlistValues(
132205
// * currentEvents: events already collected
133206
// * pipelineConfig: audit log pipeline config data
134207
// * auditLogPage: the audit log page the event belongs to
208+
// * titleContext: optional context for resolving reference link titles
135209
//
136210
// Mutates `currentGhesEvents` and updates it with any new filtered for audit
137211
// log events, the object maps GHES versions to page events for that version e.g.:
@@ -148,13 +222,21 @@ export function filterByAllowlistValues(
148222
// user: [...],
149223
// },
150224
// }
151-
export function filterAndUpdateGhesDataByAllowlistValues(
152-
eventsToCheck: RawAuditLogEventT[],
153-
allowListValue: string,
154-
currentGhesEvents: VersionedAuditLogData,
155-
pipelineConfig: PipelineConfig,
156-
auditLogPage: string,
157-
) {
225+
export async function filterAndUpdateGhesDataByAllowlistValues({
226+
eventsToCheck,
227+
allowListValue,
228+
currentGhesEvents,
229+
pipelineConfig,
230+
auditLogPage,
231+
titleContext,
232+
}: {
233+
eventsToCheck: RawAuditLogEventT[]
234+
allowListValue: string
235+
currentGhesEvents: VersionedAuditLogData
236+
pipelineConfig: PipelineConfig
237+
auditLogPage: string
238+
titleContext?: TitleResolutionContext
239+
}) {
158240
if (!currentGhesEvents) currentGhesEvents = {}
159241

160242
const seenByGhesVersion = new Map()
@@ -173,12 +255,27 @@ export function filterAndUpdateGhesDataByAllowlistValues(
173255
if (seenByGhesVersion.get(fullGhesVersion)?.has(event.action)) continue
174256

175257
if (ghesVersionAllowlists.includes(allowListValue)) {
176-
const minimal = {
258+
const minimal: AuditLogEventT = {
177259
action: event.action,
178260
description: processAndGetEventDescription(event, ghesVersionAllowlists, pipelineConfig),
179261
docs_reference_links: event.docs_reference_links,
180262
}
181263

264+
// Resolve reference link titles if context is provided
265+
if (titleContext && event.docs_reference_links && event.docs_reference_links !== 'N/A') {
266+
try {
267+
minimal.docs_reference_titles = await resolveReferenceLinksToTitles(
268+
event.docs_reference_links,
269+
titleContext,
270+
)
271+
} catch (error) {
272+
console.warn(
273+
`Failed to resolve titles for event ${event.action}:`,
274+
error instanceof Error ? error.message : String(error),
275+
)
276+
}
277+
}
278+
182279
// we need to initialize as we go to build up the `minimalEvents`
183280
// object that we'll return which will contain the GHES events for
184281
// each versions + page type combos e.g. when processing GHES events

src/audit-logs/scripts/sync.ts

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import path from 'path'
1515
import { filterByAllowlistValues, filterAndUpdateGhesDataByAllowlistValues } from '../lib/index'
1616
import { getContents, getCommitSha } from '@/workflows/git-utils'
1717
import { latest, latestStable, releaseCandidate } from '@/versions/lib/enterprise-server-releases'
18+
import { loadPages, loadPageMap } from '@/frame/lib/page-data'
19+
import loadRedirects from '@/redirects/lib/precompile'
1820
import type { AuditLogEventT, VersionedAuditLogData } from '../types'
1921

2022
if (!process.env.GITHUB_TOKEN) {
@@ -53,6 +55,13 @@ async function main() {
5355
pipelineConfig.sha = mainSha
5456
await writeFile(configFilepath, JSON.stringify(pipelineConfig, null, 2))
5557

58+
// Load pages and redirects for title resolution
59+
console.log('Loading pages and redirects for title resolution...')
60+
const pageList = await loadPages(undefined, ['en'])
61+
const pages = await loadPageMap(pageList)
62+
const redirects = await loadRedirects(pageList)
63+
const titleContext = { pages, redirects }
64+
5665
// store an array of audit log event data keyed by version and audit log page,
5766
// will look like this (depends on supported GHES versions):
5867
//
@@ -75,32 +84,39 @@ async function main() {
7584
// Wrapper around filterByAllowlistValues() because we always need all the
7685
// schema events and pipeline config data.
7786
const filter = (allowListValues: string | string[], currentEvents: AuditLogEventT[] = []) =>
78-
filterByAllowlistValues(schemaEvents, allowListValues, currentEvents, pipelineConfig)
87+
filterByAllowlistValues({
88+
eventsToCheck: schemaEvents,
89+
allowListValues,
90+
currentEvents,
91+
pipelineConfig,
92+
titleContext,
93+
})
7994
// Wrapper around filterGhesByAllowlistValues() because we always need all the
8095
// schema events and pipeline config data.
8196
const filterAndUpdateGhes = (
82-
allowListValues: string,
97+
allowListValue: string,
8398
auditLogPage: string,
84-
currentEvents: VersionedAuditLogData,
99+
currentGhesEvents: VersionedAuditLogData,
85100
) =>
86-
filterAndUpdateGhesDataByAllowlistValues(
87-
schemaEvents,
88-
allowListValues,
89-
currentEvents,
101+
filterAndUpdateGhesDataByAllowlistValues({
102+
eventsToCheck: schemaEvents,
103+
allowListValue,
104+
currentGhesEvents,
90105
pipelineConfig,
91106
auditLogPage,
92-
)
107+
titleContext,
108+
})
93109

94110
auditLogData.fpt = {}
95-
auditLogData.fpt.user = filter('user')
96-
auditLogData.fpt.organization = filter(['organization', 'org_api_only'])
111+
auditLogData.fpt.user = await filter('user')
112+
auditLogData.fpt.organization = await filter(['organization', 'org_api_only'])
97113

98114
auditLogData.ghec = {}
99-
auditLogData.ghec.user = filter('user')
100-
auditLogData.ghec.organization = filter('organization')
101-
auditLogData.ghec.organization = filter('org_api_only', auditLogData.ghec.organization)
102-
auditLogData.ghec.enterprise = filter('business')
103-
auditLogData.ghec.enterprise = filter('business_api_only', auditLogData.ghec.enterprise)
115+
auditLogData.ghec.user = await filter('user')
116+
auditLogData.ghec.organization = await filter('organization')
117+
auditLogData.ghec.organization = await filter('org_api_only', auditLogData.ghec.organization)
118+
auditLogData.ghec.enterprise = await filter('business')
119+
auditLogData.ghec.enterprise = await filter('business_api_only', auditLogData.ghec.enterprise)
104120

105121
// GHES versions are numbered (i.e. "3.9", "3.10", etc.) and filterGhes()
106122
// gives us back an object of GHES versions to page events for each version
@@ -114,11 +130,15 @@ async function main() {
114130
// so there's no single auditLogData.ghes like the other versions.
115131
const ghesVersionsAuditLogData = {}
116132

117-
filterAndUpdateGhes('business', AUDIT_LOG_PAGES.ENTERPRISE, ghesVersionsAuditLogData)
118-
filterAndUpdateGhes('business_api_only', AUDIT_LOG_PAGES.ENTERPRISE, ghesVersionsAuditLogData)
119-
filterAndUpdateGhes('user', AUDIT_LOG_PAGES.USER, ghesVersionsAuditLogData)
120-
filterAndUpdateGhes('organization', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData)
121-
filterAndUpdateGhes('org_api_only', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData)
133+
await filterAndUpdateGhes('business', AUDIT_LOG_PAGES.ENTERPRISE, ghesVersionsAuditLogData)
134+
await filterAndUpdateGhes(
135+
'business_api_only',
136+
AUDIT_LOG_PAGES.ENTERPRISE,
137+
ghesVersionsAuditLogData,
138+
)
139+
await filterAndUpdateGhes('user', AUDIT_LOG_PAGES.USER, ghesVersionsAuditLogData)
140+
await filterAndUpdateGhes('organization', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData)
141+
await filterAndUpdateGhes('org_api_only', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData)
122142
Object.assign(auditLogData, ghesVersionsAuditLogData)
123143

124144
// We don't maintain the order of events as we process them so after filtering

0 commit comments

Comments
 (0)