Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80d992795c | ||
|
|
b9de68a590 | ||
|
|
1d61e5fb19 | ||
|
|
4abb5a2ae0 | ||
|
|
6b18932b86 | ||
|
|
56c460630a | ||
|
|
e1a991ffb7 | ||
|
|
cc9ec0424e | ||
|
|
c91bdbadbf | ||
|
|
23811ac52f | ||
|
|
819157bf87 | ||
|
|
86102e88e9 |
@@ -24,10 +24,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set Node.js 12.x
|
- name: Set Node.js 16.x
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
|
|
||||||
# In order to upload & download artifacts from a shell script, certain env variables need to be set that are only available in the
|
# In order to upload & download artifacts from a shell script, certain env variables need to be set that are only available in the
|
||||||
# node context. This runs a local action that gets and sets the necessary env variables that are needed
|
# node context. This runs a local action that gets and sets the necessary env variables that are needed
|
||||||
@@ -55,16 +55,16 @@ jobs:
|
|||||||
- name: Create files that will be uploaded
|
- name: Create files that will be uploaded
|
||||||
run: |
|
run: |
|
||||||
mkdir artifact-path
|
mkdir artifact-path
|
||||||
echo ${{ env.non-gzip-artifact-content }} > artifact-path/world.txt
|
echo '${{ env.non-gzip-artifact-content }}' > artifact-path/world.txt
|
||||||
echo ${{ env.gzip-artifact-content }} > artifact-path/gzip.txt
|
echo '${{ env.gzip-artifact-content }}' > artifact-path/gzip.txt
|
||||||
touch artifact-path/empty.txt
|
touch artifact-path/empty.txt
|
||||||
|
|
||||||
# We're using node -e to call the functions directly available in the @actions/artifact package
|
# We're using node -e to call the functions directly available in the @actions/artifact package
|
||||||
- name: Upload artifacts using uploadArtifact()
|
- name: Upload artifacts using uploadArtifact()
|
||||||
run: |
|
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-1',['artifact-path/world.txt'], process.argv[1]))" "${{ 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-2',['artifact-path/gzip.txt'], process.argv[1]))" "${{ github.workspace }}"
|
||||||
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-3',['artifact-path/empty.txt'], '${{ github.workspace }}'))"
|
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-3',['artifact-path/empty.txt'], process.argv[1]))" "${{ github.workspace }}"
|
||||||
|
|
||||||
- name: Download artifacts using downloadArtifact()
|
- name: Download artifacts using downloadArtifact()
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set Node.js 12.x
|
- name: Set Node.js 16.x
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
|
|
||||||
- name: npm install
|
- name: npm install
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set Node.js 12.x
|
- name: Set Node.js 16.x
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
|
|
||||||
# In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the
|
# In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the
|
||||||
# node context. This runs a local action that gets and sets the necessary env variables that are needed
|
# node context. This runs a local action that gets and sets the necessary env variables that are needed
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ jobs:
|
|||||||
- name: verify package exists
|
- name: verify package exists
|
||||||
run: ls packages/${{ github.event.inputs.package }}
|
run: ls packages/${{ github.event.inputs.package }}
|
||||||
|
|
||||||
- name: Set Node.js 12.x
|
- name: Set Node.js 16.x
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
|
|
||||||
- name: npm install
|
- name: npm install
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set Node.js 12.x
|
- name: Set Node.js 16.x
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
|
|
||||||
- name: npm install
|
- name: npm install
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|||||||
Generated
+2789
-1584
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
"@types/node": "^12.20.13",
|
"@types/node": "^16.18.1",
|
||||||
"@types/signale": "^1.4.1",
|
"@types/signale": "^1.4.1",
|
||||||
"@typescript-eslint/parser": "^4.0.0",
|
"@typescript-eslint/parser": "^4.0.0",
|
||||||
"concurrently": "^6.1.0",
|
"concurrently": "^6.1.0",
|
||||||
|
|||||||
@@ -219,6 +219,13 @@ export class DownloadHttpClient {
|
|||||||
fileDownloadPath: string
|
fileDownloadPath: string
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
destinationStream.close()
|
destinationStream.close()
|
||||||
|
// await until file is created at downloadpath; node15 and up fs.createWriteStream had not created a file yet
|
||||||
|
await new Promise<void>(resolve => {
|
||||||
|
destinationStream.on('close', resolve)
|
||||||
|
if (destinationStream.writableFinished) {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
await rmFile(fileDownloadPath)
|
await rmFile(fileDownloadPath)
|
||||||
destinationStream = fs.createWriteStream(fileDownloadPath)
|
destinationStream = fs.createWriteStream(fileDownloadPath)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+56
-173
@@ -1,12 +1,7 @@
|
|||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
import * as io from '@actions/io'
|
import * as io from '@actions/io'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {
|
import {CacheFilename, CompressionMethod} from '../src/internal/constants'
|
||||||
CacheFilename,
|
|
||||||
CompressionMethod,
|
|
||||||
GnuTarPathOnWindows,
|
|
||||||
SystemTarPathOnWindows
|
|
||||||
} from '../src/internal/constants'
|
|
||||||
import * as tar from '../src/internal/tar'
|
import * as tar from '../src/internal/tar'
|
||||||
import * as utils from '../src/internal/cacheUtils'
|
import * as utils from '../src/internal/cacheUtils'
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
@@ -18,7 +13,7 @@ jest.mock('@actions/io')
|
|||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
const IS_MAC = process.platform === 'darwin'
|
const IS_MAC = process.platform === 'darwin'
|
||||||
|
|
||||||
const defaultTarPath = IS_MAC ? 'gtar' : 'tar'
|
const defaultTarPath = process.platform === 'darwin' ? 'gtar' : 'tar'
|
||||||
|
|
||||||
function getTempDir(): string {
|
function getTempDir(): string {
|
||||||
return path.join(__dirname, '_temp', 'tar')
|
return path.join(__dirname, '_temp', 'tar')
|
||||||
@@ -33,10 +28,6 @@ beforeAll(async () => {
|
|||||||
await jest.requireActual('@actions/io').rmRF(getTempDir())
|
await jest.requireActual('@actions/io').rmRF(getTempDir())
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.restoreAllMocks()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
delete process.env['GITHUB_WORKSPACE']
|
delete process.env['GITHUB_WORKSPACE']
|
||||||
await jest.requireActual('@actions/io').rmRF(getTempDir())
|
await jest.requireActual('@actions/io').rmRF(getTempDir())
|
||||||
@@ -50,15 +41,16 @@ test('zstd extract tar', async () => {
|
|||||||
? `${process.env['windir']}\\fakepath\\cache.tar`
|
? `${process.env['windir']}\\fakepath\\cache.tar`
|
||||||
: 'cache.tar'
|
: 'cache.tar'
|
||||||
const workspace = process.env['GITHUB_WORKSPACE']
|
const workspace = process.env['GITHUB_WORKSPACE']
|
||||||
const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath
|
|
||||||
|
|
||||||
await tar.extractTar(archivePath, CompressionMethod.Zstd)
|
await tar.extractTar(archivePath, CompressionMethod.Zstd)
|
||||||
|
|
||||||
expect(mkdirMock).toHaveBeenCalledWith(workspace)
|
expect(mkdirMock).toHaveBeenCalledWith(workspace)
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"${tarPath}"`,
|
`"${defaultTarPath}"`,
|
||||||
[
|
[
|
||||||
|
'--use-compress-program',
|
||||||
|
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30',
|
||||||
'-xf',
|
'-xf',
|
||||||
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||||
'-P',
|
'-P',
|
||||||
@@ -66,50 +58,11 @@ test('zstd extract tar', async () => {
|
|||||||
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'] : [])
|
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||||
.concat([
|
|
||||||
'--use-compress-program',
|
|
||||||
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
|
|
||||||
]),
|
|
||||||
{cwd: undefined}
|
{cwd: undefined}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('zstd extract tar with windows BSDtar', async () => {
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
const mkdirMock = jest.spyOn(io, 'mkdirP')
|
|
||||||
const execMock = jest.spyOn(exec, 'exec')
|
|
||||||
jest
|
|
||||||
.spyOn(utils, 'getGnuTarPathOnWindows')
|
|
||||||
.mockReturnValue(Promise.resolve(''))
|
|
||||||
|
|
||||||
const archivePath = `${process.env['windir']}\\fakepath\\cache.tar`
|
|
||||||
const workspace = process.env['GITHUB_WORKSPACE']
|
|
||||||
const tarPath = SystemTarPathOnWindows
|
|
||||||
const tarFilename = 'cache.tar'
|
|
||||||
|
|
||||||
await tar.extractTar(archivePath, CompressionMethod.Zstd)
|
|
||||||
|
|
||||||
expect(mkdirMock).toHaveBeenCalledWith(workspace)
|
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
|
||||||
'zstd -d --long=30 -o',
|
|
||||||
[
|
|
||||||
tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'&&',
|
|
||||||
`${tarPath}`,
|
|
||||||
'-xf',
|
|
||||||
tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'-P',
|
|
||||||
'-C',
|
|
||||||
workspace?.replace(/\\/g, '/')
|
|
||||||
],
|
|
||||||
{cwd: undefined}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test('gzip extract tar', async () => {
|
test('gzip extract tar', async () => {
|
||||||
const mkdirMock = jest.spyOn(io, 'mkdirP')
|
const mkdirMock = jest.spyOn(io, 'mkdirP')
|
||||||
const execMock = jest.spyOn(exec, 'exec')
|
const execMock = jest.spyOn(exec, 'exec')
|
||||||
@@ -121,47 +74,49 @@ test('gzip extract tar', async () => {
|
|||||||
await tar.extractTar(archivePath, CompressionMethod.Gzip)
|
await tar.extractTar(archivePath, CompressionMethod.Gzip)
|
||||||
|
|
||||||
expect(mkdirMock).toHaveBeenCalledWith(workspace)
|
expect(mkdirMock).toHaveBeenCalledWith(workspace)
|
||||||
const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath
|
const tarPath = IS_WINDOWS
|
||||||
|
? `${process.env['windir']}\\System32\\tar.exe`
|
||||||
|
: defaultTarPath
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"${tarPath}"`,
|
`"${tarPath}"`,
|
||||||
[
|
[
|
||||||
|
'-z',
|
||||||
'-xf',
|
'-xf',
|
||||||
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||||
'-P',
|
'-P',
|
||||||
'-C',
|
'-C',
|
||||||
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
|
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace
|
||||||
]
|
].concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
|
||||||
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
|
|
||||||
.concat(['-z']),
|
|
||||||
{cwd: undefined}
|
{cwd: undefined}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('gzip extract GNU tar on windows with GNUtar in path', async () => {
|
test('gzip extract GNU tar on windows', async () => {
|
||||||
if (IS_WINDOWS) {
|
if (IS_WINDOWS) {
|
||||||
// GNU tar present in path but not at default location
|
jest.spyOn(fs, 'existsSync').mockReturnValueOnce(false)
|
||||||
jest
|
|
||||||
.spyOn(utils, 'getGnuTarPathOnWindows')
|
const isGnuMock = jest
|
||||||
.mockReturnValue(Promise.resolve('tar'))
|
.spyOn(utils, 'isGnuTarInstalled')
|
||||||
|
.mockReturnValue(Promise.resolve(true))
|
||||||
const execMock = jest.spyOn(exec, 'exec')
|
const execMock = jest.spyOn(exec, 'exec')
|
||||||
const archivePath = `${process.env['windir']}\\fakepath\\cache.tar`
|
const archivePath = `${process.env['windir']}\\fakepath\\cache.tar`
|
||||||
const workspace = process.env['GITHUB_WORKSPACE']
|
const workspace = process.env['GITHUB_WORKSPACE']
|
||||||
|
|
||||||
await tar.extractTar(archivePath, CompressionMethod.Gzip)
|
await tar.extractTar(archivePath, CompressionMethod.Gzip)
|
||||||
|
|
||||||
|
expect(isGnuMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"tar"`,
|
`"tar"`,
|
||||||
[
|
[
|
||||||
|
'-z',
|
||||||
'-xf',
|
'-xf',
|
||||||
archivePath.replace(/\\/g, '/'),
|
archivePath.replace(/\\/g, '/'),
|
||||||
'-P',
|
'-P',
|
||||||
'-C',
|
'-C',
|
||||||
workspace?.replace(/\\/g, '/'),
|
workspace?.replace(/\\/g, '/'),
|
||||||
'--force-local',
|
'--force-local'
|
||||||
'-z'
|
|
||||||
],
|
],
|
||||||
{cwd: undefined}
|
{cwd: undefined}
|
||||||
)
|
)
|
||||||
@@ -179,13 +134,13 @@ test('zstd create tar', async () => {
|
|||||||
|
|
||||||
await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Zstd)
|
await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Zstd)
|
||||||
|
|
||||||
const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath
|
|
||||||
|
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"${tarPath}"`,
|
`"${defaultTarPath}"`,
|
||||||
[
|
[
|
||||||
'--posix',
|
'--posix',
|
||||||
|
'--use-compress-program',
|
||||||
|
IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30',
|
||||||
'-cf',
|
'-cf',
|
||||||
IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd,
|
IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd,
|
||||||
'--exclude',
|
'--exclude',
|
||||||
@@ -197,65 +152,13 @@ test('zstd create tar', async () => {
|
|||||||
'manifest.txt'
|
'manifest.txt'
|
||||||
]
|
]
|
||||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
||||||
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
|
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||||
.concat([
|
|
||||||
'--use-compress-program',
|
|
||||||
IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30'
|
|
||||||
]),
|
|
||||||
{
|
{
|
||||||
cwd: archiveFolder
|
cwd: archiveFolder
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('zstd create tar with windows BSDtar', async () => {
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
const execMock = jest.spyOn(exec, 'exec')
|
|
||||||
jest
|
|
||||||
.spyOn(utils, 'getGnuTarPathOnWindows')
|
|
||||||
.mockReturnValue(Promise.resolve(''))
|
|
||||||
|
|
||||||
const archiveFolder = getTempDir()
|
|
||||||
const workspace = process.env['GITHUB_WORKSPACE']
|
|
||||||
const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`]
|
|
||||||
const tarFilename = 'cache.tar'
|
|
||||||
|
|
||||||
await fs.promises.mkdir(archiveFolder, {recursive: true})
|
|
||||||
|
|
||||||
await tar.createTar(
|
|
||||||
archiveFolder,
|
|
||||||
sourceDirectories,
|
|
||||||
CompressionMethod.Zstd
|
|
||||||
)
|
|
||||||
|
|
||||||
const tarPath = SystemTarPathOnWindows
|
|
||||||
|
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
|
||||||
`"${tarPath}"`,
|
|
||||||
[
|
|
||||||
'--posix',
|
|
||||||
'-cf',
|
|
||||||
tarFilename.replace(/\\/g, '/'),
|
|
||||||
'--exclude',
|
|
||||||
tarFilename.replace(/\\/g, '/'),
|
|
||||||
'-P',
|
|
||||||
'-C',
|
|
||||||
workspace?.replace(/\\/g, '/'),
|
|
||||||
'--files-from',
|
|
||||||
'manifest.txt',
|
|
||||||
'&&',
|
|
||||||
'zstd -T0 --long=30 -o',
|
|
||||||
CacheFilename.Zstd.replace(/\\/g, '/'),
|
|
||||||
tarFilename.replace(/\\/g, '/')
|
|
||||||
],
|
|
||||||
{
|
|
||||||
cwd: archiveFolder
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test('gzip create tar', async () => {
|
test('gzip create tar', async () => {
|
||||||
const execMock = jest.spyOn(exec, 'exec')
|
const execMock = jest.spyOn(exec, 'exec')
|
||||||
|
|
||||||
@@ -267,13 +170,16 @@ test('gzip create tar', async () => {
|
|||||||
|
|
||||||
await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Gzip)
|
await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Gzip)
|
||||||
|
|
||||||
const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath
|
const tarPath = IS_WINDOWS
|
||||||
|
? `${process.env['windir']}\\System32\\tar.exe`
|
||||||
|
: defaultTarPath
|
||||||
|
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"${tarPath}"`,
|
`"${tarPath}"`,
|
||||||
[
|
[
|
||||||
'--posix',
|
'--posix',
|
||||||
|
'-z',
|
||||||
'-cf',
|
'-cf',
|
||||||
IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip,
|
IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip,
|
||||||
'--exclude',
|
'--exclude',
|
||||||
@@ -283,10 +189,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'] : []),
|
||||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
|
||||||
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
|
|
||||||
.concat(['-z']),
|
|
||||||
{
|
{
|
||||||
cwd: archiveFolder
|
cwd: archiveFolder
|
||||||
}
|
}
|
||||||
@@ -302,50 +205,22 @@ test('zstd list tar', async () => {
|
|||||||
|
|
||||||
await tar.listTar(archivePath, CompressionMethod.Zstd)
|
await tar.listTar(archivePath, CompressionMethod.Zstd)
|
||||||
|
|
||||||
const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath
|
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"${tarPath}"`,
|
`"${defaultTarPath}"`,
|
||||||
['-tf', IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, '-P']
|
[
|
||||||
|
'--use-compress-program',
|
||||||
|
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30',
|
||||||
|
'-tf',
|
||||||
|
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||||
|
'-P'
|
||||||
|
]
|
||||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
||||||
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
|
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||||
.concat([
|
|
||||||
'--use-compress-program',
|
|
||||||
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
|
|
||||||
]),
|
|
||||||
{cwd: undefined}
|
{cwd: undefined}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('zstd list tar with windows BSDtar', async () => {
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
const execMock = jest.spyOn(exec, 'exec')
|
|
||||||
jest
|
|
||||||
.spyOn(utils, 'getGnuTarPathOnWindows')
|
|
||||||
.mockReturnValue(Promise.resolve(''))
|
|
||||||
const archivePath = `${process.env['windir']}\\fakepath\\cache.tar`
|
|
||||||
|
|
||||||
await tar.listTar(archivePath, CompressionMethod.Zstd)
|
|
||||||
|
|
||||||
const tarFilename = 'cache.tar'
|
|
||||||
const tarPath = SystemTarPathOnWindows
|
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
|
||||||
'zstd -d --long=30 -o',
|
|
||||||
[
|
|
||||||
tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'&&',
|
|
||||||
`${tarPath}`,
|
|
||||||
'-tf',
|
|
||||||
tarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'-P'
|
|
||||||
],
|
|
||||||
{cwd: undefined}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test('zstdWithoutLong list tar', async () => {
|
test('zstdWithoutLong list tar', async () => {
|
||||||
const execMock = jest.spyOn(exec, 'exec')
|
const execMock = jest.spyOn(exec, 'exec')
|
||||||
|
|
||||||
@@ -355,14 +230,18 @@ test('zstdWithoutLong list tar', async () => {
|
|||||||
|
|
||||||
await tar.listTar(archivePath, CompressionMethod.ZstdWithoutLong)
|
await tar.listTar(archivePath, CompressionMethod.ZstdWithoutLong)
|
||||||
|
|
||||||
const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath
|
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"${tarPath}"`,
|
`"${defaultTarPath}"`,
|
||||||
['-tf', IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, '-P']
|
[
|
||||||
|
'--use-compress-program',
|
||||||
|
IS_WINDOWS ? 'zstd -d' : 'unzstd',
|
||||||
|
'-tf',
|
||||||
|
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||||
|
'-P'
|
||||||
|
]
|
||||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
||||||
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
|
.concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||||
.concat(['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd']),
|
|
||||||
{cwd: undefined}
|
{cwd: undefined}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -375,14 +254,18 @@ test('gzip list tar', async () => {
|
|||||||
|
|
||||||
await tar.listTar(archivePath, CompressionMethod.Gzip)
|
await tar.listTar(archivePath, CompressionMethod.Gzip)
|
||||||
|
|
||||||
const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath
|
const tarPath = IS_WINDOWS
|
||||||
|
? `${process.env['windir']}\\System32\\tar.exe`
|
||||||
|
: defaultTarPath
|
||||||
expect(execMock).toHaveBeenCalledTimes(1)
|
expect(execMock).toHaveBeenCalledTimes(1)
|
||||||
expect(execMock).toHaveBeenCalledWith(
|
expect(execMock).toHaveBeenCalledWith(
|
||||||
`"${tarPath}"`,
|
`"${tarPath}"`,
|
||||||
['-tf', IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, '-P']
|
[
|
||||||
.concat(IS_WINDOWS ? ['--force-local'] : [])
|
'-z',
|
||||||
.concat(IS_MAC ? ['--delay-directory-restore'] : [])
|
'-tf',
|
||||||
.concat(['-z']),
|
IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath,
|
||||||
|
'-P'
|
||||||
|
].concat(IS_MAC ? ['--delay-directory-restore'] : []),
|
||||||
{cwd: undefined}
|
{cwd: undefined}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
+6
-6
@@ -457,9 +457,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
@@ -998,9 +998,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-11
@@ -7,11 +7,7 @@ import * as path from 'path'
|
|||||||
import * as semver from 'semver'
|
import * as semver from 'semver'
|
||||||
import * as util from 'util'
|
import * as util from 'util'
|
||||||
import {v4 as uuidV4} from 'uuid'
|
import {v4 as uuidV4} from 'uuid'
|
||||||
import {
|
import {CacheFilename, CompressionMethod} from './constants'
|
||||||
CacheFilename,
|
|
||||||
CompressionMethod,
|
|
||||||
GnuTarPathOnWindows
|
|
||||||
} from './constants'
|
|
||||||
|
|
||||||
// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23
|
// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23
|
||||||
export async function createTempDirectory(): Promise<string> {
|
export async function createTempDirectory(): Promise<string> {
|
||||||
@@ -71,7 +67,7 @@ export async function unlinkFile(filePath: fs.PathLike): Promise<void> {
|
|||||||
return util.promisify(fs.unlink)(filePath)
|
return util.promisify(fs.unlink)(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getVersion(app: string): Promise<string> {
|
async function getVersion(app: string): Promise<string> {
|
||||||
core.debug(`Checking ${app} --version`)
|
core.debug(`Checking ${app} --version`)
|
||||||
let versionOutput = ''
|
let versionOutput = ''
|
||||||
try {
|
try {
|
||||||
@@ -94,6 +90,11 @@ export async function getVersion(app: string): Promise<string> {
|
|||||||
|
|
||||||
// Use zstandard if possible to maximize cache performance
|
// Use zstandard if possible to maximize cache performance
|
||||||
export async function getCompressionMethod(): Promise<CompressionMethod> {
|
export async function getCompressionMethod(): Promise<CompressionMethod> {
|
||||||
|
if (process.platform === 'win32' && !(await isGnuTarInstalled())) {
|
||||||
|
// Disable zstd due to bug https://github.com/actions/cache/issues/301
|
||||||
|
return CompressionMethod.Gzip
|
||||||
|
}
|
||||||
|
|
||||||
const versionOutput = await getVersion('zstd')
|
const versionOutput = await getVersion('zstd')
|
||||||
const version = semver.clean(versionOutput)
|
const version = semver.clean(versionOutput)
|
||||||
|
|
||||||
@@ -115,12 +116,9 @@ export function getCacheFileName(compressionMethod: CompressionMethod): string {
|
|||||||
: CacheFilename.Zstd
|
: CacheFilename.Zstd
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getGnuTarPathOnWindows(): Promise<string> {
|
export async function isGnuTarInstalled(): Promise<boolean> {
|
||||||
if (fs.existsSync(GnuTarPathOnWindows)) {
|
|
||||||
return GnuTarPathOnWindows
|
|
||||||
}
|
|
||||||
const versionOutput = await getVersion('tar')
|
const versionOutput = await getVersion('tar')
|
||||||
return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''
|
return versionOutput.toLowerCase().includes('gnu tar')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertDefined<T>(name: string, value?: T): T {
|
export function assertDefined<T>(name: string, value?: T): T {
|
||||||
|
|||||||
-5
@@ -21,8 +21,3 @@ export const DefaultRetryDelay = 5000
|
|||||||
// over the socket during this period, the socket is destroyed and the download
|
// over the socket during this period, the socket is destroyed and the download
|
||||||
// is aborted.
|
// is aborted.
|
||||||
export const SocketTimeout = 5000
|
export const SocketTimeout = 5000
|
||||||
|
|
||||||
// The default path of GNUtar on hosted Windows runners
|
|
||||||
export const GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`
|
|
||||||
|
|
||||||
export const SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`
|
|
||||||
|
|||||||
Vendored
+58
-309
@@ -3,21 +3,25 @@ import * as io from '@actions/io'
|
|||||||
import {existsSync, writeFileSync} from 'fs'
|
import {existsSync, writeFileSync} from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as utils from './cacheUtils'
|
import * as utils from './cacheUtils'
|
||||||
import {CompressionMethod, SystemTarPathOnWindows} from './constants'
|
import {CompressionMethod} from './constants'
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32'
|
const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
|
||||||
// Function also mutates the args array. For non-mutation call with passing an empty array.
|
async function getTarPath(
|
||||||
async function getTarPath(): Promise<string> {
|
args: string[],
|
||||||
|
compressionMethod: CompressionMethod
|
||||||
|
): Promise<string> {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case 'win32': {
|
case 'win32': {
|
||||||
const gnuTar = await utils.getGnuTarPathOnWindows()
|
const systemTar = `${process.env['windir']}\\System32\\tar.exe`
|
||||||
const systemTar = SystemTarPathOnWindows
|
if (compressionMethod !== CompressionMethod.Gzip) {
|
||||||
if (gnuTar) {
|
// We only use zstandard compression on windows when gnu tar is installed due to
|
||||||
// Use GNUtar as default on windows
|
// a bug with compressing large files with bsdtar + zstd
|
||||||
return gnuTar
|
args.push('--force-local')
|
||||||
} else if (existsSync(systemTar)) {
|
} else if (existsSync(systemTar)) {
|
||||||
return systemTar
|
return systemTar
|
||||||
|
} else if (await utils.isGnuTarInstalled()) {
|
||||||
|
args.push('--force-local')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -25,6 +29,7 @@ async function getTarPath(): Promise<string> {
|
|||||||
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
|
// 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
|
||||||
@@ -35,101 +40,13 @@ async function getTarPath(): Promise<string> {
|
|||||||
return await io.which('tar', true)
|
return await io.which('tar', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTarArgs(
|
async function execTar(
|
||||||
compressionMethod: CompressionMethod,
|
|
||||||
type: string,
|
|
||||||
archivePath = ''
|
|
||||||
): Promise<string[]> {
|
|
||||||
const args = []
|
|
||||||
const manifestFilename = 'manifest.txt'
|
|
||||||
const cacheFileName = utils.getCacheFileName(compressionMethod)
|
|
||||||
const tarFile = 'cache.tar'
|
|
||||||
const tarPath = await getTarPath()
|
|
||||||
const workingDirectory = getWorkingDirectory()
|
|
||||||
const BSD_TAR_ZSTD =
|
|
||||||
tarPath === SystemTarPathOnWindows &&
|
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
|
||||||
|
|
||||||
// Method specific args
|
|
||||||
switch (type) {
|
|
||||||
case 'create':
|
|
||||||
args.push(
|
|
||||||
'--posix',
|
|
||||||
'-cf',
|
|
||||||
BSD_TAR_ZSTD
|
|
||||||
? tarFile
|
|
||||||
: cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'--exclude',
|
|
||||||
BSD_TAR_ZSTD
|
|
||||||
? tarFile
|
|
||||||
: cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'-P',
|
|
||||||
'-C',
|
|
||||||
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'--files-from',
|
|
||||||
manifestFilename
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'extract':
|
|
||||||
args.push(
|
|
||||||
'-xf',
|
|
||||||
BSD_TAR_ZSTD
|
|
||||||
? tarFile
|
|
||||||
: archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'-P',
|
|
||||||
'-C',
|
|
||||||
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
|
|
||||||
)
|
|
||||||
break
|
|
||||||
case 'list':
|
|
||||||
args.push(
|
|
||||||
'-tf',
|
|
||||||
BSD_TAR_ZSTD
|
|
||||||
? tarFile
|
|
||||||
: archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'-P'
|
|
||||||
)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Platform specific args
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32': {
|
|
||||||
const gnuTar = await utils.getGnuTarPathOnWindows()
|
|
||||||
if (gnuTar) {
|
|
||||||
// Use GNUtar as default on windows
|
|
||||||
args.push('--force-local')
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'darwin': {
|
|
||||||
const gnuTar = await io.which('gtar', false)
|
|
||||||
if (gnuTar) {
|
|
||||||
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
|
|
||||||
args.push('--delay-directory-restore')
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
async function execTar(args: string[], cwd?: string): Promise<void> {
|
|
||||||
try {
|
|
||||||
await exec(`"${await getTarPath()}"`, args, {cwd})
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(`Tar failed with error: ${error?.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function execCommand(
|
|
||||||
command: string,
|
|
||||||
args: string[],
|
args: string[],
|
||||||
|
compressionMethod: CompressionMethod,
|
||||||
cwd?: string
|
cwd?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await exec(command, args, {cwd})
|
await exec(`"${await getTarPath(args, compressionMethod)}"`, args, {cwd})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Tar failed with error: ${error?.message}`)
|
throw new Error(`Tar failed with error: ${error?.message}`)
|
||||||
}
|
}
|
||||||
@@ -140,41 +57,19 @@ function getWorkingDirectory(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Common function for extractTar and listTar to get the compression method
|
// Common function for extractTar and listTar to get the compression method
|
||||||
async function getCompressionProgram(
|
function getCompressionProgram(compressionMethod: CompressionMethod): string[] {
|
||||||
compressionMethod: CompressionMethod,
|
|
||||||
archivePath: string
|
|
||||||
): Promise<string[]> {
|
|
||||||
// -d: Decompress.
|
// -d: Decompress.
|
||||||
// unzstd is equivalent to 'zstd -d'
|
// unzstd is equivalent to 'zstd -d'
|
||||||
// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
|
// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
|
||||||
// Using 30 here because we also support 32-bit self-hosted runners.
|
// Using 30 here because we also support 32-bit self-hosted runners.
|
||||||
const tarPath = await getTarPath()
|
|
||||||
const tarFile = 'cache.tar'
|
|
||||||
const BSD_TAR_ZSTD =
|
|
||||||
tarPath === SystemTarPathOnWindows &&
|
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
|
||||||
switch (compressionMethod) {
|
switch (compressionMethod) {
|
||||||
case CompressionMethod.Zstd:
|
case CompressionMethod.Zstd:
|
||||||
return BSD_TAR_ZSTD
|
return [
|
||||||
? [
|
'--use-compress-program',
|
||||||
'zstd -d --long=30 -o',
|
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
|
||||||
tarFile,
|
]
|
||||||
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'&&'
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
'--use-compress-program',
|
|
||||||
IS_WINDOWS ? 'zstd -d --long=30' : 'unzstd --long=30'
|
|
||||||
]
|
|
||||||
case CompressionMethod.ZstdWithoutLong:
|
case CompressionMethod.ZstdWithoutLong:
|
||||||
return BSD_TAR_ZSTD
|
return ['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd']
|
||||||
? [
|
|
||||||
'zstd -d -o',
|
|
||||||
tarFile,
|
|
||||||
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
'&&'
|
|
||||||
]
|
|
||||||
: ['--use-compress-program', IS_WINDOWS ? 'zstd -d' : 'unzstd']
|
|
||||||
default:
|
default:
|
||||||
return ['-z']
|
return ['-z']
|
||||||
}
|
}
|
||||||
@@ -184,27 +79,13 @@ export async function listTar(
|
|||||||
archivePath: string,
|
archivePath: string,
|
||||||
compressionMethod: CompressionMethod
|
compressionMethod: CompressionMethod
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tarPath = await getTarPath()
|
const args = [
|
||||||
const BSD_TAR_ZSTD =
|
...getCompressionProgram(compressionMethod),
|
||||||
tarPath === SystemTarPathOnWindows &&
|
'-tf',
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
||||||
const compressionArgs = await getCompressionProgram(
|
'-P'
|
||||||
compressionMethod,
|
]
|
||||||
archivePath
|
await execTar(args, compressionMethod)
|
||||||
)
|
|
||||||
const tarArgs = await getTarArgs(compressionMethod, 'list', archivePath)
|
|
||||||
// TODO: Add a test for BSD tar on windows
|
|
||||||
if (BSD_TAR_ZSTD) {
|
|
||||||
const command = compressionArgs[0]
|
|
||||||
const args = compressionArgs
|
|
||||||
.slice(1)
|
|
||||||
.concat([tarPath])
|
|
||||||
.concat(tarArgs)
|
|
||||||
await execCommand(command, args)
|
|
||||||
} else {
|
|
||||||
const args = tarArgs.concat(compressionArgs)
|
|
||||||
await execTar(args)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function extractTar(
|
export async function extractTar(
|
||||||
@@ -213,27 +94,16 @@ export async function extractTar(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Create directory to extract tar into
|
// Create directory to extract tar into
|
||||||
const workingDirectory = getWorkingDirectory()
|
const workingDirectory = getWorkingDirectory()
|
||||||
const tarPath = await getTarPath()
|
|
||||||
const BSD_TAR_ZSTD =
|
|
||||||
tarPath === SystemTarPathOnWindows &&
|
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
|
||||||
await io.mkdirP(workingDirectory)
|
await io.mkdirP(workingDirectory)
|
||||||
const tarArgs = await getTarArgs(compressionMethod, 'extract', archivePath)
|
const args = [
|
||||||
const compressionArgs = await getCompressionProgram(
|
...getCompressionProgram(compressionMethod),
|
||||||
compressionMethod,
|
'-xf',
|
||||||
archivePath
|
archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
||||||
)
|
'-P',
|
||||||
if (BSD_TAR_ZSTD) {
|
'-C',
|
||||||
const command = compressionArgs[0]
|
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
|
||||||
const args = compressionArgs
|
]
|
||||||
.slice(1)
|
await execTar(args, compressionMethod)
|
||||||
.concat([tarPath])
|
|
||||||
.concat(tarArgs)
|
|
||||||
await execCommand(command, args)
|
|
||||||
} else {
|
|
||||||
const args = tarArgs.concat(compressionArgs)
|
|
||||||
await execTar(args)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createTar(
|
export async function createTar(
|
||||||
@@ -244,15 +114,11 @@ export async function createTar(
|
|||||||
// Write source directories to manifest.txt to avoid command length limits
|
// Write source directories to manifest.txt to avoid command length limits
|
||||||
const manifestFilename = 'manifest.txt'
|
const manifestFilename = 'manifest.txt'
|
||||||
const cacheFileName = utils.getCacheFileName(compressionMethod)
|
const cacheFileName = utils.getCacheFileName(compressionMethod)
|
||||||
const tarFile = 'cache.tar'
|
|
||||||
const tarPath = await getTarPath()
|
|
||||||
const BSD_TAR_ZSTD =
|
|
||||||
tarPath === SystemTarPathOnWindows &&
|
|
||||||
compressionMethod !== CompressionMethod.Gzip
|
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
path.join(archiveFolder, manifestFilename),
|
path.join(archiveFolder, manifestFilename),
|
||||||
sourceDirectories.join('\n')
|
sourceDirectories.join('\n')
|
||||||
)
|
)
|
||||||
|
const workingDirectory = getWorkingDirectory()
|
||||||
|
|
||||||
// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores.
|
// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores.
|
||||||
// zstdmt is equivalent to 'zstd -T0'
|
// zstdmt is equivalent to 'zstd -T0'
|
||||||
@@ -262,145 +128,28 @@ export async function createTar(
|
|||||||
function getCompressionProgram(): string[] {
|
function getCompressionProgram(): string[] {
|
||||||
switch (compressionMethod) {
|
switch (compressionMethod) {
|
||||||
case CompressionMethod.Zstd:
|
case CompressionMethod.Zstd:
|
||||||
return BSD_TAR_ZSTD
|
return [
|
||||||
? [
|
'--use-compress-program',
|
||||||
'&&',
|
IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30'
|
||||||
'zstd -T0 --long=30 -o',
|
]
|
||||||
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
tarFile
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
'--use-compress-program',
|
|
||||||
IS_WINDOWS ? 'zstd -T0 --long=30' : 'zstdmt --long=30'
|
|
||||||
]
|
|
||||||
case CompressionMethod.ZstdWithoutLong:
|
case CompressionMethod.ZstdWithoutLong:
|
||||||
return BSD_TAR_ZSTD
|
return ['--use-compress-program', IS_WINDOWS ? 'zstd -T0' : 'zstdmt']
|
||||||
? [
|
|
||||||
'&&',
|
|
||||||
'zstd -T0 -o',
|
|
||||||
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
|
||||||
tarFile
|
|
||||||
]
|
|
||||||
: ['--use-compress-program', IS_WINDOWS ? 'zstd -T0' : 'zstdmt']
|
|
||||||
default:
|
default:
|
||||||
return ['-z']
|
return ['-z']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const tarArgs = await getTarArgs(compressionMethod, 'create')
|
const args = [
|
||||||
const compressionArgs = getCompressionProgram()
|
'--posix',
|
||||||
const args = tarArgs.concat(compressionArgs)
|
...getCompressionProgram(),
|
||||||
await execTar(args, archiveFolder)
|
'-cf',
|
||||||
|
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
||||||
|
'--exclude',
|
||||||
|
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
||||||
|
'-P',
|
||||||
|
'-C',
|
||||||
|
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
|
||||||
|
'--files-from',
|
||||||
|
manifestFilename
|
||||||
|
]
|
||||||
|
await execTar(args, compressionMethod, archiveFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ARCHIVE_TOOL_GNU: string = 'gnu'
|
|
||||||
const ARCHIVE_TOOL_BSD: string = 'bsd'
|
|
||||||
|
|
||||||
const manifestFilename = 'manifest.txt'
|
|
||||||
|
|
||||||
export async function createTar2(
|
|
||||||
archiveFolder: string,
|
|
||||||
sourceDirectories: string[]
|
|
||||||
): Promise<void> {
|
|
||||||
|
|
||||||
// 1. decide the compression algo - zstd otherwise gzip
|
|
||||||
// 2. decide tar - gnutar otherwise bsdtar or systemtar
|
|
||||||
// 3. deide archiving/unarchiving args depending on tar
|
|
||||||
// 4. decide compression/decompression args depending on tar and compression
|
|
||||||
// 5. decide the exec command
|
|
||||||
|
|
||||||
let tar = getTarPath2()
|
|
||||||
let compression = getCompressionMethod2()
|
|
||||||
let args = argsMap.get({tar: tar, compression: compression, os: process.platform, "create"})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// return archive tool name and path
|
|
||||||
async function getTarPath2(): Promise<ArchiveTool> {
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32': {
|
|
||||||
const gnuTar = await utils.getGnuTarPathOnWindows()
|
|
||||||
const systemTar = SystemTarPathOnWindows
|
|
||||||
if (gnuTar) {
|
|
||||||
// Use GNUtar as default on windows
|
|
||||||
return <ArchiveTool>{ name: ARCHIVE_TOOL_GNU, path: gnuTar }
|
|
||||||
} else if (existsSync(systemTar)) {
|
|
||||||
return <ArchiveTool>{name: ARCHIVE_TOOL_BSD, path: systemTar}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'darwin': {
|
|
||||||
const gnuTar = await io.which('gtar', false)
|
|
||||||
if (gnuTar) {
|
|
||||||
// fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527
|
|
||||||
return <ArchiveTool>{ name: ARCHIVE_TOOL_GNU, path: gnuTar }
|
|
||||||
} else {
|
|
||||||
const path = await io.which('tar', true)
|
|
||||||
return <ArchiveTool>{name: ARCHIVE_TOOL_BSD, path: path}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
const path = await io.which('tar', true)
|
|
||||||
return <ArchiveTool>{name: ARCHIVE_TOOL_GNU, path: path}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use zstandard if possible to maximize cache performance
|
|
||||||
export async function getCompressionMethod2(): Promise<CompressionMethod> {
|
|
||||||
const versionOutput = await utils.getVersion('zstd')
|
|
||||||
const version = semver.clean(versionOutput)
|
|
||||||
|
|
||||||
if (!versionOutput.toLowerCase().includes('zstd command line interface')) {
|
|
||||||
// zstd is not installed
|
|
||||||
return CompressionMethod.Gzip
|
|
||||||
} else if (!version || semver.lt(version, 'v1.3.2')) {
|
|
||||||
// zstd is installed but using a version earlier than v1.3.2
|
|
||||||
// v1.3.2 is required to use the `--long` options in zstd
|
|
||||||
return CompressionMethod.ZstdWithoutLong
|
|
||||||
} else {
|
|
||||||
return CompressionMethod.Zstd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArchiveTool {
|
|
||||||
name: string
|
|
||||||
path: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// tar, compression, os, operation -> tar args + compression args
|
|
||||||
const argsMap: Map<ArgsLookupKey, string[][]> = new Map()
|
|
||||||
argsMap.set({tar: ARCHIVE_TOOL_GNU, compression: CompressionMethod.Zstd, os: "windows", operation: "create"},
|
|
||||||
[[
|
|
||||||
'--posix',
|
|
||||||
'-cf',
|
|
||||||
'%%cacheFileName%%',
|
|
||||||
'--exclude',
|
|
||||||
'%%cacheFileName%%',
|
|
||||||
'-P',
|
|
||||||
'-C',
|
|
||||||
'%%workingDirectory%%',
|
|
||||||
'--files-from',
|
|
||||||
manifestFilename],
|
|
||||||
['--use-compress-program',
|
|
||||||
'zstd -T0 --long=30']
|
|
||||||
])
|
|
||||||
|
|
||||||
argsMap.set({tar: ARCHIVE_TOOL_GNU, compression: CompressionMethod.Zstd, os: "linux", operation: "create"},
|
|
||||||
[
|
|
||||||
'--use-compress-program',
|
|
||||||
'zstdmt --long=30'
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface ArgsLookupKey {
|
|
||||||
tar: string
|
|
||||||
compression: string
|
|
||||||
os: string
|
|
||||||
operation: string
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe('@actions/github', () => {
|
|||||||
proxyServer.listen(port, () => resolve())
|
proxyServer.listen(port, () => resolve())
|
||||||
})
|
})
|
||||||
proxyServer.on('connect', req => {
|
proxyServer.on('connect', req => {
|
||||||
proxyConnects.push(req.url)
|
proxyConnects.push(req.url ?? '')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('@actions/github', () => {
|
|||||||
proxyServer.listen(port, () => resolve(null))
|
proxyServer.listen(port, () => resolve(null))
|
||||||
})
|
})
|
||||||
proxyServer.on('connect', req => {
|
proxyServer.on('connect', req => {
|
||||||
proxyConnects.push(req.url)
|
proxyConnects.push(req.url ?? '')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Generated
+6
-6
@@ -50,9 +50,9 @@
|
|||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
@@ -115,9 +115,9 @@
|
|||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ describe('proxy', () => {
|
|||||||
_proxyServer.listen(port, () => resolve())
|
_proxyServer.listen(port, () => resolve())
|
||||||
})
|
})
|
||||||
_proxyServer.on('connect', req => {
|
_proxyServer.on('connect', req => {
|
||||||
_proxyConnects.push(req.url)
|
_proxyConnects.push(req.url ?? '')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Generated
+29
-242
@@ -17,10 +17,10 @@
|
|||||||
"uuid": "^3.3.2"
|
"uuid": "^3.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/nock": "^10.0.3",
|
"@types/nock": "^11.1.0",
|
||||||
"@types/semver": "^6.0.0",
|
"@types/semver": "^6.0.0",
|
||||||
"@types/uuid": "^3.4.4",
|
"@types/uuid": "^3.4.4",
|
||||||
"nock": "^10.0.6"
|
"nock": "^13.2.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/core": {
|
"node_modules/@actions/core": {
|
||||||
@@ -62,12 +62,13 @@
|
|||||||
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
|
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/nock": {
|
"node_modules/@types/nock": {
|
||||||
"version": "10.0.3",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/nock/-/nock-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz",
|
||||||
"integrity": "sha512-OthuN+2FuzfZO3yONJ/QVjKmLEuRagS9TV9lEId+WHL9KhftYG+/2z+pxlr0UgVVXSpVD8woie/3fzQn8ft/Ow==",
|
"integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==",
|
||||||
|
"deprecated": "This is a stub types definition. nock provides its own type definitions, so you do not need this installed.",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"nock": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
@@ -91,41 +92,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/assertion-error": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/chai": {
|
|
||||||
"version": "4.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
|
|
||||||
"integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"assertion-error": "^1.1.0",
|
|
||||||
"check-error": "^1.0.2",
|
|
||||||
"deep-eql": "^3.0.1",
|
|
||||||
"get-func-name": "^2.0.0",
|
|
||||||
"pathval": "^1.1.0",
|
|
||||||
"type-detect": "^4.0.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/check-error": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
@@ -136,33 +102,6 @@
|
|||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/deep-eql": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"type-detect": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/deep-equal": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/get-func-name": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/json-stringify-safe": {
|
"node_modules/json-stringify-safe": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||||
@@ -175,24 +114,6 @@
|
|||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/minimist": {
|
|
||||||
"version": "1.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
|
||||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/mkdirp": {
|
|
||||||
"version": "0.5.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
|
||||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"minimist": "^1.2.5"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"mkdirp": "bin/cmd.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@@ -200,59 +121,27 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/nock": {
|
"node_modules/nock": {
|
||||||
"version": "10.0.6",
|
"version": "13.2.9",
|
||||||
"resolved": "https://registry.npmjs.org/nock/-/nock-10.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz",
|
||||||
"integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==",
|
"integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chai": "^4.1.2",
|
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"deep-equal": "^1.0.0",
|
|
||||||
"json-stringify-safe": "^5.0.1",
|
"json-stringify-safe": "^5.0.1",
|
||||||
"lodash": "^4.17.5",
|
"lodash": "^4.17.21",
|
||||||
"mkdirp": "^0.5.0",
|
"propagate": "^2.0.0"
|
||||||
"propagate": "^1.0.0",
|
|
||||||
"qs": "^6.5.1",
|
|
||||||
"semver": "^5.5.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6.0"
|
"node": ">= 10.13"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/nock/node_modules/semver": {
|
|
||||||
"version": "5.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
|
|
||||||
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pathval": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/propagate": {
|
"node_modules/propagate": {
|
||||||
"version": "1.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
|
||||||
"integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=",
|
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
|
||||||
"dev": true,
|
|
||||||
"engines": [
|
|
||||||
"node >= 0.8.1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/qs": {
|
|
||||||
"version": "6.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
|
||||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
|
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
@@ -271,15 +160,6 @@
|
|||||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/type-detect": {
|
|
||||||
"version": "4.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
|
||||||
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||||
@@ -329,12 +209,12 @@
|
|||||||
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
|
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
|
||||||
},
|
},
|
||||||
"@types/nock": {
|
"@types/nock": {
|
||||||
"version": "10.0.3",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/nock/-/nock-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz",
|
||||||
"integrity": "sha512-OthuN+2FuzfZO3yONJ/QVjKmLEuRagS9TV9lEId+WHL9KhftYG+/2z+pxlr0UgVVXSpVD8woie/3fzQn8ft/Ow==",
|
"integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"nock": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
@@ -358,32 +238,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"assertion-error": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"chai": {
|
|
||||||
"version": "4.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
|
|
||||||
"integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"assertion-error": "^1.1.0",
|
|
||||||
"check-error": "^1.0.2",
|
|
||||||
"deep-eql": "^3.0.1",
|
|
||||||
"get-func-name": "^2.0.0",
|
|
||||||
"pathval": "^1.1.0",
|
|
||||||
"type-detect": "^4.0.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"check-error": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
@@ -393,27 +247,6 @@
|
|||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deep-eql": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"type-detect": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"deep-equal": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"get-func-name": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"json-stringify-safe": {
|
"json-stringify-safe": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||||
@@ -426,21 +259,6 @@
|
|||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"minimist": {
|
|
||||||
"version": "1.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
|
||||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"mkdirp": {
|
|
||||||
"version": "0.5.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
|
||||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"minimist": "^1.2.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@@ -448,46 +266,21 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"nock": {
|
"nock": {
|
||||||
"version": "10.0.6",
|
"version": "13.2.9",
|
||||||
"resolved": "https://registry.npmjs.org/nock/-/nock-10.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz",
|
||||||
"integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==",
|
"integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chai": "^4.1.2",
|
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"deep-equal": "^1.0.0",
|
|
||||||
"json-stringify-safe": "^5.0.1",
|
"json-stringify-safe": "^5.0.1",
|
||||||
"lodash": "^4.17.5",
|
"lodash": "^4.17.21",
|
||||||
"mkdirp": "^0.5.0",
|
"propagate": "^2.0.0"
|
||||||
"propagate": "^1.0.0",
|
|
||||||
"qs": "^6.5.1",
|
|
||||||
"semver": "^5.5.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"semver": {
|
|
||||||
"version": "5.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
|
|
||||||
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pathval": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"propagate": {
|
"propagate": {
|
||||||
"version": "1.0.0",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
|
||||||
"integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=",
|
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"qs": {
|
|
||||||
"version": "6.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
|
||||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
@@ -500,12 +293,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||||
},
|
},
|
||||||
"type-detect": {
|
|
||||||
"version": "4.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
|
||||||
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||||
|
|||||||
@@ -44,9 +44,9 @@
|
|||||||
"uuid": "^3.3.2"
|
"uuid": "^3.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/nock": "^10.0.3",
|
"@types/nock": "^11.1.0",
|
||||||
"@types/semver": "^6.0.0",
|
"@types/semver": "^6.0.0",
|
||||||
"@types/uuid": "^3.4.4",
|
"@types/uuid": "^3.4.4",
|
||||||
"nock": "^10.0.6"
|
"nock": "^13.2.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user