Compare commits

..
Author SHA1 Message Date
github-actions[bot] d3ad3eeb7f Update Dependencies
Code Scanning - Action / CodeQL-Build (push) Has been cancelled
2021-03-28 18:13:37 +00:00
48 changed files with 303 additions and 562 deletions
+43
View File
@@ -0,0 +1,43 @@
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'
-75
View File
@@ -1,75 +0,0 @@
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 }}
+2 -1
View File
@@ -1,7 +1,8 @@
name: "UpdateOctokit" name: "UpdateOctokit"
on: on:
workflow_dispatch: schedule:
- cron: '0 18 * * 0' # sunday at 18 UTC
jobs: jobs:
UpdateOctokit: UpdateOctokit:
+1
View File
@@ -2,3 +2,4 @@
/packages/artifact/ @actions/actions-service /packages/artifact/ @actions/actions-service
/packages/cache/ @actions/actions-service /packages/cache/ @actions/actions-service
/packages/tool-cache/ @actions/spark
+4 -11
View File
@@ -57,7 +57,7 @@ For example, if you mask the letter `l`, running `echo "Hello FOO BAR World"` wi
### Group and Ungroup Log Lines ### Group and Ungroup Log Lines
Emitting a group with a title will instruct the logs to create a collapsible region up to the next endgroup command. Emitting a group with a title will instruct the logs to create a collapsable region up to the next ungroup command.
```bash ```bash
echo "::group::my title" echo "::group::my title"
@@ -72,7 +72,6 @@ function endGroup(): void {}
``` ```
### Problem Matchers ### 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. 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 ```bash
@@ -82,7 +81,6 @@ echo "::remove-matcher owner=eslint-compact::"
`add-matcher` takes a path to a Problem Matcher file `add-matcher` takes a path to a Problem Matcher file
`remove-matcher` removes a Problem Matcher by owner `remove-matcher` removes a Problem Matcher by owner
### Save State ### Save State
Save a state to an environmental variable that can later be used in the main or post action. Save a state to an environmental variable that can later be used in the main or post action.
@@ -104,7 +102,6 @@ There are several commands to emit different levels of log output:
| error | `echo "::error::My error message"` | | error | `echo "::error::My error message"` |
### Command Echoing ### 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) 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. You can enable or disable this for the current step by using the `echo` command.
@@ -130,12 +127,12 @@ The `add-mask`, `debug`, `warning` and `error` commands do not support echoing.
### Command Prompt ### 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 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 ```cmd
echo ::set-output name=FOO::BAR 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. 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.
@@ -149,8 +146,7 @@ echo "FOO=BAR" >> $GITHUB_ENV
Running `$FOO` in a future step will now return `BAR` 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: steps:
- name: Set the value - name: Set the value
@@ -164,7 +160,6 @@ steps:
This would set the value of the `JSON_RESPONSE` env variable to the value of the curl response. 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: The expected syntax for the heredoc style is:
``` ```
{VARIABLE_NAME}<<{DELIMETER} {VARIABLE_NAME}<<{DELIMETER}
{VARIABLE_VALUE} {VARIABLE_VALUE}
@@ -188,7 +183,6 @@ 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}`; 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: This is wrapped by the core addPath method:
```javascript ```javascript
export function addPath(inputPath: string): void {} export function addPath(inputPath: string): void {}
``` ```
@@ -196,7 +190,6 @@ export function addPath(inputPath: string): void {}
### Powershell ### 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: 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: steps:
- run: echo "mypath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - run: echo "mypath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
-20
View File
@@ -2,16 +2,6 @@
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. 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 arent 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 ## Single Line Matchers
Let's consider the ESLint compact output: Let's consider the ESLint compact output:
@@ -110,16 +100,6 @@ 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 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. 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 ## Adding and Removing Problem Matchers
Problem Matchers are enabled and removed via the toolkit [commands](commands.md#problem-matchers). Problem Matchers are enabled and removed via the toolkit [commands](commands.md#problem-matchers).
+9 -9
View File
@@ -18684,9 +18684,9 @@
} }
}, },
"ssri": { "ssri": {
"version": "6.0.2", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
"integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
"dev": true, "dev": true,
"requires": { "requires": {
"figgy-pudding": "^3.5.1" "figgy-pudding": "^3.5.1"
@@ -19336,9 +19336,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "3.9.9", "version": "3.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
@@ -19859,9 +19859,9 @@
"dev": true "dev": true
}, },
"y18n": { "y18n": {
"version": "4.0.1", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true "dev": true
}, },
"yallist": { "yallist": {
+1 -1
View File
@@ -27,6 +27,6 @@
"lerna": "^3.18.4", "lerna": "^3.18.4",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"ts-jest": "^25.4.0", "ts-jest": "^25.4.0",
"typescript": "^3.9.9" "typescript": "^3.7.4"
} }
} }
-4
View File
@@ -54,7 +54,3 @@
- Improved retry-ability for all http calls during artifact upload and download if an error is encountered - 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
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/artifact", "name": "@actions/artifact",
"version": "0.5.1", "version": "0.5.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -10,9 +10,9 @@
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==" "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
}, },
"@actions/http-client": { "@actions/http-client": {
"version": "1.0.11", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
"requires": { "requires": {
"tunnel": "0.0.6" "tunnel": "0.0.6"
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/artifact", "name": "@actions/artifact",
"version": "0.5.1", "version": "0.5.0",
"preview": true, "preview": true,
"description": "Actions artifact lib", "description": "Actions artifact lib",
"keywords": [ "keywords": [
@@ -38,7 +38,7 @@
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.2.6", "@actions/core": "^1.2.6",
"@actions/http-client": "^1.0.11", "@actions/http-client": "^1.0.7",
"@types/tmp": "^0.1.0", "@types/tmp": "^0.1.0",
"tmp": "^0.1.0", "tmp": "^0.1.0",
"tmp-promise": "^2.0.2" "tmp-promise": "^2.0.2"
+14 -14
View File
@@ -10,20 +10,6 @@ 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). 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 #### 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. 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.
@@ -42,3 +28,17 @@ const restoreKeys = [
const cacheKey = await cache.restoreCache(paths, key, 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)
```
+1 -4
View File
@@ -36,7 +36,4 @@
### 1.0.6 ### 1.0.6
- Make caching more verbose [#650](https://github.com/actions/toolkit/pull/650) - 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
View File
@@ -2,10 +2,10 @@ import {promises as fs} from 'fs'
import * as path from 'path' import * as path from 'path'
import * as cacheUtils from '../src/internal/cacheUtils' import * as cacheUtils from '../src/internal/cacheUtils'
test('getArchiveFileSizeInBytes returns file size', () => { test('getArchiveFileSizeIsBytes returns file size', () => {
const filePath = path.join(__dirname, '__fixtures__', 'helloWorld.txt') const filePath = path.join(__dirname, '__fixtures__', 'helloWorld.txt')
const size = cacheUtils.getArchiveFileSizeInBytes(filePath) const size = cacheUtils.getArchiveFileSizeIsBytes(filePath)
expect(size).toBe(11) expect(size).toBe(11)
}) })
+9 -9
View File
@@ -123,8 +123,8 @@ test('restore with gzip compressed cache found', async () => {
const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache')
const fileSize = 142 const fileSize = 142
const getArchiveFileSizeInBytesMock = jest const getArchiveFileSizeIsBytesMock = jest
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes') .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
.mockReturnValue(fileSize) .mockReturnValue(fileSize)
const extractTarMock = jest.spyOn(tar, 'extractTar') const extractTarMock = jest.spyOn(tar, 'extractTar')
@@ -147,7 +147,7 @@ test('restore with gzip compressed cache found', async () => {
archivePath, archivePath,
undefined undefined
) )
expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath) expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath)
expect(extractTarMock).toHaveBeenCalledTimes(1) expect(extractTarMock).toHaveBeenCalledTimes(1)
expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression)
@@ -184,8 +184,8 @@ test('restore with zstd compressed cache found', async () => {
const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache')
const fileSize = 62915000 const fileSize = 62915000
const getArchiveFileSizeInBytesMock = jest const getArchiveFileSizeIsBytesMock = jest
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes') .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
.mockReturnValue(fileSize) .mockReturnValue(fileSize)
const extractTarMock = jest.spyOn(tar, 'extractTar') const extractTarMock = jest.spyOn(tar, 'extractTar')
@@ -206,7 +206,7 @@ test('restore with zstd compressed cache found', async () => {
archivePath, archivePath,
undefined undefined
) )
expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath) expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath)
expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`) expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`)
expect(extractTarMock).toHaveBeenCalledTimes(1) expect(extractTarMock).toHaveBeenCalledTimes(1)
@@ -241,8 +241,8 @@ test('restore with cache found for restore key', async () => {
const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache')
const fileSize = 142 const fileSize = 142
const getArchiveFileSizeInBytesMock = jest const getArchiveFileSizeIsBytesMock = jest
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes') .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
.mockReturnValue(fileSize) .mockReturnValue(fileSize)
const extractTarMock = jest.spyOn(tar, 'extractTar') const extractTarMock = jest.spyOn(tar, 'extractTar')
@@ -263,7 +263,7 @@ test('restore with cache found for restore key', async () => {
archivePath, archivePath,
undefined undefined
) )
expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath) expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath)
expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`) expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`)
expect(extractTarMock).toHaveBeenCalledTimes(1) expect(extractTarMock).toHaveBeenCalledTimes(1)
+1 -1
View File
@@ -49,7 +49,7 @@ test('save with large cache outputs should fail', async () => {
const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit
jest jest
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes') .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes')
.mockReturnValueOnce(cacheSize) .mockReturnValueOnce(cacheSize)
const compression = CompressionMethod.Gzip const compression = CompressionMethod.Gzip
const getCompressionMock = jest const getCompressionMock = jest
+7 -16
View File
@@ -11,7 +11,6 @@ jest.mock('@actions/exec')
jest.mock('@actions/io') jest.mock('@actions/io')
const IS_WINDOWS = process.platform === 'win32' const IS_WINDOWS = process.platform === 'win32'
const IS_MAC = process.platform === 'darwin'
const defaultTarPath = process.platform === 'darwin' ? 'gtar' : 'tar' const defaultTarPath = process.platform === 'darwin' ? 'gtar' : 'tar'
@@ -56,9 +55,7 @@ test('zstd extract tar', async () => {
'-P', '-P',
'-C', '-C',
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
] ].concat(IS_WINDOWS ? ['--force-local'] : []),
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
{cwd: undefined} {cwd: undefined}
) )
}) })
@@ -87,7 +84,7 @@ test('gzip extract tar', async () => {
'-P', '-P',
'-C', '-C',
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
].concat(IS_MAC ? ['--delay-directory-restore'] : []), ],
{cwd: undefined} {cwd: undefined}
) )
}) })
@@ -148,9 +145,7 @@ test('zstd create tar', async () => {
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace, IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace,
'--files-from', '--files-from',
'manifest.txt' 'manifest.txt'
] ].concat(IS_WINDOWS ? ['--force-local'] : []),
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
{ {
cwd: archiveFolder cwd: archiveFolder
} }
@@ -185,7 +180,7 @@ test('gzip create tar', async () => {
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace, IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace,
'--files-from', '--files-from',
'manifest.txt' 'manifest.txt'
].concat(IS_MAC ? ['--delay-directory-restore'] : []), ],
{ {
cwd: archiveFolder cwd: archiveFolder
} }
@@ -210,9 +205,7 @@ test('zstd list tar', async () => {
'-tf', '-tf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P' '-P'
] ].concat(IS_WINDOWS ? ['--force-local'] : []),
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
{cwd: undefined} {cwd: undefined}
) )
}) })
@@ -235,9 +228,7 @@ test('zstdWithoutLong list tar', async () => {
'-tf', '-tf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P' '-P'
] ].concat(IS_WINDOWS ? ['--force-local'] : []),
.concat(IS_WINDOWS ? ['--force-local'] : [])
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
{cwd: undefined} {cwd: undefined}
) )
}) })
@@ -261,7 +252,7 @@ test('gzip list tar', async () => {
'-tf', '-tf',
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
'-P' '-P'
].concat(IS_MAC ? ['--delay-directory-restore'] : []), ],
{cwd: undefined} {cwd: undefined}
) )
}) })
+7 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/cache", "name": "@actions/cache",
"version": "1.0.7", "version": "1.0.6",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -4199,6 +4199,11 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" "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": { "yargs": {
"version": "15.3.1", "version": "15.3.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
@@ -4213,6 +4218,7 @@
"set-blocking": "^2.0.0", "set-blocking": "^2.0.0",
"string-width": "^4.2.0", "string-width": "^4.2.0",
"which-module": "^2.0.0", "which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.1" "yargs-parser": "^18.1.1"
} }
}, },
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/cache", "name": "@actions/cache",
"version": "1.0.7", "version": "1.0.6",
"preview": true, "preview": true,
"description": "Actions cache lib", "description": "Actions cache lib",
"keywords": [ "keywords": [
+2 -2
View File
@@ -104,7 +104,7 @@ export async function restoreCache(
await listTar(archivePath, compressionMethod) await listTar(archivePath, compressionMethod)
} }
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath) const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath)
core.info( core.info(
`Cache Size: ~${Math.round( `Cache Size: ~${Math.round(
archiveFileSize / (1024 * 1024) archiveFileSize / (1024 * 1024)
@@ -172,7 +172,7 @@ export async function saveCache(
} }
const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath) const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath)
core.debug(`File Size: ${archiveFileSize}`) core.debug(`File Size: ${archiveFileSize}`)
if (archiveFileSize > fileSizeLimit) { if (archiveFileSize > fileSizeLimit) {
throw new Error( throw new Error(
+2 -2
View File
@@ -219,7 +219,7 @@ async function uploadFile(
options?: UploadOptions options?: UploadOptions
): Promise<void> { ): Promise<void> {
// Upload Chunks // Upload Chunks
const fileSize = utils.getArchiveFileSizeInBytes(archivePath) const fileSize = fs.statSync(archivePath).size
const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`) const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`)
const fd = fs.openSync(archivePath, 'r') const fd = fs.openSync(archivePath, 'r')
const uploadOptions = getUploadOptions(options) const uploadOptions = getUploadOptions(options)
@@ -300,7 +300,7 @@ export async function saveCache(
// Commit Cache // Commit Cache
core.debug('Commiting cache') core.debug('Commiting cache')
const cacheSize = utils.getArchiveFileSizeInBytes(archivePath) const cacheSize = utils.getArchiveFileSizeIsBytes(archivePath)
core.info( core.info(
`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)` `Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`
) )
+1 -1
View File
@@ -35,7 +35,7 @@ export async function createTempDirectory(): Promise<string> {
return dest return dest
} }
export function getArchiveFileSizeInBytes(filePath: string): number { export function getArchiveFileSizeIsBytes(filePath: string): number {
return fs.statSync(filePath).size return fs.statSync(filePath).size
} }
+1 -1
View File
@@ -190,7 +190,7 @@ export async function downloadCacheHttpClient(
if (contentLengthHeader) { if (contentLengthHeader) {
const expectedLength = parseInt(contentLengthHeader) const expectedLength = parseInt(contentLengthHeader)
const actualLength = utils.getArchiveFileSizeInBytes(archivePath) const actualLength = utils.getArchiveFileSizeIsBytes(archivePath)
if (actualLength !== expectedLength) { if (actualLength !== expectedLength) {
throw new Error( throw new Error(
-2
View File
@@ -26,8 +26,6 @@ async function getTarPath(
case 'darwin': { case 'darwin': {
const gnuTar = await io.which('gtar', false) const gnuTar = await io.which('gtar', false)
if (gnuTar) { 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 return gnuTar
} }
break break
+6 -12
View File
@@ -16,13 +16,11 @@ import * as core from '@actions/core';
#### Inputs/Outputs #### Inputs/Outputs
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`. 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.
Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
```js ```js
const myInput = core.getInput('inputName', { required: true }); const myInput = core.getInput('inputName', { required: true });
const myBooleanInput = core.getBooleanInput('booleanInputName', { required: true });
core.setOutput('outputKey', 'outputVal'); core.setOutput('outputKey', 'outputVal');
``` ```
@@ -64,10 +62,11 @@ catch (err) {
// setFailed logs the message and sets a failing exit code // setFailed logs the message and sets a failing exit code
core.setFailed(`Action failed with error ${err}`); core.setFailed(`Action failed with error ${err}`);
} }
```
Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned. Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned.
```
#### Logging #### 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). 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).
@@ -119,7 +118,6 @@ 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. 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: Foreground colors:
```js ```js
// 3/4 bit // 3/4 bit
core.info('\u001b[35mThis foreground will be magenta') core.info('\u001b[35mThis foreground will be magenta')
@@ -132,7 +130,6 @@ core.info('\u001b[38;2;255;0;0mThis foreground will be bright red')
``` ```
Background colors: Background colors:
```js ```js
// 3/4 bit // 3/4 bit
core.info('\u001b[43mThis background will be yellow'); core.info('\u001b[43mThis background will be yellow');
@@ -159,7 +156,6 @@ core.info('\u001b[31;46mRed foreground with a cyan background and \u001b[1mbold
``` ```
> Note: Escape codes reset at the start of each line > Note: Escape codes reset at the start of each line
```js ```js
core.info('\u001b[35mThis foreground will be magenta') core.info('\u001b[35mThis foreground will be magenta')
core.info('This foreground will reset to the default') core.info('This foreground will reset to the default')
@@ -174,10 +170,9 @@ core.info(style.color.ansi16m.hex('#abcdef') + 'Hello world!')
#### Action state #### 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 ```yaml
name: 'Wrapper action sample' name: 'Wrapper action sample'
inputs: inputs:
@@ -198,7 +193,6 @@ core.saveState("pidToKill", 12345);
``` ```
In action's `cleanup.js`: In action's `cleanup.js`:
```js ```js
const core = require('@actions/core'); const core = require('@actions/core');
-3
View File
@@ -1,8 +1,5 @@
# @actions/core Releases # @actions/core Releases
### 1.2.7
- [Prepend newline for set-output](https://github.com/actions/toolkit/pull/772)
### 1.2.6 ### 1.2.6
- [Update `exportVariable` and `addPath` to use environment files](https://github.com/actions/toolkit/pull/571) - [Update `exportVariable` and `addPath` to use environment files](https://github.com/actions/toolkit/pull/571)
+3 -38
View File
@@ -19,14 +19,6 @@ const testEnvVars = {
INPUT_MISSING: '', INPUT_MISSING: '',
'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ response ', 'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ response ',
INPUT_MULTIPLE_SPACES_VARIABLE: 'I have multiple spaces', 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 // Save inputs
STATE_TEST_1: 'state_val', STATE_TEST_1: 'state_val',
@@ -165,46 +157,19 @@ 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', () => { it('setOutput produces the correct command', () => {
core.setOutput('some output', 'some value') core.setOutput('some output', 'some value')
assertWriteCalls([ assertWriteCalls([`::set-output name=some output::some value${os.EOL}`])
os.EOL,
`::set-output name=some output::some value${os.EOL}`
])
}) })
it('setOutput handles bools', () => { it('setOutput handles bools', () => {
core.setOutput('some output', false) core.setOutput('some output', false)
assertWriteCalls([os.EOL, `::set-output name=some output::false${os.EOL}`]) assertWriteCalls([`::set-output name=some output::false${os.EOL}`])
}) })
it('setOutput handles numbers', () => { it('setOutput handles numbers', () => {
core.setOutput('some output', 1.01) core.setOutput('some output', 1.01)
assertWriteCalls([os.EOL, `::set-output name=some output::1.01${os.EOL}`]) assertWriteCalls([`::set-output name=some output::1.01${os.EOL}`])
}) })
it('setFailed sets the correct exit code and failure message', () => { it('setFailed sets the correct exit code and failure message', () => {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/core", "name": "@actions/core",
"version": "1.2.7", "version": "1.2.6",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/core", "name": "@actions/core",
"version": "1.2.7", "version": "1.2.6",
"description": "Actions core lib", "description": "Actions core lib",
"keywords": [ "keywords": [
"github", "github",
-23
View File
@@ -91,28 +91,6 @@ export function getInput(name: string, options?: InputOptions): string {
return val.trim() 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. * Sets the value of an output.
* *
@@ -121,7 +99,6 @@ export function getBooleanInput(name: string, options?: InputOptions): boolean {
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export function setOutput(name: string, value: any): void { export function setOutput(name: string, value: any): void {
process.stdout.write(os.EOL)
issueCommand('set-output', {name}, value) issueCommand('set-output', {name}, value)
} }
-9
View File
@@ -1,5 +1,4 @@
import * as exec from '../src/exec' import * as exec from '../src/exec'
import * as tr from '../src/toolrunner'
import * as im from '../src/interfaces' import * as im from '../src/interfaces'
import * as childProcess from 'child_process' import * as childProcess from 'child_process'
@@ -621,14 +620,6 @@ describe('@actions/exec', () => {
expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) 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) { if (IS_WINDOWS) {
it('Exec roots relative tool path using process.cwd (Windows path separator)', async () => { it('Exec roots relative tool path using process.cwd (Windows path separator)', async () => {
let exitCode: number let exitCode: number
+1 -1
View File
@@ -6,7 +6,7 @@ export interface ExecOptions {
/** optional working directory. defaults to current */ /** optional working directory. defaults to current */
cwd?: string cwd?: string
/** optional envvar dictionary. defaults to current process's env with `INPUT_*` variables removed */ /** optional envvar dictionary. defaults to current process's env */
env?: {[key: string]: string} env?: {[key: string]: string}
/** optional. defaults to false */ /** optional. defaults to false */
+1 -16
View File
@@ -6,7 +6,6 @@ import * as stream from 'stream'
import * as im from './interfaces' import * as im from './interfaces'
import * as io from '@actions/io' import * as io from '@actions/io'
import * as ioUtil from '@actions/io/lib/io-util' import * as ioUtil from '@actions/io/lib/io-util'
import {setTimeout} from 'timers'
/* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/unbound-method */
@@ -377,7 +376,7 @@ export class ToolRunner extends events.EventEmitter {
options = options || <im.ExecOptions>{} options = options || <im.ExecOptions>{}
const result = <child.SpawnOptions>{} const result = <child.SpawnOptions>{}
result.cwd = options.cwd result.cwd = options.cwd
result.env = options.env || stripInputEnvironmentVariables(process.env) result.env = options.env
result['windowsVerbatimArguments'] = result['windowsVerbatimArguments'] =
options.windowsVerbatimArguments || this._isCmdFile() options.windowsVerbatimArguments || this._isCmdFile()
if (options.windowsVerbatimArguments) { if (options.windowsVerbatimArguments) {
@@ -600,20 +599,6 @@ export function argStringToArray(argString: string): string[] {
return args 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 { class ExecState extends events.EventEmitter {
constructor(options: im.ExecOptions, toolPath: string) { constructor(options: im.ExecOptions, toolPath: string) {
super() super()
+3 -3
View File
@@ -22,7 +22,7 @@ async function run() {
// You can also pass in additional options as a second parameter to getOctokit // You can also pass in additional options as a second parameter to getOctokit
// const octokit = github.getOctokit(myToken, {userAgent: "MyActionVersion1"}); // const octokit = github.getOctokit(myToken, {userAgent: "MyActionVersion1"});
const { data: pullRequest } = await octokit.rest.pulls.get({ const { data: pullRequest } = await octokit.pulls.get({
owner: 'octokit', owner: 'octokit',
repo: 'rest.js', repo: 'rest.js',
pull_number: 123, pull_number: 123,
@@ -50,7 +50,7 @@ const github = require('@actions/github');
const context = github.context; const context = github.context;
const newIssue = await octokit.rest.issues.create({ const newIssue = await octokit.issues.create({
...context.repo, ...context.repo,
title: 'New issue!', title: 'New issue!',
body: 'Hello Universe!' body: 'Hello Universe!'
@@ -90,7 +90,7 @@ const octokit = GitHub.plugin(enterpriseServer220Admin)
const myToken = core.getInput('myToken'); const myToken = core.getInput('myToken');
const myOctokit = new octokit(getOctokitOptions(token)) const myOctokit = new octokit(getOctokitOptions(token))
// Create a new user // Create a new user
myOctokit.rest.enterpriseAdmin.createUser({ myOctokit.enterpriseAdmin.createUser({
login: "testuser", login: "testuser",
email: "[email protected]", email: "[email protected]",
}); });
-3
View File
@@ -1,8 +1,5 @@
# @actions/github Releases # @actions/github Releases
### 5.0.0
- [Update @actions/github to include latest octokit definitions](https://github.com/actions/toolkit/pull/783)
### 4.0.0 ### 4.0.0
- [Add execution state information to context](https://github.com/actions/toolkit/pull/499) - [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) - [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 octokit = getOctokit(token)
const branch = await octokit.rest.repos.getBranch({ const branch = await octokit.repos.getBranch({
owner: 'actions', owner: 'actions',
repo: 'toolkit', repo: 'toolkit',
branch: 'main' branch: 'main'
@@ -85,7 +85,7 @@ describe('@actions/github', () => {
agent: new https.Agent() agent: new https.Agent()
} }
}) })
const branch = await octokit.rest.repos.getBranch({ const branch = await octokit.repos.getBranch({
owner: 'actions', owner: 'actions',
repo: 'toolkit', repo: 'toolkit',
branch: 'main' branch: 'main'
+6 -6
View File
@@ -15,7 +15,7 @@ describe('@actions/github', () => {
proxyServer = proxy() proxyServer = proxy()
await new Promise(resolve => { await new Promise(resolve => {
const port = Number(proxyUrl.split(':')[2]) const port = Number(proxyUrl.split(':')[2])
proxyServer.listen(port, () => resolve(null)) proxyServer.listen(port, () => resolve())
}) })
proxyServer.on('connect', req => { proxyServer.on('connect', req => {
proxyConnects.push(req.url) proxyConnects.push(req.url)
@@ -30,7 +30,7 @@ describe('@actions/github', () => {
afterAll(async () => { afterAll(async () => {
// Stop proxy server // Stop proxy server
await new Promise(resolve => { await new Promise(resolve => {
proxyServer.once('close', () => resolve(null)) proxyServer.once('close', () => resolve())
proxyServer.close() proxyServer.close()
}) })
@@ -45,7 +45,7 @@ describe('@actions/github', () => {
return return
} }
const octokit = new GitHub(getOctokitOptions(token)) const octokit = new GitHub(getOctokitOptions(token))
const branch = await octokit.rest.repos.getBranch({ const branch = await octokit.repos.getBranch({
owner: 'actions', owner: 'actions',
repo: 'toolkit', repo: 'toolkit',
branch: 'main' branch: 'main'
@@ -60,7 +60,7 @@ describe('@actions/github', () => {
return return
} }
const octokit = getOctokit(token) const octokit = getOctokit(token)
const branch = await octokit.rest.repos.getBranch({ const branch = await octokit.repos.getBranch({
owner: 'actions', owner: 'actions',
repo: 'toolkit', repo: 'toolkit',
branch: 'main' branch: 'main'
@@ -77,7 +77,7 @@ describe('@actions/github', () => {
// Valid token // Valid token
let octokit = new GitHub({auth: `token ${token}`}) let octokit = new GitHub({auth: `token ${token}`})
const branch = await octokit.rest.repos.getBranch({ const branch = await octokit.repos.getBranch({
owner: 'actions', owner: 'actions',
repo: 'toolkit', repo: 'toolkit',
branch: 'main' branch: 'main'
@@ -89,7 +89,7 @@ describe('@actions/github', () => {
octokit = new GitHub({auth: `token asdf`}) octokit = new GitHub({auth: `token asdf`})
let failed = false let failed = false
try { try {
await octokit.rest.repos.getBranch({ await octokit.repos.getBranch({
owner: 'actions', owner: 'actions',
repo: 'toolkit', repo: 'toolkit',
branch: 'main' branch: 'main'
+26 -24
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/github", "name": "@actions/github",
"version": "5.0.0", "version": "4.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -465,9 +465,9 @@
} }
}, },
"@octokit/core": { "@octokit/core": {
"version": "3.4.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.4.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.3.1.tgz",
"integrity": "sha512-6/vlKPP8NF17cgYXqucdshWqmMZGXkuvtcrWCgU5NOI0Pl2GjlmZyWgBMrU8zJ3v2MJlM6++CiB45VKYmhiWWg==", "integrity": "sha512-Dc5NNQOYjgZU5S1goN6A/E500yXOfDUFRGQB8/2Tl16AcfvS3H9PudyOe3ZNE/MaVyHPIfC0htReHMJb1tMrvw==",
"requires": { "requires": {
"@octokit/auth-token": "^2.4.4", "@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8", "@octokit/graphql": "^4.5.8",
@@ -499,9 +499,9 @@
} }
}, },
"@octokit/openapi-types": { "@octokit/openapi-types": {
"version": "7.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-6.0.0.tgz",
"integrity": "sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw==" "integrity": "sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ=="
}, },
"@octokit/plugin-paginate-rest": { "@octokit/plugin-paginate-rest": {
"version": "2.13.3", "version": "2.13.3",
@@ -512,24 +512,26 @@
} }
}, },
"@octokit/plugin-rest-endpoint-methods": { "@octokit/plugin-rest-endpoint-methods": {
"version": "5.1.1", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.1.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz",
"integrity": "sha512-u4zy0rVA8darm/AYsIeWkRalhQR99qPL1D/EXHejV2yaECMdHfxXiTXtba8NMBSajOJe8+C9g+EqMKSvysx0dg==", "integrity": "sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg==",
"requires": { "requires": {
"@octokit/types": "^6.14.1", "@octokit/types": "^6.13.0",
"deprecation": "^2.3.1" "deprecation": "^2.3.1"
} }
}, },
"@octokit/request": { "@octokit/request": {
"version": "5.4.15", "version": "5.4.14",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.14.tgz",
"integrity": "sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==", "integrity": "sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==",
"requires": { "requires": {
"@octokit/endpoint": "^6.0.1", "@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.0.0", "@octokit/request-error": "^2.0.0",
"@octokit/types": "^6.7.1", "@octokit/types": "^6.7.1",
"deprecation": "^2.0.0",
"is-plain-object": "^5.0.0", "is-plain-object": "^5.0.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"once": "^1.4.0",
"universal-user-agent": "^6.0.0" "universal-user-agent": "^6.0.0"
} }
}, },
@@ -544,11 +546,11 @@
} }
}, },
"@octokit/types": { "@octokit/types": {
"version": "6.14.2", "version": "6.13.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.13.0.tgz",
"integrity": "sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA==", "integrity": "sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA==",
"requires": { "requires": {
"@octokit/openapi-types": "^7.0.0" "@octokit/openapi-types": "^6.0.0"
} }
}, },
"@sinonjs/commons": { "@sinonjs/commons": {
@@ -1018,9 +1020,9 @@
} }
}, },
"before-after-hook": { "before-after-hook": {
"version": "2.2.1", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.0.tgz",
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==" "integrity": "sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ=="
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
@@ -4781,9 +4783,9 @@
"dev": true "dev": true
}, },
"y18n": { "y18n": {
"version": "4.0.1", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true "dev": true
}, },
"yargs": { "yargs": {
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/github", "name": "@actions/github",
"version": "5.0.0", "version": "4.0.0",
"description": "Actions github lib", "description": "Actions github lib",
"keywords": [ "keywords": [
"github", "github",
@@ -39,9 +39,9 @@
}, },
"dependencies": { "dependencies": {
"@actions/http-client": "^1.0.11", "@actions/http-client": "^1.0.11",
"@octokit/core": "^3.4.0", "@octokit/core": "^3.3.1",
"@octokit/plugin-paginate-rest": "^2.13.3", "@octokit/plugin-paginate-rest": "^2.13.3",
"@octokit/plugin-rest-endpoint-methods": "^5.1.1" "@octokit/plugin-rest-endpoint-methods": "^5.0.0"
}, },
"devDependencies": { "devDependencies": {
"jest": "^25.1.0", "jest": "^25.1.0",
+1 -5
View File
@@ -1,13 +1,9 @@
# @actions/io Releases # @actions/io Releases
### 1.1.0
- Add `findInPath` method to locate all matching executables in the system path
### 1.0.2 ### 1.0.2
- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) - [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221)
### 1.0.0 ### 1.0.0
- Initial release - Initial release
+26 -86
View File
@@ -3,12 +3,9 @@ import {promises as fs} from 'fs'
import * as os from 'os' import * as os from 'os'
import * as path from 'path' import * as path from 'path'
import * as io from '../src/io' import * as io from '../src/io'
import * as ioUtil from '../src/io-util'
describe('cp', () => { describe('cp', () => {
beforeAll(async () => {
await io.rmRF(getTestTemp())
})
it('copies file with no flags', async () => { it('copies file with no flags', async () => {
const root = path.join(getTestTemp(), 'cp_with_no_flags') const root = path.join(getTestTemp(), 'cp_with_no_flags')
const sourceFile = path.join(root, 'cp_source') const sourceFile = path.join(root, 'cp_source')
@@ -90,29 +87,6 @@ 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 () => { it('copies directory into non-existing destination with -r', async () => {
const root: string = path.join(getTestTemp(), 'cp_with_-r_nonexistent_dest') const root: string = path.join(getTestTemp(), 'cp_with_-r_nonexistent_dest')
const sourceFolder: string = path.join(root, 'cp_source') const sourceFolder: string = path.join(root, 'cp_source')
@@ -192,10 +166,6 @@ describe('cp', () => {
}) })
describe('mv', () => { describe('mv', () => {
beforeAll(async () => {
await io.rmRF(getTestTemp())
})
it('moves file with no flags', async () => { it('moves file with no flags', async () => {
const root = path.join(getTestTemp(), ' mv_with_no_flags') const root = path.join(getTestTemp(), ' mv_with_no_flags')
const sourceFile = path.join(root, ' mv_source') const sourceFile = path.join(root, ' mv_source')
@@ -294,10 +264,6 @@ describe('mv', () => {
}) })
describe('rmRF', () => { describe('rmRF', () => {
beforeAll(async () => {
await io.rmRF(getTestTemp())
})
it('removes single folder with rmRF', async () => { it('removes single folder with rmRF', async () => {
const testPath = path.join(getTestTemp(), 'testFolder') const testPath = path.join(getTestTemp(), 'testFolder')
@@ -847,13 +813,34 @@ describe('mkdirP', () => {
(await fs.lstat(path.join(realDirPath, 'sub_dir'))).isDirectory() (await fs.lstat(path.join(realDirPath, 'sub_dir'))).isDirectory()
).toBe(true) ).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', () => { describe('which', () => {
beforeAll(async () => {
await io.rmRF(getTestTemp())
})
it('which() finds file name', async () => { it('which() finds file name', async () => {
// create a executable file // create a executable file
const testPath = path.join(getTestTemp(), 'which-finds-file-name') const testPath = path.join(getTestTemp(), 'which-finds-file-name')
@@ -1386,53 +1373,6 @@ 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( async function findsExecutableWithScopedPermissions(
chmodOptions: string chmodOptions: string
): Promise<void> { ): Promise<void> {
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "@actions/io", "name": "@actions/io",
"version": "1.1.0", "version": "1.0.2",
"lockfileVersion": 1 "lockfileVersion": 1
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/io", "name": "@actions/io",
"version": "1.1.0", "version": "1.0.2",
"description": "Actions io lib", "description": "Actions io lib",
"keywords": [ "keywords": [
"github", "github",
+47
View File
@@ -1,3 +1,4 @@
import {ok} from 'assert'
import * as fs from 'fs' import * as fs from 'fs'
import * as path from 'path' import * as path from 'path'
@@ -58,6 +59,52 @@ export function isRooted(p: string): boolean {
return p.startsWith('/') 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. * Best effort attempt to determine whether a file exists and is executable.
* @param filePath file path to check * @param filePath file path to check
+52 -76
View File
@@ -1,4 +1,3 @@
import {ok} from 'assert'
import * as childProcess from 'child_process' import * as childProcess from 'child_process'
import * as path from 'path' import * as path from 'path'
import {promisify} from 'util' import {promisify} from 'util'
@@ -14,8 +13,6 @@ export interface CopyOptions {
recursive?: boolean recursive?: boolean
/** Optional. Whether to overwrite existing files in the destination. Defaults to true */ /** Optional. Whether to overwrite existing files in the destination. Defaults to true */
force?: boolean 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
} }
/** /**
@@ -39,7 +36,7 @@ export async function cp(
dest: string, dest: string,
options: CopyOptions = {} options: CopyOptions = {}
): Promise<void> { ): Promise<void> {
const {force, recursive, copySourceDirectory} = readCopyOptions(options) const {force, recursive} = readCopyOptions(options)
const destStat = (await ioUtil.exists(dest)) ? await ioUtil.stat(dest) : null const destStat = (await ioUtil.exists(dest)) ? await ioUtil.stat(dest) : null
// Dest is an existing file, but not forcing // Dest is an existing file, but not forcing
@@ -49,7 +46,7 @@ export async function cp(
// If dest is an existing directory, should copy inside. // If dest is an existing directory, should copy inside.
const newDest: string = const newDest: string =
destStat && destStat.isDirectory() && copySourceDirectory destStat && destStat.isDirectory()
? path.join(dest, path.basename(source)) ? path.join(dest, path.basename(source))
: dest : dest
@@ -164,8 +161,7 @@ export async function rmRF(inputPath: string): Promise<void> {
* @returns Promise<void> * @returns Promise<void>
*/ */
export async function mkdirP(fsPath: string): Promise<void> { export async function mkdirP(fsPath: string): Promise<void> {
ok(fsPath, 'a path argument must be provided') await ioUtil.mkdirP(fsPath)
await ioUtil.mkdir(fsPath, {recursive: true})
} }
/** /**
@@ -196,95 +192,75 @@ export async function which(tool: string, check?: boolean): Promise<string> {
) )
} }
} }
return result
} }
const matches: string[] = await findInPath(tool) try {
// build the list of extensions to try
if (matches && matches.length > 0) { const extensions: string[] = []
return matches[0] if (ioUtil.IS_WINDOWS && process.env.PATHEXT) {
} for (const extension of process.env.PATHEXT.split(path.delimiter)) {
if (extension) {
return '' extensions.push(extension)
} }
/**
* 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 it's rooted, return it if exists. otherwise return empty.
if (ioUtil.isRooted(tool)) { if (ioUtil.isRooted(tool)) {
const filePath: string = await ioUtil.tryGetExecutablePath(tool, extensions) const filePath: string = await ioUtil.tryGetExecutablePath(
tool,
extensions
)
if (filePath) { if (filePath) {
return [filePath] return filePath
}
return ''
} }
return [] // if any path separators, return empty
} if (tool.includes('/') || (ioUtil.IS_WINDOWS && tool.includes('\\'))) {
return ''
}
// if any path separators, return empty // build the list of directories
if (tool.includes(path.sep)) { //
return [] // 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[] = []
// build the list of directories if (process.env.PATH) {
// for (const p of process.env.PATH.split(path.delimiter)) {
// Note, technically "where" checks the current directory on Windows. From a toolkit perspective, if (p) {
// it feels like we should not do this. Checking the current directory seems like more of a use directories.push(p)
// 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 // return the first match
const matches: string[] = [] for (const directory of directories) {
const filePath = await ioUtil.tryGetExecutablePath(
for (const directory of directories) { directory + path.sep + tool,
const filePath = await ioUtil.tryGetExecutablePath( extensions
path.join(directory, tool), )
extensions if (filePath) {
) return filePath
if (filePath) { }
matches.push(filePath)
} }
}
return matches return ''
} catch (err) {
throw new Error(`which failed with message ${err.message}`)
}
} }
function readCopyOptions(options: CopyOptions): Required<CopyOptions> { function readCopyOptions(options: CopyOptions): Required<CopyOptions> {
const force = options.force == null ? true : options.force const force = options.force == null ? true : options.force
const recursive = Boolean(options.recursive) const recursive = Boolean(options.recursive)
const copySourceDirectory = return {force, recursive}
options.copySourceDirectory == null
? true
: Boolean(options.copySourceDirectory)
return {force, recursive, copySourceDirectory}
} }
async function cpDirRecursive( async function cpDirRecursive(
@@ -763,61 +763,6 @@ describe('@actions/tool-cache', function() {
expect(err.toString()).toContain('404') 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'
}
)
})
}) })
/** /**
+6 -9
View File
@@ -32,14 +32,12 @@ const userAgent = 'actions/tool-cache'
* @param url url of tool to download * @param url url of tool to download
* @param dest path to download tool * @param dest path to download tool
* @param auth authorization header * @param auth authorization header
* @param headers other headers
* @returns path to downloaded tool * @returns path to downloaded tool
*/ */
export async function downloadTool( export async function downloadTool(
url: string, url: string,
dest?: string, dest?: string,
auth?: string, auth?: string
headers?: IHeaders
): Promise<string> { ): Promise<string> {
dest = dest || path.join(_getTempDirectory(), uuidV4()) dest = dest || path.join(_getTempDirectory(), uuidV4())
await io.mkdirP(path.dirname(dest)) await io.mkdirP(path.dirname(dest))
@@ -58,7 +56,7 @@ export async function downloadTool(
const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds) const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds)
return await retryHelper.execute( return await retryHelper.execute(
async () => { async () => {
return await downloadToolAttempt(url, dest || '', auth, headers) return await downloadToolAttempt(url, dest || '', auth)
}, },
(err: Error) => { (err: Error) => {
if (err instanceof HTTPError && err.httpStatusCode) { if (err instanceof HTTPError && err.httpStatusCode) {
@@ -81,8 +79,7 @@ export async function downloadTool(
async function downloadToolAttempt( async function downloadToolAttempt(
url: string, url: string,
dest: string, dest: string,
auth?: string, auth?: string
headers?: IHeaders
): Promise<string> { ): Promise<string> {
if (fs.existsSync(dest)) { if (fs.existsSync(dest)) {
throw new Error(`Destination file path ${dest} already exists`) throw new Error(`Destination file path ${dest} already exists`)
@@ -93,12 +90,12 @@ async function downloadToolAttempt(
allowRetries: false allowRetries: false
}) })
let headers: IHeaders | undefined
if (auth) { if (auth) {
core.debug('set auth') core.debug('set auth')
if (headers === undefined) { headers = {
headers = {} authorization: auth
} }
headers.authorization = auth
} }
const response: httpm.HttpClientResponse = await http.get(url, headers) const response: httpm.HttpClientResponse = await http.get(url, headers)
+2 -1
View File
@@ -5,7 +5,8 @@
"strict": true, "strict": true,
"declaration": true, "declaration": true,
"target": "es6", "target": "es6",
"sourceMap": true "sourceMap": true,
"lib": ["es6"]
}, },
"exclude": [ "exclude": [
"node_modules", "node_modules",