dd-trace
Version:
Datadog APM tracing client for JavaScript
130 lines (114 loc) • 3.38 kB
JavaScript
const os = require('os')
const path = require('path')
const fs = require('fs')
const crypto = require('crypto')
const cp = require('child_process')
const log = require('../../log')
const getConfig = require('../../config')
let isGitEnabled
let gitCacheDir
function ensureCacheDir () {
if (isGitEnabled === undefined) {
const config = getConfig()
isGitEnabled = config.DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_ENABLED
// TODO: Move the default to config/index.js applyCalculated
// when using the config singleton.
gitCacheDir = config.DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_DIR ||
path.join(os.tmpdir(), 'dd-trace-git-cache')
}
if (!isGitEnabled) return false
try {
if (fs.existsSync(gitCacheDir)) {
const stats = fs.statSync(gitCacheDir)
if (!stats.isDirectory()) {
throw new Error(`Cache directory path exists but is not a directory: ${gitCacheDir}`)
}
} else {
fs.mkdirSync(gitCacheDir, { recursive: true })
}
return true
} catch (err) {
log.error('Failed to create git cache directory, disabling cache', err)
isGitEnabled = false
return false
}
}
function getCacheKey (cmd, flags) {
// Create a hash of the command and flags to use as cache key
const commandString = `${cmd} ${flags.join(' ')}`
return crypto.createHash('sha256').update(commandString).digest('hex')
}
function getCacheFilePath (cacheKey) {
ensureCacheDir()
return path.join(gitCacheDir, `${cacheKey}.cache`)
}
function getCache (cacheKey) {
if (!ensureCacheDir()) return null
try {
const cacheFilePath = getCacheFilePath(cacheKey)
if (!fs.existsSync(cacheFilePath)) {
return
}
return fs.readFileSync(cacheFilePath, 'utf8')
} catch (err) {
log.error('Failed to read git cache', err)
}
}
function setCache (cacheKey, result) {
if (!ensureCacheDir()) return
try {
const cacheFilePath = getCacheFilePath(cacheKey)
fs.writeFileSync(cacheFilePath, result, 'utf8')
} catch (err) {
log.error('Failed to write git cache', err)
}
}
function cachedExec (cmd, flags, options) {
if (options === undefined) {
options = { stdio: 'pipe' }
}
if (!ensureCacheDir()) {
return cp.execFileSync(cmd, flags, options)
}
const cacheKey = getCacheKey(cmd, flags)
const cachedResult = getCache(cacheKey)
if (cachedResult !== undefined) {
if (cachedResult.startsWith('__GIT_COMMAND_FAILED__')) {
let error
try {
const errorData = cachedResult.replace('__GIT_COMMAND_FAILED__', '')
const { message, code, status, errno } = JSON.parse(errorData)
error = new Error(message)
error.code = code
error.status = status
error.errno = errno
} catch {
// we couldn't parse the error data, so we'll throw a generic error
throw new Error('Git command failed')
}
throw error
}
return cachedResult
}
try {
const result = cp.execFileSync(cmd, flags, options)
setCache(cacheKey, result)
return result
} catch (err) {
const cacheValue = '__GIT_COMMAND_FAILED__' +
JSON.stringify({
code: err.code,
status: err.status,
errno: err.errno,
message: err.message,
})
setCache(cacheKey, cacheValue)
throw err
}
}
module.exports = {
getCacheKey,
getCacheFilePath,
cachedExec,
}