prefetch-yarn-deps: add --fixup-lockfile flag to fixup a yarn.lock (#214062)

The flag iterates through the lockfile entries, rewrites `resolved` URLs
to those that will be in the cache (like `fixup_yarn_lock` from
yarn2nix), removes `integrity` for git deps whose hash won't match the
reproducible repacking that the fetcher does, writes the amended
lockfile, and exits.

authored by Lily Foster and committed by GitHub 5c32e0ba 680794db

+95 -16
+17
pkgs/build-support/node/fetch-yarn-deps/common.js
··· 1 + const path = require('path') 2 + 3 + // This has to match the logic in pkgs/development/tools/yarn2nix-moretea/yarn2nix/lib/urlToName.js 4 + // so that fixup_yarn_lock produces the same paths 5 + const urlToName = url => { 6 + const isCodeloadGitTarballUrl = url.startsWith('https://codeload.github.com/') && url.includes('/tar.gz/') 7 + 8 + if (url.startsWith('git+') || isCodeloadGitTarballUrl) { 9 + return path.basename(url) 10 + } else { 11 + return url 12 + .replace(/https:\/\/(.)*(.com)\//g, '') // prevents having long directory names 13 + .replace(/[@/%:-]/g, '_') // replace @ and : and - and % characters with underscore 14 + } 15 + } 16 + 17 + module.exports = { urlToName };
+3 -2
pkgs/build-support/node/fetch-yarn-deps/default.nix
··· 21 21 mkdir libexec 22 22 tar --strip-components=1 -xf ${yarnpkg-lockfile-tar} package/index.js 23 23 mv index.js libexec/yarnpkg-lockfile.js 24 - cp ${./index.js} libexec/index.js 25 - patchShebangs libexec/index.js 24 + cp ${./.}/*.js libexec/ 25 + patchShebangs libexec 26 26 27 27 runHook postBuild 28 28 ''; ··· 34 34 cp -r libexec $out 35 35 makeWrapper $out/libexec/index.js $out/bin/prefetch-yarn-deps \ 36 36 --prefix PATH : ${lib.makeBinPath [ coreutils nix-prefetch-git nix ]} 37 + makeWrapper $out/libexec/fixup.js $out/bin/fixup-yarn-lock 37 38 38 39 runHook postInstall 39 40 '';
+74
pkgs/build-support/node/fetch-yarn-deps/fixup.js
··· 1 + #!/usr/bin/env node 2 + 'use strict' 3 + 4 + const fs = require('fs') 5 + const process = require('process') 6 + const lockfile = require('./yarnpkg-lockfile.js') 7 + const { urlToName } = require('./common.js') 8 + 9 + const fixupYarnLock = async (lockContents, verbose) => { 10 + const lockData = lockfile.parse(lockContents) 11 + 12 + const fixedData = Object.fromEntries( 13 + Object.entries(lockData.object) 14 + .map(([dep, pkg]) => { 15 + const [ url, hash ] = pkg.resolved.split("#", 2) 16 + 17 + if (hash || url.startsWith("https://codeload.github.com")) { 18 + if (verbose) console.log(`Removing integrity for git dependency ${dep}`) 19 + delete pkg.integrity 20 + } 21 + 22 + if (verbose) console.log(`Rewriting URL ${url} for dependency ${dep}`) 23 + pkg.resolved = urlToName(url) 24 + 25 + return [dep, pkg] 26 + }) 27 + ) 28 + 29 + if (verbose) console.log('Done') 30 + 31 + return fixedData 32 + } 33 + 34 + const showUsage = async () => { 35 + process.stderr.write(` 36 + syntax: fixup-yarn-lock [path to yarn.lock] [options] 37 + 38 + Options: 39 + -h --help Show this help 40 + -v --verbose Verbose output 41 + `) 42 + process.exit(1) 43 + } 44 + 45 + const main = async () => { 46 + const args = process.argv.slice(2) 47 + let next, lockFile, verbose 48 + while (next = args.shift()) { 49 + if (next == '--verbose' || next == '-v') { 50 + verbose = true 51 + } else if (next == '--help' || next == '-h') { 52 + showUsage() 53 + } else if (!lockFile) { 54 + lockFile = next 55 + } else { 56 + showUsage() 57 + } 58 + } 59 + let lockContents 60 + try { 61 + lockContents = await fs.promises.readFile(lockFile || 'yarn.lock', 'utf-8') 62 + } catch { 63 + showUsage() 64 + } 65 + 66 + const fixedData = await fixupYarnLock(lockContents, verbose) 67 + await fs.promises.writeFile(lockFile || 'yarn.lock', lockfile.stringify(fixedData)) 68 + } 69 + 70 + main() 71 + .catch(e => { 72 + console.error(e) 73 + process.exit(1) 74 + })
+1 -14
pkgs/build-support/node/fetch-yarn-deps/index.js
··· 10 10 const lockfile = require('./yarnpkg-lockfile.js') 11 11 const { promisify } = require('util') 12 12 const url = require('url') 13 + const { urlToName } = require('./common.js') 13 14 14 15 const execFile = promisify(child_process.execFile) 15 16 ··· 17 18 const res = await execFile(...args) 18 19 if (res.error) throw new Error(res.stderr) 19 20 return res 20 - } 21 - 22 - // This has to match the logic in pkgs/development/tools/yarn2nix-moretea/yarn2nix/lib/urlToName.js 23 - // so that fixup_yarn_lock produces the same paths 24 - const urlToName = url => { 25 - const isCodeloadGitTarballUrl = url.startsWith('https://codeload.github.com/') && url.includes('/tar.gz/') 26 - 27 - if (url.startsWith('git+') || isCodeloadGitTarballUrl) { 28 - return path.basename(url) 29 - } else { 30 - return url 31 - .replace(/https:\/\/(.)*(.com)\//g, '') // prevents having long directory names 32 - .replace(/[@/%:-]/g, '_') // replace @ and : and - and % characters with underscore 33 - } 34 21 } 35 22 36 23 const downloadFileHttps = (fileName, url, expectedHash, hashType = 'sha1') => {