From 440611913d0fb2416443c852765d8b0e71815716 Mon Sep 17 00:00:00 2001 From: C-3PO Date: Mon, 9 Jul 2018 15:23:11 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Update=20getPatch=20to=20write?= =?UTF-8?q?=20files=20to=20disk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/installPatch.ts | 13 +++++-- src/ssn/getPatch.ts | 91 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/installPatch.ts b/src/installPatch.ts index c60ac9a..1ee5419 100644 --- a/src/installPatch.ts +++ b/src/installPatch.ts @@ -29,10 +29,17 @@ if (!to.match(/^(0|[1-9][0-9]{0,2})$/)) { //TODO: set target directory where patch should be installed //TODO: set temp directory where patch files should be stored. //TODO: set location of xdelta3 executable -//TODO: set from=any so it detects current version automatically +//TODO: set from=any so it detects current version automatically (based on .version files) //TODO: set to=manifest/current to install whatever is the current version in manifest/on CDN (async () => { - const patch = await getPatch(product as Product, Number(from), Number(to)); - //console.log(patch); + await getPatch({ + /* tslint:disable:object-literal-sort-keys */ + product: product as Product, + from: Number(from), + to: Number(to), + sourceDirectory: process.cwd(), + targetDirectory: process.cwd(), + /* tslint:enable:object-literal-sort-keys */ + }); })(); diff --git a/src/ssn/getPatch.ts b/src/ssn/getPatch.ts index a681d22..f56f8a6 100644 --- a/src/ssn/getPatch.ts +++ b/src/ssn/getPatch.ts @@ -1,4 +1,7 @@ +import * as fs from 'fs'; +import * as path from 'path'; import downloadUrlContents from '../cdn/downloadUrlContents'; +import createDirRecursively from '../cdn/funcs/createDirRecursively'; import getUrlContents from '../cdn/getUrlContents'; import { Product } from '../interfaces/ISettings'; import { SsnDiffType } from '../interfaces/ISsnFileEntry'; @@ -6,10 +9,46 @@ import extractFileAsStream from './extractFileAsStream'; import getSolidpkg from './getSolidpkg'; import readSsnFile from './reader/readSsnFile'; import getFileFromDisks from './streams/getFileFromDisks'; -import streamToArrayBuffer from './streams/streamToArrayBuffer'; import verifyPatch from './verify/verifyPatch'; +import verifyProductName from './verify/verifyProductName'; + +interface IGetPatchArgs { + /** The product that should be patched. */ + product: Product; + /** The release that we want to patch from, or -1 if patching fresh. */ + from: number; + /** The release that we want to patch to. */ + to: number; + /** The source directory where the files from the "from" release are stored. Not needed if from=-1. */ + sourceDirectory: string | undefined; + /** The target directory where the files should be extracted to. */ + targetDirectory: string; +} + +/** Downloads and applies the specified patch. */ +export default async function getPatch({ product, from, to, sourceDirectory, targetDirectory }: IGetPatchArgs) { + //Verify function arguments + + if (!verifyProductName(product)) { + throw new TypeError(`"${product}" is not a valid product.`); + } + if (typeof from !== 'number' || (from | 0) !== from || from < -1 || from > 999) { + throw new TypeError(`from must be an integer greater than or equal to -1 but it is "${from}"`); + } + if (typeof to !== 'number' || (to | 0) !== to || to < 0 || to > 999) { + throw new TypeError(`to must be an integer greater than or equal to 0 but it is "${to}"`); + } + + if (sourceDirectory === undefined && from !== -1) { + throw new Error('Must specify a sourceDirectory if patching from a release other than -1.'); + } + const sourceDir = (from === -1) ? '' : path.resolve(sourceDirectory as string); + const targetDir = path.resolve(targetDirectory); + await createDirRecursively(targetDir); + + //-------------------------------------------------------------------------------------------------------------- + //Download and verify files -export default async function getPatch(product: Product, from: number, to: number) { const solidpkg = await getSolidpkg(product, from, to); function createUrlObject({ name, length }: {name: string, length: number}) { @@ -33,13 +72,21 @@ export default async function getPatch(product: Product, from: number, to: numbe //TODO: Verify that downloaded files match the hash in `solidpkg.pieces` + //-------------------------------------------------------------------------------------------------------------- + //Perform the patching + //Extract newly added files fileEntries.filter((file) => file.diffType === SsnDiffType.NewFile).forEach(async (file) => { try { + //extract file const fileStream = await getFileFromDisks(diskFilenames, { diskStart: file.diskNumberStart, offset: file.offset, length: file.compressedSize }); const fileContents = await extractFileAsStream(file, fileStream); - console.debug(file.name, file.compressedSize, await streamToArrayBuffer(fileContents)); - //TODO: need to write to disk + + //write to disk + const outputName = path.join(targetDir, file.name); + await createDirRecursively(path.dirname(outputName)); + const outputStream = fs.createWriteStream(outputName); + fileContents.pipe(outputStream); } catch (error) { console.error(`Could not extract file "${file.name}"`, error); } @@ -48,17 +95,43 @@ export default async function getPatch(product: Product, from: number, to: numbe //Extract changed files fileEntries.filter((file) => file.diffType === SsnDiffType.Changed).forEach(async (file) => { try { + //extract file const fileStream = await getFileFromDisks(diskFilenames, { diskStart: file.diskNumberStart, offset: file.offset, length: file.compressedSize }); const fileContents = await extractFileAsStream(file, fileStream); - console.debug(file.name, file.compressedSize, await streamToArrayBuffer(fileContents)); - //TODO: need to apply diffing, then write to disk + + //TODO: need to apply xdelta3 diffing, then write to disk + //if source === target, need to create .temp file, and when it's done, delete source file and rename .temp to actual name + console.warn(`Could not extract "${file.name}"; xdelta3 diffing is not yet implemented.`); } catch (error) { console.error(`Could not extract file "${file.name}"`, error); } }); //Need to delete deleted files - fileEntries.filter((file) => file.diffType === SsnDiffType.Deleted).forEach((file) => { - //TODO: need to delete file - }); + if (sourceDirectory === targetDirectory) { + fileEntries.filter((file) => file.diffType === SsnDiffType.Deleted).forEach((file) => { + //delete file + const fileName = path.join(targetDir, file.name); + fs.unlink(fileName, (error) => { + if (error) { + console.error(`Could not delete removed file "${file.name}"`, error); + } + }); + }); + } + + //Need to copy unchanged files (if we are patching into a different directory) + if (sourceDirectory !== targetDirectory) { + fileEntries.filter((file) => file.diffType === SsnDiffType.Unchanged).forEach(async (file) => { + //copy file + const sourceName = path.join(sourceDir, file.name); + const targetName = path.join(targetDir, file.name); + await createDirRecursively(path.dirname(targetName)); + fs.copyFile(sourceName, targetName, (error) => { + if (error) { + console.error(`Could not copy unchanged file "${file.name}"`, error); + } + }); + }); + } }