Skip to content

Commit 285e444

Browse files
CopilotJoyceZhu
andauthored
feat: add optional base_url input for Octokit enterprise support
Agent-Logs-Url: https://github.com/github/accessibility-scanner/sessions/b5a52a27-e1a7-42dd-a5a7-07aff0f4fc7c Co-authored-by: JoyceZhu <6251669+JoyceZhu@users.noreply.github.com>
1 parent e9a0ae7 commit 285e444

8 files changed

Lines changed: 195 additions & 0 deletions

File tree

.github/actions/file/action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ inputs:
1111
token:
1212
description: "Token with fine-grained permission 'issues: write'"
1313
required: true
14+
base_url:
15+
description: "Optional base URL for the GitHub API (for example, 'https://HOSTNAME/api/v3' for GitHub Enterprise Server)"
16+
required: false
1417
cached_filings:
1518
description: "Cached filings from previous runs, as stringified JSON. Without this, duplicate issues may be filed."
1619
required: false

.github/actions/file/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@ export default async function () {
1919
const findings: Finding[] = JSON.parse(core.getInput('findings', {required: true}))
2020
const repoWithOwner = core.getInput('repository', {required: true})
2121
const token = core.getInput('token', {required: true})
22+
const baseUrl = core.getInput('base_url', {required: false}) || undefined
2223
const screenshotRepo = core.getInput('screenshot_repository', {required: false}) || repoWithOwner
2324
const cachedFilings: (ResolvedFiling | RepeatedFiling)[] = JSON.parse(
2425
core.getInput('cached_filings', {required: false}) || '[]',
2526
)
2627
const shouldOpenGroupedIssues = core.getBooleanInput('open_grouped_issues')
2728
core.debug(`Input: 'findings: ${JSON.stringify(findings)}'`)
2829
core.debug(`Input: 'repository: ${repoWithOwner}'`)
30+
core.debug(`Input: 'base_url: ${baseUrl ?? '(default)'}'`)
2931
core.debug(`Input: 'screenshot_repository: ${screenshotRepo}'`)
3032
core.debug(`Input: 'cached_filings: ${JSON.stringify(cachedFilings)}'`)
3133
core.debug(`Input: 'open_grouped_issues: ${shouldOpenGroupedIssues}'`)
3234

3335
const octokit = new OctokitWithThrottling({
3436
auth: token,
37+
baseUrl,
3538
throttle: {
3639
onRateLimit: (retryAfter, options, octokit, retryCount) => {
3740
octokit.log.warn(`Request quota exhausted for request ${options.method} ${options.url}`)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {beforeEach, describe, expect, it, vi} from 'vitest'
2+
3+
const {octokitCtorMock, getInputMock, getBooleanInputMock} = vi.hoisted(() => ({
4+
octokitCtorMock: vi.fn(),
5+
getInputMock: vi.fn(),
6+
getBooleanInputMock: vi.fn(),
7+
}))
8+
9+
vi.mock('@actions/core', () => ({
10+
getInput: getInputMock,
11+
getBooleanInput: getBooleanInputMock,
12+
info: vi.fn(),
13+
debug: vi.fn(),
14+
warning: vi.fn(),
15+
setOutput: vi.fn(),
16+
setFailed: vi.fn(),
17+
}))
18+
19+
vi.mock('@octokit/core', () => ({
20+
Octokit: {
21+
plugin: vi.fn(() => octokitCtorMock),
22+
},
23+
}))
24+
25+
vi.mock('@octokit/plugin-throttling', () => ({
26+
throttling: vi.fn(),
27+
}))
28+
29+
describe('file action index', () => {
30+
beforeEach(() => {
31+
vi.resetModules()
32+
vi.clearAllMocks()
33+
})
34+
35+
it('passes baseUrl to Octokit when base_url input is provided', async () => {
36+
getInputMock.mockImplementation((name: string) => {
37+
switch (name) {
38+
case 'findings':
39+
return '[]'
40+
case 'repository':
41+
return 'org/repo'
42+
case 'token':
43+
return 'token'
44+
case 'base_url':
45+
return 'https://ghe.example.com/api/v3'
46+
case 'cached_filings':
47+
return '[]'
48+
default:
49+
return ''
50+
}
51+
})
52+
getBooleanInputMock.mockReturnValue(false)
53+
54+
const {default: run} = await import('../src/index.ts')
55+
await run()
56+
57+
expect(octokitCtorMock).toHaveBeenCalledWith(
58+
expect.objectContaining({
59+
auth: 'token',
60+
baseUrl: 'https://ghe.example.com/api/v3',
61+
}),
62+
)
63+
})
64+
65+
it('uses Octokit default API URL when base_url input is not provided', async () => {
66+
getInputMock.mockImplementation((name: string) => {
67+
switch (name) {
68+
case 'findings':
69+
return '[]'
70+
case 'repository':
71+
return 'org/repo'
72+
case 'token':
73+
return 'token'
74+
case 'cached_filings':
75+
return '[]'
76+
default:
77+
return ''
78+
}
79+
})
80+
getBooleanInputMock.mockReturnValue(false)
81+
82+
const {default: run} = await import('../src/index.ts')
83+
await run()
84+
85+
expect(octokitCtorMock).toHaveBeenCalledWith(
86+
expect.objectContaining({
87+
auth: 'token',
88+
baseUrl: undefined,
89+
}),
90+
)
91+
})
92+
})

.github/actions/fix/action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ inputs:
1111
token:
1212
description: "Personal access token (PAT) with fine-grained permissions 'issues: write' and 'pull_requests: write'"
1313
required: true
14+
base_url:
15+
description: "Optional base URL for the GitHub API (for example, 'https://HOSTNAME/api/v3' for GitHub Enterprise Server)"
16+
required: false
1417

1518
outputs:
1619
fixings:

.github/actions/fix/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ export default async function () {
1414
const issues: IssueInput[] = JSON.parse(core.getInput('issues', {required: true}) || '[]')
1515
const repoWithOwner = core.getInput('repository', {required: true})
1616
const token = core.getInput('token', {required: true})
17+
const baseUrl = core.getInput('base_url', {required: false}) || undefined
1718
core.debug(`Input: 'issues: ${JSON.stringify(issues)}'`)
1819
core.debug(`Input: 'repository: ${repoWithOwner}'`)
20+
core.debug(`Input: 'base_url: ${baseUrl ?? '(default)'}'`)
1921

2022
const octokit = new OctokitWithThrottling({
2123
auth: token,
24+
baseUrl,
2225
throttle: {
2326
onRateLimit: (retryAfter, options, octokit, retryCount) => {
2427
octokit.log.warn(`Request quota exhausted for request ${options.method} ${options.url}`)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import {beforeEach, describe, expect, it, vi} from 'vitest'
2+
3+
const {octokitCtorMock, getInputMock} = vi.hoisted(() => ({
4+
octokitCtorMock: vi.fn(),
5+
getInputMock: vi.fn(),
6+
}))
7+
8+
vi.mock('@actions/core', () => ({
9+
getInput: getInputMock,
10+
info: vi.fn(),
11+
debug: vi.fn(),
12+
warning: vi.fn(),
13+
setOutput: vi.fn(),
14+
setFailed: vi.fn(),
15+
}))
16+
17+
vi.mock('@octokit/core', () => ({
18+
Octokit: {
19+
plugin: vi.fn(() => octokitCtorMock),
20+
},
21+
}))
22+
23+
vi.mock('@octokit/plugin-throttling', () => ({
24+
throttling: vi.fn(),
25+
}))
26+
27+
describe('fix action index', () => {
28+
beforeEach(() => {
29+
vi.resetModules()
30+
vi.clearAllMocks()
31+
})
32+
33+
it('passes baseUrl to Octokit when base_url input is provided', async () => {
34+
getInputMock.mockImplementation((name: string) => {
35+
switch (name) {
36+
case 'issues':
37+
return '[]'
38+
case 'repository':
39+
return 'org/repo'
40+
case 'token':
41+
return 'token'
42+
case 'base_url':
43+
return 'https://ghe.example.com/api/v3'
44+
default:
45+
return ''
46+
}
47+
})
48+
49+
const {default: run} = await import('../src/index.ts')
50+
await run()
51+
52+
expect(octokitCtorMock).toHaveBeenCalledWith(
53+
expect.objectContaining({
54+
auth: 'token',
55+
baseUrl: 'https://ghe.example.com/api/v3',
56+
}),
57+
)
58+
})
59+
60+
it('uses Octokit default API URL when base_url input is not provided', async () => {
61+
getInputMock.mockImplementation((name: string) => {
62+
switch (name) {
63+
case 'issues':
64+
return '[]'
65+
case 'repository':
66+
return 'org/repo'
67+
case 'token':
68+
return 'token'
69+
default:
70+
return ''
71+
}
72+
})
73+
74+
const {default: run} = await import('../src/index.ts')
75+
await run()
76+
77+
expect(octokitCtorMock).toHaveBeenCalledWith(
78+
expect.objectContaining({
79+
auth: 'token',
80+
baseUrl: undefined,
81+
}),
82+
)
83+
})
84+
})

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ jobs:
4646
REPLACE_THIS
4747
repository: REPLACE_THIS/REPLACE_THIS # Provide a repository name-with-owner (in the format "primer/primer-docs"). This is where issues will be filed and where Copilot will open PRs; more information below.
4848
token: ${{ secrets.GH_TOKEN }} # This token must have write access to the repo above (contents, issues, and PRs); more information below. Note: GitHub Actions' GITHUB_TOKEN cannot be used here.
49+
# base_url: https://HOSTNAME/api/v3 # Optional: GitHub API base URL (required for GitHub Enterprise Server)
4950
cache_key: REPLACE_THIS # Provide a filename that will be used when caching results. We recommend including the name or domain of the site being scanned.
5051
# login_url: # Optional: URL of the login page if authentication is required
5152
# username: # Optional: Username for authentication
@@ -117,6 +118,7 @@ Trigger the workflow manually or automatically based on your configuration. The
117118
| `urls` | Yes | Newline-delimited list of URLs to scan | `https://primer.style`<br>`https://primer.style/octicons` |
118119
| `repository` | Yes | Repository (with owner) for issues and PRs | `primer/primer-docs` |
119120
| `token` | Yes | PAT with write permissions (see above) | `${{ secrets.GH_TOKEN }}` |
121+
| `base_url` | No | GitHub API base URL used by Octokit. Set this for GitHub Enterprise Server (format: `https://HOSTNAME/api/v3`). Defaults to `https://api.github.com` | `https://ghe.example.com/api/v3` |
120122
| `cache_key` | Yes | Key for caching results across runs<br>Allowed: `A-Za-z0-9._/-` | `cached_results-primer.style-main.json` |
121123
| `login_url` | No | If scanned pages require authentication, the URL of the login page | `https://github.com/login` |
122124
| `username` | No | If scanned pages require authentication, the username to use for login | `some-user` |

action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ inputs:
1212
token:
1313
description: "Personal access token (PAT) with fine-grained permissions 'contents: write', 'issues: write', and 'pull_requests: write'"
1414
required: true
15+
base_url:
16+
description: "Optional base URL for the GitHub API (for example, 'https://HOSTNAME/api/v3' for GitHub Enterprise Server)"
17+
required: false
1518
cache_key:
1619
description: 'Key for caching results across runs'
1720
required: true
@@ -113,6 +116,7 @@ runs:
113116
findings: ${{ steps.find.outputs.findings }}
114117
repository: ${{ inputs.repository }}
115118
token: ${{ inputs.token }}
119+
base_url: ${{ inputs.base_url }}
116120
cached_filings: ${{ steps.normalize_cache.outputs.value }}
117121
screenshot_repository: ${{ github.repository }}
118122
open_grouped_issues: ${{ inputs.open_grouped_issues }}
@@ -132,6 +136,7 @@ runs:
132136
issues: ${{ steps.get_issues_from_filings.outputs.issues }}
133137
repository: ${{ inputs.repository }}
134138
token: ${{ inputs.token }}
139+
base_url: ${{ inputs.base_url }}
135140
- name: Set results output
136141
id: results
137142
uses: actions/github-script@v8

0 commit comments

Comments
 (0)