nixpkgs/pkgs/development/tools/yarn2nix-moretea/yarn2nix/lib/generateNix.js
Mel Bourgeois 1948179a74 yarn2nix: limit ssri parsing to supported algorithms
Nix supports md5, sha1, sha256, and sha512 hashes. The first two are
deprecated, but many npm packages provide only a sha1, so we have to
allow that one. This commit continues to leverage `ssri` for reliable
integrity string parsing and algorithm selection, but limits it to only
supported hash algorithms.

It also switches the hash representation in the generated nix file to
base64, since that format is supported by nix (suggested by jtojnar).
This saves a little disk space and computation.

Finally, it adds a .prettierignore file, since prettier called with the
arguments in the format command currently tries to format node_modules.
2022-10-30 17:31:10 -05:00

180 lines
4.5 KiB
JavaScript

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
//
// builtins.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
//
// builtins.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
? `builtins.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;