Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9cab86201 | |||
| fad1bf5141 | |||
| 3491e2eeea | |||
| 208fa83feb | |||
| 393feda10a | |||
| d972090333 | |||
| fbdf27470c | |||
| ff45a53422 | |||
| 15fef78171 | |||
| dd046652c3 | |||
| cf3d93512b | |||
| 3512925c1c | |||
| e76decaf8a | |||
| 8afb976445 | |||
| b05573d945 | |||
| 634dc61da2 | |||
| aad34ab0bc | |||
| fac664b5d0 | |||
| 74236358e6 | |||
| ac7b0e436e | |||
| 440a06ef56 | |||
| 547a77cf75 | |||
| a6966e3148 | |||
| 92488b8ab2 | |||
| f628f161c4 | |||
| ea2465fe63 | |||
| 770dc3a982 | |||
| 418978c2e0 | |||
| fc00528337 | |||
| bd9017e99f | |||
| de122731f3 |
@@ -1,43 +0,0 @@
|
||||
name: Publish NPM
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
required: true
|
||||
description: 'core, artifact, cache, exec, github, glob, io, tool-cache'
|
||||
version:
|
||||
required: true
|
||||
description: 'the version of the package to publish'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Setup repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: npm install
|
||||
run: npm install
|
||||
|
||||
- name: bootstrap
|
||||
run: npm run bootstrap
|
||||
|
||||
- name: build
|
||||
run: npm run build
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: echo inputs
|
||||
run: echo ${{ github.event.inputs.package }} ${{ github.event.inputs.version }}
|
||||
|
||||
publish:
|
||||
runs-on: macos-latest
|
||||
needs: test
|
||||
environment: npm-publish
|
||||
steps:
|
||||
- name: Testing
|
||||
run: echo 'this is where we publish'
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
name: Publish NPM
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package:
|
||||
required: true
|
||||
description: 'core, artifact, cache, exec, github, glob, io, tool-cache'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: setup repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: verify package exists
|
||||
run: ls packages/${{ github.event.inputs.package }}
|
||||
|
||||
- name: npm install
|
||||
run: npm install
|
||||
|
||||
- name: bootstrap
|
||||
run: npm run bootstrap
|
||||
|
||||
- name: build
|
||||
run: npm run build
|
||||
|
||||
- name: test
|
||||
run: npm run test
|
||||
|
||||
- name: pack
|
||||
run: npm pack
|
||||
working-directory: packages/${{ github.event.inputs.package }}
|
||||
|
||||
- name: upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.event.inputs.package }}
|
||||
path: packages/${{ github.event.inputs.package }}/*.tgz
|
||||
|
||||
publish:
|
||||
runs-on: macos-latest
|
||||
needs: test
|
||||
environment: npm-publish
|
||||
steps:
|
||||
|
||||
- name: download artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ github.event.inputs.package }}
|
||||
|
||||
- name: setup authentication
|
||||
run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.TOKEN }}
|
||||
|
||||
- name: publish
|
||||
run: npm publish *.tgz
|
||||
|
||||
- name: notify slack on failure
|
||||
if: failure()
|
||||
run: |
|
||||
curl -X POST -H 'Content-type: application/json' --data '{"text":":pb__failed: Failed to publish a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK }}
|
||||
|
||||
- name: notify slack on success
|
||||
if: success()
|
||||
run: |
|
||||
curl -X POST -H 'Content-type: application/json' --data '{"text":":dance: Successfully published a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK }}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
name: "UpdateOctokit"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 18 * * 0' # sunday at 18 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
UpdateOctokit:
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
|
||||
/packages/artifact/ @actions/actions-service
|
||||
/packages/cache/ @actions/actions-service
|
||||
/packages/tool-cache/ @actions/spark
|
||||
+11
-4
@@ -57,7 +57,7 @@ For example, if you mask the letter `l`, running `echo "Hello FOO BAR World"` wi
|
||||
|
||||
### Group and Ungroup Log Lines
|
||||
|
||||
Emitting a group with a title will instruct the logs to create a collapsable region up to the next ungroup command.
|
||||
Emitting a group with a title will instruct the logs to create a collapsible region up to the next endgroup command.
|
||||
|
||||
```bash
|
||||
echo "::group::my title"
|
||||
@@ -72,6 +72,7 @@ function endGroup(): void {}
|
||||
```
|
||||
|
||||
### Problem Matchers
|
||||
|
||||
Problems matchers can be used to scan a build's output to automatically surface lines to the user that matches the provided pattern. A file path to a .json Problem Matcher must be provided. See [Problem Matchers](problem-matchers.md) for more information on how to define a Problem Matcher.
|
||||
|
||||
```bash
|
||||
@@ -81,6 +82,7 @@ echo "::remove-matcher owner=eslint-compact::"
|
||||
|
||||
`add-matcher` takes a path to a Problem Matcher file
|
||||
`remove-matcher` removes a Problem Matcher by owner
|
||||
|
||||
### Save State
|
||||
|
||||
Save a state to an environmental variable that can later be used in the main or post action.
|
||||
@@ -102,6 +104,7 @@ There are several commands to emit different levels of log output:
|
||||
| error | `echo "::error::My error message"` |
|
||||
|
||||
### Command Echoing
|
||||
|
||||
By default, the echoing of commands to stdout only occurs if [Step Debugging is enabled](./action-debugging.md#How-to-Access-Step-Debug-Logs)
|
||||
|
||||
You can enable or disable this for the current step by using the `echo` command.
|
||||
@@ -127,12 +130,12 @@ The `add-mask`, `debug`, `warning` and `error` commands do not support echoing.
|
||||
### Command Prompt
|
||||
|
||||
CMD processes the `"` character differently from other shells when echoing. In CMD, the above snippets should have the `"` characters removed in order to correctly process. For example, the set output command would be:
|
||||
|
||||
```cmd
|
||||
echo ::set-output name=FOO::BAR
|
||||
```
|
||||
|
||||
|
||||
# Environment files
|
||||
## Environment files
|
||||
|
||||
During the execution of a workflow, the runner generates temporary files that can be used to perform certain actions. The path to these files are exposed via environment variables. You will need to use the `utf-8` encoding when writing to these files to ensure proper processing of the commands. Multiple commands can be written to the same file, separated by newlines.
|
||||
|
||||
@@ -146,7 +149,8 @@ echo "FOO=BAR" >> $GITHUB_ENV
|
||||
|
||||
Running `$FOO` in a future step will now return `BAR`
|
||||
|
||||
For multiline strings, you may use a heredoc style syntax with your choice of delimeter. In the below example, we use `EOF`
|
||||
For multiline strings, you may use a heredoc style syntax with your choice of delimeter. In the below example, we use `EOF`.
|
||||
|
||||
```
|
||||
steps:
|
||||
- name: Set the value
|
||||
@@ -160,6 +164,7 @@ steps:
|
||||
This would set the value of the `JSON_RESPONSE` env variable to the value of the curl response.
|
||||
|
||||
The expected syntax for the heredoc style is:
|
||||
|
||||
```
|
||||
{VARIABLE_NAME}<<{DELIMETER}
|
||||
{VARIABLE_VALUE}
|
||||
@@ -183,6 +188,7 @@ echo "/Users/test/.nvm/versions/node/v12.18.3/bin" >> $GITHUB_PATH
|
||||
Running `$PATH` in a future step will now return `/Users/test/.nvm/versions/node/v12.18.3/bin:{Previous Path}`;
|
||||
|
||||
This is wrapped by the core addPath method:
|
||||
|
||||
```javascript
|
||||
export function addPath(inputPath: string): void {}
|
||||
```
|
||||
@@ -190,6 +196,7 @@ export function addPath(inputPath: string): void {}
|
||||
### Powershell
|
||||
|
||||
Powershell does not use UTF8 by default. You will want to make sure you write in the correct encoding. For example, to set the path:
|
||||
|
||||
```
|
||||
steps:
|
||||
- run: echo "mypath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. Both [GitHub Annotations](https://developer.github.com/v3/checks/runs/#annotations-object-1) and log file decorations are created when a match is detected.
|
||||
|
||||
## Limitations
|
||||
|
||||
Currently, GitHub Actions limit the annotation count in a workflow run.
|
||||
|
||||
- 10 warning annotations and 10 error annotations per step
|
||||
- 50 annotations per job (sum of annotations from all the steps)
|
||||
- 50 annotations per run (separate from the job annotations, these annotations aren’t created by users)
|
||||
|
||||
If your workflow may exceed these annotation counts, consider filtering of the log messages which the Problem Matcher is exposed to (e.g. by PR touched files, lines, or other).
|
||||
|
||||
## Single Line Matchers
|
||||
|
||||
Let's consider the ESLint compact output:
|
||||
@@ -100,6 +110,16 @@ The eslint-stylish problem matcher defined below catches that output, and create
|
||||
The first pattern matches the `test.js` line and records the file information. This line is not decorated in the UI.
|
||||
The second pattern loops through the remaining lines with `loop: true` until it fails to find a match, and surfaces these lines prominently in the UI.
|
||||
|
||||
Note that the pattern matches must be on consecutive lines. The following would not result in any match findings.
|
||||
|
||||
```
|
||||
test.js
|
||||
extraneous log line of no interest
|
||||
1:0 error Missing "use strict" statement strict
|
||||
5:10 error 'addOne' is defined but never used no-unused-vars
|
||||
✖ 2 problems (2 errors, 0 warnings)
|
||||
```
|
||||
|
||||
## Adding and Removing Problem Matchers
|
||||
|
||||
Problem Matchers are enabled and removed via the toolkit [commands](commands.md#problem-matchers).
|
||||
|
||||
Generated
+9
-9
@@ -18684,9 +18684,9 @@
|
||||
}
|
||||
},
|
||||
"ssri": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
|
||||
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
|
||||
"integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"figgy-pudding": "^3.5.1"
|
||||
@@ -19336,9 +19336,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
|
||||
"integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
|
||||
"version": "3.9.9",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz",
|
||||
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
@@ -19859,9 +19859,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
|
||||
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
|
||||
+1
-1
@@ -27,6 +27,6 @@
|
||||
"lerna": "^3.18.4",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-jest": "^25.4.0",
|
||||
"typescript": "^3.7.4"
|
||||
"typescript": "^3.9.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,3 +54,7 @@
|
||||
|
||||
- Improved retry-ability for all http calls during artifact upload and download if an error is encountered
|
||||
|
||||
### 0.5.1
|
||||
|
||||
- Bump @actions/http-client to version 1.0.11 to fix proxy related issues during artifact upload and download
|
||||
|
||||
|
||||
Generated
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/artifact",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -10,9 +10,9 @@
|
||||
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
|
||||
},
|
||||
"@actions/http-client": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
|
||||
"integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
|
||||
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
|
||||
"requires": {
|
||||
"tunnel": "0.0.6"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/artifact",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.1",
|
||||
"preview": true,
|
||||
"description": "Actions artifact lib",
|
||||
"keywords": [
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/http-client": "^1.0.7",
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@types/tmp": "^0.1.0",
|
||||
"tmp": "^0.1.0",
|
||||
"tmp-promise": "^2.0.2"
|
||||
|
||||
Vendored
+14
-14
@@ -10,6 +10,20 @@ Note that GitHub will remove any cache entries that have not been accessed in ov
|
||||
|
||||
This package is used by the v2+ versions of our first party cache action. You can find an example implementation in the cache repo [here](https://github.com/actions/cache).
|
||||
|
||||
#### Save Cache
|
||||
|
||||
Saves a cache containing the files in `paths` using the `key` provided. The files would be compressed using zstandard compression algorithm if zstd is installed, otherwise gzip is used. Function returns the cache id if the cache was saved succesfully and throws an error if cache upload fails.
|
||||
|
||||
```js
|
||||
const cache = require('@actions/cache');
|
||||
const paths = [
|
||||
'node_modules',
|
||||
'packages/*/node_modules/'
|
||||
]
|
||||
const key = 'npm-foobar-d5ea0750'
|
||||
const cacheId = await cache.saveCache(paths, key)
|
||||
```
|
||||
|
||||
#### Restore Cache
|
||||
|
||||
Restores a cache based on `key` and `restoreKeys` to the `paths` provided. Function returns the cache key for cache hit and returns undefined if cache not found.
|
||||
@@ -28,17 +42,3 @@ const restoreKeys = [
|
||||
const cacheKey = await cache.restoreCache(paths, key, restoreKeys)
|
||||
```
|
||||
|
||||
#### Save Cache
|
||||
|
||||
Saves a cache containing the files in `paths` using the `key` provided. The files would be compressed using zstandard compression algorithm if zstd is installed, otherwise gzip is used. Function returns the cache id if the cache was saved succesfully and throws an error if cache upload fails.
|
||||
|
||||
```js
|
||||
const cache = require('@actions/cache');
|
||||
const paths = [
|
||||
'node_modules',
|
||||
'packages/*/node_modules/'
|
||||
]
|
||||
const key = 'npm-foobar-d5ea0750'
|
||||
const cacheId = await cache.saveCache(paths, key)
|
||||
```
|
||||
|
||||
|
||||
Vendored
+4
-1
@@ -36,4 +36,7 @@
|
||||
|
||||
### 1.0.6
|
||||
- Make caching more verbose [#650](https://github.com/actions/toolkit/pull/650)
|
||||
- Use GNU tar on macOS if available [#701](https://github.com/actions/toolkit/pull/701)
|
||||
- Use GNU tar on macOS if available [#701](https://github.com/actions/toolkit/pull/701)
|
||||
|
||||
### 1.0.7
|
||||
- Fixes permissions issue extracting archives with GNU tar on macOS ([issue](https://github.com/actions/cache/issues/527))
|
||||
|
||||
+2
-2
@@ -2,10 +2,10 @@ import {promises as fs} from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as cacheUtils from '../src/internal/cacheUtils'
|
||||
|
||||
test('getArchiveFileSizeIsBytes returns file size', () => {
|
||||
test('getArchiveFileSizeInBytes returns file size', () => {
|
||||
const filePath = path.join(__dirname, '__fixtures__', 'helloWorld.txt')
|
||||
|
||||
const size = cacheUtils.getArchiveFileSizeIsBytes(filePath)
|
||||
const size = cacheUtils.getArchiveFileSizeInBytes(filePath)
|
||||
|
||||
expect(size).toBe(11)
|
||||
})
|
||||
|
||||
+9
-9
@@ -123,8 +123,8 @@ test('restore with gzip compressed cache found', async () => {
|
||||
const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache')
|
||||
|
||||
const fileSize = 142
|
||||
const getArchiveFileSizeIsBytesMock = jest
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
|
||||
const getArchiveFileSizeInBytesMock = jest
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
|
||||
.mockReturnValue(fileSize)
|
||||
|
||||
const extractTarMock = jest.spyOn(tar, 'extractTar')
|
||||
@@ -147,7 +147,7 @@ test('restore with gzip compressed cache found', async () => {
|
||||
archivePath,
|
||||
undefined
|
||||
)
|
||||
expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath)
|
||||
expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath)
|
||||
|
||||
expect(extractTarMock).toHaveBeenCalledTimes(1)
|
||||
expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression)
|
||||
@@ -184,8 +184,8 @@ test('restore with zstd compressed cache found', async () => {
|
||||
const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache')
|
||||
|
||||
const fileSize = 62915000
|
||||
const getArchiveFileSizeIsBytesMock = jest
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
|
||||
const getArchiveFileSizeInBytesMock = jest
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
|
||||
.mockReturnValue(fileSize)
|
||||
|
||||
const extractTarMock = jest.spyOn(tar, 'extractTar')
|
||||
@@ -206,7 +206,7 @@ test('restore with zstd compressed cache found', async () => {
|
||||
archivePath,
|
||||
undefined
|
||||
)
|
||||
expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath)
|
||||
expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath)
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`)
|
||||
|
||||
expect(extractTarMock).toHaveBeenCalledTimes(1)
|
||||
@@ -241,8 +241,8 @@ test('restore with cache found for restore key', async () => {
|
||||
const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache')
|
||||
|
||||
const fileSize = 142
|
||||
const getArchiveFileSizeIsBytesMock = jest
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
|
||||
const getArchiveFileSizeInBytesMock = jest
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
|
||||
.mockReturnValue(fileSize)
|
||||
|
||||
const extractTarMock = jest.spyOn(tar, 'extractTar')
|
||||
@@ -263,7 +263,7 @@ test('restore with cache found for restore key', async () => {
|
||||
archivePath,
|
||||
undefined
|
||||
)
|
||||
expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath)
|
||||
expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath)
|
||||
expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`)
|
||||
|
||||
expect(extractTarMock).toHaveBeenCalledTimes(1)
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ test('save with large cache outputs should fail', async () => {
|
||||
|
||||
const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit
|
||||
jest
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
|
||||
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
|
||||
.mockReturnValueOnce(cacheSize)
|
||||
const compression = CompressionMethod.Gzip
|
||||
const getCompressionMock = jest
|
||||
|
||||
Vendored
+16
-7
@@ -11,6 +11,7 @@ jest.mock('@actions/exec')
|
||||
jest.mock('@actions/io')
|
||||
|
||||
const IS_WINDOWS = process.platform === 'win32'
|
||||
const IS_MAC = process.platform === 'darwin'
|
||||
|
||||
const defaultTarPath = process.platform === 'darwin' ? 'gtar' : 'tar'
|
||||
|
||||
@@ -55,7 +56,9 @@ test('zstd extract tar', async () => {
|
||||
'-P',
|
||||
'-C',
|
||||
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
|
||||
].concat(IS_WINDOWS ? ['--force-local'] : []),
|
||||
]
|
||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
||||
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||
{cwd: undefined}
|
||||
)
|
||||
})
|
||||
@@ -84,7 +87,7 @@ test('gzip extract tar', async () => {
|
||||
'-P',
|
||||
'-C',
|
||||
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
|
||||
],
|
||||
].concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||
{cwd: undefined}
|
||||
)
|
||||
})
|
||||
@@ -145,7 +148,9 @@ test('zstd create tar', async () => {
|
||||
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace,
|
||||
'--files-from',
|
||||
'manifest.txt'
|
||||
].concat(IS_WINDOWS ? ['--force-local'] : []),
|
||||
]
|
||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
||||
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||
{
|
||||
cwd: archiveFolder
|
||||
}
|
||||
@@ -180,7 +185,7 @@ test('gzip create tar', async () => {
|
||||
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace,
|
||||
'--files-from',
|
||||
'manifest.txt'
|
||||
],
|
||||
].concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||
{
|
||||
cwd: archiveFolder
|
||||
}
|
||||
@@ -205,7 +210,9 @@ test('zstd list tar', async () => {
|
||||
'-tf',
|
||||
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||
'-P'
|
||||
].concat(IS_WINDOWS ? ['--force-local'] : []),
|
||||
]
|
||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
||||
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||
{cwd: undefined}
|
||||
)
|
||||
})
|
||||
@@ -228,7 +235,9 @@ test('zstdWithoutLong list tar', async () => {
|
||||
'-tf',
|
||||
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||
'-P'
|
||||
].concat(IS_WINDOWS ? ['--force-local'] : []),
|
||||
]
|
||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
||||
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||
{cwd: undefined}
|
||||
)
|
||||
})
|
||||
@@ -252,7 +261,7 @@ test('gzip list tar', async () => {
|
||||
'-tf',
|
||||
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||
'-P'
|
||||
],
|
||||
].concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||
{cwd: undefined}
|
||||
)
|
||||
})
|
||||
|
||||
+1
-7
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/cache",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -4199,11 +4199,6 @@
|
||||
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "15.3.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
|
||||
@@ -4218,7 +4213,6 @@
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.1"
|
||||
}
|
||||
},
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/cache",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"preview": true,
|
||||
"description": "Actions cache lib",
|
||||
"keywords": [
|
||||
|
||||
Vendored
+2
-2
@@ -104,7 +104,7 @@ export async function restoreCache(
|
||||
await listTar(archivePath, compressionMethod)
|
||||
}
|
||||
|
||||
const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath)
|
||||
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath)
|
||||
core.info(
|
||||
`Cache Size: ~${Math.round(
|
||||
archiveFileSize / (1024 * 1024)
|
||||
@@ -172,7 +172,7 @@ export async function saveCache(
|
||||
}
|
||||
|
||||
const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit
|
||||
const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath)
|
||||
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath)
|
||||
core.debug(`File Size: ${archiveFileSize}`)
|
||||
if (archiveFileSize > fileSizeLimit) {
|
||||
throw new Error(
|
||||
|
||||
+2
-2
@@ -219,7 +219,7 @@ async function uploadFile(
|
||||
options?: UploadOptions
|
||||
): Promise<void> {
|
||||
// Upload Chunks
|
||||
const fileSize = fs.statSync(archivePath).size
|
||||
const fileSize = utils.getArchiveFileSizeInBytes(archivePath)
|
||||
const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`)
|
||||
const fd = fs.openSync(archivePath, 'r')
|
||||
const uploadOptions = getUploadOptions(options)
|
||||
@@ -300,7 +300,7 @@ export async function saveCache(
|
||||
|
||||
// Commit Cache
|
||||
core.debug('Commiting cache')
|
||||
const cacheSize = utils.getArchiveFileSizeIsBytes(archivePath)
|
||||
const cacheSize = utils.getArchiveFileSizeInBytes(archivePath)
|
||||
core.info(
|
||||
`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`
|
||||
)
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ export async function createTempDirectory(): Promise<string> {
|
||||
return dest
|
||||
}
|
||||
|
||||
export function getArchiveFileSizeIsBytes(filePath: string): number {
|
||||
export function getArchiveFileSizeInBytes(filePath: string): number {
|
||||
return fs.statSync(filePath).size
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ export async function downloadCacheHttpClient(
|
||||
|
||||
if (contentLengthHeader) {
|
||||
const expectedLength = parseInt(contentLengthHeader)
|
||||
const actualLength = utils.getArchiveFileSizeIsBytes(archivePath)
|
||||
const actualLength = utils.getArchiveFileSizeInBytes(archivePath)
|
||||
|
||||
if (actualLength !== expectedLength) {
|
||||
throw new Error(
|
||||
|
||||
Vendored
+2
@@ -26,6 +26,8 @@ async function getTarPath(
|
||||
case 'darwin': {
|
||||
const gnuTar = await io.which('gtar', false)
|
||||
if (gnuTar) {
|
||||
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
|
||||
args.push('--delay-directory-restore')
|
||||
return gnuTar
|
||||
}
|
||||
break
|
||||
|
||||
+12
-6
@@ -16,11 +16,13 @@ import * as core from '@actions/core';
|
||||
|
||||
#### Inputs/Outputs
|
||||
|
||||
Action inputs can be read with `getInput`. Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
|
||||
Action inputs can be read with `getInput` which returns a `string` or `getBooleanInput` which parses a boolean based on the [yaml 1.2 specification](https://yaml.org/spec/1.2/spec.html#id2804923). If `required` set to be false, the input should have a default value in `action.yml`.
|
||||
|
||||
Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
|
||||
|
||||
```js
|
||||
const myInput = core.getInput('inputName', { required: true });
|
||||
|
||||
const myBooleanInput = core.getBooleanInput('booleanInputName', { required: true });
|
||||
core.setOutput('outputKey', 'outputVal');
|
||||
```
|
||||
|
||||
@@ -62,11 +64,10 @@ catch (err) {
|
||||
// setFailed logs the message and sets a failing exit code
|
||||
core.setFailed(`Action failed with error ${err}`);
|
||||
}
|
||||
```
|
||||
|
||||
Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned.
|
||||
|
||||
```
|
||||
|
||||
#### Logging
|
||||
|
||||
Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs).
|
||||
@@ -118,6 +119,7 @@ const result = await core.group('Do something async', async () => {
|
||||
Colored output is supported in the Action logs via standard [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). 3/4 bit, 8 bit and 24 bit colors are all supported.
|
||||
|
||||
Foreground colors:
|
||||
|
||||
```js
|
||||
// 3/4 bit
|
||||
core.info('\u001b[35mThis foreground will be magenta')
|
||||
@@ -130,6 +132,7 @@ core.info('\u001b[38;2;255;0;0mThis foreground will be bright red')
|
||||
```
|
||||
|
||||
Background colors:
|
||||
|
||||
```js
|
||||
// 3/4 bit
|
||||
core.info('\u001b[43mThis background will be yellow');
|
||||
@@ -156,6 +159,7 @@ core.info('\u001b[31;46mRed foreground with a cyan background and \u001b[1mbold
|
||||
```
|
||||
|
||||
> Note: Escape codes reset at the start of each line
|
||||
|
||||
```js
|
||||
core.info('\u001b[35mThis foreground will be magenta')
|
||||
core.info('This foreground will reset to the default')
|
||||
@@ -170,9 +174,10 @@ core.info(style.color.ansi16m.hex('#abcdef') + 'Hello world!')
|
||||
|
||||
#### Action state
|
||||
|
||||
You can use this library to save state and get state for sharing information between a given wrapper action:
|
||||
You can use this library to save state and get state for sharing information between a given wrapper action:
|
||||
|
||||
**action.yml**:
|
||||
|
||||
**action.yml**
|
||||
```yaml
|
||||
name: 'Wrapper action sample'
|
||||
inputs:
|
||||
@@ -193,6 +198,7 @@ core.saveState("pidToKill", 12345);
|
||||
```
|
||||
|
||||
In action's `cleanup.js`:
|
||||
|
||||
```js
|
||||
const core = require('@actions/core');
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# @actions/core Releases
|
||||
|
||||
### 1.2.7
|
||||
- [Prepend newline for set-output](https://github.com/actions/toolkit/pull/772)
|
||||
|
||||
### 1.2.6
|
||||
- [Update `exportVariable` and `addPath` to use environment files](https://github.com/actions/toolkit/pull/571)
|
||||
|
||||
|
||||
@@ -19,6 +19,14 @@ const testEnvVars = {
|
||||
INPUT_MISSING: '',
|
||||
'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ response ',
|
||||
INPUT_MULTIPLE_SPACES_VARIABLE: 'I have multiple spaces',
|
||||
INPUT_BOOLEAN_INPUT: 'true',
|
||||
INPUT_BOOLEAN_INPUT_TRUE1: 'true',
|
||||
INPUT_BOOLEAN_INPUT_TRUE2: 'True',
|
||||
INPUT_BOOLEAN_INPUT_TRUE3: 'TRUE',
|
||||
INPUT_BOOLEAN_INPUT_FALSE1: 'false',
|
||||
INPUT_BOOLEAN_INPUT_FALSE2: 'False',
|
||||
INPUT_BOOLEAN_INPUT_FALSE3: 'FALSE',
|
||||
INPUT_WRONG_BOOLEAN_INPUT: 'wrong',
|
||||
|
||||
// Save inputs
|
||||
STATE_TEST_1: 'state_val',
|
||||
@@ -157,19 +165,46 @@ describe('@actions/core', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('getInput gets non-required boolean input', () => {
|
||||
expect(core.getBooleanInput('boolean input')).toBe(true)
|
||||
})
|
||||
|
||||
it('getInput gets required input', () => {
|
||||
expect(core.getBooleanInput('boolean input', {required: true})).toBe(true)
|
||||
})
|
||||
|
||||
it('getBooleanInput handles boolean input', () => {
|
||||
expect(core.getBooleanInput('boolean input true1')).toBe(true)
|
||||
expect(core.getBooleanInput('boolean input true2')).toBe(true)
|
||||
expect(core.getBooleanInput('boolean input true3')).toBe(true)
|
||||
expect(core.getBooleanInput('boolean input false1')).toBe(false)
|
||||
expect(core.getBooleanInput('boolean input false2')).toBe(false)
|
||||
expect(core.getBooleanInput('boolean input false3')).toBe(false)
|
||||
})
|
||||
|
||||
it('getBooleanInput handles wrong boolean input', () => {
|
||||
expect(() => core.getBooleanInput('wrong boolean input')).toThrow(
|
||||
'Input does not meet YAML 1.2 "Core Schema" specification: wrong boolean input\n' +
|
||||
`Support boolean input list: \`true | True | TRUE | false | False | FALSE\``
|
||||
)
|
||||
})
|
||||
|
||||
it('setOutput produces the correct command', () => {
|
||||
core.setOutput('some output', 'some value')
|
||||
assertWriteCalls([`::set-output name=some output::some value${os.EOL}`])
|
||||
assertWriteCalls([
|
||||
os.EOL,
|
||||
`::set-output name=some output::some value${os.EOL}`
|
||||
])
|
||||
})
|
||||
|
||||
it('setOutput handles bools', () => {
|
||||
core.setOutput('some output', false)
|
||||
assertWriteCalls([`::set-output name=some output::false${os.EOL}`])
|
||||
assertWriteCalls([os.EOL, `::set-output name=some output::false${os.EOL}`])
|
||||
})
|
||||
|
||||
it('setOutput handles numbers', () => {
|
||||
core.setOutput('some output', 1.01)
|
||||
assertWriteCalls([`::set-output name=some output::1.01${os.EOL}`])
|
||||
assertWriteCalls([os.EOL, `::set-output name=some output::1.01${os.EOL}`])
|
||||
})
|
||||
|
||||
it('setFailed sets the correct exit code and failure message', () => {
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/core",
|
||||
"version": "1.2.6",
|
||||
"version": "1.2.7",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/core",
|
||||
"version": "1.2.6",
|
||||
"version": "1.2.7",
|
||||
"description": "Actions core lib",
|
||||
"keywords": [
|
||||
"github",
|
||||
|
||||
@@ -91,6 +91,28 @@ export function getInput(name: string, options?: InputOptions): string {
|
||||
return val.trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input value of the boolean type in the YAML 1.2 "core schema" specification.
|
||||
* Support boolean input list: `true | True | TRUE | false | False | FALSE` .
|
||||
* The return value is also in boolean type.
|
||||
* ref: https://yaml.org/spec/1.2/spec.html#id2804923
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns boolean
|
||||
*/
|
||||
export function getBooleanInput(name: string, options?: InputOptions): boolean {
|
||||
const trueValue = ['true', 'True', 'TRUE']
|
||||
const falseValue = ['false', 'False', 'FALSE']
|
||||
const val = getInput(name, options)
|
||||
if (trueValue.includes(val)) return true
|
||||
if (falseValue.includes(val)) return false
|
||||
throw new TypeError(
|
||||
`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` +
|
||||
`Support boolean input list: \`true | True | TRUE | false | False | FALSE\``
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of an output.
|
||||
*
|
||||
@@ -99,6 +121,7 @@ export function getInput(name: string, options?: InputOptions): string {
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function setOutput(name: string, value: any): void {
|
||||
process.stdout.write(os.EOL)
|
||||
issueCommand('set-output', {name}, value)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as exec from '../src/exec'
|
||||
import * as tr from '../src/toolrunner'
|
||||
import * as im from '../src/interfaces'
|
||||
|
||||
import * as childProcess from 'child_process'
|
||||
@@ -620,6 +621,14 @@ describe('@actions/exec', () => {
|
||||
expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`)
|
||||
})
|
||||
|
||||
it('tool runner strips INPUT_ params from environment for child process', () => {
|
||||
const env = {INPUT_TEST: 'input value', SOME_OTHER_ENV: 'some other value'}
|
||||
const sanitizedEnv = tr.stripInputEnvironmentVariables(env)
|
||||
|
||||
expect(sanitizedEnv).not.toHaveProperty('INPUT_TEST')
|
||||
expect(sanitizedEnv).toHaveProperty('SOME_OTHER_ENV')
|
||||
})
|
||||
|
||||
if (IS_WINDOWS) {
|
||||
it('Exec roots relative tool path using process.cwd (Windows path separator)', async () => {
|
||||
let exitCode: number
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface ExecOptions {
|
||||
/** optional working directory. defaults to current */
|
||||
cwd?: string
|
||||
|
||||
/** optional envvar dictionary. defaults to current process's env */
|
||||
/** optional envvar dictionary. defaults to current process's env with `INPUT_*` variables removed */
|
||||
env?: {[key: string]: string}
|
||||
|
||||
/** optional. defaults to false */
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as stream from 'stream'
|
||||
import * as im from './interfaces'
|
||||
import * as io from '@actions/io'
|
||||
import * as ioUtil from '@actions/io/lib/io-util'
|
||||
import {setTimeout} from 'timers'
|
||||
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
|
||||
@@ -376,7 +377,7 @@ export class ToolRunner extends events.EventEmitter {
|
||||
options = options || <im.ExecOptions>{}
|
||||
const result = <child.SpawnOptions>{}
|
||||
result.cwd = options.cwd
|
||||
result.env = options.env
|
||||
result.env = options.env || stripInputEnvironmentVariables(process.env)
|
||||
result['windowsVerbatimArguments'] =
|
||||
options.windowsVerbatimArguments || this._isCmdFile()
|
||||
if (options.windowsVerbatimArguments) {
|
||||
@@ -599,6 +600,20 @@ export function argStringToArray(argString: string): string[] {
|
||||
return args
|
||||
}
|
||||
|
||||
// Strips INPUT_ environment variables to prevent them leaking to child processes
|
||||
export function stripInputEnvironmentVariables(
|
||||
env: NodeJS.ProcessEnv
|
||||
): NodeJS.ProcessEnv {
|
||||
return Object.entries(env)
|
||||
.filter(([key]) => {
|
||||
return !key.startsWith('INPUT_')
|
||||
})
|
||||
.reduce((obj: NodeJS.ProcessEnv, [key, value]) => {
|
||||
obj[key] = value
|
||||
return obj
|
||||
}, {})
|
||||
}
|
||||
|
||||
class ExecState extends events.EventEmitter {
|
||||
constructor(options: im.ExecOptions, toolPath: string) {
|
||||
super()
|
||||
|
||||
@@ -22,7 +22,7 @@ async function run() {
|
||||
// You can also pass in additional options as a second parameter to getOctokit
|
||||
// const octokit = github.getOctokit(myToken, {userAgent: "MyActionVersion1"});
|
||||
|
||||
const { data: pullRequest } = await octokit.pulls.get({
|
||||
const { data: pullRequest } = await octokit.rest.pulls.get({
|
||||
owner: 'octokit',
|
||||
repo: 'rest.js',
|
||||
pull_number: 123,
|
||||
@@ -50,7 +50,7 @@ const github = require('@actions/github');
|
||||
|
||||
const context = github.context;
|
||||
|
||||
const newIssue = await octokit.issues.create({
|
||||
const newIssue = await octokit.rest.issues.create({
|
||||
...context.repo,
|
||||
title: 'New issue!',
|
||||
body: 'Hello Universe!'
|
||||
@@ -90,7 +90,7 @@ const octokit = GitHub.plugin(enterpriseServer220Admin)
|
||||
const myToken = core.getInput('myToken');
|
||||
const myOctokit = new octokit(getOctokitOptions(token))
|
||||
// Create a new user
|
||||
myOctokit.enterpriseAdmin.createUser({
|
||||
myOctokit.rest.enterpriseAdmin.createUser({
|
||||
login: "testuser",
|
||||
email: "testuser@test.com",
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# @actions/github Releases
|
||||
|
||||
### 5.0.0
|
||||
- [Update @actions/github to include latest octokit definitions](https://github.com/actions/toolkit/pull/783)
|
||||
|
||||
### 4.0.0
|
||||
- [Add execution state information to context](https://github.com/actions/toolkit/pull/499)
|
||||
- [Update Octokit Dependencies with some api breaking changes](https://github.com/actions/toolkit/pull/498)
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('@actions/github', () => {
|
||||
}
|
||||
|
||||
const octokit = getOctokit(token)
|
||||
const branch = await octokit.repos.getBranch({
|
||||
const branch = await octokit.rest.repos.getBranch({
|
||||
owner: 'actions',
|
||||
repo: 'toolkit',
|
||||
branch: 'main'
|
||||
@@ -85,7 +85,7 @@ describe('@actions/github', () => {
|
||||
agent: new https.Agent()
|
||||
}
|
||||
})
|
||||
const branch = await octokit.repos.getBranch({
|
||||
const branch = await octokit.rest.repos.getBranch({
|
||||
owner: 'actions',
|
||||
repo: 'toolkit',
|
||||
branch: 'main'
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('@actions/github', () => {
|
||||
proxyServer = proxy()
|
||||
await new Promise(resolve => {
|
||||
const port = Number(proxyUrl.split(':')[2])
|
||||
proxyServer.listen(port, () => resolve())
|
||||
proxyServer.listen(port, () => resolve(null))
|
||||
})
|
||||
proxyServer.on('connect', req => {
|
||||
proxyConnects.push(req.url)
|
||||
@@ -30,7 +30,7 @@ describe('@actions/github', () => {
|
||||
afterAll(async () => {
|
||||
// Stop proxy server
|
||||
await new Promise(resolve => {
|
||||
proxyServer.once('close', () => resolve())
|
||||
proxyServer.once('close', () => resolve(null))
|
||||
proxyServer.close()
|
||||
})
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('@actions/github', () => {
|
||||
return
|
||||
}
|
||||
const octokit = new GitHub(getOctokitOptions(token))
|
||||
const branch = await octokit.repos.getBranch({
|
||||
const branch = await octokit.rest.repos.getBranch({
|
||||
owner: 'actions',
|
||||
repo: 'toolkit',
|
||||
branch: 'main'
|
||||
@@ -60,7 +60,7 @@ describe('@actions/github', () => {
|
||||
return
|
||||
}
|
||||
const octokit = getOctokit(token)
|
||||
const branch = await octokit.repos.getBranch({
|
||||
const branch = await octokit.rest.repos.getBranch({
|
||||
owner: 'actions',
|
||||
repo: 'toolkit',
|
||||
branch: 'main'
|
||||
@@ -77,7 +77,7 @@ describe('@actions/github', () => {
|
||||
|
||||
// Valid token
|
||||
let octokit = new GitHub({auth: `token ${token}`})
|
||||
const branch = await octokit.repos.getBranch({
|
||||
const branch = await octokit.rest.repos.getBranch({
|
||||
owner: 'actions',
|
||||
repo: 'toolkit',
|
||||
branch: 'main'
|
||||
@@ -89,7 +89,7 @@ describe('@actions/github', () => {
|
||||
octokit = new GitHub({auth: `token asdf`})
|
||||
let failed = false
|
||||
try {
|
||||
await octokit.repos.getBranch({
|
||||
await octokit.rest.repos.getBranch({
|
||||
owner: 'actions',
|
||||
repo: 'toolkit',
|
||||
branch: 'main'
|
||||
|
||||
Generated
+24
-26
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/github",
|
||||
"version": "4.0.0",
|
||||
"version": "5.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -465,9 +465,9 @@
|
||||
}
|
||||
},
|
||||
"@octokit/core": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.3.1.tgz",
|
||||
"integrity": "sha512-Dc5NNQOYjgZU5S1goN6A/E500yXOfDUFRGQB8/2Tl16AcfvS3H9PudyOe3ZNE/MaVyHPIfC0htReHMJb1tMrvw==",
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz",
|
||||
"integrity": "sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg==",
|
||||
"requires": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
@@ -499,9 +499,9 @@
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-6.0.0.tgz",
|
||||
"integrity": "sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ=="
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz",
|
||||
"integrity": "sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw=="
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "2.13.3",
|
||||
@@ -512,26 +512,24 @@
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz",
|
||||
"integrity": "sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.1.1.tgz",
|
||||
"integrity": "sha512-u4zy0rVA8darm/AYsIeWkRalhQR99qPL1D/EXHejV2yaECMdHfxXiTXtba8NMBSajOJe8+C9g+EqMKSvysx0dg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.13.0",
|
||||
"@octokit/types": "^6.14.1",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.14.tgz",
|
||||
"integrity": "sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==",
|
||||
"version": "5.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz",
|
||||
"integrity": "sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.0.0",
|
||||
"@octokit/types": "^6.7.1",
|
||||
"deprecation": "^2.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"once": "^1.4.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
@@ -546,11 +544,11 @@
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.13.0.tgz",
|
||||
"integrity": "sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA==",
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz",
|
||||
"integrity": "sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^6.0.0"
|
||||
"@octokit/openapi-types": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@sinonjs/commons": {
|
||||
@@ -1020,9 +1018,9 @@
|
||||
}
|
||||
},
|
||||
"before-after-hook": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.0.tgz",
|
||||
"integrity": "sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ=="
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
|
||||
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
@@ -4783,9 +4781,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
|
||||
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/github",
|
||||
"version": "4.0.0",
|
||||
"version": "5.0.0",
|
||||
"description": "Actions github lib",
|
||||
"keywords": [
|
||||
"github",
|
||||
@@ -39,9 +39,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@octokit/core": "^3.3.1",
|
||||
"@octokit/core": "^3.4.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.13.3",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.0.0"
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^25.1.0",
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
# @actions/io Releases
|
||||
|
||||
### 1.1.0
|
||||
|
||||
- Add `findInPath` method to locate all matching executables in the system path
|
||||
|
||||
### 1.0.2
|
||||
|
||||
- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221)
|
||||
|
||||
### 1.0.0
|
||||
|
||||
- Initial release
|
||||
- Initial release
|
||||
|
||||
@@ -3,9 +3,12 @@ import {promises as fs} from 'fs'
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
import * as io from '../src/io'
|
||||
import * as ioUtil from '../src/io-util'
|
||||
|
||||
describe('cp', () => {
|
||||
beforeAll(async () => {
|
||||
await io.rmRF(getTestTemp())
|
||||
})
|
||||
|
||||
it('copies file with no flags', async () => {
|
||||
const root = path.join(getTestTemp(), 'cp_with_no_flags')
|
||||
const sourceFile = path.join(root, 'cp_source')
|
||||
@@ -87,6 +90,29 @@ describe('cp', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('copies directory into existing destination with -r without copying source directory', async () => {
|
||||
const root: string = path.join(
|
||||
getTestTemp(),
|
||||
'cp_with_-r_existing_dest_no_source_dir'
|
||||
)
|
||||
const sourceFolder: string = path.join(root, 'cp_source')
|
||||
const sourceFile: string = path.join(sourceFolder, 'cp_source_file')
|
||||
|
||||
const targetFolder: string = path.join(root, 'cp_target')
|
||||
const targetFile: string = path.join(targetFolder, 'cp_source_file')
|
||||
await io.mkdirP(sourceFolder)
|
||||
await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'})
|
||||
await io.mkdirP(targetFolder)
|
||||
await io.cp(sourceFolder, targetFolder, {
|
||||
recursive: true,
|
||||
copySourceDirectory: false
|
||||
})
|
||||
|
||||
expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe(
|
||||
'test file content'
|
||||
)
|
||||
})
|
||||
|
||||
it('copies directory into non-existing destination with -r', async () => {
|
||||
const root: string = path.join(getTestTemp(), 'cp_with_-r_nonexistent_dest')
|
||||
const sourceFolder: string = path.join(root, 'cp_source')
|
||||
@@ -166,6 +192,10 @@ describe('cp', () => {
|
||||
})
|
||||
|
||||
describe('mv', () => {
|
||||
beforeAll(async () => {
|
||||
await io.rmRF(getTestTemp())
|
||||
})
|
||||
|
||||
it('moves file with no flags', async () => {
|
||||
const root = path.join(getTestTemp(), ' mv_with_no_flags')
|
||||
const sourceFile = path.join(root, ' mv_source')
|
||||
@@ -264,6 +294,10 @@ describe('mv', () => {
|
||||
})
|
||||
|
||||
describe('rmRF', () => {
|
||||
beforeAll(async () => {
|
||||
await io.rmRF(getTestTemp())
|
||||
})
|
||||
|
||||
it('removes single folder with rmRF', async () => {
|
||||
const testPath = path.join(getTestTemp(), 'testFolder')
|
||||
|
||||
@@ -813,34 +847,13 @@ describe('mkdirP', () => {
|
||||
(await fs.lstat(path.join(realDirPath, 'sub_dir'))).isDirectory()
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('breaks if mkdirP loop out of control', async () => {
|
||||
const testPath = path.join(
|
||||
getTestTemp(),
|
||||
'mkdirP_failsafe',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'10'
|
||||
)
|
||||
|
||||
expect.assertions(1)
|
||||
|
||||
try {
|
||||
await ioUtil.mkdirP(testPath, 10)
|
||||
} catch (err) {
|
||||
expect(err.code).toBe('ENOENT')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('which', () => {
|
||||
beforeAll(async () => {
|
||||
await io.rmRF(getTestTemp())
|
||||
})
|
||||
|
||||
it('which() finds file name', async () => {
|
||||
// create a executable file
|
||||
const testPath = path.join(getTestTemp(), 'which-finds-file-name')
|
||||
@@ -1373,6 +1386,53 @@ describe('which', () => {
|
||||
}
|
||||
})
|
||||
|
||||
describe('findInPath', () => {
|
||||
beforeAll(async () => {
|
||||
await io.rmRF(getTestTemp())
|
||||
})
|
||||
|
||||
it('findInPath() not found', async () => {
|
||||
expect(await io.findInPath('findInPath-test-no-such-file')).toEqual([])
|
||||
})
|
||||
|
||||
it('findInPath() finds file names', async () => {
|
||||
// create executable files
|
||||
let fileName = 'FindInPath-Test-File'
|
||||
if (process.platform === 'win32') {
|
||||
fileName += '.exe'
|
||||
}
|
||||
|
||||
const testPaths = ['1', '2', '3'].map(count =>
|
||||
path.join(getTestTemp(), `findInPath-finds-file-names-${count}`)
|
||||
)
|
||||
for (const testPath of testPaths) {
|
||||
await io.mkdirP(testPath)
|
||||
}
|
||||
|
||||
const filePaths = testPaths.map(testPath => path.join(testPath, fileName))
|
||||
for (const filePath of filePaths) {
|
||||
await fs.writeFile(filePath, '')
|
||||
if (process.platform !== 'win32') {
|
||||
chmod(filePath, '+x')
|
||||
}
|
||||
}
|
||||
|
||||
const originalPath = process.env['PATH']
|
||||
try {
|
||||
// update the PATH
|
||||
for (const testPath of testPaths) {
|
||||
process.env[
|
||||
'PATH'
|
||||
] = `${process.env['PATH']}${path.delimiter}${testPath}`
|
||||
}
|
||||
// exact file names
|
||||
expect(await io.findInPath(fileName)).toEqual(filePaths)
|
||||
} finally {
|
||||
process.env['PATH'] = originalPath
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
async function findsExecutableWithScopedPermissions(
|
||||
chmodOptions: string
|
||||
): Promise<void> {
|
||||
|
||||
Generated
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@actions/io",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/io",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"description": "Actions io lib",
|
||||
"keywords": [
|
||||
"github",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {ok} from 'assert'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
@@ -59,52 +58,6 @@ export function isRooted(p: string): boolean {
|
||||
return p.startsWith('/')
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively create a directory at `fsPath`.
|
||||
*
|
||||
* This implementation is optimistic, meaning it attempts to create the full
|
||||
* path first, and backs up the path stack from there.
|
||||
*
|
||||
* @param fsPath The path to create
|
||||
* @param maxDepth The maximum recursion depth
|
||||
* @param depth The current recursion depth
|
||||
*/
|
||||
export async function mkdirP(
|
||||
fsPath: string,
|
||||
maxDepth: number = 1000,
|
||||
depth: number = 1
|
||||
): Promise<void> {
|
||||
ok(fsPath, 'a path argument must be provided')
|
||||
|
||||
fsPath = path.resolve(fsPath)
|
||||
|
||||
if (depth >= maxDepth) return mkdir(fsPath)
|
||||
|
||||
try {
|
||||
await mkdir(fsPath)
|
||||
return
|
||||
} catch (err) {
|
||||
switch (err.code) {
|
||||
case 'ENOENT': {
|
||||
await mkdirP(path.dirname(fsPath), maxDepth, depth + 1)
|
||||
await mkdir(fsPath)
|
||||
return
|
||||
}
|
||||
default: {
|
||||
let stats: fs.Stats
|
||||
|
||||
try {
|
||||
stats = await stat(fsPath)
|
||||
} catch (err2) {
|
||||
throw err
|
||||
}
|
||||
|
||||
if (!stats.isDirectory()) throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Best effort attempt to determine whether a file exists and is executable.
|
||||
* @param filePath file path to check
|
||||
|
||||
+87
-63
@@ -1,3 +1,4 @@
|
||||
import {ok} from 'assert'
|
||||
import * as childProcess from 'child_process'
|
||||
import * as path from 'path'
|
||||
import {promisify} from 'util'
|
||||
@@ -13,6 +14,8 @@ export interface CopyOptions {
|
||||
recursive?: boolean
|
||||
/** Optional. Whether to overwrite existing files in the destination. Defaults to true */
|
||||
force?: boolean
|
||||
/** Optional. Whether to copy the source directory along with all the files. Only takes effect when recursive=true and copying a directory. Default is true*/
|
||||
copySourceDirectory?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,7 +39,7 @@ export async function cp(
|
||||
dest: string,
|
||||
options: CopyOptions = {}
|
||||
): Promise<void> {
|
||||
const {force, recursive} = readCopyOptions(options)
|
||||
const {force, recursive, copySourceDirectory} = readCopyOptions(options)
|
||||
|
||||
const destStat = (await ioUtil.exists(dest)) ? await ioUtil.stat(dest) : null
|
||||
// Dest is an existing file, but not forcing
|
||||
@@ -46,7 +49,7 @@ export async function cp(
|
||||
|
||||
// If dest is an existing directory, should copy inside.
|
||||
const newDest: string =
|
||||
destStat && destStat.isDirectory()
|
||||
destStat && destStat.isDirectory() && copySourceDirectory
|
||||
? path.join(dest, path.basename(source))
|
||||
: dest
|
||||
|
||||
@@ -161,7 +164,8 @@ export async function rmRF(inputPath: string): Promise<void> {
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
export async function mkdirP(fsPath: string): Promise<void> {
|
||||
await ioUtil.mkdirP(fsPath)
|
||||
ok(fsPath, 'a path argument must be provided')
|
||||
await ioUtil.mkdir(fsPath, {recursive: true})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,75 +196,95 @@ export async function which(tool: string, check?: boolean): Promise<string> {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
try {
|
||||
// build the list of extensions to try
|
||||
const extensions: string[] = []
|
||||
if (ioUtil.IS_WINDOWS && process.env.PATHEXT) {
|
||||
for (const extension of process.env.PATHEXT.split(path.delimiter)) {
|
||||
if (extension) {
|
||||
extensions.push(extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
const matches: string[] = await findInPath(tool)
|
||||
|
||||
// if it's rooted, return it if exists. otherwise return empty.
|
||||
if (ioUtil.isRooted(tool)) {
|
||||
const filePath: string = await ioUtil.tryGetExecutablePath(
|
||||
tool,
|
||||
extensions
|
||||
)
|
||||
|
||||
if (filePath) {
|
||||
return filePath
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
// if any path separators, return empty
|
||||
if (tool.includes('/') || (ioUtil.IS_WINDOWS && tool.includes('\\'))) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// build the list of directories
|
||||
//
|
||||
// Note, technically "where" checks the current directory on Windows. From a toolkit perspective,
|
||||
// it feels like we should not do this. Checking the current directory seems like more of a use
|
||||
// case of a shell, and the which() function exposed by the toolkit should strive for consistency
|
||||
// across platforms.
|
||||
const directories: string[] = []
|
||||
|
||||
if (process.env.PATH) {
|
||||
for (const p of process.env.PATH.split(path.delimiter)) {
|
||||
if (p) {
|
||||
directories.push(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return the first match
|
||||
for (const directory of directories) {
|
||||
const filePath = await ioUtil.tryGetExecutablePath(
|
||||
directory + path.sep + tool,
|
||||
extensions
|
||||
)
|
||||
if (filePath) {
|
||||
return filePath
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
} catch (err) {
|
||||
throw new Error(`which failed with message ${err.message}`)
|
||||
if (matches && matches.length > 0) {
|
||||
return matches[0]
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all occurrences of the given tool on the system path.
|
||||
*
|
||||
* @returns Promise<string[]> the paths of the tool
|
||||
*/
|
||||
export async function findInPath(tool: string): Promise<string[]> {
|
||||
if (!tool) {
|
||||
throw new Error("parameter 'tool' is required")
|
||||
}
|
||||
|
||||
// build the list of extensions to try
|
||||
const extensions: string[] = []
|
||||
if (ioUtil.IS_WINDOWS && process.env['PATHEXT']) {
|
||||
for (const extension of process.env['PATHEXT'].split(path.delimiter)) {
|
||||
if (extension) {
|
||||
extensions.push(extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it's rooted, return it if exists. otherwise return empty.
|
||||
if (ioUtil.isRooted(tool)) {
|
||||
const filePath: string = await ioUtil.tryGetExecutablePath(tool, extensions)
|
||||
|
||||
if (filePath) {
|
||||
return [filePath]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
// if any path separators, return empty
|
||||
if (tool.includes(path.sep)) {
|
||||
return []
|
||||
}
|
||||
|
||||
// build the list of directories
|
||||
//
|
||||
// Note, technically "where" checks the current directory on Windows. From a toolkit perspective,
|
||||
// it feels like we should not do this. Checking the current directory seems like more of a use
|
||||
// case of a shell, and the which() function exposed by the toolkit should strive for consistency
|
||||
// across platforms.
|
||||
const directories: string[] = []
|
||||
|
||||
if (process.env.PATH) {
|
||||
for (const p of process.env.PATH.split(path.delimiter)) {
|
||||
if (p) {
|
||||
directories.push(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find all matches
|
||||
const matches: string[] = []
|
||||
|
||||
for (const directory of directories) {
|
||||
const filePath = await ioUtil.tryGetExecutablePath(
|
||||
path.join(directory, tool),
|
||||
extensions
|
||||
)
|
||||
if (filePath) {
|
||||
matches.push(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
function readCopyOptions(options: CopyOptions): Required<CopyOptions> {
|
||||
const force = options.force == null ? true : options.force
|
||||
const recursive = Boolean(options.recursive)
|
||||
return {force, recursive}
|
||||
const copySourceDirectory =
|
||||
options.copySourceDirectory == null
|
||||
? true
|
||||
: Boolean(options.copySourceDirectory)
|
||||
return {force, recursive, copySourceDirectory}
|
||||
}
|
||||
|
||||
async function cpDirRecursive(
|
||||
|
||||
@@ -763,6 +763,61 @@ describe('@actions/tool-cache', function() {
|
||||
expect(err.toString()).toContain('404')
|
||||
}
|
||||
})
|
||||
|
||||
it('supports authorization headers', async function() {
|
||||
nock('http://example.com', {
|
||||
reqheaders: {
|
||||
authorization: 'token abc123'
|
||||
}
|
||||
})
|
||||
.get('/some-file-that-needs-authorization')
|
||||
.reply(200, undefined)
|
||||
|
||||
await tc.downloadTool(
|
||||
'http://example.com/some-file-that-needs-authorization',
|
||||
undefined,
|
||||
'token abc123'
|
||||
)
|
||||
})
|
||||
|
||||
it('supports custom headers', async function() {
|
||||
nock('http://example.com', {
|
||||
reqheaders: {
|
||||
accept: 'application/octet-stream'
|
||||
}
|
||||
})
|
||||
.get('/some-file-that-needs-headers')
|
||||
.reply(200, undefined)
|
||||
|
||||
await tc.downloadTool(
|
||||
'http://example.com/some-file-that-needs-headers',
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
accept: 'application/octet-stream'
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('supports authorization and custom headers', async function() {
|
||||
nock('http://example.com', {
|
||||
reqheaders: {
|
||||
accept: 'application/octet-stream',
|
||||
authorization: 'token abc123'
|
||||
}
|
||||
})
|
||||
.get('/some-file-that-needs-authorization-and-headers')
|
||||
.reply(200, undefined)
|
||||
|
||||
await tc.downloadTool(
|
||||
'http://example.com/some-file-that-needs-authorization-and-headers',
|
||||
undefined,
|
||||
'token abc123',
|
||||
{
|
||||
accept: 'application/octet-stream'
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,12 +32,14 @@ const userAgent = 'actions/tool-cache'
|
||||
* @param url url of tool to download
|
||||
* @param dest path to download tool
|
||||
* @param auth authorization header
|
||||
* @param headers other headers
|
||||
* @returns path to downloaded tool
|
||||
*/
|
||||
export async function downloadTool(
|
||||
url: string,
|
||||
dest?: string,
|
||||
auth?: string
|
||||
auth?: string,
|
||||
headers?: IHeaders
|
||||
): Promise<string> {
|
||||
dest = dest || path.join(_getTempDirectory(), uuidV4())
|
||||
await io.mkdirP(path.dirname(dest))
|
||||
@@ -56,7 +58,7 @@ export async function downloadTool(
|
||||
const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds)
|
||||
return await retryHelper.execute(
|
||||
async () => {
|
||||
return await downloadToolAttempt(url, dest || '', auth)
|
||||
return await downloadToolAttempt(url, dest || '', auth, headers)
|
||||
},
|
||||
(err: Error) => {
|
||||
if (err instanceof HTTPError && err.httpStatusCode) {
|
||||
@@ -79,7 +81,8 @@ export async function downloadTool(
|
||||
async function downloadToolAttempt(
|
||||
url: string,
|
||||
dest: string,
|
||||
auth?: string
|
||||
auth?: string,
|
||||
headers?: IHeaders
|
||||
): Promise<string> {
|
||||
if (fs.existsSync(dest)) {
|
||||
throw new Error(`Destination file path ${dest} already exists`)
|
||||
@@ -90,12 +93,12 @@ async function downloadToolAttempt(
|
||||
allowRetries: false
|
||||
})
|
||||
|
||||
let headers: IHeaders | undefined
|
||||
if (auth) {
|
||||
core.debug('set auth')
|
||||
headers = {
|
||||
authorization: auth
|
||||
if (headers === undefined) {
|
||||
headers = {}
|
||||
}
|
||||
headers.authorization = auth
|
||||
}
|
||||
|
||||
const response: httpm.HttpClientResponse = await http.get(url, headers)
|
||||
|
||||
+1
-2
@@ -5,8 +5,7 @@
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"lib": ["es6"]
|
||||
"sourceMap": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
||||
Reference in New Issue
Block a user