Skip to content
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 @@ -1228,7 +1228,7 @@ jobs:
path: pr/
```

When a run of the above workflow completes, it triggers a run of the following workflow. The following workflow uses the `github.event.workflow_run` context and the {% data variables.product.github %} REST API to download the artifact that was uploaded by the above workflow, unzips the downloaded artifact, and comments on the pull request whose number was uploaded as an artifact.
When a run of the above workflow completes, it triggers a run of the following workflow. The following workflow uses the `github.event.workflow_run` context and the {% data reusables.actions.action-download-artifact %} action to download the artifact that was uploaded by the above workflow, then comments on the pull request whose number was uploaded as an artifact.

```yaml
name: Use the data
Expand All @@ -1244,33 +1244,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Download artifact'
uses: {% data reusables.actions.action-github-script %}
uses: {% data reusables.actions.action-download-artifact %}
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr_number"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
const fs = require('fs');
const path = require('path');
const temp = '{% raw %}${{ runner.temp }}{% endraw %}/artifacts';
if (!fs.existsSync(temp)){
fs.mkdirSync(temp);
}
fs.writeFileSync(path.join(temp, 'pr_number.zip'), Buffer.from(download.data));

- name: 'Unzip artifact'
run: unzip "{% raw %}${{ runner.temp }}{% endraw %}/artifacts/pr_number.zip" -d "{% raw %}${{ runner.temp }}{% endraw %}/artifacts"
name: pr_number
# do not extract in the workspace dir that may contain executable scripts
path: {% raw %}${{ runner.temp }}{% endraw %}/artifacts
run-id: {% raw %}${{ github.event.workflow_run.id }}{% endraw %}
github-token: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}

- name: 'Comment on PR'
uses: {% data reusables.actions.action-github-script %}
Expand All @@ -1280,7 +1260,11 @@ jobs:
const fs = require('fs');
const path = require('path');
const temp = '{% raw %}${{ runner.temp }}{% endraw %}/artifacts';
const issue_number = Number(fs.readFileSync(path.join(temp, 'pr_number')));
const issue_number_raw = fs.readFileSync(path.join(temp, 'pr_number'), 'utf8').trim();
const issue_number = Number(issue_number_raw);
if (!Number.isInteger(issue_number)) {
throw new Error(`Invalid PR number in pr_number artifact: "${issue_number_raw}"`);
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
Expand Down
4 changes: 2 additions & 2 deletions content/copilot/concepts/agents/github-copilot-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: About the GitHub Copilot app
shortTitle: GitHub Copilot app
intro: 'The {% data variables.copilot.github_copilot_app %} is a desktop application for agent-driven development that brings parallel workstreams, {% data variables.product.github %} integration, and PR lifecycle management into one place.'
product: '{% data reusables.gated-features.github-app %}'
product: '{% data reusables.gated-features.github-app %}<br><a href="https://github.com/features/copilot/plans?ref_product=copilot&ref_type=purchase&ref_style=button&utm_source=docs-about-app-signup&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions:
feature: copilot
contentType: concepts
Expand All @@ -12,7 +12,7 @@ category:
- Learn about Copilot
---

Install the app from the [download page for {% data variables.copilot.github_copilot_app %}](https://gh.io/app).
Install the app from the [download page for {% data variables.copilot.github_copilot_app %}](https://gh.io/app?ref_product=copilot&ref_type=engagement&ref_style=text&utm_source=docs-about-app-download&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026).

## Introduction

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Working with agent sessions in the GitHub Copilot app
shortTitle: Agent sessions
intro: 'Run multiple isolated agent sessions simultaneously, each with its own branch, and steer them using different session modes, models, and tools.'
allowTitleToDifferFromFilename: true
product: '{% data reusables.gated-features.github-app %}'
product: '{% data reusables.gated-features.github-app %}<br><a href="https://github.com/features/copilot/plans?ref_product=copilot&ref_type=purchase&ref_style=button&utm_source=docs-agent-sessions-signup&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions:
feature: copilot
contentType: how-tos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Customizing the GitHub Copilot app
shortTitle: Customize the GitHub Copilot app
intro: 'Customize the {% data variables.copilot.github_copilot_app %} so it works the way you and your team do.'
allowTitleToDifferFromFilename: true
product: '{% data reusables.gated-features.github-app %}'
product: '{% data reusables.gated-features.github-app %}<br><a href="https://github.com/features/copilot/plans?ref_product=copilot&ref_type=purchase&ref_style=button&utm_source=docs-customize-app-signup&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions:
feature: copilot
contentType: how-tos
Expand Down
4 changes: 2 additions & 2 deletions content/copilot/how-tos/github-copilot-app/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Getting started with the GitHub Copilot app
shortTitle: Quickstart
allowTitleToDifferFromFilename: true
intro: 'Sign in to the {% data variables.copilot.github_copilot_app %}, ask your first question in a quick chat, and then create a full agent session to make changes to your code.'
product: '{% data reusables.gated-features.github-app %}'
product: '{% data reusables.gated-features.github-app %}<br><a href="https://github.com/features/copilot/plans?ref_product=copilot&ref_type=purchase&ref_style=button&utm_source=docs-getting-started-signup&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions:
feature: copilot
contentType: how-tos
Expand All @@ -24,7 +24,7 @@ For a conceptual overview of the {% data variables.copilot.github_copilot_app %}

## Installing the {% data variables.copilot.github_copilot_app %}

1. Visit the [download page for {% data variables.copilot.github_copilot_app %}](https://gh.io/app).
1. Visit the [download page for {% data variables.copilot.github_copilot_app %}](https://gh.io/app?ref_product=copilot&ref_type=engagement&ref_style=text&utm_source=docs-getting-started-download&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026).
1. Download the app for your platform.

## Opening the {% data variables.copilot.github_copilot_app %} for the first time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Managing issues and pull requests with the GitHub Copilot app
shortTitle: Managing issues and pull requests
allowTitleToDifferFromFilename: true
intro: 'Pick up an issue, direct an agent to implement changes, and land a pull request—all without leaving the {% data variables.copilot.github_copilot_app %}.'
product: '{% data reusables.gated-features.github-app %}'
product: '{% data reusables.gated-features.github-app %}<br><a href="https://github.com/features/copilot/plans?ref_product=copilot&ref_type=purchase&ref_style=button&utm_source=docs-manage-issues-prs-signup&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions:
feature: copilot
contentType: how-tos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Using automations in the GitHub Copilot app
shortTitle: Automations
intro: 'Automate recurring agent tasks so they run on a schedule or on demand, without manual intervention.'
allowTitleToDifferFromFilename: true
product: '{% data reusables.gated-features.github-app %}'
product: '{% data reusables.gated-features.github-app %}<br><a href="https://github.com/features/copilot/plans?ref_product=copilot&ref_type=purchase&ref_style=button&utm_source=docs-automations-signup&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions:
feature: copilot
contentType: how-tos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Working with canvas extensions in the GitHub Copilot app
shortTitle: Canvas extensions
intro: 'Use canvases in the {% data variables.copilot.github_copilot_app %} to build shared, agent-driven artifacts and interfaces for human-agent collaboration.'
allowTitleToDifferFromFilename: true
product: '{% data reusables.gated-features.github-app %}'
product: '{% data reusables.gated-features.github-app %}<br><a href="https://github.com/features/copilot/plans?ref_product=copilot&ref_type=purchase&ref_style=button&utm_source=docs-canvas-extensions-signup&utm_medium=docs&utm_campaign=github-copilot-app-ga-2026" target="_blank" class="btn btn-primary mt-3 mr-3 no-underline"><span>Sign up for {% data variables.product.prodname_copilot_short %}</span> {% octicon "link-external" height:16 %}</a>'
versions:
feature: copilot
contentType: how-tos
Expand Down
1 change: 1 addition & 0 deletions src/audit-logs/data/shared/entries.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/audit-logs/data/shared/fields-pool.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/audit-logs/data/version-index.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/audit-logs/lib/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"git": "Note: Git events have special access requirements and retention policies that differ from other audit log events. For GitHub Enterprise Cloud, access Git events via the REST API only with 7-day retention. For GitHub Enterprise Server, Git events must be enabled in audit log configuration and are not included in search results.",
"sso_redirect": "Note: Automatically redirecting users to sign in is currently in beta for Enterprise Managed Users and subject to change."
},
"sha": "961b64fe9080f83cc3f408a8dd1e30fe2eafaee1"
"sha": "ad0dc7ebe4a70afe77bb03487060481fe7a9f13a"
}
107 changes: 96 additions & 11 deletions src/audit-logs/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import type {
RawAuditLogEventT,
CategoryNotes,
AuditLogConfig,
DeduplicatedAuditLogEntry,
AuditLogVersionIndex,
} from '../types'
import config from './config.json'

Expand All @@ -21,6 +23,86 @@ export const AUDIT_LOG_DATA_DIR = 'src/audit-logs/data'
const auditLogEventsCache = new Map<string, Map<string, AuditLogEventT[]>>()
const categorizedAuditLogEventsCache = new Map<string, Map<string, CategorizedEvents>>()

// Shared dedup data — loaded once, shared across all versions
let sharedEntries: DeduplicatedAuditLogEntry[] | null = null
let sharedFieldsPool: string[][] | null = null
let sharedVersionIndex: AuditLogVersionIndex | null = null
let sharedFormatAvailable: boolean | null = null // null = not checked yet

// A missing shared-format file is expected (per-version files are the fallback),
// but a corrupt or unparseable file should fail loudly rather than silently
// degrade to the per-version files and hide bad generated data.
function isFileNotFoundError(err: unknown): boolean {
if (!(err instanceof Error) || !('code' in err)) return false
const code = (err as NodeJS.ErrnoException).code
return code === 'ENOENT' || code === 'ENOTDIR'
}

function loadSharedFormat(): boolean {
if (sharedFormatAvailable !== null) return sharedFormatAvailable
try {
sharedEntries = readCompressedJsonFileFallback(
path.join(AUDIT_LOG_DATA_DIR, 'shared', 'entries.json'),
) as DeduplicatedAuditLogEntry[]
sharedFieldsPool = readCompressedJsonFileFallback(
path.join(AUDIT_LOG_DATA_DIR, 'shared', 'fields-pool.json'),
) as string[][]
sharedVersionIndex = readCompressedJsonFileFallback(
path.join(AUDIT_LOG_DATA_DIR, 'version-index.json'),
) as AuditLogVersionIndex
// Freeze pool data so reconstructed events (which return references into
// these pools) can't be mutated by downstream code and leak across versions.
Object.freeze(sharedEntries)
Object.freeze(sharedFieldsPool)
for (const fields of sharedFieldsPool) Object.freeze(fields)
sharedFormatAvailable = true
} catch (err) {
if (isFileNotFoundError(err)) {
// Shared files don't exist — fall back to per-version files silently.
sharedFormatAvailable = false
} else {
// Corrupt JSON, schema mismatch, etc. — surface this instead of hiding it.
console.error('Failed to load shared audit log dedup format (corrupt data?):', err)
throw err
}
}
return sharedFormatAvailable
}

function reconstructEventsFromSharedFormat(version: string, page: string): AuditLogEventT[] | null {
if (!loadSharedFormat()) return null
const indices = sharedVersionIndex?.[version]?.[page]
if (!indices) return null

return indices.map((idx) => {
if (idx < 0 || idx >= sharedEntries!.length) {
throw new RangeError(
`Audit log version-index references entry ${idx} for ${version}/${page}, ` +
`but the entries pool only has ${sharedEntries!.length} entries. ` +
`The shared dedup data may be stale or corrupt.`,
)
}
const entry = sharedEntries![idx]
const event: AuditLogEventT = {
action: entry.action,
description: entry.description,
}
if (entry.docs_reference_links) event.docs_reference_links = entry.docs_reference_links
if (entry.docs_reference_titles) event.docs_reference_titles = entry.docs_reference_titles
if (entry.fieldsIndex !== undefined) {
if (entry.fieldsIndex < 0 || entry.fieldsIndex >= sharedFieldsPool!.length) {
throw new RangeError(
`Audit log entry references fields index ${entry.fieldsIndex} for ${version}/${page}, ` +
`but the fields pool only has ${sharedFieldsPool!.length} entries. ` +
`The shared dedup data may be stale or corrupt.`,
)
}
event.fields = sharedFieldsPool![entry.fieldsIndex]
}
return event
})
}

type PipelineConfig = {
sha: string
appendedDescriptions: Record<string, string>
Expand Down Expand Up @@ -169,21 +251,24 @@ async function resolveReferenceLinksToTitles(
// ]
export function getAuditLogEvents(page: string, version: string): AuditLogEventT[] {
const openApiVersion = getOpenApiVersion(version)
const auditLogFileName = path.join(AUDIT_LOG_DATA_DIR, openApiVersion, `${page}.json`)

// If the data isn't cached for an entire version or a particular page, read
// the data from the JSON file the first time around
// the data from the shared dedup format or fall back to per-version JSON files
if (!auditLogEventsCache.has(openApiVersion)) {
auditLogEventsCache.set(openApiVersion, new Map())
auditLogEventsCache.get(openApiVersion)?.set(page, [])
auditLogEventsCache
.get(openApiVersion)
?.set(page, readCompressedJsonFileFallback(auditLogFileName) as AuditLogEventT[])
} else if (!auditLogEventsCache.get(openApiVersion)?.has(page)) {
auditLogEventsCache.get(openApiVersion)?.set(page, [])
auditLogEventsCache
.get(openApiVersion)
?.set(page, readCompressedJsonFileFallback(auditLogFileName) as AuditLogEventT[])
}
if (!auditLogEventsCache.get(openApiVersion)?.has(page)) {
// Try shared deduplicated format first
const events = reconstructEventsFromSharedFormat(openApiVersion, page)
if (events) {
auditLogEventsCache.get(openApiVersion)?.set(page, events)
} else {
// Fall back to per-version JSON file
const auditLogFileName = path.join(AUDIT_LOG_DATA_DIR, openApiVersion, `${page}.json`)
auditLogEventsCache
.get(openApiVersion)
?.set(page, readCompressedJsonFileFallback(auditLogFileName) as AuditLogEventT[])
}
}

const auditLogEvents = auditLogEventsCache.get(openApiVersion)?.get(page)
Expand Down
Loading
Loading