Added test to ensure that release notes are parsed properly Updated to latest dependencies to fix security vulnerability Updated GH Actions Workflow to run test before updating tag Updated README.md to reflect proper whatsnew documentation.
126 lines
3.5 KiB
JavaScript
126 lines
3.5 KiB
JavaScript
'use strict'
|
|
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
/* istanbul ignore next */
|
|
const LCHOWN = fs.lchown ? 'lchown' : 'chown'
|
|
/* istanbul ignore next */
|
|
const LCHOWNSYNC = fs.lchownSync ? 'lchownSync' : 'chownSync'
|
|
|
|
const needEISDIRHandled = fs.lchown &&
|
|
!process.version.match(/v1[1-9]+\./) &&
|
|
!process.version.match(/v10\.[6-9]/)
|
|
|
|
/* istanbul ignore next */
|
|
const handleEISDIR =
|
|
needEISDIRHandled ? (path, uid, gid, cb) => er => {
|
|
// Node prior to v10 had a very questionable implementation of
|
|
// fs.lchown, which would always try to call fs.open on a directory
|
|
// Fall back to fs.chown in those cases.
|
|
if (!er || er.code !== 'EISDIR')
|
|
cb(er)
|
|
else
|
|
fs.chown(path, uid, gid, cb)
|
|
}
|
|
: (_, __, ___, cb) => cb
|
|
|
|
/* istanbul ignore next */
|
|
const handleEISDirSync =
|
|
needEISDIRHandled ? (path, uid, gid) => {
|
|
try {
|
|
return fs[LCHOWNSYNC](path, uid, gid)
|
|
} catch (er) {
|
|
if (er.code !== 'EISDIR')
|
|
throw er
|
|
fs.chownSync(path, uid, gid)
|
|
}
|
|
}
|
|
: (path, uid, gid) => fs[LCHOWNSYNC](path, uid, gid)
|
|
|
|
// fs.readdir could only accept an options object as of node v6
|
|
const nodeVersion = process.version
|
|
let readdir = (path, options, cb) => fs.readdir(path, options, cb)
|
|
let readdirSync = (path, options) => fs.readdirSync(path, options)
|
|
/* istanbul ignore next */
|
|
if (/^v4\./.test(nodeVersion))
|
|
readdir = (path, options, cb) => fs.readdir(path, cb)
|
|
|
|
const chownrKid = (p, child, uid, gid, cb) => {
|
|
if (typeof child === 'string')
|
|
return fs.lstat(path.resolve(p, child), (er, stats) => {
|
|
if (er)
|
|
return cb(er)
|
|
stats.name = child
|
|
chownrKid(p, stats, uid, gid, cb)
|
|
})
|
|
|
|
if (child.isDirectory()) {
|
|
chownr(path.resolve(p, child.name), uid, gid, er => {
|
|
if (er)
|
|
return cb(er)
|
|
const cpath = path.resolve(p, child.name)
|
|
fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, cb))
|
|
})
|
|
} else {
|
|
const cpath = path.resolve(p, child.name)
|
|
fs[LCHOWN](cpath, uid, gid, handleEISDIR(cpath, uid, gid, cb))
|
|
}
|
|
}
|
|
|
|
|
|
const chownr = (p, uid, gid, cb) => {
|
|
readdir(p, { withFileTypes: true }, (er, children) => {
|
|
// any error other than ENOTDIR or ENOTSUP means it's not readable,
|
|
// or doesn't exist. give up.
|
|
if (er && er.code !== 'ENOTDIR' && er.code !== 'ENOTSUP')
|
|
return cb(er)
|
|
if (er || !children.length)
|
|
return fs[LCHOWN](p, uid, gid, handleEISDIR(p, uid, gid, cb))
|
|
|
|
let len = children.length
|
|
let errState = null
|
|
const then = er => {
|
|
if (errState)
|
|
return
|
|
if (er)
|
|
return cb(errState = er)
|
|
if (-- len === 0)
|
|
return fs[LCHOWN](p, uid, gid, handleEISDIR(p, uid, gid, cb))
|
|
}
|
|
|
|
children.forEach(child => chownrKid(p, child, uid, gid, then))
|
|
})
|
|
}
|
|
|
|
const chownrKidSync = (p, child, uid, gid) => {
|
|
if (typeof child === 'string') {
|
|
const stats = fs.lstatSync(path.resolve(p, child))
|
|
stats.name = child
|
|
child = stats
|
|
}
|
|
|
|
if (child.isDirectory())
|
|
chownrSync(path.resolve(p, child.name), uid, gid)
|
|
|
|
handleEISDirSync(path.resolve(p, child.name), uid, gid)
|
|
}
|
|
|
|
const chownrSync = (p, uid, gid) => {
|
|
let children
|
|
try {
|
|
children = readdirSync(p, { withFileTypes: true })
|
|
} catch (er) {
|
|
if (er && er.code === 'ENOTDIR' && er.code !== 'ENOTSUP')
|
|
return handleEISDirSync(p, uid, gid)
|
|
throw er
|
|
}
|
|
|
|
if (children.length)
|
|
children.forEach(child => chownrKidSync(p, child, uid, gid))
|
|
|
|
return handleEISDirSync(p, uid, gid)
|
|
}
|
|
|
|
module.exports = chownr
|
|
chownr.sync = chownrSync
|