Switch to a hook, allow absolute yarnPath
This commit is contained in:
+47
-57
@@ -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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
{ lib, bash, nodejs, perl, stdenv, writeText }:
|
||||
{ lib, bash, coreutils, nodejs, perl, stdenv, writeText }:
|
||||
|
||||
with lib;
|
||||
|
||||
@@ -43,7 +43,7 @@ let
|
||||
export HOME="$TEMP"
|
||||
|
||||
# Setup to environment so we can run Yarn.
|
||||
export NIX_YARN_PATH="$PWD/${yarn-path}"
|
||||
export NIX_YARN_PATH="$(readlink -f '${yarn-path}')"
|
||||
${yarn-alias}
|
||||
|
||||
# Invoke a plugin internal command to build the cache.
|
||||
@@ -64,7 +64,7 @@ in stdenv.mkDerivation {
|
||||
npm_config_build_from_source = "true";
|
||||
|
||||
# Make sure the build uses the right Node.js version everywhere.
|
||||
buildInputs = [ nodejs ];
|
||||
buildInputs = [ coreutils nodejs ];
|
||||
|
||||
# Define the Yarn alias in the build environment.
|
||||
postHook = yarn-alias;
|
||||
@@ -80,7 +80,7 @@ in stdenv.mkDerivation {
|
||||
cd "$out/libexec/$sourceRoot"
|
||||
|
||||
# Store the absolute path to Yarn for the 'yarn' alias.
|
||||
export NIX_YARN_PATH="$PWD/${yarn-path}"
|
||||
export NIX_YARN_PATH="$(readlink -f ${yarn-path})"
|
||||
|
||||
# Point Yarn to the offline cache built separately.
|
||||
yarn config set cacheFolder '${offline-cache}'
|
||||
|
||||
Reference in New Issue
Block a user