mirror of https://github.com/actions/toolkit
Removing childprocess for rmRF (#1373)
* try awaiting spawn on windows * formatting * updating package-lock * . * . * updating packages * adding sync rm * test with sync * pointing to rmsync * adding error handling * testing rmsync * adding try/catch * adding windows conditional for locked file * switch to contians * fixing formatting * fixing formatting * fixing formatting * adding enonet catch for windows files * adding enonet catch for windows files * adding catch for file not found * updating stat call * updating stat call * adding conditonal for symlink * removing symlink test * adding ebusy check * changing error check * changing error check * changing error check * changing error check * cleanup and comments * Update packages/io/__tests__/io.test.ts Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com> * Update packages/io/src/io-util.ts Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com> * moving comment placement * updating eperm * change back to ebusy * Update packages/io/__tests__/io.test.ts Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com> * Formatting * converting to async --------- Co-authored-by: Cory Miller <13227161+cory-miller@users.noreply.github.com>pull/1378/head
parent
a91ee0b497
commit
463b49d872
|
@ -1,12 +1,27 @@
|
||||||
{
|
{
|
||||||
"name": "@actions/glob",
|
"name": "@actions/glob",
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"description": "Actions glob lib",
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"!.DS_Store"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/actions/toolkit/tree/main/packages/glob",
|
||||||
|
"keywords": [
|
||||||
|
"github",
|
||||||
|
"actions",
|
||||||
|
"glob"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "lib/glob.js",
|
||||||
|
"preview": true,
|
||||||
|
"types": "lib/glob.d.ts",
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@actions/glob",
|
"name": "@actions/glob",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.9.1",
|
"@actions/core": "^1.9.1",
|
||||||
|
@ -14,26 +29,17 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/core": {
|
"node_modules/@actions/core": {
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
|
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||||
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/http-client": "^2.0.1",
|
"@actions/http-client": "^2.0.1",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/http-client": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
|
||||||
"dependencies": {
|
|
||||||
"tunnel": "^0.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
},
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
|
@ -47,7 +53,7 @@
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
|
@ -59,78 +65,26 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/tunnel": {
|
|
||||||
"version": "0.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
|
||||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/uuid": {
|
|
||||||
"version": "8.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
|
||||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
|
||||||
"bin": {
|
|
||||||
"uuid": "dist/bin/uuid"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"bugs": {
|
||||||
"@actions/core": {
|
"url": "https://github.com/actions/toolkit/issues"
|
||||||
"version": "1.9.1",
|
},
|
||||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
|
"directories": {
|
||||||
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
|
"lib": "lib",
|
||||||
"requires": {
|
"test": "__tests__"
|
||||||
"@actions/http-client": "^2.0.1",
|
},
|
||||||
"uuid": "^8.3.2"
|
"publishConfig": {
|
||||||
}
|
"access": "public"
|
||||||
},
|
},
|
||||||
"@actions/http-client": {
|
"repository": {
|
||||||
"version": "2.0.1",
|
"directory": "packages/glob",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
"type": "git",
|
||||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
"url": "git+https://github.com/actions/toolkit.git"
|
||||||
"requires": {
|
},
|
||||||
"tunnel": "^0.0.6"
|
"scripts": {
|
||||||
}
|
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
|
||||||
},
|
"test": "echo \"Error: run tests from root\" && exit 1",
|
||||||
"balanced-match": {
|
"tsc": "tsc"
|
||||||
"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",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
|
||||||
"requires": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"concat-map": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
|
||||||
},
|
|
||||||
"minimatch": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
|
||||||
"requires": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tunnel": {
|
|
||||||
"version": "0.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
|
||||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
|
||||||
},
|
|
||||||
"uuid": {
|
|
||||||
"version": "8.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
|
||||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {promises as fs} from 'fs'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as io from '../src/io'
|
import * as io from '../src/io'
|
||||||
|
import * as ioUtil from '../src/io-util'
|
||||||
|
|
||||||
describe('cp', () => {
|
describe('cp', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
@ -331,11 +332,22 @@ describe('rmRF', () => {
|
||||||
await fs.appendFile(filePath, 'some data')
|
await fs.appendFile(filePath, 'some data')
|
||||||
await assertExists(filePath)
|
await assertExists(filePath)
|
||||||
|
|
||||||
const fd = await fs.open(filePath, 'r')
|
// For windows we need to explicitly set an exclusive lock flag, because by default Node will open the file with the 'Delete' FileShare flag.
|
||||||
await io.rmRF(testPath)
|
// See the exclusive lock windows flag definition:
|
||||||
|
// https://github.com/nodejs/node/blob/c2e4b1fa9ad0b744616c4e4c13a5017772a630c4/deps/uv/src/win/fs.c#L499-L513
|
||||||
await assertNotExists(testPath)
|
const fd = await fs.open(
|
||||||
|
filePath,
|
||||||
|
fs.constants.O_RDONLY | ioUtil.UV_FS_O_EXLOCK
|
||||||
|
)
|
||||||
|
if (ioUtil.IS_WINDOWS) {
|
||||||
|
// On Windows, we expect an error due to an lstat call implementation in the underlying libuv code.
|
||||||
|
// See https://github.com/libuv/libuv/issues/3267 is resolved
|
||||||
|
await expect(async () => io.rmRF(testPath)).rejects.toThrow('EBUSY')
|
||||||
|
} else {
|
||||||
|
await io.rmRF(testPath)
|
||||||
|
|
||||||
|
await assertNotExists(testPath)
|
||||||
|
}
|
||||||
await fd.close()
|
await fd.close()
|
||||||
await io.rmRF(testPath)
|
await io.rmRF(testPath)
|
||||||
await assertNotExists(testPath)
|
await assertNotExists(testPath)
|
||||||
|
@ -373,26 +385,6 @@ describe('rmRF', () => {
|
||||||
await assertNotExists(file)
|
await assertNotExists(file)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('removes symlink folder with rmRF', async () => {
|
|
||||||
// create the following layout:
|
|
||||||
// real_directory
|
|
||||||
// real_directory/real_file
|
|
||||||
// symlink_directory -> real_directory
|
|
||||||
const root: string = path.join(getTestTemp(), 'rmRF_sym_dir_test')
|
|
||||||
const realDirectory: string = path.join(root, 'real_directory')
|
|
||||||
const realFile: string = path.join(root, 'real_directory', 'real_file')
|
|
||||||
const symlinkDirectory: string = path.join(root, 'symlink_directory')
|
|
||||||
await io.mkdirP(realDirectory)
|
|
||||||
await fs.writeFile(realFile, 'test file content')
|
|
||||||
await createSymlinkDir(realDirectory, symlinkDirectory)
|
|
||||||
await assertExists(path.join(symlinkDirectory, 'real_file'))
|
|
||||||
|
|
||||||
await io.rmRF(symlinkDirectory)
|
|
||||||
await assertExists(realDirectory)
|
|
||||||
await assertExists(realFile)
|
|
||||||
await assertNotExists(symlinkDirectory)
|
|
||||||
})
|
|
||||||
|
|
||||||
// creating a symlink to a file on Windows requires elevated
|
// creating a symlink to a file on Windows requires elevated
|
||||||
if (os.platform() !== 'win32') {
|
if (os.platform() !== 'win32') {
|
||||||
it('removes symlink file with rmRF', async () => {
|
it('removes symlink file with rmRF', async () => {
|
||||||
|
|
|
@ -6,16 +6,21 @@ export const {
|
||||||
copyFile,
|
copyFile,
|
||||||
lstat,
|
lstat,
|
||||||
mkdir,
|
mkdir,
|
||||||
|
open,
|
||||||
readdir,
|
readdir,
|
||||||
readlink,
|
readlink,
|
||||||
rename,
|
rename,
|
||||||
|
rm,
|
||||||
rmdir,
|
rmdir,
|
||||||
stat,
|
stat,
|
||||||
symlink,
|
symlink,
|
||||||
unlink
|
unlink
|
||||||
} = fs.promises
|
} = fs.promises
|
||||||
|
// export const {open} = 'fs'
|
||||||
export const IS_WINDOWS = process.platform === 'win32'
|
export const IS_WINDOWS = process.platform === 'win32'
|
||||||
|
// See https://github.com/nodejs/node/blob/d0153aee367422d0858105abec186da4dff0a0c5/deps/uv/include/uv/win.h#L691
|
||||||
|
export const UV_FS_O_EXLOCK = 0x10000000
|
||||||
|
export const READONLY = fs.constants.O_RDONLY
|
||||||
|
|
||||||
export async function exists(fsPath: string): Promise<boolean> {
|
export async function exists(fsPath: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
import {ok} from 'assert'
|
import {ok} from 'assert'
|
||||||
import * as childProcess from 'child_process'
|
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {promisify} from 'util'
|
|
||||||
import * as ioUtil from './io-util'
|
import * as ioUtil from './io-util'
|
||||||
|
|
||||||
const exec = promisify(childProcess.exec)
|
|
||||||
const execFile = promisify(childProcess.execFile)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for cp/mv options
|
* Interface for cp/mv options
|
||||||
*/
|
*/
|
||||||
|
@ -116,9 +111,6 @@ export async function mv(
|
||||||
*/
|
*/
|
||||||
export async function rmRF(inputPath: string): Promise<void> {
|
export async function rmRF(inputPath: string): Promise<void> {
|
||||||
if (ioUtil.IS_WINDOWS) {
|
if (ioUtil.IS_WINDOWS) {
|
||||||
// Node doesn't provide a delete operation, only an unlink function. This means that if the file is being used by another
|
|
||||||
// program (e.g. antivirus), it won't be deleted. To address this, we shell out the work to rd/del.
|
|
||||||
|
|
||||||
// Check for invalid characters
|
// Check for invalid characters
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
||||||
if (/[*"<>|]/.test(inputPath)) {
|
if (/[*"<>|]/.test(inputPath)) {
|
||||||
|
@ -126,47 +118,17 @@ export async function rmRF(inputPath: string): Promise<void> {
|
||||||
'File path must not contain `*`, `"`, `<`, `>` or `|` on Windows'
|
'File path must not contain `*`, `"`, `<`, `>` or `|` on Windows'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
try {
|
}
|
||||||
const cmdPath = ioUtil.getCmdPath()
|
try {
|
||||||
if (await ioUtil.isDirectory(inputPath, true)) {
|
// note if path does not exist, error is silent
|
||||||
await exec(`${cmdPath} /s /c "rd /s /q "%inputPath%""`, {
|
await ioUtil.rm(inputPath, {
|
||||||
env: {inputPath}
|
force: true,
|
||||||
})
|
maxRetries: 3,
|
||||||
} else {
|
recursive: true,
|
||||||
await exec(`${cmdPath} /s /c "del /f /a "%inputPath%""`, {
|
retryDelay: 300
|
||||||
env: {inputPath}
|
})
|
||||||
})
|
} catch (err) {
|
||||||
}
|
throw new Error(`File was unable to be removed ${err}`)
|
||||||
} catch (err) {
|
|
||||||
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
||||||
// other errors are valid
|
|
||||||
if (err.code !== 'ENOENT') throw err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shelling out fails to remove a symlink folder with missing source, this unlink catches that
|
|
||||||
try {
|
|
||||||
await ioUtil.unlink(inputPath)
|
|
||||||
} catch (err) {
|
|
||||||
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
||||||
// other errors are valid
|
|
||||||
if (err.code !== 'ENOENT') throw err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let isDir = false
|
|
||||||
try {
|
|
||||||
isDir = await ioUtil.isDirectory(inputPath)
|
|
||||||
} catch (err) {
|
|
||||||
// if you try to delete a file that doesn't exist, desired result is achieved
|
|
||||||
// other errors are valid
|
|
||||||
if (err.code !== 'ENOENT') throw err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDir) {
|
|
||||||
await execFile(`rm`, [`-rf`, `${inputPath}`])
|
|
||||||
} else {
|
|
||||||
await ioUtil.unlink(inputPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue