From a9cf17ceb1ec11031cb7b4a2bb96f51213a08088 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 17 Oct 2017 14:08:01 +0100 Subject: [PATCH] introduce a yarn.lock patch phase For resources loaded directly from github, add the missing SHA1 when invoking yarn2nix. yarn doesn't touch entries unless they are modified directly so it should only be needed when bumping the dependencies directly. Ideally this would be supported by upstream. --- bin/yarn2nix.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++--- default.nix | 2 +- package.json | 3 +- yarn.lock | 4 ++ yarn.nix | 8 ++-- 5 files changed, 102 insertions(+), 12 deletions(-) diff --git a/bin/yarn2nix.js b/bin/yarn2nix.js index dee395e..6213edb 100755 --- a/bin/yarn2nix.js +++ b/bin/yarn2nix.js @@ -1,9 +1,26 @@ #!/usr/bin/env node "use strict"; +const crypto = require('crypto'); const fs = require("fs"); -const lockfile = require('@yarnpkg/lockfile') +const https = require("https"); const path = require("path"); +const util = require("util"); + +const lockfile = require("@yarnpkg/lockfile") +const docopt = require("docopt").docopt; + +//////////////////////////////////////////////////////////////////////////////// + +const USAGE = ` +Usage: yarn2nix [options] + +Options: + -h --help Shows this help. + --no-nix Hide the nix output + --no-patch Don't patch the lockfile if hashes are missing + --lockfile=FILE Specify path to the lockfile [default: ./yarn.lock]. +` const HEAD = ` {fetchurl, linkFarm}: rec { @@ -11,6 +28,8 @@ const HEAD = ` packages = [ `.trim(); +//////////////////////////////////////////////////////////////////////////////// + function generateNix(lockedDependencies) { let found = {}; @@ -45,15 +64,81 @@ function generateNix(lockedDependencies) { console.log("}") } -// Main -const yarnLock = process.argv[2] || "yarn.lock"; +function getSha1(url) { + return new Promise((resolve, reject) => { + https.get(url, (res) => { + const { statusCode } = res; + const hash = crypto.createHash('sha1'); + if (statusCode !== 200) { + const err = new Error('Request Failed.\n' + + `Status Code: ${statusCode}`); + // consume response data to free up memory + res.resume(); + reject(err); + } -let file = fs.readFileSync(yarnLock, 'utf8') -let json = lockfile.parse(file) + res.on('data', (chunk) => { hash.update(chunk); }); + res.on('end', () => { resolve(hash.digest('hex')) }); + res.on('error', reject); + }); + }); +}; + +function updateResolvedSha1(pkg) { + // local dependency + if (!pkg.resolved) { return Promise.resolve(); } + let [url, sha1] = pkg.resolved.split("#", 2) + if (!sha1) { + return new Promise((resolve, reject) => { + getSha1(url).then(sha1 => { + pkg.resolved = `${url}#${sha1}`; + resolve(); + }).catch(reject); + }); + } else { + // nothing to do + return Promise.resolve(); + }; +} + +function values(obj) { + var entries = []; + for (let key in obj) { + entries.push(obj[key]); + } + return entries; +} +//////////////////////////////////////////////////////////////////////////////// +// Main +//////////////////////////////////////////////////////////////////////////////// + +var options = docopt(USAGE); + +let data = fs.readFileSync(options['--lockfile'], 'utf8') +let json = lockfile.parse(data) if (json.type != "success") { throw new Error("yarn.lock parse error") } -generateNix(json.object); +// Check fore missing hashes in the yarn.lock and patch if necessary +var pkgs = values(json.object); +Promise.all(pkgs.map(updateResolvedSha1)).then(() => { + let newData = lockfile.stringify(json.object); + + if (newData != data) { + console.error("found changes in the lockfile", options["--lockfile"]); + + if (options["--no-patch"]) { + console.error("...aborting"); + process.exit(1); + } + + fs.writeFileSync(options['--lockfile'], newData); + } + + if (!options['--no-nix']) { + generateNix(json.object); + } +}) \ No newline at end of file diff --git a/default.nix b/default.nix index 3cbc4b2..4f093a7 100644 --- a/default.nix +++ b/default.nix @@ -15,7 +15,7 @@ in rec { # Generates the yarn.nix from the yarn.lock file mkYarnNix = yarnLock: pkgs.runCommand "yarn.nix" {} - "${yarn2nix}/bin/yarn2nix ${yarnLock} > $out"; + "${yarn2nix}/bin/yarn2nix --lockfile ${yarnLock} --no-patch > $out"; # Loads the generated offline cache. This will be used by yarn as # the package source. diff --git a/package.json b/package.json index 53e1817..2f4dbe6 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "yarn2nix": "bin/yarn2nix.js" }, "dependencies": { - "@yarnpkg/lockfile": "^1.0.0" + "@yarnpkg/lockfile": "^1.0.0", + "docopt": "^0.6.2" } } diff --git a/yarn.lock b/yarn.lock index 11a0d1d..34e48ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,3 +5,7 @@ "@yarnpkg/lockfile@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.0.0.tgz#33d1dbb659a23b81f87f048762b35a446172add3" + +docopt@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/docopt/-/docopt-0.6.2.tgz#b28e9e2220da5ec49f7ea5bb24a47787405eeb11" diff --git a/yarn.nix b/yarn.nix index 1f531d6..a9e42e7 100644 --- a/yarn.nix +++ b/yarn.nix @@ -12,11 +12,11 @@ } { - name = "yarn-lockfile-1.1.1.tgz"; + name = "docopt-0.6.2.tgz"; path = fetchurl { - name = "yarn-lockfile-1.1.1.tgz"; - url = "https://registry.yarnpkg.com/yarn-lockfile/-/yarn-lockfile-1.1.1.tgz"; - sha1 = "3e58898c601f3d2511e2b2abb4638088918849e9"; + name = "docopt-0.6.2.tgz"; + url = "https://registry.yarnpkg.com/docopt/-/docopt-0.6.2.tgz"; + sha1 = "b28e9e2220da5ec49f7ea5bb24a47787405eeb11"; }; } ];