From 177b030ff8f214923570cbcb98a9e29c815e5fa3 Mon Sep 17 00:00:00 2001 From: Daryl Ronningen Date: Sat, 8 Oct 2022 12:30:52 -0700 Subject: [PATCH] feat: add lockfile support --- package.json | 2 + src/commands/install.ts | 9 ++++- src/commands/uninstall.ts | 77 +++++++++++++++++++++------------------ src/utils.ts | 5 +++ yarn.lock | 33 ++++++++++++++++- 5 files changed, 87 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index d2d6020..e3e24be 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "commander": "^9.4.1", "fs-extra": "^10.1.0", "lowdb": "^3.0.0", + "proper-lockfile": "^4.1.2", "tar": "^6.1.11", "type-fest": "^3.0.0" }, @@ -23,6 +24,7 @@ "@rollup/plugin-node-resolve": "^14.1.0", "@rushstack/eslint-patch": "^1.2.0", "@types/fs-extra": "^9.0.13", + "@types/proper-lockfile": "^4.1.2", "@types/tar": "^6.1.3", "@typescript-eslint/eslint-plugin": "^5.39.0", "@typescript-eslint/parser": "^5.39.0", diff --git a/src/commands/install.ts b/src/commands/install.ts index b7fa96c..a5e1ccf 100644 --- a/src/commands/install.ts +++ b/src/commands/install.ts @@ -4,10 +4,11 @@ import * as lowdb from 'lowdb'; import crypto from 'node:crypto'; import path from 'node:path'; import os from 'node:os'; +import { lock, unlock } from 'proper-lockfile'; import tar from 'tar'; import { GlobalOptions } from '../index.js'; -import { makeDbFile, parseWahInfo, walk } from '../utils.js'; +import { checkLockFile, makeDbFile, parseWahInfo, walk } from '../utils.js'; export default class Install { public name: string; @@ -27,6 +28,9 @@ export default class Install { } await makeDbFile(this.options.sysroot); + await checkLockFile(this.options.sysroot); + + await lock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg.lock` }); if (!await fs.pathExists(path.join(this.options.sysroot, os.tmpdir(), 'wahpkg'))) await fs.mkdirp(path.join(this.options.sysroot, os.tmpdir(), 'wahpkg')); const makeExtractDir = await fs.mkdtemp(path.join(this.options.sysroot, os.tmpdir(), 'wahpkg', `${path.basename(this.options.file)}-`)); @@ -81,6 +85,9 @@ export default class Install { await fs.rm(path.join(makeExtractDir), { recursive: true, force: true }); console.log(chalk.bold.green`Package %s has been installed!`, getWahInfo.name); + + await unlock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg.lock` }); + process.exit(0); } } diff --git a/src/commands/uninstall.ts b/src/commands/uninstall.ts index c7c43c9..8c430fd 100644 --- a/src/commands/uninstall.ts +++ b/src/commands/uninstall.ts @@ -3,9 +3,10 @@ import fs from 'fs-extra'; import * as lowdb from 'lowdb'; import crypto from 'node:crypto'; import path from 'node:path'; +import { lock, unlock } from 'proper-lockfile'; import { GlobalOptions } from '../index.js'; -import { makeDbFile } from '../utils.js'; +import { checkLockFile, makeDbFile } from '../utils.js'; import { dbData } from './install.js'; export default class Uninstall { @@ -19,46 +20,50 @@ export default class Uninstall { public async run(): Promise { await makeDbFile(this.options.sysroot); + await checkLockFile(this.options.sysroot); - if (this.name) { - const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`; - const adapter = new lowdb.JSONFile(dbFile); - const db = new lowdb.Low(adapter); + await lock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg/wahpkg.lock` }); - await db.read(); + const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`; + const adapter = new lowdb.JSONFile(dbFile); + const db = new lowdb.Low(adapter); - if (!db.data.pkgs.find((val) => val.name === this.name)) { - console.error(chalk.red.bold`%s is not installed!`, this.name); - process.exit(1); - } + await db.read(); - const md5File = await fs.readFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}/MD5HASHES`); - - for (const file of md5File.toString().split('\n')) { - const fileName = file.split(' ')[0]; - const hash = file.split(' ')[1]; - - // Ignore blank or last line - if (fileName === '') continue; - - const verifyHash = crypto.createHash('md5').update(await fs.readFile(path.join(this.options.sysroot, fileName))).digest('hex'); - - if (verifyHash !== hash && this.options['ignore-hash']) { - console.error(chalk.red.bold`%s does not match MD5 hash of %s! To ignore this error, please add --ignore-hash in the command`, this.name, hash); - process.exit(1); - } else { - await fs.rm(path.join(this.options.sysroot, fileName)); - } - } - - await fs.rm(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}`, { force: true, recursive: true }); - - db.data.pkgs = db.data.pkgs.filter((item) => item.name !== this.name); - await db.write(); - - console.log(chalk.green.bold`%s has been successfully removed!`, this.name); - process.exit(0); + if (!db.data.pkgs.find((val) => val.name === this.name)) { + console.error(chalk.red.bold`%s is not installed!`, this.name); + process.exit(1); } + + const md5File = await fs.readFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}/MD5HASHES`); + + for (const file of md5File.toString().split('\n')) { + const fileName = file.split(' ')[0]; + const hash = file.split(' ')[1]; + + // Ignore blank or last line + if (fileName === '') continue; + + const verifyHash = crypto.createHash('md5').update(await fs.readFile(path.join(this.options.sysroot, fileName))).digest('hex'); + + if (verifyHash !== hash && this.options['ignore-hash']) { + console.error(chalk.red.bold`%s does not match MD5 hash of %s! To ignore this error, please add --ignore-hash in the command`, this.name, hash); + process.exit(1); + } else { + await fs.rm(path.join(this.options.sysroot, fileName)); + } + } + + await fs.rm(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}`, { force: true, recursive: true }); + + db.data.pkgs = db.data.pkgs.filter((item) => item.name !== this.name); + await db.write(); + + console.log(chalk.green.bold`%s has been successfully removed!`, this.name); + + await unlock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg/wahpkg.lock` }); + + process.exit(0); } } diff --git a/src/utils.ts b/src/utils.ts index 9c282b5..df59db7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,7 @@ import chalk from 'chalk'; import fs from 'fs-extra'; import path from 'node:path'; +import { check, lock } from 'proper-lockfile'; import type { PartialDeep, RequireAllOrNone } from 'type-fest'; export function parseWahInfo(file: string): WahInfo { @@ -72,6 +73,10 @@ export const makeDbFile = async (sysroot: string): Promise => { } }; +export const checkLockFile = async (sysroot: string): Promise => { + return await check(`${sysroot}/var/lib/wahpkg`, { lockfilePath: `${sysroot}/var/lib/wahpkg/wahpkg.lock` }); +}; + export interface WahInfo { name: string; version: { diff --git a/yarn.lock b/yarn.lock index 7620e86..03f4613 100644 --- a/yarn.lock +++ b/yarn.lock @@ -354,6 +354,15 @@ __metadata: languageName: node linkType: hard +"@types/proper-lockfile@npm:^4.1.2": + version: 4.1.2 + resolution: "@types/proper-lockfile@npm:4.1.2" + dependencies: + "@types/retry": "npm:*" + checksum: 0d4f149fac026588f4bb02a888ffff74499ddfdee4d46cf5c59f7abb80200327f696f45a8d77e14211976711a38f0517d526a0da8fe8ded6ba8792d39c9aaceb + languageName: node + linkType: hard + "@types/resolve@npm:1.17.1": version: 1.17.1 resolution: "@types/resolve@npm:1.17.1" @@ -372,6 +381,13 @@ __metadata: languageName: node linkType: hard +"@types/retry@npm:*": + version: 0.12.2 + resolution: "@types/retry@npm:0.12.2" + checksum: 2534b2da403e94ae5dd5c6967d5b97231fc709fc60d760c41ccfedf9eb93842d160ce913107c1363990524eb6d11c775c8c0236f4d9e83d6e1400d6c443d0f24 + languageName: node + linkType: hard + "@types/semver@npm:^7.1.0": version: 7.3.12 resolution: "@types/semver@npm:7.3.12" @@ -1618,7 +1634,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" checksum: 6b5f9b5aeaee0459b9c37bdbf9624f788703ce291d6bf2d7751f5003942e853f232ca613aec818d1ff7622379bc8b434c635bfda99db93e0b9b8da80ec3d844d @@ -2451,6 +2467,17 @@ __metadata: languageName: node linkType: hard +"proper-lockfile@npm:^4.1.2": + version: 4.1.2 + resolution: "proper-lockfile@npm:4.1.2" + dependencies: + graceful-fs: "npm:^4.2.4" + retry: "npm:^0.12.0" + signal-exit: "npm:^3.0.2" + checksum: c015725de952f366e9386330f558b4a7c3089e1db167837840ddd00ac4d1f127ed3d184a5518576ec2cbf514e15d007730d86cf0eea88a810ee0c7bb67345072 + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.0 resolution: "pump@npm:3.0.0" @@ -2709,7 +2736,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: 5cf7525c55a72d8d104d914acf2e470f74b2c156197277ad7b331bc5de3d8790170fed3c82ff98c7c31adaa8ff941bfd5ba44f55171cbe8ed0e939fa82a8322a @@ -3048,6 +3075,7 @@ __metadata: "@rollup/plugin-node-resolve": "npm:^14.1.0" "@rushstack/eslint-patch": "npm:^1.2.0" "@types/fs-extra": "npm:^9.0.13" + "@types/proper-lockfile": "npm:^4.1.2" "@types/tar": "npm:^6.1.3" "@typescript-eslint/eslint-plugin": "npm:^5.39.0" "@typescript-eslint/parser": "npm:^5.39.0" @@ -3057,6 +3085,7 @@ __metadata: eslint: "npm:^8.24.0" fs-extra: "npm:^10.1.0" lowdb: "npm:^3.0.0" + proper-lockfile: "npm:^4.1.2" rollup: "npm:^2.79.1" rollup-plugin-terser: "npm:^7.0.2" rollup-plugin-typescript2: "npm:^0.34.1"