Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose async-function argument type #402

Merged
merged 5 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,25 @@ jobs:
await printStuff()
```

### Use scripts with jsDoc support

If you want type support for your scripts, you could use the command below to install the
`github-script` type declaration.
```sh
$ npm i -D @types/github-script@github:actions/github-script
```

And then add the `jsDoc` declaration to your script like this:
```js
// @ts-check
/** @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
export default async ({ core, context }) => {
core.debug("Running something at the moment");
return context.actor;
};
```
For an alternative setup, please read (alternative-setup)[./docs/alternative-setup.md].

### Use env as input

You can set env vars to use them in your script:
Expand Down
154 changes: 154 additions & 0 deletions docs/alternative-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
## Alternative setup

### Example repository structure
In this example we're using the repo structure below, but you are free
to structure it how ever you like.
```
root # Your repository
├── .github
│ ├── ...
│ └── workflows
│ ├── ...
│ └── ci-workflow.yml
├── ...
├── actions
│ ├── action.yml (optional)
│ └── ci-test.mjs
├── ...
└── package.json
```

### 1. Install the github-script type
```sh
$ npm i -D @types/github-script@github:actions/github-script
```


### 2. Create `ci-test.mjs` file
```js
// @ts-check
/** @param {import('@types/github-script').AsyncFunctionArguments} AsyncFunctionArguments */
export default async ({ core, context }) => {
core.debug("Running something at the moment");
return context.actor;
};
```

### 3. Create `ci-workflow.yml` file
```yml
on: push

permissions:
pull-requests: read
contents: read

jobs:
example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16

- run: npm ci
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const { default: script } = await import('${{ github.workspace }}/actions/ci-test.mjs');
return await script({ github, context, core, exec, glob, io, fetch, __original_require__ });
```


## Cleaner setup (Optional)

Note that the `ci-workflow.yml` example above can be kind of tedious once you add more of them. So
to address this, one could instead use `composite` actions.
### The `action.yml` file
```yml
name: Typed GitHub Script
author: GitHub
description: Run simple scripts using the GitHub client
branding:
color: blue
icon: code
inputs:
script:
description: The path to script (e.g actions/ci-test.mjs)
required: true
github-token:
description: The GitHub token used to create an authenticated client
default: ${{ github.token }}
required: false
debug:
description: Whether to tell the GitHub client to log details of its requests. true or false. Default is to run in debug mode when the GitHub Actions step debug logging is turned on.
default: ${{ runner.debug == '1' }}
user-agent:
description: An optional user-agent string
default: actions/github-script
previews:
description: A comma-separated list of API previews to accept
result-encoding:
description: Either "string" or "json" (default "json")—how the result will be encoded
default: json
retries:
description: The number of times to retry a request
default: "0"
retry-exempt-status-codes:
description: A comma separated list of status codes that will NOT be retried e.g. "400,500". No effect unless `retries` is set
default: 400,401,403,404,422 # from https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14

outputs:
result:
description: The return value of the script, stringified with `JSON.stringify`
value: ${{ steps.github-script-result.outputs.result }}

runs:
using: "composite"
steps:
- uses: actions/github-script@v6
id: github-script-result
with:
github-token: ${{ inputs.github-token }}
result-encoding: ${{ inputs.result-encoding }}
debug: ${{ inputs.debug }}
user-agent: ${{ inputs.user-agent }}
previews: ${{ inputs.previews }}
retries: ${{ inputs.retries }}
retry-exempt-status-codes: ${{ inputs.retry-exempt-status-codes }}
script: |
const { default: script } = await import(process.env.GITHUB_ACTION_PATH + '/${{ inputs.script }}');
return await script({ github, context, core, exec, glob, io, fetch, __original_require__ });
```


### The `ci-workflow.yml` file
Note that we only need to specify the script name here because the path will be
resolved to the `uses: ./actions` path by `process.env.GITHUB_ACTION_PATH`.
i.e the same folder as we are executing the action from.
```yml
on: push

permissions:
pull-requests: read
contents: read

jobs:
example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16

- run: npm ci
- uses: ./actions
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: ci-test.mjs
```

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
"author": "GitHub",
"license": "MIT",
"main": "dist/index.js",
"types": "types/async-function.d.ts",
"private": true,
"scripts": {
"build": "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",
"format:check": "prettier --check src __test__",
"format:write": "prettier --write src __test__",
"lint": "eslint src __test__",
Expand Down
2 changes: 1 addition & 1 deletion src/async-function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import fetch from 'node-fetch'

const AsyncFunction = Object.getPrototypeOf(async () => null).constructor

type AsyncFunctionArguments = {
export declare type AsyncFunctionArguments = {
viktorlott marked this conversation as resolved.
Show resolved Hide resolved
context: Context
core: typeof core
github: InstanceType<typeof GitHub>
Expand Down
20 changes: 20 additions & 0 deletions types/async-function.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/// <reference types="node" />
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 fetch from 'node-fetch';
export declare type AsyncFunctionArguments = {
context: Context;
core: typeof core;
github: InstanceType<typeof GitHub>;
exec: typeof exec;
glob: typeof glob;
io: typeof io;
fetch: typeof fetch;
require: NodeRequire;
__original_require__: NodeRequire;
};
export declare function callAsyncFunction<T>(args: AsyncFunctionArguments, source: string): Promise<T>;