Replace @action/github dependency with latest Octokit

This commit is contained in:
Josh Gross 2025-06-02 21:46:06 -04:00
parent 5ee2b97722
commit f734337387
No known key found for this signature in database
18 changed files with 32329 additions and 5341 deletions

View file

@ -10,10 +10,9 @@ uses the GitHub API and the workflow run context.
To use this action, provide an input named `script` that contains the body of an asynchronous JavaScript function call. To use this action, provide an input named `script` that contains the body of an asynchronous JavaScript function call.
The following arguments will be provided: The following arguments will be provided:
- `github` A pre-authenticated - `github`/`octokit` A pre-authenticated
[octokit/rest.js](https://octokit.github.io/rest.js) client with pagination plugins [octokit/rest.js](https://octokit.github.io/rest.js) client with pagination plugins
- `context` An object containing the [context of the workflow - `context` An object containing partial [context of the workflow run](./src/context.ts)
run](https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts)
- `core` A reference to the [@actions/core](https://github.com/actions/toolkit/tree/main/packages/core) package - `core` A reference to the [@actions/core](https://github.com/actions/toolkit/tree/main/packages/core) package
- `glob` A reference to the [@actions/glob](https://github.com/actions/toolkit/tree/main/packages/glob) package - `glob` A reference to the [@actions/glob](https://github.com/actions/toolkit/tree/main/packages/glob) package
- `io` A reference to the [@actions/io](https://github.com/actions/toolkit/tree/main/packages/io) package - `io` A reference to the [@actions/io](https://github.com/actions/toolkit/tree/main/packages/io) package
@ -33,6 +32,11 @@ documentation.
## Breaking Changes ## Breaking Changes
### V8
Version 8 of this action upgraded `@octokit/core` from v5 to v7, which could impact scripts that would
be impacted by those breaking changes.
### V7 ### V7
Version 7 of this action updated the runtime to Node 20 - https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-javascript-actions Version 7 of this action updated the runtime to Node 20 - https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-javascript-actions

View file

@ -4,66 +4,30 @@ import {getRetryOptions} from '../src/retry-options'
describe('getRequestOptions', () => { describe('getRequestOptions', () => {
test('retries disabled if retries == 0', async () => { test('retries disabled if retries == 0', async () => {
const [retryOptions, requestOptions] = getRetryOptions( const retryOptions = getRetryOptions(0, [400, 500, 502])
0,
[400, 500, 502],
[]
)
expect(retryOptions.enabled).toBe(false) expect(retryOptions.enabled).toBe(false)
expect(retryOptions.doNotRetry).toBeFalsy() expect(retryOptions.doNotRetry).toBeFalsy()
expect(requestOptions?.retries).toBeFalsy()
}) })
test('properties set if retries > 0', async () => { test('properties set if retries > 0', async () => {
const [retryOptions, requestOptions] = getRetryOptions( const retryOptions = getRetryOptions(1, [400, 500, 502])
1,
[400, 500, 502],
[]
)
expect(retryOptions.enabled).toBe(true) expect(retryOptions.enabled).toBe(true)
expect(retryOptions.doNotRetry).toEqual([400, 500, 502]) expect(retryOptions.doNotRetry).toEqual([400, 500, 502])
expect(requestOptions?.retries).toEqual(1)
}) })
test('properties set if retries > 0', async () => { test('properties set if retries > 0', async () => {
const [retryOptions, requestOptions] = getRetryOptions( const retryOptions = getRetryOptions(1, [400, 500, 502])
1,
[400, 500, 502],
[]
)
expect(retryOptions.enabled).toBe(true) expect(retryOptions.enabled).toBe(true)
expect(retryOptions.doNotRetry).toEqual([400, 500, 502]) expect(retryOptions.doNotRetry).toEqual([400, 500, 502])
expect(requestOptions?.retries).toEqual(1)
}) })
test('retryOptions.doNotRetry not set if exemptStatusCodes isEmpty', async () => { test('retryOptions.doNotRetry not set if exemptStatusCodes isEmpty', async () => {
const [retryOptions, requestOptions] = getRetryOptions(1, [], []) const retryOptions = getRetryOptions(1, [])
expect(retryOptions.enabled).toBe(true) expect(retryOptions.enabled).toBe(true)
expect(retryOptions.doNotRetry).toBeUndefined() expect(retryOptions.doNotRetry).toBeUndefined()
expect(requestOptions?.retries).toEqual(1)
})
test('requestOptions does not override defaults from @actions/github', async () => {
const [retryOptions, requestOptions] = getRetryOptions(1, [], {
request: {
agent: 'default-user-agent'
},
foo: 'bar'
})
expect(retryOptions.enabled).toBe(true)
expect(retryOptions.doNotRetry).toBeUndefined()
expect(requestOptions?.retries).toEqual(1)
expect(requestOptions?.agent).toEqual('default-user-agent')
expect(requestOptions?.foo).toBeUndefined() // this should not be in the `options.request` object, but at the same level as `request`
}) })
}) })

35682
dist/index.js vendored

File diff suppressed because one or more lines are too long

3
dist/package.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"type": "module"
}

1275
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,11 @@
{ {
"name": "@actions/github-script", "name": "@actions/github-script",
"description": "A GitHub action for executing a simple script", "description": "A GitHub action for executing a simple script",
"version": "7.0.1", "version": "8.0.0",
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module",
"types": "types/async-function.d.ts", "types": "types/async-function.d.ts",
"private": true, "private": true,
"engines": { "engines": {
@ -12,7 +13,7 @@
}, },
"scripts": { "scripts": {
"build": "npm run build:types && ncc build src/main.ts", "build": "npm run build:types && ncc build src/main.ts",
"build:types": "tsc src/async-function.ts -t es5 --declaration --allowJs --emitDeclarationOnly --outDir types", "build:types": "tsc --project tsconfig.types.json",
"format:check": "prettier --check src __test__", "format:check": "prettier --check src __test__",
"format:write": "prettier --write src __test__", "format:write": "prettier --write src __test__",
"lint": "eslint src __test__", "lint": "eslint src __test__",
@ -41,16 +42,17 @@
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@actions/glob": "^0.4.0", "@actions/glob": "^0.4.0",
"@actions/io": "^1.1.3", "@actions/io": "^1.1.3",
"@octokit/core": "^5.0.1", "@octokit/action": "^8.0.2",
"@octokit/plugin-request-log": "^4.0.0", "@octokit/core": "^7.0.2",
"@octokit/plugin-retry": "^6.0.1", "@octokit/plugin-request-log": "^6.0.0",
"@octokit/plugin-retry": "^8.0.1",
"@octokit/types": "^14.1.0",
"@types/node": "^20.9.0" "@types/node": "^20.9.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.5", "@types/jest": "^29.5.14",
"@typescript-eslint/eslint-plugin": "^6.7.5", "@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5", "@typescript-eslint/parser": "^6.7.5",
"@vercel/ncc": "^0.38.0", "@vercel/ncc": "^0.38.0",
@ -61,7 +63,7 @@
"jest": "^29.7.0", "jest": "^29.7.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"ts-jest": "^29.1.1", "ts-jest": "^29.3.4",
"typescript": "^5.2.2" "typescript": "^5.8.3"
} }
} }

View file

@ -1,17 +1,17 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as exec from '@actions/exec' import * as exec from '@actions/exec'
import {Context} from '@actions/github/lib/context'
import {GitHub} from '@actions/github/lib/utils'
import * as glob from '@actions/glob' import * as glob from '@actions/glob'
import * as io from '@actions/io' import * as io from '@actions/io'
import {Octokit} from '@octokit/action'
import {Context} from './context.js'
const AsyncFunction = Object.getPrototypeOf(async () => null).constructor const AsyncFunction = Object.getPrototypeOf(async () => null).constructor
export declare type AsyncFunctionArguments = { export declare type AsyncFunctionArguments = {
context: Context context: Context
core: typeof core core: typeof core
github: InstanceType<typeof GitHub> github: InstanceType<typeof Octokit>
octokit: InstanceType<typeof GitHub> octokit: InstanceType<typeof Octokit>
exec: typeof exec exec: typeof exec
glob: typeof glob glob: typeof glob
io: typeof io io: typeof io

128
src/context.ts Normal file
View file

@ -0,0 +1,128 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {readFileSync, existsSync} from 'fs'
import {EOL} from 'os'
// Copied from https://github.com/actions/toolkit/tree/f31c2921c1228a97be08cdb38b919a83077354d9/packages/github/src
// to minimize breaking changes from the removal of @actions/github from this action
// That code originated from https://github.com/JasonEtco/actions-toolkit
export interface PayloadRepository {
[key: string]: any
full_name?: string
name: string
owner: {
[key: string]: any
login: string
name?: string
}
html_url?: string
}
export interface WebhookPayload {
[key: string]: any
repository?: PayloadRepository
issue?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
pull_request?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
sender?: {
[key: string]: any
type: string
}
action?: string
installation?: {
id: number
[key: string]: any
}
comment?: {
id: number
[key: string]: any
}
}
export class Context {
/**
* Webhook payload object that triggered the workflow
*/
payload: WebhookPayload
eventName: string
sha: string
ref: string
workflow: string
action: string
actor: string
job: string
runAttempt: number
runNumber: number
runId: number
apiUrl: string
serverUrl: string
graphqlUrl: string
/**
* Hydrate the context from the environment
*/
constructor() {
this.payload = {}
if (process.env.GITHUB_EVENT_PATH) {
if (existsSync(process.env.GITHUB_EVENT_PATH)) {
this.payload = JSON.parse(
readFileSync(process.env.GITHUB_EVENT_PATH, {encoding: 'utf8'})
)
} else {
const path = process.env.GITHUB_EVENT_PATH
process.stdout.write(`GITHUB_EVENT_PATH ${path} does not exist${EOL}`)
}
}
this.eventName = process.env.GITHUB_EVENT_NAME as string
this.sha = process.env.GITHUB_SHA as string
this.ref = process.env.GITHUB_REF as string
this.workflow = process.env.GITHUB_WORKFLOW as string
this.action = process.env.GITHUB_ACTION as string
this.actor = process.env.GITHUB_ACTOR as string
this.job = process.env.GITHUB_JOB as string
this.runAttempt = parseInt(process.env.GITHUB_RUN_ATTEMPT as string, 10)
this.runNumber = parseInt(process.env.GITHUB_RUN_NUMBER as string, 10)
this.runId = parseInt(process.env.GITHUB_RUN_ID as string, 10)
this.apiUrl = process.env.GITHUB_API_URL ?? `https://api.github.com`
this.serverUrl = process.env.GITHUB_SERVER_URL ?? `https://github.com`
this.graphqlUrl =
process.env.GITHUB_GRAPHQL_URL ?? `https://api.github.com/graphql`
}
get issue(): {owner: string; repo: string; number: number} {
const payload = this.payload
return {
...this.repo,
number: (payload.issue || payload.pull_request || payload).number
}
}
get repo(): {owner: string; repo: string} {
if (process.env.GITHUB_REPOSITORY) {
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/')
return {owner, repo}
}
if (this.payload.repository) {
return {
owner: this.payload.repository.owner.login,
repo: this.payload.repository.name
}
}
throw new Error(
"context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'"
)
}
}

View file

@ -1,30 +1,25 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as exec from '@actions/exec' import * as exec from '@actions/exec'
import {context, getOctokit} from '@actions/github'
import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils'
import * as glob from '@actions/glob' import * as glob from '@actions/glob'
import * as io from '@actions/io' import * as io from '@actions/io'
import {requestLog} from '@octokit/plugin-request-log' import {Octokit} from '@octokit/action'
import {retry} from '@octokit/plugin-retry' import {retry} from '@octokit/plugin-retry'
import {RequestRequestOptions} from '@octokit/types' import {requestLog} from '@octokit/plugin-request-log'
import {callAsyncFunction} from './async-function' import {callAsyncFunction} from './async-function.js'
import {RetryOptions, getRetryOptions, parseNumberArray} from './retry-options' import {Context} from './context.js'
import {wrapRequire} from './wrap-require' import {getRetryOptions, parseNumberArray} from './retry-options.js'
import {wrapRequire} from './wrap-require.js'
import {OctokitOptions} from '@octokit/core'
process.on('unhandledRejection', handleError) process.on('unhandledRejection', handleError)
main().catch(handleError) main().catch(handleError)
type Options = {
log?: Console
userAgent?: string
baseUrl?: string
previews?: string[]
retry?: RetryOptions
request?: RequestRequestOptions
}
async function main(): Promise<void> { async function main(): Promise<void> {
const token = core.getInput('github-token', {required: true}) // @octokit/aciton will use @octokit/auth-action which automatically
// reads the GITHUB_TOKEN input
// We should still validate that the token is provided early though
core.getInput('github-token', {required: true})
const debug = core.getBooleanInput('debug') const debug = core.getBooleanInput('debug')
const userAgent = core.getInput('user-agent') const userAgent = core.getInput('user-agent')
const previews = core.getInput('previews') const previews = core.getInput('previews')
@ -33,18 +28,16 @@ async function main(): Promise<void> {
const exemptStatusCodes = parseNumberArray( const exemptStatusCodes = parseNumberArray(
core.getInput('retry-exempt-status-codes') core.getInput('retry-exempt-status-codes')
) )
const [retryOpts, requestOpts] = getRetryOptions( const retryOpts = getRetryOptions(retries, exemptStatusCodes)
retries,
exemptStatusCodes,
defaultGitHubOptions
)
const opts: Options = { const opts: OctokitOptions = {
log: debug ? console : undefined, log: debug ? console : undefined,
userAgent: userAgent || undefined, userAgent: userAgent || undefined,
previews: previews ? previews.split(',') : undefined, previews: previews ? previews.split(',') : undefined,
retry: retryOpts, retry: retryOpts,
request: requestOpts request: {
retries
}
} }
// Setting `baseUrl` to undefined will prevent the default value from being used // Setting `baseUrl` to undefined will prevent the default value from being used
@ -53,7 +46,8 @@ async function main(): Promise<void> {
opts.baseUrl = baseUrl opts.baseUrl = baseUrl
} }
const github = getOctokit(token, opts, retry, requestLog) const OctokitWithPlugins = Octokit.plugin(retry, requestLog)
const octokit = new OctokitWithPlugins(opts)
const script = core.getInput('script', {required: true}) const script = core.getInput('script', {required: true})
// Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors. // Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors.
@ -61,9 +55,9 @@ async function main(): Promise<void> {
{ {
require: wrapRequire, require: wrapRequire,
__original_require__: __non_webpack_require__, __original_require__: __non_webpack_require__,
github, github: octokit,
octokit: github, octokit,
context, context: new Context(),
core, core,
exec, exec,
glob, glob,

View file

@ -1,6 +1,4 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import {OctokitOptions} from '@octokit/core/dist-types/types'
import {RequestRequestOptions} from '@octokit/types'
export type RetryOptions = { export type RetryOptions = {
doNotRetry?: number[] doNotRetry?: number[]
@ -9,11 +7,10 @@ export type RetryOptions = {
export function getRetryOptions( export function getRetryOptions(
retries: number, retries: number,
exemptStatusCodes: number[], exemptStatusCodes: number[]
defaultOptions: OctokitOptions ): RetryOptions {
): [RetryOptions, RequestRequestOptions | undefined] {
if (retries <= 0) { if (retries <= 0) {
return [{enabled: false}, defaultOptions.request] return {enabled: false}
} }
const retryOptions: RetryOptions = { const retryOptions: RetryOptions = {
@ -24,23 +21,13 @@ export function getRetryOptions(
retryOptions.doNotRetry = exemptStatusCodes retryOptions.doNotRetry = exemptStatusCodes
} }
// The GitHub type has some defaults for `options.request`
// see: https://github.com/actions/toolkit/blob/4fbc5c941a57249b19562015edbd72add14be93d/packages/github/src/utils.ts#L15
// We pass these in here so they are not overidden.
const requestOptions: RequestRequestOptions = {
...defaultOptions.request,
retries
}
core.debug( core.debug(
`GitHub client configured with: (retries: ${ `GitHub client configured with: (retries: ${retries}, retry-exempt-status-code: ${
requestOptions.retries
}, retry-exempt-status-code: ${
retryOptions.doNotRetry ?? 'octokit default: [400, 401, 403, 404, 422]' retryOptions.doNotRetry ?? 'octokit default: [400, 401, 403, 404, 422]'
})` })`
) )
return [retryOptions, requestOptions] return retryOptions
} }
export function parseNumberArray(listString: string): number[] { export function parseNumberArray(listString: string): number[] {

View file

@ -1,9 +1,16 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es2018", "target": "es2022",
"moduleResolution": "node", "module": "Node16",
"moduleResolution": "node16",
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true "forceConsistentCasingInFileNames": true
}, },
"exclude": ["__test__"] // "include": [
// "src/**/*",
// "types/non-webpack-require.d.ts"
// ],
"exclude": [
"__test__"
]
} }

12
tsconfig.types.json Normal file
View file

@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"allowJs": true,
"emitDeclarationOnly": true,
"outDir": "types"
},
"include": [
"src/async-function.ts"
]
}

View file

@ -1,15 +1,14 @@
/// <reference types="node" />
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
import { Context } from '@actions/github/lib/context';
import { GitHub } from '@actions/github/lib/utils';
import * as glob from '@actions/glob'; import * as glob from '@actions/glob';
import * as io from '@actions/io'; import * as io from '@actions/io';
import { Octokit } from '@octokit/action';
import { Context } from './context.js';
export declare type AsyncFunctionArguments = { export declare type AsyncFunctionArguments = {
context: Context; context: Context;
core: typeof core; core: typeof core;
github: InstanceType<typeof GitHub>; github: InstanceType<typeof Octokit>;
octokit: InstanceType<typeof GitHub>; octokit: InstanceType<typeof Octokit>;
exec: typeof exec; exec: typeof exec;
glob: typeof glob; glob: typeof glob;
io: typeof io; io: typeof io;

72
types/context.d.ts vendored Normal file
View file

@ -0,0 +1,72 @@
export interface PayloadRepository {
[key: string]: any;
full_name?: string;
name: string;
owner: {
[key: string]: any;
login: string;
name?: string;
};
html_url?: string;
}
export interface WebhookPayload {
[key: string]: any;
repository?: PayloadRepository;
issue?: {
[key: string]: any;
number: number;
html_url?: string;
body?: string;
};
pull_request?: {
[key: string]: any;
number: number;
html_url?: string;
body?: string;
};
sender?: {
[key: string]: any;
type: string;
};
action?: string;
installation?: {
id: number;
[key: string]: any;
};
comment?: {
id: number;
[key: string]: any;
};
}
export declare class Context {
/**
* Webhook payload object that triggered the workflow
*/
payload: WebhookPayload;
eventName: string;
sha: string;
ref: string;
workflow: string;
action: string;
actor: string;
job: string;
runAttempt: number;
runNumber: number;
runId: number;
apiUrl: string;
serverUrl: string;
graphqlUrl: string;
/**
* Hydrate the context from the environment
*/
constructor();
get issue(): {
owner: string;
repo: string;
number: number;
};
get repo(): {
owner: string;
repo: string;
};
}

18
types/src/async-function.d.ts vendored Normal file
View file

@ -0,0 +1,18 @@
import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as glob from '@actions/glob';
import * as io from '@actions/io';
import { Octokit } from '@octokit/action';
import { Context } from './context.js';
export declare type AsyncFunctionArguments = {
context: Context;
core: typeof core;
github: InstanceType<typeof Octokit>;
octokit: InstanceType<typeof Octokit>;
exec: typeof exec;
glob: typeof glob;
io: typeof io;
require: NodeRequire;
__original_require__: NodeRequire;
};
export declare function callAsyncFunction<T>(args: AsyncFunctionArguments, source: string): Promise<T>;

72
types/src/context.d.ts vendored Normal file
View file

@ -0,0 +1,72 @@
export interface PayloadRepository {
[key: string]: any;
full_name?: string;
name: string;
owner: {
[key: string]: any;
login: string;
name?: string;
};
html_url?: string;
}
export interface WebhookPayload {
[key: string]: any;
repository?: PayloadRepository;
issue?: {
[key: string]: any;
number: number;
html_url?: string;
body?: string;
};
pull_request?: {
[key: string]: any;
number: number;
html_url?: string;
body?: string;
};
sender?: {
[key: string]: any;
type: string;
};
action?: string;
installation?: {
id: number;
[key: string]: any;
};
comment?: {
id: number;
[key: string]: any;
};
}
export declare class Context {
/**
* Webhook payload object that triggered the workflow
*/
payload: WebhookPayload;
eventName: string;
sha: string;
ref: string;
workflow: string;
action: string;
actor: string;
job: string;
runAttempt: number;
runNumber: number;
runId: number;
apiUrl: string;
serverUrl: string;
graphqlUrl: string;
/**
* Hydrate the context from the environment
*/
constructor();
get issue(): {
owner: string;
repo: string;
number: number;
};
get repo(): {
owner: string;
repo: string;
};
}

1
types/types/non-webpack-require.d.ts vendored Normal file
View file

@ -0,0 +1 @@
export {};