Compare commits

...

54 Commits

Author SHA1 Message Date
Sean Goedecke d645f067d8 Merge pull request #27 from mattleibow/dev/system-prompt-file
feat: Add system-prompt-file input for file-based system prompts
2025-05-27 09:48:56 +10:00
Matthew Leibowitz 9c57490bf1 regen 2025-05-27 01:40:03 +02:00
Matthew Leibowitz aa31275bdc Merge remote-tracking branch 'upstream/main' into dev/system-prompt-file 2025-05-27 01:33:37 +02:00
Sean Goedecke cacab0de8c Merge pull request #28 from actions/dependabot/npm_and_yarn/github/local-action-3.2.1
Bump @github/local-action from 2.2.1 to 3.2.1
2025-05-27 07:40:55 +10:00
dependabot[bot] 8562e77a99 Bump @github/local-action from 2.2.1 to 3.2.1
Bumps [@github/local-action](https://github.com/github/local-action) from 2.2.1 to 3.2.1.
- [Release notes](https://github.com/github/local-action/releases)
- [Changelog](https://github.com/github/local-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/local-action/compare/v2.2.1...v3.2.1)

---
updated-dependencies:
- dependency-name: "@github/local-action"
  dependency-version: 3.2.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 02:24:05 +00:00
Matthew Leibowitz 9aac9c75b3 Formatting 2025-05-26 04:03:09 +02:00
Matthew Leibowitz eb37c9a493 Formatting 2025-05-26 03:46:39 +02:00
dependabot[bot] 7ee5d2347b Merge pull request #24 from actions/dependabot/github_actions/actions-minor-88e1b89f1e 2025-05-26 00:50:33 +00:00
dependabot[bot] c9a9379c71 Bump super-linter/super-linter in the actions-minor group
Bumps the actions-minor group with 1 update: [super-linter/super-linter](https://github.com/super-linter/super-linter).


Updates `super-linter/super-linter` from 7.3.0 to 7.4.0
- [Release notes](https://github.com/super-linter/super-linter/releases)
- [Changelog](https://github.com/super-linter/super-linter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/super-linter/super-linter/compare/4e8a7c2bf106c4c766c816b35ec612638dc9b6b2...12150456a73e248bdc94d0794898f94e23127c88)

---
updated-dependencies:
- dependency-name: super-linter/super-linter
  dependency-version: 7.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-26 00:48:34 +00:00
Marais Rossouw ad31e754e3 Merge pull request #25 from actions/mr/bump-versions
Bumps all package versions, and re-builds
2025-05-26 10:46:40 +10:00
Marais Rossouw c4ce17bc84 chore: updates licenses 2025-05-26 00:35:07 +00:00
Marais Rossouw c0259b3c7d chore: re-builds bundle 2025-05-26 00:34:45 +00:00
Marais Rossouw 989a68a941 chore: bumps all package versions 2025-05-26 00:34:16 +00:00
Matthew Leibowitz 3e924fe06b Refactor the prompt reading logic 2025-05-25 19:08:36 +02:00
Matthew Leibowitz 96e0fda3bb Improve the tests a bit
Removed the duplicate code and just use helper functions.
2025-05-24 04:07:29 +02:00
Matthew Leibowitz 91ba53d8b4 feat: Add system-prompt-file input for file-based system prompts
This enhancement adds the ability to load a system prompt from a file, similar to
the existing prompt-file functionality, providing more flexibility when working with
complex system prompts.

Key changes:
- Added new `system-prompt-file` input to action.yml with proper description
- Updated main.ts implementation to handle file-based system prompts with:
  - File existence checking and appropriate error handling
  - Proper precedence (system-prompt-file takes priority over system-prompt)
  - Consistent error messages with existing prompt-file implementation

Test coverage added:
- Basic usage: Test verifies system-prompt-file loads content correctly
- Error handling: Test ensures proper errors when system-prompt-file doesn't exist
- Precedence: Test confirms system-prompt-file overrides system-prompt when both exist
- Integration: Test validates both prompt-file and system-prompt-file work together
- Max tokens: Test verifies custom token limits are properly passed to the model
- Testing infrastructure: Improved mock implementations that detect unexpected calls

Documentation:
- Updated README.md with system-prompt-file in inputs table
- Added dedicated usage example for system-prompt-file
- Fixed formatting in inputs table

CI/CD:
- Updated workflow to test system-prompt-file functionality with actual file content

This feature maintains backward compatibility while adding a useful option
for workflows that need to use more complex system prompts stored in files.
2025-05-24 03:50:15 +02:00
dependabot[bot] f8ee4c952b Merge pull request #16 from actions/dependabot/npm_and_yarn/npm-development-43ab01df74 2025-04-23 08:27:57 +00:00
dependabot[bot] d0b41e9e29 Bump the npm-development group across 1 directory with 14 updates
Bumps the npm-development group with 14 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@azure/core-sse](https://github.com/Azure/azure-sdk-for-js) | `2.1.3` | `2.2.0` |
| [@eslint/compat](https://github.com/eslint/rewrite) | `1.2.7` | `1.2.8` |
| [@github/local-action](https://github.com/github/local-action) | `3.1.4` | `3.2.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.17.28` | `20.17.30` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.28.0` | `8.30.1` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.28.0` | `8.30.1` |
| [eslint](https://github.com/eslint/eslint) | `9.23.0` | `9.24.0` |
| [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) | `10.1.1` | `10.1.2` |
| [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) | `4.3.1` | `4.3.2` |
| [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) | `5.2.5` | `5.2.6` |
| [prettier-eslint](https://github.com/prettier/prettier-eslint) | `16.3.0` | `16.3.2` |
| [rollup](https://github.com/rollup/rollup) | `4.38.0` | `4.40.0` |
| [ts-jest](https://github.com/kulshekhar/ts-jest) | `29.3.0` | `29.3.2` |
| [typescript](https://github.com/microsoft/TypeScript) | `5.8.2` | `5.8.3` |



Updates `@azure/core-sse` from 2.1.3 to 2.2.0
- [Release notes](https://github.com/Azure/azure-sdk-for-js/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-js/blob/main/documentation/Changelog-for-next-generation.md)
- [Commits](https://github.com/Azure/azure-sdk-for-js/compare/@azure/core-sse_2.1.3...@azure/core-sse_2.2.0)

Updates `@eslint/compat` from 1.2.7 to 1.2.8
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/release-please-config.json)
- [Commits](https://github.com/eslint/rewrite/compare/compat-v1.2.7...compat-v1.2.8)

Updates `@github/local-action` from 3.1.4 to 3.2.0
- [Release notes](https://github.com/github/local-action/releases)
- [Changelog](https://github.com/github/local-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/local-action/compare/v3.1.4...v3.2)

Updates `@types/node` from 20.17.28 to 20.17.30
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@typescript-eslint/eslint-plugin` from 8.28.0 to 8.30.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.30.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.28.0 to 8.30.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.30.1/packages/parser)

Updates `eslint` from 9.23.0 to 9.24.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.23.0...v9.24.0)

Updates `eslint-config-prettier` from 10.1.1 to 10.1.2
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v10.1.1...v10.1.2)

Updates `eslint-import-resolver-typescript` from 4.3.1 to 4.3.2
- [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases)
- [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v4.3.1...v4.3.2)

Updates `eslint-plugin-prettier` from 5.2.5 to 5.2.6
- [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.2.5...v5.2.6)

Updates `prettier-eslint` from 16.3.0 to 16.3.2
- [Release notes](https://github.com/prettier/prettier-eslint/releases)
- [Changelog](https://github.com/prettier/prettier-eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier-eslint/compare/v16.3.0...v16.3.2)

Updates `rollup` from 4.38.0 to 4.40.0
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.38.0...v4.40.0)

Updates `ts-jest` from 29.3.0 to 29.3.2
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.3.0...v29.3.2)

Updates `typescript` from 5.8.2 to 5.8.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.2...v5.8.3)

---
updated-dependencies:
- dependency-name: "@azure/core-sse"
  dependency-version: 2.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@eslint/compat"
  dependency-version: 1.2.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: "@github/local-action"
  dependency-version: 3.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@types/node"
  dependency-version: 20.17.30
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.30.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.30.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: eslint
  dependency-version: 9.24.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: eslint-config-prettier
  dependency-version: 10.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: eslint-import-resolver-typescript
  dependency-version: 4.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: eslint-plugin-prettier
  dependency-version: 5.2.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: prettier-eslint
  dependency-version: 16.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: rollup
  dependency-version: 4.40.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-development
- dependency-name: ts-jest
  dependency-version: 29.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
- dependency-name: typescript
  dependency-version: 5.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-23 08:24:38 +00:00
dependabot[bot] 8eaf9b3bbc Merge pull request #13 from actions/dependabot/npm_and_yarn/rollup/rollup-linux-x64-gnu-4.40.0 2025-04-23 08:21:41 +00:00
Marais Rossouw d9d6269e33 Merge branch 'main' into dependabot/npm_and_yarn/rollup/rollup-linux-x64-gnu-4.40.0 2025-04-23 18:19:29 +10:00
Sean Goedecke d043c3eaa1 Merge pull request #10 from actions/joshmgross/fix-readme
Clarify description in README
2025-04-22 08:38:45 +10:00
Sean Goedecke 5b3308935f Merge branch 'main' into joshmgross/fix-readme 2025-04-22 08:36:33 +10:00
dependabot[bot] 2d37c90e93 Bump @rollup/rollup-linux-x64-gnu from 4.39.0 to 4.40.0
Bumps [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) from 4.39.0 to 4.40.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.39.0...v4.40.0)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-17 21:12:36 +00:00
Aiqiao Yan b72d8483c7 Merge pull request #15 from actions/aiqiaoy/read-prompt-from-file
read prompt from file and print output to file
2025-04-17 17:11:26 -04:00
Aiqiao Yan 2aea1d4fc8 add a ci test 2025-04-17 21:04:14 +00:00
Aiqiao Yan 1c557cdc25 react to feedback 2025-04-17 20:23:07 +00:00
Aiqiao Yan f1591cfa68 add test 2025-04-17 20:13:47 +00:00
Aiqiao Yan 43f6a3831f read prompt from file and print output to file 2025-04-17 17:41:35 +00:00
Josh Gross a2fd55fb87 Merge branch 'main' into joshmgross/fix-readme 2025-04-14 14:22:13 -04:00
Sean Goedecke c7105a4c1e Merge pull request #11 from actions/sgoedecke/tweaks
Update readme and update/pin dependencies
2025-04-11 07:55:13 +10:00
Sean Goedecke b75b177af3 update codeowners 2025-04-10 21:37:11 +00:00
Sean Goedecke a3d57cc6dc Linting 2025-04-10 21:33:06 +00:00
Sean Goedecke 9239ab5d65 version to v1 2025-04-10 21:27:55 +00:00
Sean Goedecke 5ac68dad30 bump local action package 2025-04-10 21:26:29 +00:00
Sean Goedecke 3f8bae9a6e Pin linter CI to SHA 2025-04-10 21:24:44 +00:00
Sean Goedecke 6baaaac50c Add license section to readme 2025-04-10 21:24:37 +00:00
Sean Goedecke e8a0bebb73 Merge pull request #9 from actions/sgoedecke/use-user-provided-endpoint
Use user-provided endpoint
2025-04-11 06:17:11 +10:00
Josh Gross 72f855ca83 Clarify description in README 2025-04-10 08:53:19 -04:00
Sean Goedecke ba8a6b0374 Add security and support docs 2025-04-10 03:02:10 +00:00
Sean Goedecke e670dd5178 Set user agent 2025-04-10 02:53:54 +00:00
Sean Goedecke 84fee7a0e6 Use user-provided endpoint 2025-04-08 20:22:47 +00:00
Sean Goedecke 83d9668e82 Merge pull request #8 from actions/sgoedecke/use-models-dot-github
Use org-owned endpoint
2025-04-08 20:05:22 +10:00
Sean Goedecke 2de3b6e872 Merge pull request #2 from actions/dependabot/npm_and_yarn/rollup/rollup-linux-x64-gnu-4.39.0
Bump @rollup/rollup-linux-x64-gnu from 4.38.0 to 4.39.0
2025-04-08 15:37:38 +10:00
Sean Goedecke 40b430b284 Merge pull request #5 from actions/dependabot/npm_and_yarn/babel/helpers-7.27.0
Bump @babel/helpers from 7.26.9 to 7.27.0
2025-04-08 15:37:16 +10:00
Sean Goedecke e5b826567d rebuild 2025-04-08 05:18:48 +00:00
Sean Goedecke 4738a2d212 use org-owned endpoint 2025-04-08 04:55:27 +00:00
Sean Goedecke 606ea175ce Merge pull request #7 from actions/sgoedecke/use-models-dot-github
Use models dot github as the base endpoint
2025-04-08 14:33:32 +10:00
Sean Goedecke afaedf6677 use models dot github as the base endpoint 2025-04-08 04:30:16 +00:00
Sean Goedecke 78d6b91d0b Lint README.md 2025-04-08 02:10:27 +00:00
Sean Goedecke 1a66a5968c Update README.md 2025-04-08 12:00:25 +10:00
Sean Goedecke efbbaa2667 Use correct Azure inference endpoint 2025-04-07 23:52:33 +00:00
Sean Goedecke a57b52eccd Merge pull request #6 from actions/sgoedecke/configurable-token
Make token configurable
2025-04-07 15:21:27 +10:00
dependabot[bot] 213eb60e28 Bump @babel/helpers from 7.26.9 to 7.27.0
Bumps [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers) from 7.26.9 to 7.27.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-helpers)

---
updated-dependencies:
- dependency-name: "@babel/helpers"
  dependency-version: 7.27.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 03:13:11 +00:00
dependabot[bot] 7ae29e4a7c Bump @rollup/rollup-linux-x64-gnu from 4.38.0 to 4.39.0
Bumps [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) from 4.38.0 to 4.39.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.38.0...v4.39.0)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-03 20:30:45 +00:00
16 changed files with 6849 additions and 3719 deletions
+30
View File
@@ -67,3 +67,33 @@ jobs:
- name: Print Output
id: output
run: echo "${{ steps.test-action.outputs.response }}"
test-action-prompt-file:
name: GitHub Actions Test with Prompt File
runs-on: ubuntu-latest
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- name: Create Prompt File
run: echo "hello" > prompt.txt
- name: Create System Prompt File
run:
echo "You are a helpful AI assistant for testing." > system-prompt.txt
- name: Test Local Action with Prompt File
id: test-action-prompt-file
uses: ./
with:
prompt-file: prompt.txt
system-prompt-file: system-prompt.txt
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Print Output
run: |
echo "Response saved to: ${{ steps.test-action-prompt-file.outputs.response-file }}"
cat "${{ steps.test-action-prompt-file.outputs.response-file }}"
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
- name: Lint Codebase
id: super-linter
uses: super-linter/super-linter/slim@v7
uses: super-linter/super-linter/slim@12150456a73e248bdc94d0794898f94e23127c88
env:
DEFAULT_BRANCH: main
FILTER_REGEX_EXCLUDE: dist/**/*
+1 -1
View File
@@ -1,6 +1,6 @@
---
name: undici
version: 5.28.5
version: 5.29.0
type: npm
summary: An HTTP/1.1 client, written from scratch for Node.js
homepage: https://undici.nodejs.org
+1 -1
View File
@@ -4,4 +4,4 @@
############################################################################
# Default owners, unless a later match takes precedence.
* @actions/actions-oss-maintainers
* @actions/models
+1 -1
View File
@@ -46,7 +46,7 @@ avoid having to include the `node_modules/` directory in the repository.
1. Make your change, add tests, and make sure the tests still pass:
`npm run test`
1. Make sure your code is correctly formatted: `npm run format`
1. Update `dist/index.js` using `npm run build`. This creates a single
1. Update `dist/index.js` using `npm run bundle`. This creates a single
JavaScript file that is used as an entrypoint for the action
1. Push to your fork and [submit a pull request][pr]
1. Pat yourself on the back and wait for your pull request to be reviewed and
+77 -15
View File
@@ -7,12 +7,65 @@
[![Coverage](./badges/coverage.svg)](./badges/coverage.svg)
Use AI models from [GitHub Models](https://github.com/marketplace/models) in
your actions.
your workflows.
## Usage
Create a workflow to use the AI inference action:
```yaml
name: 'AI inference'
on: workflow_dispatch
jobs:
inference:
permissions:
models: read
runs-on: ubuntu-latest
steps:
- name: Test Local Action
id: inference
uses: actions/ai-inference@v1
with:
prompt: 'Hello!'
- name: Print Output
id: output
run: echo "${{ steps.inference.outputs.response }}"
```
### Using a prompt file
You can also provide a prompt file instead of an inline prompt:
```yaml
steps:
- name: Run AI Inference with Prompt File
id: inference
uses: actions/ai-inference@v1
with:
prompt-file: './path/to/prompt.txt'
```
### Using a system prompt file
In addition to the regular prompt, you can provide a system prompt file instead
of an inline system prompt:
```yaml
steps:
- name: Run AI Inference with System Prompt File
id: inference
uses: actions/ai-inference@v1
with:
prompt: 'Hello!'
system-prompt-file: './path/to/system-prompt.txt'
```
### Read output from file instead of output
This can be useful when model response exceeds actions output limit
```yaml
steps:
- name: Test Local Action
@@ -21,9 +74,10 @@ steps:
with:
prompt: 'Hello!'
- name: Print Output
id: output
run: echo "${{ steps.test-action.outputs.response }}"
- name: Use Response File
run: |
echo "Response saved to: ${{ steps.inference.outputs.response-file }}"
cat "${{ steps.inference.outputs.response-file }}"
```
## Inputs
@@ -31,22 +85,25 @@ steps:
Various inputs are defined in [`action.yml`](action.yml) to let you configure
the action:
| Name | Description | Default |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `token` | Token to use for inference. Typically the GITHUB_TOKEN secret | `github.token` |
| `prompt` | The prompt to send to the model | N/A |
| `system-prompt` | The system prompt to send to the model | `""` |
| `model` | The model to use for inference. Must be available in the [GitHub Models](https://github.com/marketplace?type=models) catalog | `gpt-4o` |
| `endpoint` | The endpoint to use for inference. If you're running this as part of an org, you should probably use the org-specific Models endpoint | `https://models.github.ai/inference` |
| `max-tokens` | The max number of tokens to generate | 200 |
| Name | Description | Default |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `token` | Token to use for inference. Typically the GITHUB_TOKEN secret | `github.token` |
| `prompt` | The prompt to send to the model | N/A |
| `prompt-file` | Path to a file containing the prompt. If both `prompt` and `prompt-file` are provided, `prompt-file` takes precedence | `""` |
| `system-prompt` | The system prompt to send to the model | `"You are a helpful assistant"` |
| `system-prompt-file` | Path to a file containing the system prompt. If both `system-prompt` and `system-prompt-file` are provided, `system-prompt-file` takes precedence | `""` |
| `model` | The model to use for inference. Must be available in the [GitHub Models](https://github.com/marketplace?type=models) catalog | `gpt-4o` |
| `endpoint` | The endpoint to use for inference. If you're running this as part of an org, you should probably use the org-specific Models endpoint | `https://models.github.ai/inference` |
| `max-tokens` | The max number of tokens to generate | 200 |
## Outputs
The AI inference action provides the following outputs:
| Name | Description |
| ---------- | --------------------------- |
| `response` | The response from the model |
| Name | Description |
| --------------- | ----------------------------------------------------------------------- |
| `response` | The response from the model |
| `response-file` | The file path where the response is saved (useful for larger responses) |
## Required Permissions
@@ -88,6 +145,11 @@ following steps:
to create a new release in GitHub so users can easily reference the new tags
in their workflows.
## License
This project is licensed under the terms of the MIT open source license. Please
refer to [MIT](./LICENSE.txt) for the full terms.
## Contributions
Contributions are welcome! See the [Contributor's Guide](CONTRIBUTING.md).
+39
View File
@@ -0,0 +1,39 @@
# Security
GitHub takes the security of our software products and services seriously,
including all of the open source code repositories managed through our GitHub
organizations, such as [GitHub](https://github.com/GitHub).
Even though
[open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope)
and therefore not eligible for bounty rewards, we will ensure that your finding
gets passed along to the appropriate maintainers for remediation.
## Reporting Security Issues
If you believe you have found a security vulnerability in any GitHub-owned
repository, please report it to us through coordinated disclosure.
**Please do not report security vulnerabilities through public GitHub issues,
discussions, or pull requests.**
Instead, please send an email to opensource-security[@]github.com.
Please include as much of the information listed below as you can to help us
better understand and resolve the issue:
- The type of issue (e.g., buffer overflow, SQL injection, or cross-site
scripting)
- Full paths of source file(s) related to the manifestation of the issue
- The location of the affected source code (tag/branch/commit or direct URL)
- Any special configuration required to reproduce the issue
- Step-by-step instructions to reproduce the issue
- Proof-of-concept or exploit code (if possible)
- Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
## Policy
See
[GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms)
+17
View File
@@ -0,0 +1,17 @@
# Support
## How to file issues and get help
This project uses GitHub issues to track bugs and feature requests. Please
search the existing issues before filing new issues to avoid duplicates. For new
issues, file your bug or feature request as a new issue.
For help or questions about using this project, please file an issue.
This project is under active development and maintained by GitHub staff and the
community. We will do our best to respond to support, feature requests, and
community questions in a timely manner.
## GitHub Support Policy
Support for this project is limited to the resources listed above.
+328 -21
View File
@@ -7,7 +7,6 @@
*/
import { jest } from '@jest/globals'
import * as core from '../__fixtures__/core.js'
const mockPost = jest.fn().mockImplementation(() => ({
body: {
choices: [
@@ -29,6 +28,81 @@ jest.unstable_mockModule('@azure-rest/ai-inference', () => ({
isUnexpected: jest.fn(() => false)
}))
// Default to throwing errors to catch unexpected calls
const mockExistsSync = jest.fn().mockImplementation(() => {
throw new Error(
'Unexpected call to existsSync - test should override this implementation'
)
})
const mockReadFileSync = jest.fn().mockImplementation(() => {
throw new Error(
'Unexpected call to readFileSync - test should override this implementation'
)
})
/**
* Helper function to mock file system operations for one or more files
* @param fileContents - Object mapping file paths to their contents
* @param nonExistentFiles - Array of file paths that should be treated as non-existent
*/
function mockFileContent(
fileContents: Record<string, string> = {},
nonExistentFiles: string[] = []
): void {
// Mock existsSync to return true for files that exist, false for those that don't
mockExistsSync.mockImplementation((...args: unknown[]): boolean => {
const [path] = args as [string]
if (nonExistentFiles.includes(path)) {
return false
}
return path in fileContents || true
})
// Mock readFileSync to return the content for known files
mockReadFileSync.mockImplementation((...args: unknown[]): string => {
const [path, options] = args as [string, BufferEncoding]
if (options === 'utf-8' && path in fileContents) {
return fileContents[path]
}
throw new Error(`Unexpected file read: ${path}`)
})
}
/**
* Helper function to mock action inputs
* @param inputs - Object mapping input names to their values
*/
function mockInputs(inputs: Record<string, string> = {}): void {
// Default values that are applied unless overridden
const defaultInputs: Record<string, string> = {
token: 'fake-token'
}
// Combine defaults with user-provided inputs
const allInputs: Record<string, string> = { ...defaultInputs, ...inputs }
core.getInput.mockImplementation((name: string) => {
return allInputs[name] || ''
})
}
/**
* Helper function to verify common response assertions
*/
function verifyStandardResponse(): void {
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'response', 'Hello, user!')
expect(core.setOutput).toHaveBeenNthCalledWith(
2,
'response-file',
expect.stringContaining('modelResponse.txt')
)
}
jest.unstable_mockModule('fs', () => ({
existsSync: mockExistsSync,
readFileSync: mockReadFileSync
}))
jest.unstable_mockModule('@actions/core', () => core)
// The module being tested should be imported dynamically. This ensures that the
@@ -36,37 +110,270 @@ jest.unstable_mockModule('@actions/core', () => core)
const { run } = await import('../src/main.js')
describe('main.ts', () => {
// Reset all mocks before each test
beforeEach(() => {
// Set the action's inputs as return values from core.getInput().
core.getInput.mockImplementation((name) => {
if (name === 'prompt') return 'Hello, AI!'
if (name === 'system_prompt') return 'You are a test assistant.'
if (name === 'model_name') return 'gpt-4o'
return ''
})
})
afterEach(() => {
jest.resetAllMocks()
jest.clearAllMocks()
})
it('Sets the response output', async () => {
// Set the action's inputs as return values from core.getInput().
mockInputs({
prompt: 'Hello, AI!',
'system-prompt': 'You are a test assistant.'
})
await run()
expect(core.setOutput).toHaveBeenNthCalledWith(
1,
'response',
'Hello, user!'
)
expect(core.setOutput).toHaveBeenCalled()
verifyStandardResponse()
})
it('Sets a failed status', async () => {
// Clear the getInput mock and return an empty prompt
core.getInput.mockClear().mockReturnValueOnce('')
it('Sets a failed status when no prompt is set', async () => {
// Clear the getInput mock and simulate no prompt or prompt-file input
mockInputs({
prompt: '',
'prompt-file': ''
})
await run()
// Verify that the action was marked as failed.
expect(core.setFailed).toHaveBeenNthCalledWith(1, 'prompt is not set')
expect(core.setFailed).toHaveBeenNthCalledWith(
1,
'Neither prompt-file nor prompt was set'
)
})
it('uses prompt-file', async () => {
const promptFile = 'prompt.txt'
const promptContent = 'This is a prompt from a file'
// Set up mock to return specific content for the prompt file
mockFileContent({
[promptFile]: promptContent
})
// Set up input mocks
mockInputs({
'prompt-file': promptFile,
'system-prompt': 'You are a test assistant.'
})
await run()
expect(mockExistsSync).toHaveBeenCalledWith(promptFile)
expect(mockReadFileSync).toHaveBeenCalledWith(promptFile, 'utf-8')
verifyStandardResponse()
})
it('handles non-existent prompt-file with an error', async () => {
const promptFile = 'non-existent-prompt.txt'
// Mock the file not existing
mockFileContent({}, [promptFile])
// Set up input mocks
mockInputs({
'prompt-file': promptFile
})
await run()
// Verify that the error was correctly reported
expect(core.setFailed).toHaveBeenCalledWith(
`File for prompt-file was not found: ${promptFile}`
)
})
it('prefers prompt-file over prompt when both are provided', async () => {
const promptFile = 'prompt.txt'
const promptFileContent = 'This is a prompt from a file that should be used'
const promptString = 'This is a direct prompt that should be ignored'
// Set up mock to return specific content for the prompt file
mockFileContent({
[promptFile]: promptFileContent
})
// Set up input mocks
mockInputs({
prompt: promptString,
'prompt-file': promptFile,
'system-prompt': 'You are a test assistant.'
})
await run()
expect(mockExistsSync).toHaveBeenCalledWith(promptFile)
expect(mockReadFileSync).toHaveBeenCalledWith(promptFile, 'utf-8')
// Check that the post call was made with the prompt from the file, not the input parameter
expect(mockPost).toHaveBeenCalledWith({
body: {
messages: [
{
role: 'system',
content: expect.any(String)
},
{ role: 'user', content: promptFileContent } // Should use the file content, not the string input
],
max_tokens: expect.any(Number),
model: expect.any(String)
}
})
verifyStandardResponse()
})
it('uses system-prompt-file', async () => {
const systemPromptFile = 'system-prompt.txt'
const systemPromptContent =
'You are a specialized system assistant for testing'
// Set up mock to return specific content for the system prompt file
mockFileContent({
[systemPromptFile]: systemPromptContent
})
// Set up input mocks
mockInputs({
prompt: 'Hello, AI!',
'system-prompt-file': systemPromptFile
})
await run()
expect(mockExistsSync).toHaveBeenCalledWith(systemPromptFile)
expect(mockReadFileSync).toHaveBeenCalledWith(systemPromptFile, 'utf-8')
verifyStandardResponse()
})
it('handles non-existent system-prompt-file with an error', async () => {
const systemPromptFile = 'non-existent-system-prompt.txt'
// Mock the file not existing
mockFileContent({}, [systemPromptFile])
// Set up input mocks
mockInputs({
prompt: 'Hello, AI!',
'system-prompt-file': systemPromptFile
})
await run()
// Verify that the error was correctly reported
expect(core.setFailed).toHaveBeenCalledWith(
`File for system-prompt-file was not found: ${systemPromptFile}`
)
})
it('prefers system-prompt-file over system-prompt when both are provided', async () => {
const systemPromptFile = 'system-prompt.txt'
const systemPromptFileContent =
'You are a specialized system assistant from file'
const systemPromptString =
'You are a basic system assistant from input parameter'
// Set up mock to return specific content for the system prompt file
mockFileContent({
[systemPromptFile]: systemPromptFileContent
})
// Set up input mocks
mockInputs({
prompt: 'Hello, AI!',
'system-prompt-file': systemPromptFile,
'system-prompt': systemPromptString
})
await run()
expect(mockExistsSync).toHaveBeenCalledWith(systemPromptFile)
expect(mockReadFileSync).toHaveBeenCalledWith(systemPromptFile, 'utf-8')
// Check that the post call was made with the system prompt from the file, not the input parameter
expect(mockPost).toHaveBeenCalledWith({
body: {
messages: [
{
role: 'system',
content: systemPromptFileContent // Should use the file content, not the string input
},
{ role: 'user', content: 'Hello, AI!' }
],
max_tokens: expect.any(Number),
model: expect.any(String)
}
})
verifyStandardResponse()
})
it('uses both prompt-file and system-prompt-file together', async () => {
const promptFile = 'prompt.txt'
const promptContent = 'This is a prompt from a file'
const systemPromptFile = 'system-prompt.txt'
const systemPromptContent =
'You are a specialized system assistant from file'
// Set up mock to return specific content for both files
mockFileContent({
[promptFile]: promptContent,
[systemPromptFile]: systemPromptContent
})
// Set up input mocks
mockInputs({
'prompt-file': promptFile,
'system-prompt-file': systemPromptFile
})
await run()
expect(mockExistsSync).toHaveBeenCalledWith(promptFile)
expect(mockExistsSync).toHaveBeenCalledWith(systemPromptFile)
expect(mockReadFileSync).toHaveBeenCalledWith(promptFile, 'utf-8')
expect(mockReadFileSync).toHaveBeenCalledWith(systemPromptFile, 'utf-8')
// Check that the post call was made with both the prompt and system prompt from files
expect(mockPost).toHaveBeenCalledWith({
body: {
messages: [
{
role: 'system',
content: systemPromptContent
},
{ role: 'user', content: promptContent }
],
max_tokens: expect.any(Number),
model: expect.any(String)
}
})
verifyStandardResponse()
})
it('passes custom max-tokens parameter to the model', async () => {
const customMaxTokens = 500
mockInputs({
prompt: 'Hello, AI!',
'system-prompt': 'You are a test assistant.',
'max-tokens': customMaxTokens.toString()
})
await run()
// Check that the post call was made with the correct max_tokens parameter
expect(mockPost).toHaveBeenCalledWith({
body: {
messages: expect.any(Array),
max_tokens: customMaxTokens,
model: expect.any(String)
}
})
verifyStandardResponse()
})
})
+13 -3
View File
@@ -4,19 +4,23 @@ author: 'GitHub'
# Add your action's branding here. This will appear on the GitHub Marketplace.
branding:
icon: 'play-circle'
icon: 'message-square'
color: red
# Define your inputs here.
inputs:
prompt:
description: The prompt for the model
required: true
required: false
default: ''
prompt-file:
description: Path to a file containing the prompt
required: false
default: ''
model:
description: The model to use
required: false
default: 'gpt-4o'
default: 'openai/gpt-4o'
endpoint:
description: The endpoint to use
required: false
@@ -25,6 +29,10 @@ inputs:
description: The system prompt for the model
required: false
default: 'You are a helpful assistant'
system-prompt-file:
description: Path to a file containing the system prompt
required: false
default: ''
max-tokens:
description: The maximum number of tokens to generate
required: false
@@ -38,6 +46,8 @@ inputs:
outputs:
response:
description: The response from the model
response-file:
description: The file path where the response is saved
runs:
using: node20
+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20" role="img" aria-label="Coverage: 89.47%"><title>Coverage: 89.47%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">89.47%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">89.47%</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="116" height="20" role="img" aria-label="Coverage: 84.21%"><title>Coverage: 84.21%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="116" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="63" height="20" fill="#555"/><rect x="63" width="53" height="20" fill="#dfb317"/><rect width="116" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">Coverage</text><text x="325" y="140" transform="scale(.1)" fill="#fff" textLength="530">Coverage</text><text aria-hidden="true" x="885" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">84.21%</text><text x="885" y="140" transform="scale(.1)" fill="#fff" textLength="430">84.21%</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Generated Vendored
+2595 -1753
View File
File diff suppressed because it is too large Load Diff
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+3653 -1894
View File
File diff suppressed because it is too large Load Diff
+17 -17
View File
@@ -1,7 +1,7 @@
{
"name": "typescript-action",
"description": "GitHub Actions TypeScript template",
"version": "0.0.0",
"version": "1.0.0",
"author": "",
"type": "module",
"private": true,
@@ -32,7 +32,7 @@
"local-action": "npx @github/local-action . src/main.ts .env",
"package": "npx rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript",
"package:watch": "npm run package -- --watch",
"test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
"test": "npx cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
"all": "npm run format:write && npm run lint && npm run test && npm run coverage && npm run package"
},
"license": "MIT",
@@ -43,30 +43,30 @@
"@azure-rest/ai-inference": "latest",
"@azure/core-auth": "latest",
"@azure/core-sse": "latest",
"@eslint/compat": "^1.2.7",
"@github/local-action": "^3.1.3",
"@eslint/compat": "^1.2.9",
"@github/local-action": "^3.2.1",
"@jest/globals": "^29.7.0",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-typescript": "^12.1.1",
"@rollup/plugin-typescript": "^12.1.2",
"@types/jest": "^29.5.14",
"@types/node": "^20.17.28",
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"eslint": "^9.23.0",
"eslint-config-prettier": "^10.0.2",
"eslint-import-resolver-typescript": "^4.3.1",
"@types/node": "^22.15.21",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.5",
"eslint-import-resolver-typescript": "^4.4.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.11.0",
"eslint-plugin-prettier": "^5.2.5",
"eslint-plugin-prettier": "^5.4.0",
"jest": "^29.7.0",
"make-coverage-badge": "^1.2.0",
"prettier": "^3.5.3",
"prettier-eslint": "^16.3.0",
"rollup": "^4.38.0",
"ts-jest": "^29.3.0",
"prettier-eslint": "^16.4.2",
"rollup": "^4.41.1",
"ts-jest": "^29.3.4",
"ts-jest-resolver": "^2.0.1",
"typescript": "^5.8.2"
"typescript": "^5.8.3"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "*"
+74 -10
View File
@@ -1,6 +1,40 @@
import * as core from '@actions/core'
import ModelClient, { isUnexpected } from '@azure-rest/ai-inference'
import { AzureKeyCredential } from '@azure/core-auth'
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
const RESPONSE_FILE = 'modelResponse.txt'
/**
* Helper function to load content from a file or use fallback input
* @param filePathInput - Input name for the file path
* @param contentInput - Input name for the direct content
* @param defaultValue - Default value to use if neither file nor content is provided
* @returns The loaded content
*/
function loadContentFromFileOrInput(
filePathInput: string,
contentInput: string,
defaultValue?: string
): string {
const filePath = core.getInput(filePathInput)
const contentString = core.getInput(contentInput)
if (filePath !== undefined && filePath !== '') {
if (!fs.existsSync(filePath)) {
throw new Error(`File for ${filePathInput} was not found: ${filePath}`)
}
return fs.readFileSync(filePath, 'utf-8')
} else if (contentString !== undefined && contentString !== '') {
return contentString
} else if (defaultValue !== undefined) {
return defaultValue
} else {
throw new Error(`Neither ${filePathInput} nor ${contentInput} was set`)
}
}
/**
* The main function for the action.
@@ -9,12 +43,16 @@ import { AzureKeyCredential } from '@azure/core-auth'
*/
export async function run(): Promise<void> {
try {
const prompt: string = core.getInput('prompt')
if (prompt === undefined || prompt === '') {
throw new Error('prompt is not set')
}
// Load prompt content - required
const prompt = loadContentFromFileOrInput('prompt-file', 'prompt')
// Load system prompt with default value
const systemPrompt = loadContentFromFileOrInput(
'system-prompt-file',
'system-prompt',
'You are a helpful assistant'
)
const systemPrompt: string = core.getInput('system-prompt')
const modelName: string = core.getInput('model')
const maxTokens: number = parseInt(core.getInput('max-tokens'), 10)
@@ -22,9 +60,12 @@ export async function run(): Promise<void> {
if (token === undefined) {
throw new Error('GITHUB_TOKEN is not set')
}
const endpoint = core.getInput('endpoint')
const client = ModelClient(endpoint, new AzureKeyCredential(token))
const client = ModelClient(endpoint, new AzureKeyCredential(token), {
userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' }
})
const response = await client.path('/chat/completions').post({
body: {
@@ -35,15 +76,21 @@ export async function run(): Promise<void> {
},
{ role: 'user', content: prompt }
],
temperature: 1.0,
top_p: 1.0,
max_tokens: maxTokens,
model: modelName
}
})
if (isUnexpected(response)) {
throw response.body.error
if (response.body.error) {
throw response.body.error
}
throw new Error(
'An error occurred while fetching the response (' +
response.status +
'): ' +
response.body
)
}
const modelResponse: string | null =
@@ -51,8 +98,25 @@ export async function run(): Promise<void> {
// Set outputs for other workflow steps to use
core.setOutput('response', modelResponse || '')
// Save the response to a file in case the response overflow the output limit
const responseFilePath = path.join(tempDir(), RESPONSE_FILE)
core.setOutput('response-file', responseFilePath)
if (modelResponse && modelResponse !== '') {
fs.writeFileSync(responseFilePath, modelResponse, 'utf-8')
}
} catch (error) {
// Fail the workflow run if an error occurs
if (error instanceof Error) core.setFailed(error.message)
if (error instanceof Error) {
core.setFailed(error.message)
} else {
core.setFailed('An unexpected error occurred')
}
}
}
function tempDir(): string {
const tempDirectory = process.env['RUNNER_TEMP'] || os.tmpdir()
return tempDirectory
}