From 868afebd42d75d72906bd484fe70762db6528cbf Mon Sep 17 00:00:00 2001 From: Egor Kushnarev Date: Mon, 1 Apr 2024 10:42:18 +0300 Subject: [PATCH] by-hash initial commit --- src/deb/deb-builder.mts | 42 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/deb/deb-builder.mts b/src/deb/deb-builder.mts index 1464a4b..6954fe7 100644 --- a/src/deb/deb-builder.mts +++ b/src/deb/deb-builder.mts @@ -1,3 +1,4 @@ +import { createHash } from 'crypto'; import { createGzip } from 'zlib'; import * as fs from 'fs'; @@ -8,6 +9,7 @@ import * as tar from 'tar'; import type { Artifact, ArtifactProvider } from '../artifact-provider.mjs'; import { createDir, execToolToFile, removeDir } from '../fs.mjs'; import type { Deployer } from '../deployer.mjs'; +import { createReadStream } from 'fs'; type DebRepoDistribution = string; type DebRepoComponent = string; @@ -23,6 +25,7 @@ const ReleaseFileTemplate = Label: Ubuntu/Debian Architecture: $ARCH Component: $COMPONENT +Acquire-By-Hash: yes Codename: $DISTRIBUTION\n`; function iterateComponents(repo: DebRepo, callback: (distribution: string, component: string, deb: Artifact[]) => void): void { @@ -111,6 +114,10 @@ export class DebBuilder implements Deployer { await execToolToFile('apt-ftparchive', ['release', distributionPath], releaseFilePath, true); await execToolToFile('gpg', ['--no-tty', '--default-key', this.config.gpgKeyName, '-abs', '-o', releaseGpgFilePath, releaseFilePath]); await execToolToFile('gpg', ['--no-tty', '--default-key', this.config.gpgKeyName, '--clearsign', '-o', inReleaseFilePath, releaseFilePath]); + + await this.handleByHash(releaseFilePath); + await this.handleByHash(releaseGpgFilePath); + await this.handleByHash(inReleaseFilePath); } private async dpkgScanpackages(): Promise { @@ -201,7 +208,7 @@ export class DebBuilder implements Deployer { } private async makeRelease(): Promise<{}> { - const compressFile = (filePath: string): Promise => new Promise(resolve => { + const compressFile = (filePath: string): Promise => new Promise(resolve => { const inp = fs.createReadStream(filePath); const out = fs.createWriteStream(`${filePath}.gz`); @@ -209,11 +216,12 @@ export class DebBuilder implements Deployer { inp.pipe(gzip).pipe(out) .on('finish', () => { - resolve(); + resolve(`${filePath}.gz`); }); }); - const compressPromises: Promise[] = []; + const compressPromises: Promise[] = []; + const byHashPromises: Promise[] = []; iterateComponents(this.config.repo, (distribution, component) => { const componentRoot = path.join(this.distsPath, distribution, component); @@ -234,12 +242,18 @@ export class DebBuilder implements Deployer { } fs.writeFileSync(targetPackagesFile, packagesContent); - + byHashPromises.push(this.handleByHash(targetPackagesFile)); compressPromises.push(compressFile(targetPackagesFile)); }); }); - await Promise.all(compressPromises); + const compressedPackages = await Promise.all(compressPromises); + + compressedPackages.forEach(packagePath => { + byHashPromises.push(this.handleByHash(packagePath)); + }); + + await Promise.all(byHashPromises); const releasesPromises: Promise[] = []; @@ -254,4 +268,22 @@ export class DebBuilder implements Deployer { return Promise.all(releasesPromises); } + + private async handleByHash(filePath: string): Promise { + const byHashDir = path.join(path.dirname(filePath), 'by-hash', 'SHA256'); + const byHashFileName = path.resolve(path.join(byHashDir, await this.sha256(filePath))); + + createDir(byHashDir); + return fs.promises.copyFile(filePath, byHashFileName); + } + + private async sha256(filePath: string): Promise{ + return new Promise((resolve, reject) => { + const hash = createHash('sha256'); + createReadStream(filePath) + .once('end', () => resolve(hash.digest('hex'))) + .once('error', () => reject) + .pipe(hash); + }); + } }