const R = require("ramda"); const ssri = require("ssri"); const urlToName = require("./urlToName"); const { execFileSync } = require("child_process"); // fetchgit transforms // // "shell-quote@git+https://github.com/srghma/node-shell-quote.git#without_unlicenced_jsonify": // version "1.6.0" // resolved "git+https://github.com/srghma/node-shell-quote.git#1234commit" // // to // // fetchGit { // url = "https://github.com/srghma/node-shell-quote.git"; // ref = "without_unlicenced_jsonify"; // rev = "1234commit"; // } // // and transforms // // "@graphile/plugin-supporter@git+https://1234user:1234pass@git.graphile.com/git/users/1234user/postgraphile-supporter.git": // version "0.6.0" // resolved "git+https://1234user:1234pass@git.graphile.com/git/users/1234user/postgraphile-supporter.git#1234commit" // // to // // fetchGit { // url = "https://1234user:1234pass@git.graphile.com/git/users/1234user/postgraphile-supporter.git"; // ref = "master"; // rev = "1234commit"; // } function prefetchgit(url, rev) { return JSON.parse( execFileSync( "nix-prefetch-git", ["--rev", rev, url, "--fetch-submodules"], { stdio: ["ignore", "pipe", "ignore"], timeout: 60000 } ) ).sha256; } function fetchgit(fileName, url, rev, branch, builtinFetchGit) { const repo = builtinFetchGit ? `fetchGit ({ url = "${url}"; ref = "${branch}"; rev = "${rev}"; } // (if builtins.compareVersions "2.4pre" builtins.nixVersion < 0 then { # workaround for https://github.com/NixOS/nix/issues/5128 allRefs = true; } else {}))` : `fetchgit { url = "${url}"; rev = "${rev}"; sha256 = "${prefetchgit(url, rev)}"; }`; return ` { name = "${fileName}"; path = let repo = ${repo}; in runCommand "${fileName}" { buildInputs = [gnutar]; } '' # Set u+w because tar-fs can't unpack archives with read-only dirs # https://github.com/mafintosh/tar-fs/issues/79 tar cf $out --mode u+w -C \${repo} . ''; }`; } /** * Parse an integrity hash out of an SSRI string. * * Provides a default and uses the "best" supported algorithm if there are multiple. */ function parseIntegrity(maybeIntegrity, fallbackHash) { if (!maybeIntegrity && fallbackHash) { return { algo: "sha1", hash: fallbackHash }; } const integrities = ssri.parse(maybeIntegrity); for (const key in integrities) { if (!/^sha(1|256|512)$/.test(key)) { delete integrities[key]; } } algo = integrities.pickAlgorithm(); hash = integrities[algo][0].digest; return { algo, hash }; } function fetchLockedDep(builtinFetchGit) { return function(pkg) { const { integrity, nameWithVersion, resolved } = pkg; if (!resolved) { console.error( `yarn2nix: can't find "resolved" field for package ${nameWithVersion}, you probably required it using "file:...", this feature is not supported, ignoring` ); return ""; } const [url, sha1OrRev] = resolved.split("#"); const fileName = urlToName(url); if (resolved.startsWith("https://codeload.github.com/")) { const s = resolved.split("/"); const githubUrl = `https://github.com/${s[3]}/${s[4]}.git`; const githubRev = s[6]; const [_, branch] = nameWithVersion.split("#"); return fetchgit( fileName, githubUrl, githubRev, branch || "master", builtinFetchGit ); } if (url.startsWith("git+") || url.startsWith("git:")) { const rev = sha1OrRev; const [_, branch] = nameWithVersion.split("#"); const urlForGit = url.replace(/^git\+/, ""); return fetchgit( fileName, urlForGit, rev, branch || "master", builtinFetchGit ); } const { algo, hash } = parseIntegrity(integrity, sha1OrRev); return ` { name = "${fileName}"; path = fetchurl { name = "${fileName}"; url = "${url}"; ${algo} = "${hash}"; }; }`; }; } const HEAD = ` { fetchurl, fetchgit, linkFarm, runCommand, gnutar }: rec { offline_cache = linkFarm "offline" packages; packages = [ `.trim(); // Object -> String function generateNix(pkgs, builtinFetchGit) { const nameWithVersionAndPackageNix = R.map( fetchLockedDep(builtinFetchGit), pkgs ); const packagesDefinition = R.join( "\n", R.values(nameWithVersionAndPackageNix) ); return R.join("\n", [HEAD, packagesDefinition, " ];", "}"]); } module.exports = generateNix;