86 lines
2.8 KiB
JavaScript
86 lines
2.8 KiB
JavaScript
import { execFile } from 'node:child_process'
|
|
import { existsSync } from 'node:fs'
|
|
import { mkdtemp, writeFile } from 'node:fs/promises'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import process from 'node:process'
|
|
import { promisify } from 'node:util'
|
|
import { loadReleaseEnvironment } from '../shared/environment.mjs'
|
|
import { assertLivePublishAllowed, resolveReleaseStorageConfig } from './config.mjs'
|
|
|
|
const execFileAsync = promisify(execFile)
|
|
|
|
export const createAwsCliRuntime = async (config) => {
|
|
const env = { ...process.env, ...config.credentials }
|
|
if (config.addressingStyle === 'virtual') {
|
|
const directory = await mkdtemp(path.join(os.tmpdir(), 'ncurator-aws-'))
|
|
const configPath = path.join(directory, 'config')
|
|
await writeFile(
|
|
configPath,
|
|
`[default]\nregion = ${config.region}\ns3 =\n addressing_style = virtual\n`
|
|
)
|
|
env.AWS_CONFIG_FILE = configPath
|
|
env.AWS_DEFAULT_REGION = config.region
|
|
}
|
|
return {
|
|
env,
|
|
extraArgs: ['--endpoint-url', config.endpoint, '--region', config.region]
|
|
}
|
|
}
|
|
|
|
export const createReleaseStorageClient = async (root = process.cwd()) => {
|
|
await loadReleaseEnvironment(root)
|
|
assertLivePublishAllowed()
|
|
const config = resolveReleaseStorageConfig()
|
|
const runtime = await createAwsCliRuntime(config)
|
|
const localAws = path.join(os.homedir(), '.ncurator', 'release-tools', 'awscli', 'bin', 'aws')
|
|
const windowsAws = process.env.ProgramFiles
|
|
? path.join(process.env.ProgramFiles, 'Amazon', 'AWSCLIV2', 'aws.exe')
|
|
: ''
|
|
const awsExecutable =
|
|
String(process.env.NCURATOR_AWS_CLI || '').trim() ||
|
|
(existsSync(localAws) ? localAws : existsSync(windowsAws) ? windowsAws : 'aws')
|
|
const aws = (args) =>
|
|
execFileAsync(awsExecutable, [...args, ...runtime.extraArgs], {
|
|
env: runtime.env,
|
|
maxBuffer: 20 * 1024 * 1024
|
|
})
|
|
|
|
const listKeys = async (prefix) => {
|
|
const keys = []
|
|
let continuationToken
|
|
do {
|
|
const args = [
|
|
's3api',
|
|
'list-objects-v2',
|
|
'--bucket',
|
|
config.bucket,
|
|
'--prefix',
|
|
prefix,
|
|
'--output',
|
|
'json'
|
|
]
|
|
if (continuationToken) args.push('--continuation-token', continuationToken)
|
|
const page = JSON.parse((await aws(args)).stdout)
|
|
keys.push(...(page.Contents || []).map((entry) => entry.Key))
|
|
continuationToken = page.IsTruncated ? page.NextContinuationToken : undefined
|
|
} while (continuationToken)
|
|
return keys
|
|
}
|
|
|
|
const deleteKeys = async (keys) => {
|
|
for (const key of [...new Set(keys)]) {
|
|
await aws(['s3api', 'delete-object', '--bucket', config.bucket, '--key', key])
|
|
process.stdout.write(`[release] removed old ${config.label} object ${key}\n`)
|
|
}
|
|
}
|
|
|
|
return {
|
|
...config,
|
|
aws,
|
|
awsExecutable,
|
|
listKeys,
|
|
deleteKeys
|
|
}
|
|
}
|