From 987642dc3c19667438a79ff4d991e348a2febb38 Mon Sep 17 00:00:00 2001 From: Tom Lienard Date: Sun, 25 Jun 2023 10:25:11 +0200 Subject: [PATCH] refactor(cli): binary install (#990) --- .changeset/nine-waves-grow.md | 5 ++ crates/cli/npm/binary-install.js | 119 +++++++++++++++++++++++++++++++ crates/cli/npm/getBinary.js | 6 +- crates/cli/npm/install.js | 12 ++-- crates/cli/npm/run.js | 5 +- crates/cli/package.json | 4 +- pnpm-lock.yaml | 101 ++++++++++---------------- 7 files changed, 175 insertions(+), 77 deletions(-) create mode 100644 .changeset/nine-waves-grow.md create mode 100644 crates/cli/npm/binary-install.js diff --git a/.changeset/nine-waves-grow.md b/.changeset/nine-waves-grow.md new file mode 100644 index 000000000..f1d639e3c --- /dev/null +++ b/.changeset/nine-waves-grow.md @@ -0,0 +1,5 @@ +--- +'@lagon/cli': patch +--- + +Refactor binary installation to be more stable diff --git a/crates/cli/npm/binary-install.js b/crates/cli/npm/binary-install.js new file mode 100644 index 000000000..bd0482d71 --- /dev/null +++ b/crates/cli/npm/binary-install.js @@ -0,0 +1,119 @@ +// All credit goes to Cloudflare wrangler-legacy and +// the initial binary-install package, which this code +// is heavily based on. +// +// Cloudflare wrangler-legacy: https://github.com/cloudflare/wrangler-legacy/blob/master/npm/binary-install.js +// binary-install: https://github.com/EverlastingBugstopper/binary-install#readme +import { existsSync, mkdirSync } from 'node:fs'; +import { join } from 'node:path'; +import { spawnSync } from 'node:child_process'; + +import axios from 'axios'; +import tar from 'tar'; +import { rimraf } from 'rimraf'; + +const error = msg => { + console.error(msg); + process.exit(1); +}; + +export class Binary { + constructor(url, data) { + this.url = url; + this.name = data.name || -1; + this.installDirectory = data.installDirectory; + this.binaryDirectory = -1; + this.binaryPath = -1; + } + + _getInstallDirectory() { + if (!existsSync(this.installDirectory)) { + mkdirSync(this.installDirectory, { recursive: true }); + } + return this.installDirectory; + } + + _getBinaryDirectory() { + const installDirectory = this._getInstallDirectory(); + const binaryDirectory = join(installDirectory, 'bin'); + if (existsSync(binaryDirectory)) { + this.binaryDirectory = binaryDirectory; + } else { + error(`You have not installed ${this.name}`); + } + return this.binaryDirectory; + } + + _getBinaryPath() { + if (this.binaryPath === -1) { + const binaryDirectory = this._getBinaryDirectory(); + this.binaryPath = join(binaryDirectory, this.name); + } + + return this.binaryPath; + } + + install() { + const dir = this._getInstallDirectory(); + if (!existsSync(dir)) { + mkdirSync(dir, { recursive: true }); + } + + this.binaryDirectory = join(dir, 'bin'); + + if (existsSync(this.binaryDirectory)) { + rimraf.sync(this.binaryDirectory); + } + + mkdirSync(this.binaryDirectory, { recursive: true }); + + console.log(`Downloading release from ${this.url}`); + + return axios({ url: this.url, responseType: 'stream' }) + .then(res => { + const writer = tar.x({ strip: 1, C: this.binaryDirectory }); + + return new Promise((resolve, reject) => { + res.data.pipe(writer); + let error = null; + writer.on('error', err => { + error = err; + reject(err); + }); + writer.on('close', () => { + if (!error) { + resolve(true); + } + }); + }); + }) + .then(() => { + console.log(`${this.name} has been installed!`); + }) + .catch(e => { + error(`Error fetching release: ${e.message}`); + }); + } + + uninstall() { + if (existsSync(this._getInstallDirectory())) { + rimraf.sync(this.installDirectory); + console.log(`${this.name} has been uninstalled`); + } + } + + run() { + const binaryPath = this._getBinaryPath(); + const [, , ...args] = process.argv; + + const options = { cwd: process.cwd(), stdio: 'inherit' }; + + const result = spawnSync(binaryPath, args, options); + + if (result.error) { + error(result.error); + } + + process.exit(result.status); + } +} diff --git a/crates/cli/npm/getBinary.js b/crates/cli/npm/getBinary.js index 9d527893f..23fb72863 100644 --- a/crates/cli/npm/getBinary.js +++ b/crates/cli/npm/getBinary.js @@ -1,6 +1,7 @@ import { createRequire } from 'node:module'; -import { Binary } from 'binary-install'; +import { Binary } from './binary-install.js'; import os from 'node:os'; +import path from 'node:path'; function getPlatform() { const type = os.type(); @@ -51,6 +52,7 @@ export function getBinary() { const { name: packageName, version } = customRequire('../package.json'); const url = `https://github.com/lagonapp/lagon/releases/download/${packageName}@${version}/${platform}.tar.gz`; + const installDirectory = path.join(os.homedir(), '.lagon'); - return new Binary(name, url); + return new Binary(url, { name, installDirectory }); } diff --git a/crates/cli/npm/install.js b/crates/cli/npm/install.js index 1ae4fb4a1..de5ea7b0b 100644 --- a/crates/cli/npm/install.js +++ b/crates/cli/npm/install.js @@ -1,12 +1,8 @@ #!/usr/bin/env node -// Prevent exiting with code 1 +// Prevent exiting with code 1 when +// the changeset PR is created process.exit = () => {}; -try { - import('./getBinary.js').then(({ getBinary }) => { - getBinary().install(); - }); -} catch (error) { - console.error(error); -} +import { getBinary } from './getBinary.js'; +getBinary().install(); diff --git a/crates/cli/npm/run.js b/crates/cli/npm/run.js index 7eb978b29..12859a4ec 100755 --- a/crates/cli/npm/run.js +++ b/crates/cli/npm/run.js @@ -9,7 +9,4 @@ const pkg = customRequire('../package.json'); updateNotifier({ pkg }).notify(); -const binary = getBinary(); - -// Try to install the binary before executing the CLI -binary.install({}, true).then(() => binary.run()); +getBinary().run(); diff --git a/crates/cli/package.json b/crates/cli/package.json index 4fd62eb68..811ebd989 100644 --- a/crates/cli/package.json +++ b/crates/cli/package.json @@ -15,7 +15,9 @@ "postinstall": "node npm/install.js" }, "dependencies": { - "binary-install": "^1.0.6", + "axios": "^1.4.0", + "rimraf": "^5.0.1", + "tar": "^6.1.15", "update-notifier": "^6.0.2" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78a7b07a9..60b47e603 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,9 +59,15 @@ importers: crates/cli: dependencies: - binary-install: - specifier: ^1.0.6 - version: 1.0.6 + axios: + specifier: ^1.4.0 + version: 1.4.0 + rimraf: + specifier: ^5.0.1 + version: 5.0.1 + tar: + specifier: ^6.1.15 + version: 6.1.15 update-notifier: specifier: ^6.0.2 version: 6.0.2 @@ -4764,7 +4770,7 @@ packages: '@babel/core': 7.20.7 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-create-class-features-plugin': 7.21.0(@babel/core@7.20.7) - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.21.5 '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.20.7) transitivePeerDependencies: - supports-color @@ -7096,7 +7102,6 @@ packages: strip-ansi-cjs: /strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true /@istanbuljs/load-nyc-config@1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} @@ -7301,7 +7306,7 @@ packages: npmlog: 5.0.1 rimraf: 3.0.2 semver: 7.5.2 - tar: 6.1.13 + tar: 6.1.15 transitivePeerDependencies: - encoding - supports-color @@ -8029,7 +8034,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} requiresBuild: true - dev: true optional: true /@pkgr/utils@2.3.1: @@ -12587,22 +12591,24 @@ packages: engines: {node: '>=12'} dev: true - /axios@0.26.1: - resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} + /axios@0.27.2: + resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: follow-redirects: 1.15.2 + form-data: 4.0.0 transitivePeerDependencies: - debug - dev: false + dev: true - /axios@0.27.2: - resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + /axios@1.4.0: + resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==} dependencies: follow-redirects: 1.15.2 form-data: 4.0.0 + proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: true + dev: false /axobject-query@2.2.0: resolution: {integrity: sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==} @@ -12661,7 +12667,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.22.5 + '@babel/compat-data': 7.21.4 '@babel/core': 7.20.7 '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.7) semver: 6.3.0 @@ -12674,7 +12680,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.22.5 + '@babel/compat-data': 7.21.4 '@babel/core': 7.21.0 '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.0) semver: 6.3.0 @@ -12860,17 +12866,6 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} - /binary-install@1.0.6: - resolution: {integrity: sha512-h3K4jaC4jEauK3csXI9GxGBJldkpuJlHCIBv8i+XBNhPuxnlERnD1PWVczQYDqvhJfv0IHUbB3lhDrZUMHvSgw==} - engines: {node: '>=10'} - dependencies: - axios: 0.26.1 - rimraf: 3.0.2 - tar: 6.1.11 - transitivePeerDependencies: - - debug - dev: false - /bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} requiresBuild: true @@ -13287,7 +13282,7 @@ packages: promise-inflight: 1.0.1 rimraf: 3.0.2 ssri: 8.0.1 - tar: 6.1.13 + tar: 6.1.15 unique-filename: 1.1.1 transitivePeerDependencies: - bluebird @@ -13307,7 +13302,7 @@ packages: minipass-pipeline: 1.2.4 p-map: 4.0.0 ssri: 10.0.4 - tar: 6.1.13 + tar: 6.1.15 unique-filename: 3.0.0 dev: true @@ -16948,7 +16943,6 @@ packages: dependencies: cross-spawn: 7.0.3 signal-exit: 4.0.2 - dev: true /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} @@ -17010,7 +17004,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: true /format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} @@ -17314,7 +17307,7 @@ packages: mri: 1.2.0 node-fetch-native: 1.2.0 pathe: 1.1.1 - tar: 6.1.13 + tar: 6.1.15 transitivePeerDependencies: - supports-color @@ -17385,7 +17378,6 @@ packages: minimatch: 9.0.1 minipass: 5.0.0 path-scurry: 1.9.2 - dev: true /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} @@ -18583,7 +18575,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 dev: true /is-natural-number@4.0.1: @@ -18900,7 +18892,6 @@ packages: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true /jake@10.8.5: resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} @@ -19645,7 +19636,6 @@ packages: /lru-cache@9.1.2: resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==} engines: {node: 14 || >=16.14} - dev: true /lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} @@ -20583,7 +20573,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: true /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} @@ -20649,14 +20638,9 @@ packages: dependencies: yallist: 4.0.0 - /minipass@4.2.1: - resolution: {integrity: sha512-KS4CHIsDfOZetnT+u6fwxyFADXLamtkPxkGScmmtTW//MlRrImV+LtbmbJpLQ86Hw7km/utbfEfndhGBrfwvlA==} - engines: {node: '>=8'} - /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - dev: true /minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} @@ -21260,7 +21244,7 @@ packages: npmlog: 6.0.2 rimraf: 3.0.2 semver: 7.5.2 - tar: 6.1.13 + tar: 6.1.15 which: 2.0.2 transitivePeerDependencies: - supports-color @@ -21629,7 +21613,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 dev: true /object-keys@1.1.1: @@ -22008,7 +21992,7 @@ packages: read-package-json-fast: 3.0.2 sigstore: 1.6.0 ssri: 10.0.4 - tar: 6.1.13 + tar: 6.1.15 transitivePeerDependencies: - bluebird - supports-color @@ -22190,7 +22174,6 @@ packages: dependencies: lru-cache: 9.1.2 minipass: 5.0.0 - dev: true /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -24324,6 +24307,14 @@ packages: dependencies: glob: 7.2.3 + /rimraf@5.0.1: + resolution: {integrity: sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 10.2.7 + dev: false + /ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} dependencies: @@ -24824,7 +24815,6 @@ packages: /signal-exit@4.0.2: resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==} engines: {node: '>=14'} - dev: true /sigstore@1.6.0: resolution: {integrity: sha512-QODKff/qW/TXOZI6V/Clqu74xnInAS6it05mufj4/fSewexLtfEntgLZZcBtUK44CDQyUE5TUXYy1ARYzlfG9g==} @@ -25537,25 +25527,13 @@ packages: inherits: 2.0.4 readable-stream: 3.6.0 - /tar@6.1.11: - resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==} - engines: {node: '>= 10'} - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 3.3.4 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - dev: false - - /tar@6.1.13: - resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} + /tar@6.1.15: + resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} engines: {node: '>=10'} dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 - minipass: 4.2.1 + minipass: 5.0.0 minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 @@ -27753,7 +27731,6 @@ packages: ansi-styles: 6.1.0 string-width: 5.1.2 strip-ansi: 7.0.1 - dev: true /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}