Files
publish-immutable-action/src/fs-helper.ts
T
Conor Sloan 7c7ef462b7 fix or disable linting in fs-helper
There is a bit of a catch-22 bere where the createArchives function should not use .then and .catch in favour of async/await according to the linter, but can't use async/await because it's discouraged to make new promise executor functions async. There is a hacky workaround to make this work, but it seems like it isn't worth making the code more fragmented, so i just disabled the linter for these lines.
2023-11-22 13:03:09 +00:00

118 lines
3.0 KiB
TypeScript

import * as fs from 'fs'
import * as path from 'path'
import * as tar from 'tar'
import * as archiver from 'archiver'
import * as crypto from 'crypto'
import * as os from 'os'
export function createTempDir(): string {
const randomDirName = crypto.randomBytes(4).toString('hex')
const tempDir = path.join(os.tmpdir(), randomDirName)
if (!fs.existsSync(tempDir)) {
fs.mkdirSync(tempDir)
}
return tempDir
}
export function removeDir(dir: string): void {
fs.rmSync(dir, { recursive: true })
}
export interface FileMetadata {
path: string
size: number
sha256: string
}
// Creates both a tar.gz and zip archive of the given directory and returns the paths to both archives (stored in the provided target directory)
// as well as the size/sha256 hash of each file.
export async function createArchives(
distPath: string,
archiveTargetPath: string = createTempDir()
): Promise<{ zipFile: FileMetadata; tarFile: FileMetadata }> {
const zipPath = path.join(archiveTargetPath, `archive.zip`)
const tarPath = path.join(archiveTargetPath, `archive.tar.gz`)
const createZipPromise = new Promise<FileMetadata>((resolve, reject) => {
const output = fs.createWriteStream(zipPath)
const archive = archiver.create('zip')
output.on('error', (err: Error) => {
reject(err)
})
archive.on('error', (err: Error) => {
reject(err)
})
output.on('close', () => {
resolve(fileMetadata(zipPath))
})
archive.pipe(output)
archive.directory(distPath, false)
archive.finalize()
})
const createTarPromise = new Promise<FileMetadata>((resolve, reject) => {
tar
.c(
{
file: tarPath,
C: distPath, // Change to the source directory for relative paths (TODO)
gzip: true
},
['.']
)
// eslint-disable-next-line github/no-then
.catch(err => {
reject(err)
})
// eslint-disable-next-line github/no-then
.then(() => {
resolve(fileMetadata(tarPath))
})
})
const [zipFile, tarFile] = await Promise.all([
createZipPromise,
createTarPromise
])
return { zipFile, tarFile }
}
export function isDirectory(dirPath: string): boolean {
return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory()
}
export function readFileContents(filePath: string): Buffer {
return fs.readFileSync(filePath)
}
// Converts a file path to a filemetadata object by querying the fs for relevant metadata.
async function fileMetadata(filePath: string): Promise<FileMetadata> {
const stats = fs.statSync(filePath)
const size = stats.size
const hash = crypto.createHash('sha256')
const fileStream = fs.createReadStream(filePath)
return new Promise((resolve, reject) => {
fileStream.on('data', data => {
hash.update(data)
})
fileStream.on('end', () => {
const sha256 = hash.digest('hex')
resolve({
path: filePath,
size,
sha256: `sha256:${sha256}`
})
})
fileStream.on('error', err => {
reject(err)
})
})
}