Compare commits

..

55 Commits

Author SHA1 Message Date
Thomas Boop 4eaf5d56fb fix release notes 2021-09-28 10:00:54 -04:00
Thomas Boop 0a588c33a3 update for merging into main 2021-09-28 08:53:32 -04:00
Thomas Boop 8360baed2c beta release of 1.6.0 for oidc preview (#894) 2021-08-26 13:01:14 -04:00
Tingluo Huang 5c3e1c231d Merge pull request #893 from actions/users/tihuang/oidcupdate
react to OIDC service change.
2021-08-26 09:27:16 -07:00
TingluoHuang fe8d95a8fc lint 2021-08-25 21:53:12 -04:00
TingluoHuang b2c6bee10a encoding. 2021-08-25 17:35:01 -04:00
TingluoHuang eb88fce3c0 lint 2021-08-25 17:02:45 -04:00
TingluoHuang a7aa89a929 lint 2021-08-25 16:54:45 -04:00
TingluoHuang d7dd89f52b react to service changes. 2021-08-25 16:47:39 -04:00
Tingluo Huang 3da67ac4cb Merge pull request #887 from souravchanduka/main-oidc-client
Added OIDC client functionality in core package
2021-08-25 13:14:28 -07:00
Sourav Chanduka 0bab3623f4 eslint fix 2021-08-23 18:49:15 +05:30
Sourav Chanduka af75719a1e Merge branch 'main-oidc-client' of https://github.com/souravchanduka/toolkit into main-oidc-client 2021-08-23 10:51:29 +05:30
Sourav Chanduka d9212ff45b Addressed minor comments 2021-08-23 10:49:53 +05:30
Sourav Chanduka 2b58973dac Merge branch 'main' into main-oidc-client 2021-08-19 13:43:18 +05:30
Sourav Chanduka 4631854e0f version updated 2021-08-19 10:52:15 +05:30
Sourav Chanduka 09e9478907 comments resolved 2021-08-19 10:49:39 +05:30
Sourav Chanduka 1f8d7b5a64 default aud parameter 2021-08-18 16:53:54 +05:30
Sourav Chanduka 1c03cd3284 audience can be undefined 2021-08-18 14:38:04 +05:30
Sourav Chanduka 1162975200 removed whitespaces 2021-08-18 07:53:23 +05:30
Sourav Chanduka 3ceb264e9b readme updated 2021-08-18 07:51:22 +05:30
Sourav Chanduka 619566e5b8 Merge branch 'main' into main-oidc-client 2021-08-18 07:25:05 +05:30
Sourav Chanduka 547e30cada addressed comments 2021-08-18 07:22:04 +05:30
Sourav Chanduka 22e5d95310 addressed comments 2021-08-17 09:32:42 +05:30
Sourav Chanduka 1c86c4c890 payload updated 2021-08-16 14:29:58 +05:30
Sourav Chanduka c7ec4073b7 resolved comments 2021-08-16 12:46:17 +05:30
Sourav Chanduka d0f4aae179 Error Message updated 2021-08-12 16:14:22 +05:30
Sourav Chanduka dac801e6b9 error message updated 2021-08-12 12:11:34 +05:30
Sourav Chanduka 33891d9aef addressed comments 2021-08-12 10:07:18 +05:30
Sourav Chanduka cca2b1808b Addressed Comments 2021-08-11 03:50:43 +05:30
Sourav Chanduka 5d9c674092 comments resolved 2021-08-10 15:36:13 +05:30
Sourav Chanduka aa1968c9e9 async call fix 2021-08-10 11:05:53 +05:30
Sourav Chanduka f55900670f Resolved Comments 2021-08-09 06:36:02 +05:30
Sourav Chanduka 0a94a783ee README.md updated 2021-08-04 09:55:33 +05:30
Sourav Chanduka 9c6e7d8265 Moved oidc functionality to actions/core 2021-08-04 09:24:51 +05:30
Sourav Chanduka 5afccaa9db removed whitespaces 2021-07-29 12:48:27 +05:30
Sourav Chanduka 0c1cb726c3 Resolved Comments 2021-07-29 12:17:22 +05:30
Sourav Chanduka ff90431d27 Update README.md 2021-07-28 15:56:10 +05:30
Sourav Chanduka a2adaa856b Readme updated 2021-07-28 15:54:05 +05:30
Sourav Chanduka 662a937248 Resolved comments 2021-07-28 15:41:37 +05:30
Sourav Chanduka 330dc0b5b8 Updated Readme 2021-07-28 14:01:17 +05:30
Sourav Chanduka 58dfa1c4ac readme modified 2021-07-27 09:47:27 +05:30
Sourav Chanduka 456cf5a97f package.json updated 2021-07-27 08:47:54 +05:30
Sourav Chanduka 7965cc3c7d null ref fix 2021-07-27 06:27:07 +05:30
Sourav Chanduka f541fb1ac9 version update 2021-07-26 17:39:54 +05:30
Sourav Chanduka a6114b695e version updated 2021-07-26 17:35:50 +05:30
Sourav Chanduka 885469e8ce updated version 2021-07-26 17:34:51 +05:30
Sourav Chanduka 962ff70002 updated readme 2021-07-26 15:50:36 +05:30
Sourav Chanduka 8071504f3c added dist folder 2021-07-26 15:47:48 +05:30
Sourav Chanduka 9df74283c2 package.json modified 2021-07-20 17:43:22 +05:30
Sourav Chanduka 4831d7a53b removed unnecesary files 2021-07-20 17:41:02 +05:30
Sourav Chanduka 53a752919b Resolved issues 2021-07-20 15:56:28 +05:30
Sourav Chanduka c45ad60078 require added 2021-07-20 12:08:25 +05:30
Sourav Chanduka f7330892f1 oidc client changes 2021-07-20 08:58:34 +05:30
Sourav Chanduka 1322acbcca Comments Resolved 2021-07-12 08:37:14 +05:30
Sourav Chanduka bdacfc4c65 Inital draft of OIDC Client 2021-07-01 08:11:28 +05:30
37 changed files with 16086 additions and 8924 deletions
+4 -11
View File
@@ -44,27 +44,24 @@ jobs:
npm ci
npm run tsc
working-directory: packages/artifact
- name: Set artifact file contents
shell: bash
run: |
echo "non-gzip-artifact-content=hello" >> $GITHUB_ENV
echo "gzip-artifact-content=Some large amount of text that has a compression ratio that is greater than 100%. If greater than 100%, gzip is used to upload the file" >> $GITHUB_ENV
echo "empty-artifact-content=_EMPTY_" >> $GITHUB_ENV
- name: Create files that will be uploaded
run: |
mkdir artifact-path
mkdir artifact-path
echo ${{ env.non-gzip-artifact-content }} > artifact-path/world.txt
echo ${{ env.gzip-artifact-content }} > artifact-path/gzip.txt
touch artifact-path/empty.txt
# We're using node -e to call the functions directly available in the @actions/artifact package
- name: Upload artifacts using uploadArtifact()
run: |
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-1',['artifact-path/world.txt'], '${{ github.workspace }}'))"
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-2',['artifact-path/gzip.txt'], '${{ github.workspace }}'))"
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-3',['artifact-path/empty.txt'], '${{ github.workspace }}'))"
- name: Download artifacts using downloadArtifact()
run: |
@@ -72,15 +69,12 @@ jobs:
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-1','artifact-1-directory'))"
mkdir artifact-2-directory
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-2','artifact-2-directory'))"
mkdir artifact-3-directory
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-3','artifact-3-directory'))"
- name: Verify downloadArtifact()
shell: bash
run: |
packages/artifact/__tests__/test-artifact-file.sh "artifact-1-directory/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "artifact-2-directory/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "artifact-3-directory/artifact-path/empty.txt" "${{ env.empty-artifact-content }}"
- name: Download artifacts using downloadAllArtifacts()
run: |
@@ -91,5 +85,4 @@ jobs:
shell: bash
run: |
packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-1/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-2/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-3/artifact-path/empty.txt" "${{ env.empty-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-2/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}"
+2 -3
View File
@@ -31,10 +31,9 @@ jobs:
- name: Bootstrap
run: npm run bootstrap
# commenting out until https://github.com/lerna/lerna/issues/3010 is fixed
#- name: audit tools
- name: audit tools
# `|| npm audit` to pretty-print the output if vulnerabilies are found after filtering.
# run: npm audit --audit-level=moderate --json | scripts/audit-allow-list || npm audit --audit-level=moderate
run: npm audit --audit-level=moderate --json | scripts/audit-allow-list || npm audit --audit-level=moderate
- name: audit packages
run: npm run audit-all
-5
View File
@@ -18,11 +18,6 @@ jobs:
- name: verify package exists
run: ls packages/${{ github.event.inputs.package }}
- name: Set Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: npm install
run: npm install
+2 -2
View File
@@ -1,4 +1,4 @@
* @actions/actions-runtime
/packages/artifact/ @actions/artifacts-actions
/packages/cache/ @actions/actions-cache
/packages/artifact/ @actions/actions-service
/packages/cache/ @actions/actions-service
+1
View File
@@ -4,6 +4,7 @@ module.exports = {
roots: ['<rootDir>/packages'],
testEnvironment: 'node',
testMatch: ['**/__tests__/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
+13267 -3327
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -14,7 +14,7 @@
"test": "jest --testTimeout 10000"
},
"devDependencies": {
"@types/jest": "^27.0.2",
"@types/jest": "^24.9.1",
"@types/node": "^12.20.13",
"@types/signale": "^1.4.1",
"@typescript-eslint/parser": "^4.0.0",
@@ -23,10 +23,11 @@
"eslint-plugin-github": "^4.1.3",
"eslint-plugin-jest": "^22.21.0",
"flow-bin": "^0.115.0",
"jest": "^27.2.5",
"jest": "^26.6.3",
"jest-circus": "^24.9.0",
"lerna": "^4.0.0",
"prettier": "^1.19.1",
"ts-jest": "^27.0.5",
"ts-jest": "^26.5.6",
"typescript": "^3.9.9"
}
}
+1 -14
View File
@@ -60,17 +60,4 @@
### 0.5.2
- Add HTTP 500 as a retryable status code for artifact upload and download.
### 0.6.0
- Support upload from named pipes [#748](https://github.com/actions/toolkit/pull/748)
- Fixes to percentage values being greater than 100% when downloading all artifacts [#889](https://github.com/actions/toolkit/pull/889)
- Improved logging and output during artifact upload [#949](https://github.com/actions/toolkit/pull/949)
- Improvements to client-side validation for certain invalid characters not allowed during upload: [#951](https://github.com/actions/toolkit/pull/951)
- Faster upload speeds for certain types of large files by exempting gzip compression [#956](https://github.com/actions/toolkit/pull/956)
- More detailed logging when dealing with chunked uploads [#957](https://github.com/actions/toolkit/pull/957)
### 0.6.1
- Fix for failing 0 byte file uploads on Windows [#962](https://github.com/actions/toolkit/pull/962)
- Add HTTP 500 as a retryable status code for artifact upload and download.
@@ -1,78 +0,0 @@
import {
checkArtifactName,
checkArtifactFilePath
} from '../src/internal/path-and-artifact-name-validation'
import * as core from '@actions/core'
describe('Path and artifact name validation', () => {
beforeAll(() => {
// mock all output so that there is less noise when running tests
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
})
it('Check Artifact Name for any invalid characters', () => {
const invalidNames = [
'my\\artifact',
'my/artifact',
'my"artifact',
'my:artifact',
'my<artifact',
'my>artifact',
'my|artifact',
'my*artifact',
'my?artifact',
''
]
for (const invalidName of invalidNames) {
expect(() => {
checkArtifactName(invalidName)
}).toThrow()
}
const validNames = [
'my-normal-artifact',
'myNormalArtifact',
'm¥ñðrmålÄr†ï£å¢†'
]
for (const validName of validNames) {
expect(() => {
checkArtifactName(validName)
}).not.toThrow()
}
})
it('Check Artifact File Path for any invalid characters', () => {
const invalidNames = [
'some/invalid"artifact/path',
'some/invalid:artifact/path',
'some/invalid<artifact/path',
'some/invalid>artifact/path',
'some/invalid|artifact/path',
'some/invalid*artifact/path',
'some/invalid?artifact/path',
'some/invalid\rartifact/path',
'some/invalid\nartifact/path',
'some/invalid\r\nartifact/path',
''
]
for (const invalidName of invalidNames) {
expect(() => {
checkArtifactFilePath(invalidName)
}).toThrow()
}
const validNames = [
'my/perfectly-normal/artifact-path',
'my/perfectly\\Normal/Artifact-path',
'm¥/ñðrmål/Är†ï£å¢†'
]
for (const validName of validNames) {
expect(() => {
checkArtifactFilePath(validName)
}).not.toThrow()
}
})
})
@@ -18,10 +18,8 @@ if [ ! -f "$path" ]; then
exit 1
fi
actualContent=$(cat "$path")
if [ "$expectedContent" == "_EMPTY_" ] && [ ! -s "$path" ]; then
exit 0
elif [ "$actualContent" != "$expectedContent" ]; then
actualContent=$(cat $path)
if [ "$actualContent" != "$expectedContent" ];then
echo "File contents are not correct, expected $expectedContent, received $actualContent"
exit 1
fi
@@ -1,79 +0,0 @@
import * as core from '@actions/core'
import * as tmp from 'tmp-promise'
import * as path from 'path'
import * as io from '../../io/src/io'
import {promises as fs} from 'fs'
import {createGZipFileOnDisk} from '../src/internal/upload-gzip'
const root = path.join(__dirname, '_temp', 'upload-gzip')
const tempGzipFilePath = path.join(root, 'file1.gzip')
const tempZipFilePath = path.join(root, 'file2.zip')
const tempTarlzFilePath = path.join(root, 'file3.tar.lz')
const tempGzFilePath = path.join(root, 'file4.tar.gz')
const tempBz2FilePath = path.join(root, 'file5.tar.bz2')
const temp7zFilePath = path.join(root, 'file6.7z')
const tempNormalFilePath = path.join(root, 'file6.txt')
jest.mock('../src/internal/config-variables')
beforeAll(async () => {
// mock all output so that there is less noise when running tests
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
jest.spyOn(core, 'error').mockImplementation(() => {})
// clear temp directory and create files that will be "uploaded"
await io.rmRF(root)
await fs.mkdir(path.join(root))
await fs.writeFile(tempGzipFilePath, 'a file with a .gzip file extension')
await fs.writeFile(tempZipFilePath, 'a file with a .zip file extension')
await fs.writeFile(tempTarlzFilePath, 'a file with a tar.lz file extension')
await fs.writeFile(tempGzFilePath, 'a file with a gz file file extension')
await fs.writeFile(tempBz2FilePath, 'a file with a .bz2 file extension')
await fs.writeFile(temp7zFilePath, 'a file with a .7z file extension')
await fs.writeFile(tempNormalFilePath, 'a file with a .txt file extension')
})
test('Number.MAX_SAFE_INTEGER is returned when an existing compressed file is used', async () => {
// create temporary file
const tempFile = await tmp.file()
expect(await createGZipFileOnDisk(tempGzipFilePath, tempFile.path)).toEqual(
Number.MAX_SAFE_INTEGER
)
expect(await createGZipFileOnDisk(tempZipFilePath, tempFile.path)).toEqual(
Number.MAX_SAFE_INTEGER
)
expect(await createGZipFileOnDisk(tempTarlzFilePath, tempFile.path)).toEqual(
Number.MAX_SAFE_INTEGER
)
expect(await createGZipFileOnDisk(tempGzFilePath, tempFile.path)).toEqual(
Number.MAX_SAFE_INTEGER
)
expect(await createGZipFileOnDisk(tempBz2FilePath, tempFile.path)).toEqual(
Number.MAX_SAFE_INTEGER
)
expect(await createGZipFileOnDisk(temp7zFilePath, tempFile.path)).toEqual(
Number.MAX_SAFE_INTEGER
)
expect(
await createGZipFileOnDisk(tempNormalFilePath, tempFile.path)
).not.toEqual(Number.MAX_SAFE_INTEGER)
})
test('gzip file on disk gets successfully created', async () => {
// create temporary file
const tempFile = await tmp.file()
const gzipFileSize = await createGZipFileOnDisk(
tempNormalFilePath,
tempFile.path
)
const fileStat = await fs.stat(tempNormalFilePath)
const totalFileSize = fileStat.size
// original file and gzip file should not be equal in size
expect(gzipFileSize).not.toEqual(totalFileSize)
})
@@ -2,10 +2,6 @@ import * as http from 'http'
import * as io from '../../io/src/io'
import * as net from 'net'
import * as path from 'path'
import {mocked} from 'ts-jest/utils'
import {exec, execSync} from 'child_process'
import {createGunzip} from 'zlib'
import {promisify} from 'util'
import {UploadHttpClient} from '../src/internal/upload-http-client'
import * as core from '@actions/core'
import {promises as fs} from 'fs'
@@ -178,59 +174,6 @@ describe('Upload Tests', () => {
expect(uploadResult.uploadSize).toEqual(expectedTotalSize)
})
function hasMkfifo(): boolean {
try {
// make sure we drain the stdout
return (
process.platform !== 'win32' &&
execSync('which mkfifo').toString().length > 0
)
} catch (e) {
return false
}
}
const withMkfifoIt = hasMkfifo() ? it : it.skip
withMkfifoIt(
'Upload Artifact with content from named pipe - Success',
async () => {
// create a named pipe 'pipe' with content 'hello pipe'
const content = Buffer.from('hello pipe')
const pipeFilePath = path.join(root, 'pipe')
await promisify(exec)('mkfifo pipe', {cwd: root})
// don't want to await here as that would block until read
fs.writeFile(pipeFilePath, content)
const artifactName = 'successful-artifact'
const uploadSpecification: UploadSpecification[] = [
{
absoluteFilePath: pipeFilePath,
uploadFilePath: `${artifactName}/pipe`
}
]
const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13`
const uploadHttpClient = new UploadHttpClient()
const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer(
uploadUrl,
uploadSpecification
)
// accesses the ReadableStream that was passed into sendStream
// eslint-disable-next-line @typescript-eslint/unbound-method
const stream = mocked(HttpClient.prototype.sendStream).mock.calls[0][2]
expect(stream).not.toBeNull()
// decompresses the passed stream
const data: Buffer[] = []
for await (const chunk of stream.pipe(createGunzip())) {
data.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string))
}
const uploaded = Buffer.concat(data)
expect(uploadResult.failedItems.length).toEqual(0)
expect(uploaded).toEqual(content)
}
)
it('Upload Artifact - Failed Single File Upload', async () => {
const uploadSpecification: UploadSpecification[] = [
{
+60
View File
@@ -46,6 +46,66 @@ describe('Utils', () => {
}
})
it('Check Artifact Name for any invalid characters', () => {
const invalidNames = [
'my\\artifact',
'my/artifact',
'my"artifact',
'my:artifact',
'my<artifact',
'my>artifact',
'my|artifact',
'my*artifact',
'my?artifact',
''
]
for (const invalidName of invalidNames) {
expect(() => {
utils.checkArtifactName(invalidName)
}).toThrow()
}
const validNames = [
'my-normal-artifact',
'myNormalArtifact',
'm¥ñðrmålÄr†ï£å¢†'
]
for (const validName of validNames) {
expect(() => {
utils.checkArtifactName(validName)
}).not.toThrow()
}
})
it('Check Artifact File Path for any invalid characters', () => {
const invalidNames = [
'some/invalid"artifact/path',
'some/invalid:artifact/path',
'some/invalid<artifact/path',
'some/invalid>artifact/path',
'some/invalid|artifact/path',
'some/invalid*artifact/path',
'some/invalid?artifact/path',
''
]
for (const invalidName of invalidNames) {
expect(() => {
utils.checkArtifactFilePath(invalidName)
}).toThrow()
}
const validNames = [
'my/perfectly-normal/artifact-path',
'my/perfectly\\Normal/Artifact-path',
'm¥/ñðrmål/Är†ï£å¢†'
]
for (const validName of validNames) {
expect(() => {
utils.checkArtifactFilePath(validName)
}).not.toThrow()
}
})
it('Test negative artifact retention throws', () => {
expect(() => {
utils.getProperRetention(-1, undefined)
+21 -22
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/artifact",
"version": "0.6.1",
"version": "0.5.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -18,15 +18,14 @@
}
},
"@types/tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.1.tgz",
"integrity": "sha512-7cTXwKP/HLOPVgjg+YhBdQ7bMiobGMuoBmrGmqwIWJv8elC6t1DfVc/mn4fD9UE1IjhwmhaQ5pGVXkmXbH0rhg==",
"dev": true
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.1.0.tgz",
"integrity": "sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA=="
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
@@ -48,9 +47,9 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -96,27 +95,27 @@
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"requires": {
"glob": "^7.1.3"
}
},
"tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
"integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
"integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
"requires": {
"rimraf": "^3.0.0"
"rimraf": "^2.6.3"
}
},
"tmp-promise": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.2.tgz",
"integrity": "sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-2.0.2.tgz",
"integrity": "sha512-zl71nFWjPKW2KXs+73gEk8RmqvtAeXPxhWDkTUoa3MSMkjq3I+9OeknjF178MQoMYsdqL730hfzvNfEkePxq9Q==",
"requires": {
"tmp": "^0.2.0"
"tmp": "0.1.0"
}
},
"tunnel": {
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/artifact",
"version": "0.6.1",
"version": "0.5.2",
"preview": true,
"description": "Actions artifact lib",
"keywords": [
@@ -39,11 +39,11 @@
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/http-client": "^1.0.11",
"tmp": "^0.2.1",
"tmp-promise": "^3.0.2"
"@types/tmp": "^0.1.0",
"tmp": "^0.1.0",
"tmp-promise": "^2.0.2"
},
"devDependencies": {
"@types/tmp": "^0.2.1",
"typescript": "^3.8.3"
}
}
@@ -9,10 +9,10 @@ import {UploadOptions} from './upload-options'
import {DownloadOptions} from './download-options'
import {DownloadResponse} from './download-response'
import {
checkArtifactName,
createDirectoriesForArtifact,
createEmptyFilesForArtifact
} from './utils'
import {checkArtifactName} from './path-and-artifact-name-validation'
import {DownloadHttpClient} from './download-http-client'
import {getDownloadSpecification} from './download-specification'
import {getWorkSpaceDirectory} from './config-variables'
@@ -72,10 +72,6 @@ export class DefaultArtifactClient implements ArtifactClient {
rootDirectory: string,
options?: UploadOptions | undefined
): Promise<UploadResponse> {
core.info(
`Starting artifact upload
For more detailed logs during the artifact upload process, enable step-debugging: https://docs.github.com/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging#enabling-step-debug-logging`
)
checkArtifactName(name)
// Get specification for the files being uploaded
@@ -107,11 +103,7 @@ For more detailed logs during the artifact upload process, enable step-debugging
'No URL provided by the Artifact Service to upload an artifact to'
)
}
core.debug(`Upload Resource URL: ${response.fileContainerResourceUrl}`)
core.info(
`Container for artifact "${name}" successfully created. Starting upload of file(s)`
)
// Upload each of the files that were found concurrently
const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer(
@@ -122,27 +114,10 @@ For more detailed logs during the artifact upload process, enable step-debugging
// Update the size of the artifact to indicate we are done uploading
// The uncompressed size is used for display when downloading a zip of the artifact from the UI
core.info(
`File upload process has finished. Finalizing the artifact upload`
)
await uploadHttpClient.patchArtifactSize(uploadResult.totalSize, name)
if (uploadResult.failedItems.length > 0) {
core.info(
`Upload finished. There were ${uploadResult.failedItems.length} items that failed to upload`
)
} else {
core.info(
`Artifact has been finalized. All files have been successfully uploaded!`
)
}
core.info(
`
The raw size of all the files that were specified for upload is ${uploadResult.totalSize} bytes
The size of all the files that were uploaded is ${uploadResult.uploadSize} bytes. This takes into account any gzip compression used to reduce the upload size, time and storage
Note: The size of downloaded zips can differ significantly from the reported size. For more information see: https://github.com/actions/upload-artifact#zipped-artifact-downloads \r\n`
`Finished uploading artifact ${name}. Reported size is ${uploadResult.uploadSize} bytes. There were ${uploadResult.failedItems.length} items that failed to upload`
)
uploadResponse.artifactItems = uploadSpecification.map(
@@ -240,9 +215,6 @@ Note: The size of downloaded zips can differ significantly from the reported siz
while (downloadedArtifacts < artifacts.count) {
const currentArtifactToDownload = artifacts.value[downloadedArtifacts]
downloadedArtifacts += 1
core.info(
`starting download of artifact ${currentArtifactToDownload.name} : ${downloadedArtifacts}/${artifacts.count}`
)
// Get container entries for the specific artifact
const items = await downloadHttpClient.getContainerItems(
@@ -29,20 +29,8 @@ export interface PatchArtifactSizeSuccessResponse {
}
export interface UploadResults {
/**
* The size in bytes of data that was transferred during the upload process to the actions backend service. This takes into account possible
* gzip compression to reduce the amount of data that needs to be transferred
*/
uploadSize: number
/**
* The raw size of the files that were specified for upload
*/
totalSize: number
/**
* An array of files that failed to upload
*/
failedItems: string[]
}
@@ -228,6 +228,9 @@ export class DownloadHttpClient {
let response: IHttpClientResponse
try {
response = await makeDownloadRequest()
if (core.isDebug()) {
displayHttpDiagnostics(response)
}
} catch (error) {
// if an error is caught, it is usually indicative of a timeout so retry the download
core.info('An error occurred while attempting to download a file')
@@ -1,82 +0,0 @@
import {info} from '@actions/core'
/**
* Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected
* from the server if attempted to be sent over. These characters are not allowed due to limitations with certain
* file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an
* individual filesystem/platform will not be supported on all fileSystems/platforms
*
* FilePaths can include characters such as \ and / which are not permitted in the artifact name alone
*/
const invalidArtifactFilePathCharacters = new Map<string, string>([
['"', ' Double quote "'],
[':', ' Colon :'],
['<', ' Less than <'],
['>', ' Greater than >'],
['|', ' Vertical bar |'],
['*', ' Asterisk *'],
['?', ' Question mark ?'],
['\r', ' Carriage return \\r'],
['\n', ' Line feed \\n']
])
const invalidArtifactNameCharacters = new Map<string, string>([
...invalidArtifactFilePathCharacters,
['\\', ' Backslash \\'],
['/', ' Forward slash /']
])
/**
* Scans the name of the artifact to make sure there are no illegal characters
*/
export function checkArtifactName(name: string): void {
if (!name) {
throw new Error(`Artifact name: ${name}, is incorrectly provided`)
}
for (const [
invalidCharacterKey,
errorMessageForCharacter
] of invalidArtifactNameCharacters) {
if (name.includes(invalidCharacterKey)) {
throw new Error(
`Artifact name is not valid: ${name}. Contains the following character: ${errorMessageForCharacter}
Invalid characters include: ${Array.from(
invalidArtifactNameCharacters.values()
).toString()}
These characters are not allowed in the artifact name due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems.`
)
}
}
info(`Artifact name is valid!`)
}
/**
* Scans the name of the filePath used to make sure there are no illegal characters
*/
export function checkArtifactFilePath(path: string): void {
if (!path) {
throw new Error(`Artifact path: ${path}, is incorrectly provided`)
}
for (const [
invalidCharacterKey,
errorMessageForCharacter
] of invalidArtifactFilePathCharacters) {
if (path.includes(invalidCharacterKey)) {
throw new Error(
`Artifact path is not valid: ${path}. Contains the following character: ${errorMessageForCharacter}
Invalid characters include: ${Array.from(
invalidArtifactFilePathCharacters.values()
).toString()}
The following characters are not allowed in files that are uploaded due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems.
`
)
}
}
}
@@ -14,15 +14,16 @@ export class StatusReporter {
private displayFrequencyInMilliseconds: number
private largeFiles = new Map<string, string>()
private totalFileStatus: NodeJS.Timeout | undefined
private largeFileStatus: NodeJS.Timeout | undefined
constructor(displayFrequencyInMilliseconds: number) {
this.totalFileStatus = undefined
this.largeFileStatus = undefined
this.displayFrequencyInMilliseconds = displayFrequencyInMilliseconds
}
setTotalNumberOfFilesToProcess(fileTotal: number): void {
this.totalNumberOfFilesToProcess = fileTotal
this.processedCount = 0
}
start(): void {
@@ -42,29 +43,42 @@ export class StatusReporter {
)}%)`
)
}, this.displayFrequencyInMilliseconds)
// displays extra information about any large files that take a significant amount of time to upload or download every 1 second
this.largeFileStatus = setInterval(() => {
for (const value of Array.from(this.largeFiles.values())) {
info(value)
}
// delete all entries in the map after displaying the information so it will not be displayed again unless explicitly added
this.largeFiles.clear()
}, 1000)
}
// if there is a large file that is being uploaded in chunks, this is used to display extra information about the status of the upload
updateLargeFileStatus(
fileName: string,
chunkStartIndex: number,
chunkEndIndex: number,
totalUploadFileSize: number
numerator: number,
denominator: number
): void {
// display 1 decimal place without any rounding
const percentage = this.formatPercentage(chunkEndIndex, totalUploadFileSize)
info(
`Uploaded ${fileName} (${percentage.slice(
0,
percentage.indexOf('.') + 2
)}%) bytes ${chunkStartIndex}:${chunkEndIndex}`
)
const percentage = this.formatPercentage(numerator, denominator)
const displayInformation = `Uploading ${fileName} (${percentage.slice(
0,
percentage.indexOf('.') + 2
)}%)`
// any previously added display information should be overwritten for the specific large file because a map is being used
this.largeFiles.set(fileName, displayInformation)
}
stop(): void {
if (this.totalFileStatus) {
clearInterval(this.totalFileStatus)
}
if (this.largeFileStatus) {
clearInterval(this.largeFileStatus)
}
}
incrementProcessedCount(): void {
@@ -3,20 +3,6 @@ import * as zlib from 'zlib'
import {promisify} from 'util'
const stat = promisify(fs.stat)
/**
* GZipping certain files that are already compressed will likely not yield further size reductions. Creating large temporary gzip
* files then will just waste a lot of time before ultimately being discarded (especially for very large files).
* If any of these types of files are encountered then on-disk gzip creation will be skipped and the original file will be uploaded as-is
*/
const gzipExemptFileExtensions = [
'.gzip',
'.zip',
'.tar.lz',
'.tar.gz',
'.tar.bz2',
'.7z'
]
/**
* Creates a Gzip compressed file of an original file at the provided temporary filepath location
* @param {string} originalFilePath filepath of whatever will be compressed. The original file will be unmodified
@@ -27,13 +13,6 @@ export async function createGZipFileOnDisk(
originalFilePath: string,
tempFilePath: string
): Promise<number> {
for (const gzipExemptExtension of gzipExemptFileExtensions) {
if (originalFilePath.endsWith(gzipExemptExtension)) {
// return a really large number so that the original file gets uploaded
return Number.MAX_SAFE_INTEGER
}
}
return new Promise((resolve, reject) => {
const inputStream = fs.createReadStream(originalFilePath)
const gzip = zlib.createGzip()
@@ -219,41 +219,29 @@ export class UploadHttpClient {
httpClientIndex: number,
parameters: UploadFileParameters
): Promise<UploadFileResult> {
const fileStat: fs.Stats = await stat(parameters.file)
const totalFileSize = fileStat.size
const isFIFO = fileStat.isFIFO()
const totalFileSize: number = (await stat(parameters.file)).size
let offset = 0
let isUploadSuccessful = true
let failedChunkSizes = 0
let uploadFileSize = 0
let isGzip = true
// the file that is being uploaded is less than 64k in size to increase throughput and to minimize disk I/O
// the file that is being uploaded is less than 64k in size, to increase throughput and to minimize disk I/O
// for creating a new GZip file, an in-memory buffer is used for compression
// with named pipes the file size is reported as zero in that case don't read the file in memory
if (!isFIFO && totalFileSize < 65536) {
core.debug(
`${parameters.file} is less than 64k in size. Creating a gzip file in-memory to potentially reduce the upload size`
)
if (totalFileSize < 65536) {
const buffer = await createGZipFileInBuffer(parameters.file)
// An open stream is needed in the event of a failure and we need to retry. If a NodeJS.ReadableStream is directly passed in,
//An open stream is needed in the event of a failure and we need to retry. If a NodeJS.ReadableStream is directly passed in,
// it will not properly get reset to the start of the stream if a chunk upload needs to be retried
let openUploadStream: () => NodeJS.ReadableStream
if (totalFileSize < buffer.byteLength) {
// compression did not help with reducing the size, use a readable stream from the original file for upload
core.debug(
`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`
)
openUploadStream = () => fs.createReadStream(parameters.file)
isGzip = false
uploadFileSize = totalFileSize
} else {
// create a readable stream using a PassThrough stream that is both readable and writable
core.debug(
`A gzip file created for ${parameters.file} helped with reducing the size of the original file. The file will be uploaded using gzip.`
)
openUploadStream = () => {
const passThrough = new stream.PassThrough()
passThrough.end(buffer)
@@ -289,9 +277,6 @@ export class UploadHttpClient {
// the file that is being uploaded is greater than 64k in size, a temporary file gets created on disk using the
// npm tmp-promise package and this file gets used to create a GZipped file
const tempFile = await tmp.file()
core.debug(
`${parameters.file} is greater than 64k in size. Creating a gzip file on-disk ${tempFile.path} to potentially reduce the upload size`
)
// create a GZip file of the original file being uploaded, the original file should not be modified in any way
uploadFileSize = await createGZipFileOnDisk(
@@ -302,18 +287,10 @@ export class UploadHttpClient {
let uploadFilePath = tempFile.path
// compression did not help with size reduction, use the original file for upload and delete the temp GZip file
// for named pipes totalFileSize is zero, this assumes compression did help
if (!isFIFO && totalFileSize < uploadFileSize) {
core.debug(
`The gzip file created for ${parameters.file} did not help with reducing the size of the file. The original file will be uploaded as-is`
)
if (totalFileSize < uploadFileSize) {
uploadFileSize = totalFileSize
uploadFilePath = parameters.file
isGzip = false
} else {
core.debug(
`The gzip file created for ${parameters.file} is smaller than the original file. The file will be uploaded using gzip.`
)
}
let abortFileUpload = false
@@ -324,8 +301,17 @@ export class UploadHttpClient {
parameters.maxChunkSize
)
const startChunkIndex = offset
const endChunkIndex = offset + chunkSize - 1
// if an individual file is greater than 100MB (1024*1024*100) in size, display extra information about the upload status
if (uploadFileSize > 104857600) {
this.statusReporter.updateLargeFileStatus(
parameters.file,
offset,
uploadFileSize
)
}
const start = offset
const end = offset + chunkSize - 1
offset += parameters.maxChunkSize
if (abortFileUpload) {
@@ -339,12 +325,12 @@ export class UploadHttpClient {
parameters.resourceUrl,
() =>
fs.createReadStream(uploadFilePath, {
start: startChunkIndex,
end: endChunkIndex,
start,
end,
autoClose: false
}),
startChunkIndex,
endChunkIndex,
start,
end,
uploadFileSize,
isGzip,
totalFileSize
@@ -357,22 +343,11 @@ export class UploadHttpClient {
failedChunkSizes += chunkSize
core.warning(`Aborting upload for ${parameters.file} due to failure`)
abortFileUpload = true
} else {
// if an individual file is greater than 8MB (1024*1024*8) in size, display extra information about the upload status
if (uploadFileSize > 8388608) {
this.statusReporter.updateLargeFileStatus(
parameters.file,
startChunkIndex,
endChunkIndex,
uploadFileSize
)
}
}
}
// Delete the temporary file that was created as part of the upload. If the temp file does not get manually deleted by
// calling cleanup, it gets removed when the node process exits. For more info see: https://www.npmjs.com/package/tmp-promise#about
core.debug(`deleting temporary gzip file ${tempFile.path}`)
await tempFile.cleanup()
return {
@@ -1,7 +1,7 @@
import * as fs from 'fs'
import {debug} from '@actions/core'
import {join, normalize, resolve} from 'path'
import {checkArtifactFilePath} from './path-and-artifact-name-validation'
import {checkArtifactName, checkArtifactFilePath} from './utils'
export interface UploadSpecification {
absoluteFilePath: string
@@ -19,7 +19,8 @@ export function getUploadSpecification(
rootDirectory: string,
artifactFiles: string[]
): UploadSpecification[] {
// artifact name was checked earlier on, no need to check again
checkArtifactName(artifactName)
const specifications: UploadSpecification[] = []
if (!fs.existsSync(rootDirectory)) {
+50 -1
View File
@@ -30,7 +30,7 @@ export function getExponentialRetryTimeInMilliseconds(
const maxTime = minTime * getRetryMultiplier()
// returns a random number between the minTime (inclusive) and the maxTime (exclusive)
return Math.trunc(Math.random() * (maxTime - minTime) + minTime)
return Math.random() * (maxTime - minTime) + minTime
}
/**
@@ -237,6 +237,55 @@ Header Information: ${JSON.stringify(response.message.headers, undefined, 2)}
)
}
/**
* Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected
* from the server if attempted to be sent over. These characters are not allowed due to limitations with certain
* file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an
* individual filesystem/platform will not be supported on all fileSystems/platforms
*
* FilePaths can include characters such as \ and / which are not permitted in the artifact name alone
*/
const invalidArtifactFilePathCharacters = ['"', ':', '<', '>', '|', '*', '?']
const invalidArtifactNameCharacters = [
...invalidArtifactFilePathCharacters,
'\\',
'/'
]
/**
* Scans the name of the artifact to make sure there are no illegal characters
*/
export function checkArtifactName(name: string): void {
if (!name) {
throw new Error(`Artifact name: ${name}, is incorrectly provided`)
}
for (const invalidChar of invalidArtifactNameCharacters) {
if (name.includes(invalidChar)) {
throw new Error(
`Artifact name is not valid: ${name}. Contains character: "${invalidChar}". Invalid artifact name characters include: ${invalidArtifactNameCharacters.toString()}.`
)
}
}
}
/**
* Scans the name of the filePath used to make sure there are no illegal characters
*/
export function checkArtifactFilePath(path: string): void {
if (!path) {
throw new Error(`Artifact path: ${path}, is incorrectly provided`)
}
for (const invalidChar of invalidArtifactFilePathCharacters) {
if (path.includes(invalidChar)) {
throw new Error(
`Artifact path is not valid: ${path}. Contains character: "${invalidChar}". Invalid characters include: ${invalidArtifactFilePathCharacters.toString()}.`
)
}
}
}
export async function createDirectoriesForArtifact(
directories: string[]
): Promise<void> {
+1 -1
View File
@@ -4,7 +4,7 @@
See ["Caching dependencies to speed up workflows"](https://help.github.com/github/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows) for how caching works.
Note that GitHub will remove any cache entries that have not been accessed in over 7 days. There is no limit on the number of caches you can store, but the total size of all caches in a repository is limited to 10 GB. If you exceed this limit, GitHub will save your cache but will begin evicting caches until the total size is less than 10 GB.
Note that GitHub will remove any cache entries that have not been accessed in over 7 days. There is no limit on the number of caches you can store, but the total size of all caches in a repository is limited to 5 GB. If you exceed this limit, GitHub will save your cache but will begin evicting caches until the total size is less than 5 GB.
## Usage
-3
View File
@@ -40,6 +40,3 @@
### 1.0.7
- Fixes permissions issue extracting archives with GNU tar on macOS ([issue](https://github.com/actions/cache/issues/527))
### 1.0.8
- Increase the allowed artifact cache size from 5GB to 10GB ([issue](https://github.com/actions/cache/discussions/497))
+2 -2
View File
@@ -46,7 +46,7 @@ test('save with large cache outputs should fail', async () => {
const createTarMock = jest.spyOn(tar, 'createTar')
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit
jest
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
.mockReturnValueOnce(cacheSize)
@@ -56,7 +56,7 @@ test('save with large cache outputs should fail', async () => {
.mockReturnValueOnce(Promise.resolve(compression))
await expect(saveCache([filePath], primaryKey)).rejects.toThrowError(
'Cache size of ~11264 MB (11811160064 B) is over the 10GB limit, not saving cache.'
'Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache.'
)
const archiveFolder = '/foo/bar'
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/cache",
"version": "1.0.8",
"version": "1.0.7",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/cache",
"version": "1.0.8",
"version": "1.0.7",
"preview": true,
"description": "Actions cache lib",
"keywords": [
+2 -2
View File
@@ -172,14 +172,14 @@ export async function saveCache(
await listTar(archivePath, compressionMethod)
}
const fileSizeLimit = 10 * 1024 * 1024 * 1024 // 10GB per repo limit
const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath)
core.debug(`File Size: ${archiveFileSize}`)
if (archiveFileSize > fileSizeLimit) {
throw new Error(
`Cache size of ~${Math.round(
archiveFileSize / (1024 * 1024)
)} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`
)} MB (${archiveFileSize} B) is over the 5GB limit, not saving cache.`
)
}
-5
View File
@@ -142,11 +142,6 @@ export interface AnnotationProperties {
*/
title?: string
/**
* The name of the file for which the annotation should be created.
*/
file?: string
/**
* The start line for the annotation.
*/
+2 -37
View File
@@ -275,14 +275,13 @@ describe('@actions/core', () => {
const message = 'this is my error message'
core.error(new Error(message), {
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
assertWriteCalls([
`::error title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
`::error title=A title,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
])
})
@@ -306,59 +305,25 @@ describe('@actions/core', () => {
const message = 'this is my error message'
core.warning(new Error(message), {
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
assertWriteCalls([
`::warning title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
])
})
it('notice sets the correct message', () => {
core.notice('Notice')
assertWriteCalls([`::notice::Notice${os.EOL}`])
})
it('notice escapes the message', () => {
core.notice('\r\nnotice\n')
assertWriteCalls([`::notice::%0D%0Anotice%0A${os.EOL}`])
})
it('notice handles an error object', () => {
const message = 'this is my error message'
core.notice(new Error(message))
assertWriteCalls([`::notice::Error: ${message}${os.EOL}`])
})
it('notice handles parameters correctly', () => {
const message = 'this is my error message'
core.notice(new Error(message), {
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
assertWriteCalls([
`::notice title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
`::warning title=A title,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
])
})
it('annotations map field names correctly', () => {
const commandProperties = toCommandProperties({
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
expect(commandProperties.title).toBe('A title')
expect(commandProperties.file).toBe('root/test.txt')
expect(commandProperties.col).toBe(1)
expect(commandProperties.endColumn).toBe(2)
expect(commandProperties.line).toBe(5)
-5
View File
@@ -43,11 +43,6 @@ export interface AnnotationProperties {
*/
title?: string
/**
* The path of the file for which the annotation should be created.
*/
file?: string
/**
* The start line for the annotation.
*/
-1
View File
@@ -32,7 +32,6 @@ export function toCommandProperties(
return {
title: annotationProperties.title,
file: annotationProperties.file,
line: annotationProperties.startLine,
endLine: annotationProperties.endLine,
col: annotationProperties.startColumn,
+1
View File
@@ -3,6 +3,7 @@ module.exports = {
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
+2604 -5053
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -44,6 +44,7 @@
"@octokit/plugin-rest-endpoint-methods": "^5.1.1"
},
"devDependencies": {
"jest": "^26.6.3",
"proxy": "^1.0.2"
}
}