mirror of
https://github.com/actions/github-script.git
synced 2026-02-07 19:47:26 +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'
|
||||
|
||||
describe('callAsyncFunction', () => {
|
||||
describe(callAsyncFunction.name, () => {
|
||||
test('calls the function with its arguments', async () => {
|
||||
const result = await callAsyncFunction({foo: 'bar'} as any, 'return foo')
|
||||
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)
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import {getRetryOptions} from '../src/retry-options'
|
||||
|
||||
describe('getRequestOptions', () => {
|
||||
describe(getRetryOptions.name, () => {
|
||||
test('retries disabled if retries == 0', async () => {
|
||||
const [retryOptions, requestOptions] = getRetryOptions(
|
||||
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:
|
||||
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
|
||||
language:
|
||||
description: The language to interpret the script as. Pick from "cjs", "cts", "mts".
|
||||
default: "cjs"
|
||||
outputs:
|
||||
result:
|
||||
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/plugin-request-log": "^4.0.0",
|
||||
"@octokit/plugin-retry": "^6.0.1",
|
||||
"@types/node": "^20.9.0"
|
||||
"@types/node": "^20.9.0",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.5",
|
||||
|
|
@ -31,8 +32,7 @@
|
|||
"jest": "^29.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.0.3",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.2.2"
|
||||
"ts-jest": "^29.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0 <21.0.0"
|
||||
|
|
@ -7077,7 +7077,6 @@
|
|||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -12518,8 +12517,7 @@
|
|||
"typescript": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w=="
|
||||
},
|
||||
"unbox-primitive": {
|
||||
"version": "1.0.2",
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@
|
|||
"@octokit/core": "^5.0.1",
|
||||
"@octokit/plugin-request-log": "^4.0.0",
|
||||
"@octokit/plugin-retry": "^6.0.1",
|
||||
"@types/node": "^20.9.0"
|
||||
"@types/node": "^20.9.0",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.5",
|
||||
|
|
@ -61,7 +62,6 @@
|
|||
"jest": "^29.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.0.3",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.2.2"
|
||||
"ts-jest": "^29.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ export function callAsyncFunction<T>(
|
|||
args: AsyncFunctionArguments,
|
||||
source: string
|
||||
): Promise<T> {
|
||||
const fn = new AsyncFunction(...Object.keys(args), source)
|
||||
return fn(...Object.values(args))
|
||||
const commonJsArgs = {
|
||||
...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 {retry} from '@octokit/plugin-retry'
|
||||
import {RequestRequestOptions} from '@octokit/types'
|
||||
import {callAsyncFunction} from './async-function'
|
||||
import {SupportedLanguage, interpretScript} from './interpret-script'
|
||||
import {RetryOptions, getRetryOptions, parseNumberArray} from './retry-options'
|
||||
import {wrapRequire} from './wrap-require'
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ type Options = {
|
|||
previews?: string[]
|
||||
retry?: RetryOptions
|
||||
request?: RequestRequestOptions
|
||||
language?: string
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
|
|
@ -38,6 +39,15 @@ async function main(): Promise<void> {
|
|||
exemptStatusCodes,
|
||||
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 = {
|
||||
log: debug ? console : undefined,
|
||||
|
|
@ -56,8 +66,8 @@ async function main(): Promise<void> {
|
|||
const github = getOctokit(token, opts, retry, requestLog)
|
||||
const script = core.getInput('script', {required: true})
|
||||
|
||||
// Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors.
|
||||
const result = await callAsyncFunction(
|
||||
const executable = await interpretScript(
|
||||
language,
|
||||
{
|
||||
require: wrapRequire,
|
||||
__original_require__: __non_webpack_require__,
|
||||
|
|
@ -72,6 +82,9 @@ async function main(): Promise<void> {
|
|||
script
|
||||
)
|
||||
|
||||
// Using property/value shorthand on `require` (e.g. `{require}`) causes compilation errors.
|
||||
const result = await executable()
|
||||
|
||||
let encoding = core.getInput('result-encoding')
|
||||
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