dd-trace
Version:
Datadog APM tracing client for JavaScript
130 lines (113 loc) • 3.44 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 { getEnvironmentVariable } = require('../../config-helper')
const { isTrue } = require('../../util')
let isGitEnabled = isTrue(getEnvironmentVariable('DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_ENABLED'))
const GIT_CACHE_DIR = getEnvironmentVariable('DD_EXPERIMENTAL_TEST_OPT_GIT_CACHE_DIR') ||
path.join(os.tmpdir(), 'dd-trace-git-cache')
function ensureCacheDir () {
if (!isGitEnabled) return false
try {
if (fs.existsSync(GIT_CACHE_DIR)) {
const stats = fs.statSync(GIT_CACHE_DIR)
if (!stats.isDirectory()) {
throw new Error(`Cache directory path exists but is not a directory: ${GIT_CACHE_DIR}`)
}
} else {
fs.mkdirSync(GIT_CACHE_DIR, { recursive: true })
}
return true
} catch (err) {
log.error('Failed to create git cache directory, disabling cache', err)
isGitEnabled = false
return false
}
}
// Initialize cache directory at module load time
ensureCacheDir()
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) {
return path.join(GIT_CACHE_DIR, `${cacheKey}.cache`)
}
function getCache (cacheKey) {
if (!isGitEnabled) return null
try {
const cacheFilePath = getCacheFilePath(cacheKey)
if (!fs.existsSync(cacheFilePath)) {
return null
}
const content = fs.readFileSync(cacheFilePath, 'utf8')
return content
} catch (err) {
log.error('Failed to read git cache', err)
return null
}
}
function setCache (cacheKey, result) {
if (!isGitEnabled) return
// Ensure cache directory exists
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 (!isGitEnabled) {
return cp.execFileSync(cmd, flags, options)
}
const cacheKey = getCacheKey(cmd, flags)
const cachedResult = getCache(cacheKey)
if (cachedResult !== null) {
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
}