Switch to a hook, allow absolute yarnPath

This commit is contained in:
Stéphan Kochen
2020-08-24 20:04:12 +02:00
parent 75006ce73b
commit 68cca6c0c0
2 changed files with 51 additions and 61 deletions
+47 -57
View File
@@ -6,6 +6,7 @@ import {
Cache,
CommandContext,
Configuration,
Hooks,
Plugin,
Project,
Report,
@@ -18,7 +19,7 @@ import binTmpl from "./bin-wrapper.sh.in";
import defaultExprTmpl from "./default.nix.in";
import projectExprTmpl from "./yarn-project.nix.in";
// Actual body of NixifyCommand.
// Generator function that runs after `yarn install`.
const generate = async (project: Project, report: Report) => {
const { configuration, cwd } = project;
const yarnPathAbs = configuration.get(`yarnPath`);
@@ -27,33 +28,20 @@ const generate = async (project: Project, report: Report) => {
// TODO: Should try to remove this. Our binary wrappers currently do
// `node -r .pnp.js <bin>`, but not even sure if that's supported.
if (configuration.get(`nodeLinker`) !== `pnp`) {
report.reportError(
report.reportWarning(
0,
`Only 'pnp' is currently supported for the 'nodeLinker' setting.`
`Currently, yarn-plugin-nixify only supports 'pnp' for the 'nodeLinker' setting.`
);
return;
}
const yarnPath = ppath.relative(cwd, yarnPathAbs);
if (yarnPath.startsWith(".")) {
report.reportError(
let yarnPath = ppath.relative(cwd, yarnPathAbs);
if (yarnPath.startsWith(`../`)) {
yarnPath = yarnPathAbs;
report.reportWarning(
0,
`The Yarn path ${yarnPathAbs} is outside the project directory - it cannot be reached by the Nix build`
);
return;
}
// Simple check to see if the project has been setup, so we know the Yarn
// cache is populated. TODO: Can we also see if it is up-to-date?
if (
!xfs.existsSync(ppath.join(cwd, lockfileFilename)) ||
!xfs.existsSync(getPnpPath(project).main)
) {
report.reportError(
0,
`The project in ${cwd}/package.json doesn't seem to have been installed - running an install there might help`
);
return;
}
// List files the derivation will depend on.
@@ -82,11 +70,13 @@ const generate = async (project: Project, report: Report) => {
// just user global configuration files, but the warnings can help highlight
// dependencies on private registries.)
const relativePath = ppath.relative(cwd, inputPath);
if (relativePath.startsWith(`..`)) {
report.reportWarning(
0,
`The path ${inputPath} was ignored, because it cannot be reached by the Nix build`
);
if (relativePath.startsWith(`../`)) {
if (inputPath !== yarnPath) {
report.reportWarning(
0,
`The path ${inputPath} is outside the project directory and was ignored - it may not be reachable in the Nix build`
);
}
continue;
}
// Add the file itself.
@@ -102,16 +92,28 @@ const generate = async (project: Project, report: Report) => {
// Build the Nix output-hash by hashing the Yarn cache folder. The
// derivation should build the exact same.
const cacheFolder = configuration.get(`cacheFolder`);
const hasherResult = await execUtils.execvp(
`nix-hash`,
[`--type`, `sha256`, `--base32`, cacheFolder],
{ cwd, encoding: `utf8`, strict: true }
);
const cacheHash = hasherResult.stdout.trim();
let cacheHash = ``;
try {
const hasherResult = await execUtils.execvp(
`nix-hash`,
[`--type`, `sha256`, `--base32`, cacheFolder],
{ cwd, encoding: `utf8`, strict: true }
);
cacheHash = hasherResult.stdout.trim();
} catch (err) {
if (err.code === `ENOENT`) {
report.reportWarning(
0,
`No Nix installation found - yarn-project.nix will not be updated`
);
} else {
throw err;
}
}
// Render the Nix expression.
const ident = project.topLevelWorkspace.manifest.name;
const projectName = ident ? structUtils.stringifyIdent(ident) : "workspace";
const projectName = ident ? structUtils.stringifyIdent(ident) : `workspace`;
const projectExpr = projectExprTmpl
.replace(`@@PROJECT_NAME@@`, JSON.stringify(projectName))
.replace(`@@OFFLINE_CACHE_HASH@@`, JSON.stringify(cacheHash))
@@ -126,37 +128,18 @@ const generate = async (project: Project, report: Report) => {
);
const projectExprPath = ppath.join(cwd, `yarn-project.nix` as Filename);
xfs.writeFileSync(projectExprPath, projectExpr);
report.reportInfo(0, `Wrote: ${projectExprPath}`);
// Create a wrapper if it does not exist yet.
const defaultExprPath = ppath.join(cwd, `default.nix` as Filename);
if (!xfs.existsSync(defaultExprPath)) {
xfs.writeFileSync(defaultExprPath, defaultExprTmpl);
report.reportInfo(0, `Wrote: ${defaultExprPath}`);
report.reportInfo(
0,
`A minimal default.nix was created. You may want to customize it.`
);
}
};
// Command that generates Nix expressions to setup the project.
// TODO: Automate by running on every install instead? There's a hook in Yarn,
// but it doesn't tell us if the persist flag is set.
class NixifyCommand extends Command<CommandContext> {
@Command.Path(`nixify`)
async execute() {
const configuration = await Configuration.find(
this.context.cwd,
this.context.plugins
);
const { project } = await Project.find(configuration, this.context.cwd);
const report = await StreamReport.start(
{ configuration, stdout: this.context.stdout },
async (report) => generate(project, report)
);
return report.exitCode();
}
}
// Internal command that does just the fetch part of `yarn install`.
// Used inside the Nix offline-cache derivation to build the cache.
class BuildCacheCommand extends Command<CommandContext> {
@@ -234,8 +217,15 @@ class InstallBinCommand extends Command<CommandContext> {
}
}
const plugin: Plugin = {
commands: [NixifyCommand, BuildCacheCommand, InstallBinCommand],
const plugin: Plugin<Hooks> = {
commands: [BuildCacheCommand, FetchLocatorCommand, InstallBinCommand],
hooks: {
afterAllInstalled: async (project, opts) => {
if (opts.persistProject !== false) {
await generate(project, opts.report);
}
},
},
};
export default plugin;