mirror of
https://github.com/actions/github-script.git
synced 2026-02-08 03:57:27 +00:00
Merge f27b40e6b7 into f28e40c7f3
This commit is contained in:
commit
b9725d2b7f
12 changed files with 178333 additions and 22 deletions
|
|
@ -2,13 +2,21 @@
|
||||||
|
|
||||||
import {callAsyncFunction} from '../src/async-function'
|
import {callAsyncFunction} from '../src/async-function'
|
||||||
|
|
||||||
describe('callAsyncFunction', () => {
|
describe(callAsyncFunction.name, () => {
|
||||||
test('calls the function with its arguments', async () => {
|
test('calls the function with its arguments', async () => {
|
||||||
const result = await callAsyncFunction({foo: 'bar'} as any, 'return foo')
|
const result = await callAsyncFunction({foo: 'bar'} as any, 'return foo')
|
||||||
expect(result).toEqual('bar')
|
expect(result).toEqual('bar')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('throws on ReferenceError', async () => {
|
test('can await a Promise', async () => {
|
||||||
|
const result = await callAsyncFunction(
|
||||||
|
{} as any,
|
||||||
|
'return await new Promise(resolve => resolve("bar"))'
|
||||||
|
)
|
||||||
|
expect(result).toEqual('bar')
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`throws an ${ReferenceError.name}`, async () => {
|
||||||
expect.assertions(1)
|
expect.assertions(1)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import {getRetryOptions} from '../src/retry-options'
|
import {getRetryOptions} from '../src/retry-options'
|
||||||
|
|
||||||
describe('getRequestOptions', () => {
|
describe(getRetryOptions.name, () => {
|
||||||
test('retries disabled if retries == 0', async () => {
|
test('retries disabled if retries == 0', async () => {
|
||||||
const [retryOptions, requestOptions] = getRetryOptions(
|
const [retryOptions, requestOptions] = getRetryOptions(
|
||||||
0,
|
0,
|
||||||
|
|
|
||||||
194
__test__/interpret-script.test.ts
Normal file
194
__test__/interpret-script.test.ts
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
import {SupportedLanguage, interpretScript} from '../src/interpret-script'
|
||||||
|
|
||||||
|
const scripts: Record<SupportedLanguage, string> = {
|
||||||
|
[SupportedLanguage.cjs]: `
|
||||||
|
const FS = require('node:fs') // Proof that we are in CommonJS.
|
||||||
|
var a // Proof that we are NOT in TypeScript.
|
||||||
|
return foo // Proof that we executed correctly. Also, this is the pre-existing function-style syntax.
|
||||||
|
`,
|
||||||
|
[SupportedLanguage.cts]: `
|
||||||
|
const FS = require('node:fs') // Proof that we are in CommonJS.
|
||||||
|
let a: string // Proof that we are in TypeScript.
|
||||||
|
return foo // Proof that we executed correctly.
|
||||||
|
`,
|
||||||
|
[SupportedLanguage.mts]: `
|
||||||
|
import FS from 'node:fs' // Proof that we are in an ES Module.
|
||||||
|
let a: string // Proof that we are in TypeScript.
|
||||||
|
return foo // Proof that we executed correctly.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(interpretScript.name, () => {
|
||||||
|
describe(`language set to ${SupportedLanguage.cjs}`, () => {
|
||||||
|
test(`throws when given a ${SupportedLanguage.cts} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(
|
||||||
|
SupportedLanguage.cjs,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.cts
|
||||||
|
)
|
||||||
|
).rejects
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`throws when given an ${SupportedLanguage.mts} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(
|
||||||
|
SupportedLanguage.cjs,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.mts
|
||||||
|
)
|
||||||
|
).rejects
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`interprets a ${SupportedLanguage.cjs} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(
|
||||||
|
SupportedLanguage.cjs,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.cjs
|
||||||
|
)
|
||||||
|
).resolves
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`when given a ${SupportedLanguage.cjs} script returns a function that can run it correctly`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.cjs,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.cjs
|
||||||
|
)
|
||||||
|
return expect(result()).resolves.toEqual('bar')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`language set to ${SupportedLanguage.cts}`, () => {
|
||||||
|
test(`throws when given a ${SupportedLanguage.cjs} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(
|
||||||
|
SupportedLanguage.cts,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.cjs
|
||||||
|
)
|
||||||
|
).rejects
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`throws when given an ${SupportedLanguage.mts} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(
|
||||||
|
SupportedLanguage.cts,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.mts
|
||||||
|
)
|
||||||
|
).rejects
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`interprets a ${SupportedLanguage.cts} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(
|
||||||
|
SupportedLanguage.cts,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.cts
|
||||||
|
)
|
||||||
|
).resolves
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`when given a ${SupportedLanguage.cts} script returns a function that can run it correctly`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.cts,
|
||||||
|
{foo: 'bar', require} as any,
|
||||||
|
scripts.cts
|
||||||
|
)
|
||||||
|
return expect(result()).resolves.toEqual('bar')
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`a script imports a script from disk`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.cts,
|
||||||
|
{require} as any,
|
||||||
|
`
|
||||||
|
const {test} = require('../test/requireable')
|
||||||
|
return test()
|
||||||
|
`
|
||||||
|
)
|
||||||
|
return expect(result()).resolves.toEqual('hello')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe(`language set to ${SupportedLanguage.mts}`, () => {
|
||||||
|
test(`throws when given a ${SupportedLanguage.cjs} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(SupportedLanguage.mts, {foo: 'bar'} as any, scripts.cjs)
|
||||||
|
).rejects
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`throws when given a ${SupportedLanguage.cts} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(SupportedLanguage.mts, {foo: 'bar'} as any, scripts.cts)
|
||||||
|
).rejects
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`interprets an ${SupportedLanguage.mts} script`, async () => {
|
||||||
|
return expect(
|
||||||
|
interpretScript(SupportedLanguage.mts, {foo: 'bar'} as any, scripts.mts)
|
||||||
|
).resolves
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`when given an ${SupportedLanguage.mts} script returns a function that can run it correctly`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.mts,
|
||||||
|
{foo: 'bar'} as any,
|
||||||
|
scripts.mts
|
||||||
|
)
|
||||||
|
return expect(result()).resolves.toEqual('bar')
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`can access console`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.mts,
|
||||||
|
{} as any,
|
||||||
|
`console`
|
||||||
|
)
|
||||||
|
return expect(result()).resolves
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`can access process`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.mts,
|
||||||
|
{} as any,
|
||||||
|
`process`
|
||||||
|
)
|
||||||
|
return expect(result()).resolves
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`a script that returns an object`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.mts,
|
||||||
|
{} as any,
|
||||||
|
`return {a: 'b'}`
|
||||||
|
)
|
||||||
|
return expect(result()).resolves.toEqual({a: 'b'})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`a script that uses a root level await`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.mts,
|
||||||
|
{} as any,
|
||||||
|
`await Promise.resolve()`
|
||||||
|
)
|
||||||
|
return expect(result()).resolves
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`a script imports a script from disk`, async () => {
|
||||||
|
const result = await interpretScript(
|
||||||
|
SupportedLanguage.mts,
|
||||||
|
{require} as any,
|
||||||
|
`
|
||||||
|
const {test} = await import('../test/importable')
|
||||||
|
return test()
|
||||||
|
`
|
||||||
|
)
|
||||||
|
return expect(result()).resolves.toEqual('hello')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -32,6 +32,9 @@ inputs:
|
||||||
base-url:
|
base-url:
|
||||||
description: An optional GitHub REST API URL to connect to a different GitHub instance. For example, https://my.github-enterprise-server.com/api/v3
|
description: An optional GitHub REST API URL to connect to a different GitHub instance. For example, https://my.github-enterprise-server.com/api/v3
|
||||||
required: false
|
required: false
|
||||||
|
language:
|
||||||
|
description: The language to interpret the script as. Pick from "cjs", "cts", "mts".
|
||||||
|
default: "cjs"
|
||||||
outputs:
|
outputs:
|
||||||
result:
|
result:
|
||||||
description: The return value of the script, stringified with `JSON.stringify`
|
description: The return value of the script, stringified with `JSON.stringify`
|
||||||
|
|
|
||||||
178039
dist/index.js
vendored
178039
dist/index.js
vendored
File diff suppressed because one or more lines are too long
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -17,7 +17,8 @@
|
||||||
"@octokit/core": "^5.0.1",
|
"@octokit/core": "^5.0.1",
|
||||||
"@octokit/plugin-request-log": "^4.0.0",
|
"@octokit/plugin-request-log": "^4.0.0",
|
||||||
"@octokit/plugin-retry": "^6.0.1",
|
"@octokit/plugin-retry": "^6.0.1",
|
||||||
"@types/node": "^20.9.0"
|
"@types/node": "^20.9.0",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.5",
|
"@types/jest": "^29.5.5",
|
||||||
|
|
@ -31,8 +32,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.1.1"
|
||||||
"typescript": "^5.2.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0 <21.0.0"
|
"node": ">=20.0.0 <21.0.0"
|
||||||
|
|
@ -7077,7 +7077,6 @@
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
@ -12518,8 +12517,7 @@
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"unbox-primitive": {
|
"unbox-primitive": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@
|
||||||
"@octokit/core": "^5.0.1",
|
"@octokit/core": "^5.0.1",
|
||||||
"@octokit/plugin-request-log": "^4.0.0",
|
"@octokit/plugin-request-log": "^4.0.0",
|
||||||
"@octokit/plugin-retry": "^6.0.1",
|
"@octokit/plugin-retry": "^6.0.1",
|
||||||
"@types/node": "^20.9.0"
|
"@types/node": "^20.9.0",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.5",
|
"@types/jest": "^29.5.5",
|
||||||
|
|
@ -61,7 +62,6 @@
|
||||||
"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.1.1"
|
||||||
"typescript": "^5.2.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@ export function callAsyncFunction<T>(
|
||||||
args: AsyncFunctionArguments,
|
args: AsyncFunctionArguments,
|
||||||
source: string
|
source: string
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const fn = new AsyncFunction(...Object.keys(args), source)
|
const commonJsArgs = {
|
||||||
return fn(...Object.values(args))
|
...args,
|
||||||
|
module: {exports: {}},
|
||||||
|
exports: {}
|
||||||
|
}
|
||||||
|
const fn = new AsyncFunction(...Object.keys(commonJsArgs), source)
|
||||||
|
return fn(...Object.values(commonJsArgs))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
53
src/interpret-script.ts
Normal file
53
src/interpret-script.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
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 io from '@actions/io'
|
||||||
|
import {ModuleKind, ScriptTarget, transpileModule} from 'typescript'
|
||||||
|
|
||||||
|
import {callAsyncFunction} from './async-function'
|
||||||
|
|
||||||
|
export enum SupportedLanguage {
|
||||||
|
cjs = 'cjs',
|
||||||
|
cts = 'cts',
|
||||||
|
mts = 'mts'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommonContext {
|
||||||
|
context: Context
|
||||||
|
core: typeof core
|
||||||
|
github: InstanceType<typeof GitHub>
|
||||||
|
exec: typeof exec
|
||||||
|
glob: typeof glob
|
||||||
|
io: typeof io
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CjsContext extends CommonContext {
|
||||||
|
require: NodeRequire
|
||||||
|
__original_require__: NodeRequire
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function interpretScript<T>(
|
||||||
|
language: SupportedLanguage,
|
||||||
|
context: CjsContext,
|
||||||
|
script: string
|
||||||
|
): Promise<() => Promise<T>> {
|
||||||
|
switch (language) {
|
||||||
|
case SupportedLanguage.cts:
|
||||||
|
case SupportedLanguage.mts: {
|
||||||
|
const fileName = `github-script.${language}`
|
||||||
|
|
||||||
|
script = transpileModule(script, {
|
||||||
|
compilerOptions: {
|
||||||
|
module: ModuleKind.CommonJS, // Take the incoming TypeScript and compile it to CommonJS to run in the CommonJS environment of this action.
|
||||||
|
target: ScriptTarget.Latest,
|
||||||
|
strict: true
|
||||||
|
},
|
||||||
|
fileName
|
||||||
|
}).outputText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return async () => callAsyncFunction(context, script)
|
||||||
|
}
|
||||||
19
src/main.ts
19
src/main.ts
|
|
@ -7,7 +7,7 @@ import * as io from '@actions/io'
|
||||||
import {requestLog} from '@octokit/plugin-request-log'
|
import {requestLog} from '@octokit/plugin-request-log'
|
||||||
import {retry} from '@octokit/plugin-retry'
|
import {retry} from '@octokit/plugin-retry'
|
||||||
import {RequestRequestOptions} from '@octokit/types'
|
import {RequestRequestOptions} from '@octokit/types'
|
||||||
import {callAsyncFunction} from './async-function'
|
import {SupportedLanguage, interpretScript} from './interpret-script'
|
||||||
import {RetryOptions, getRetryOptions, parseNumberArray} from './retry-options'
|
import {RetryOptions, getRetryOptions, parseNumberArray} from './retry-options'
|
||||||
import {wrapRequire} from './wrap-require'
|
import {wrapRequire} from './wrap-require'
|
||||||
|
|
||||||
|
|
@ -21,6 +21,7 @@ type Options = {
|
||||||
previews?: string[]
|
previews?: string[]
|
||||||
retry?: RetryOptions
|
retry?: RetryOptions
|
||||||
request?: RequestRequestOptions
|
request?: RequestRequestOptions
|
||||||
|
language?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
|
|
@ -38,6 +39,15 @@ async function main(): Promise<void> {
|
||||||
exemptStatusCodes,
|
exemptStatusCodes,
|
||||||
defaultGitHubOptions
|
defaultGitHubOptions
|
||||||
)
|
)
|
||||||
|
const languageRaw = core.getInput('language')
|
||||||
|
|
||||||
|
const langValues = Object.keys(SupportedLanguage)
|
||||||
|
if (!langValues.includes(languageRaw)) {
|
||||||
|
throw new Error(
|
||||||
|
`"language" must be one of the following: "${langValues.join('", "')}"`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const language = SupportedLanguage[languageRaw as SupportedLanguage]
|
||||||
|
|
||||||
const opts: Options = {
|
const opts: Options = {
|
||||||
log: debug ? console : undefined,
|
log: debug ? console : undefined,
|
||||||
|
|
@ -56,8 +66,8 @@ async function main(): Promise<void> {
|
||||||
const github = getOctokit(token, opts, retry, requestLog)
|
const github = getOctokit(token, opts, retry, requestLog)
|
||||||
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.
|
const executable = await interpretScript(
|
||||||
const result = await callAsyncFunction(
|
language,
|
||||||
{
|
{
|
||||||
require: wrapRequire,
|
require: wrapRequire,
|
||||||
__original_require__: __non_webpack_require__,
|
__original_require__: __non_webpack_require__,
|
||||||
|
|
@ -72,6 +82,9 @@ async function main(): Promise<void> {
|
||||||
script
|
script
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors.
|
||||||
|
const result = await executable()
|
||||||
|
|
||||||
let encoding = core.getInput('result-encoding')
|
let encoding = core.getInput('result-encoding')
|
||||||
encoding = encoding ? encoding : 'json'
|
encoding = encoding ? encoding : 'json'
|
||||||
|
|
||||||
|
|
|
||||||
3
test/importable.ts
Normal file
3
test/importable.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function test() {
|
||||||
|
return 'hello'
|
||||||
|
}
|
||||||
3
test/requireable.js
Normal file
3
test/requireable.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
exports.test = function test() {
|
||||||
|
return 'hello'
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue