Skip to content

Commit

Permalink
feat(nx-verdaccio): update plugin to enable env-setup caching
Browse files Browse the repository at this point in the history
  • Loading branch information
getlarge committed Oct 11, 2024
1 parent b461f89 commit f129325
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { join } from 'node:path';
import {
type RegistryResult,
startVerdaccioServer,
type StartVerdaccioOptions,
type VercaddioServerResult,
Expand Down Expand Up @@ -28,7 +29,7 @@ export async function bootstrapEnvironment(
const { verbose, environmentRoot, storage, ...rest } = options;
const parsedStorage = storage ?? join(environmentRoot, 'storage');

let registryResult;
let registryResult: RegistryResult
try {
registryResult = await startVerdaccioServer({
storage: parsedStorage,
Expand Down
6 changes: 3 additions & 3 deletions projects/nx-verdaccio/src/plugin/caching.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { hashObject } from 'nx/src/hasher/file-hasher';
import {
type CreateNodesResult,
type ProjectConfiguration,
readJsonFile,
writeJsonFile,
} from '@nx/devkit';
Expand Down Expand Up @@ -36,15 +36,15 @@ export function setCacheRecord<T>(

export function readTargetsCache(
cachePath: string
): Record<string, CreateNodesResult['projects']> {
): Record<string, Partial<ProjectConfiguration>> {
return process.env.NX_CACHE_PROJECT_GRAPH !== 'false' && existsSync(cachePath)
? readJsonFile(cachePath)
: {};
}

export function writeTargetsToCache(
cachePath: string,
results: Record<string, CreateNodesResult['projects']>
results: Record<string, Partial<ProjectConfiguration>>
) {
writeJsonFile(cachePath, results);
}
55 changes: 30 additions & 25 deletions projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
logger,
type ProjectConfiguration,
readJsonFile,
type TargetConfiguration,
} from '@nx/devkit';
import { readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import type { BuildEnvPluginCreateNodeOptions } from './schema';
import {
Expand All @@ -23,7 +23,7 @@ import {
setCacheRecord,
writeTargetsToCache,
} from './caching';
import { createTargets } from './targets/create-targets';
import { createProjectConfiguration } from './targets/create-targets';

const PROJECT_JSON_FILE_GLOB = '**/project.json';

Expand All @@ -38,15 +38,11 @@ export const createNodesV2: CreateNodesV2<BuildEnvPluginCreateNodeOptions> = [
const targetsCache = readTargetsCache(nxVerdaccioEnvPluginCachePath);
try {
return await createNodesFromFiles(
(projectConfigurationFile, internalOptions) => {
const projectConfiguration: ProjectConfiguration = readJsonFile(
join(process.cwd(), projectConfigurationFile)
);
const projectRoot = dirname(projectConfigurationFile);
const hashData = {
projectRoot,
internalOptions,
};
async (projectConfigurationFile, internalOptions) => {
const projectConfiguration: ProjectConfiguration = await readFile(
join(process.cwd(), projectConfigurationFile),
'utf8'
).then(JSON.parse);
if (
!('name' in projectConfiguration) ||
typeof projectConfiguration.name !== 'string'
Expand All @@ -55,28 +51,35 @@ export const createNodesV2: CreateNodesV2<BuildEnvPluginCreateNodeOptions> = [
}

const normalizedOptions = normalizeCreateNodesOptions(options);

let cachedProjectTargets = getCacheRecord<
Record<string, TargetConfiguration>
const projectRoot = dirname(projectConfigurationFile);
const hashData = {
projectRoot,
internalOptions,
};
let cachedProjectConfiguration = getCacheRecord<
Partial<ProjectConfiguration>
>(targetsCache, projectRoot, hashData);

if (cachedProjectTargets === undefined) {
cachedProjectTargets = createTargets(
if (cachedProjectConfiguration === undefined) {
cachedProjectConfiguration = createProjectConfiguration(
projectConfiguration,
normalizedOptions
);
setCacheRecord(
targetsCache,
projectRoot,
hashData,
cachedProjectTargets
);
if (cachedProjectConfiguration.targets) {
setCacheRecord(
targetsCache,
projectRoot,
hashData,
cachedProjectConfiguration
);
}
}

const { targets, namedInputs = {} } = cachedProjectConfiguration;
return {
projects: {
[projectRoot]: {
targets: cachedProjectTargets,
namedInputs,
targets,
},
},
};
Expand All @@ -96,6 +99,7 @@ export const createNodes: CreateNodes = [
(
projectConfigurationFile: string,
options: NormalizedCreateNodeOptions,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
context: CreateNodesContext
) => {
logger.warn(
Expand All @@ -108,7 +112,8 @@ export const createNodes: CreateNodes = [
return {
projects: {
[projectRoot]: {
targets: createTargets(projectConfiguration, options),
targets: createProjectConfiguration(projectConfiguration, options)
.targets,
},
},
};
Expand Down
42 changes: 27 additions & 15 deletions projects/nx-verdaccio/src/plugin/targets/create-targets.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { BuildEnvPluginCreateNodeOptions } from '../schema';
import type { CreateNodesResult, ProjectConfiguration } from '@nx/devkit';
import type { ProjectConfiguration } from '@nx/devkit';
import { normalizeCreateNodesOptions } from '../normalize-create-nodes-options';
import {
getEnvTargets,
Expand All @@ -9,10 +9,11 @@ import {
} from './environment.targets';
import { getPkgTargets, isPkgProject } from './package.targets';

export function createTargets(
export function createProjectConfiguration(
projectConfiguration: ProjectConfiguration,
options: BuildEnvPluginCreateNodeOptions
): CreateNodesResult['projects'][string]['targets'] {
): Pick<ProjectConfiguration, 'targets'> &
Partial<Pick<ProjectConfiguration, 'namedInputs'>> {
const { environments, packages } = normalizeCreateNodesOptions(options);

if (
Expand All @@ -22,20 +23,31 @@ export function createTargets(
return {};
}

// unfortunately namedInputs are not picked up by tasks graph: Error: Input 'build-artifacts' is not defined
const namedInputs: ProjectConfiguration['namedInputs'] = {
'build-artifacts': [
'{projectRoot}/**/*.{js,ts,tsx}',
'!{projectRoot}/**/*.spec.{ts,tsx}',
],
};
return {
// === ENVIRONMENT TARGETS ===
...(isEnvProject(projectConfiguration, environments) && {
// start-verdaccio, stop-verdaccio
...verdaccioTargets(projectConfiguration, {
environmentsDir: environments.environmentsDir,
}),
// env-bootstrap-env, env-setup-env, install-env (intermediate target to run dependency targets)
...getEnvTargets(projectConfiguration, environments),
// adjust targets to run env-setup-env
...updateEnvTargetNames(projectConfiguration, environments),
namedInputs,
// === ENVIRONMENT TARGETS ===
targets: {
// start-verdaccio, stop-verdaccio
...verdaccioTargets(projectConfiguration, {
environmentsDir: environments.environmentsDir,
}),
// env-bootstrap-env, env-setup-env, install-env (intermediate target to run dependency targets)
...getEnvTargets(projectConfiguration, environments),
// adjust targets to run env-setup-env
...updateEnvTargetNames(projectConfiguration, environments),
},
}),
...(isPkgProject(projectConfiguration, packages) && {
// === PACKAGE TARGETS ===
targets: getPkgTargets(),
}),
// === PACKAGE TARGETS ===
// pkg-publish, pkg-install
...(isPkgProject(projectConfiguration, packages) && getPkgTargets()),
};
}
49 changes: 43 additions & 6 deletions projects/nx-verdaccio/src/plugin/targets/environment.targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { PACKAGE_NAME } from '../constants';
import { EXECUTOR_ENVIRONMENT_KILL_PROCESS } from '../../executors/kill-process/constant';
import { EXECUTOR_ENVIRONMENT_SETUP } from '../../executors/env-setup/constants';
import * as constants from 'constants';
import { iterateEntries } from '../../internal/transform';

export const TARGET_ENVIRONMENT_VERDACCIO_START = 'pb-ve-env-verdaccio-start';
Expand All @@ -21,6 +20,8 @@ export const TARGET_ENVIRONMENT_INSTALL = 'pb-ve-env-install';
export const TARGET_ENVIRONMENT_SETUP = 'pb-ve-env-setup';
export const TARGET_ENVIRONMENT_VERDACCIO_STOP = 'pb-ve-env-verdaccio-stop';

const VERDACCIO_STORAGE_DIR = 'storage';

export function isEnvProject(
projectConfig: ProjectConfiguration,
options: NormalizedCreateNodeOptions['environments']
Expand Down Expand Up @@ -63,7 +64,8 @@ export function verdaccioTargets(
const { name: envProject } = projectConfig;
const { environmentsDir, ...verdaccioOptions } = options;
const environmentDir = join(environmentsDir, envProject);

// unless the verdaccio server port and pid is always the same, i don't see how to enable Distributed Task Execution with Nx agents...
// we cannot control which agent will run the task and we must enable cache for the task to be distributed
return {
[TARGET_ENVIRONMENT_VERDACCIO_START]: {
// @TODO: consider using the executor function directly to reduce the number of targets
Expand All @@ -72,7 +74,7 @@ export function verdaccioTargets(
options: {
config: '.verdaccio/config.yml',
port: uniquePort(),
storage: join(environmentDir, 'storage'),
storage: join(environmentDir, VERDACCIO_STORAGE_DIR),
clear: true,
environmentDir,
projectName: envProject,
Expand All @@ -89,6 +91,14 @@ export function verdaccioTargets(
};
}

/**
* Create new targets wrapping env-bootstrap, env-setup executors
* install-env (intermediate target to run dependency targets)
*
* @param projectConfig
* @param options
* @returns
*/
export function getEnvTargets(
projectConfig: ProjectConfiguration,
options: NormalizedCreateNodeOptions['environments']
Expand All @@ -98,6 +108,8 @@ export function getEnvTargets(
const environmentRoot = join(environmentsDir, envProject);
return {
[TARGET_ENVIRONMENT_BOOTSTRAP]: {
//? how to cache this target individually? since it starts the verdaccio server
// problem is: if we can't cache, we can't distribute the task
executor: `${PACKAGE_NAME}:${EXECUTOR_ENVIRONMENT_BOOTSTRAP}`,
options: { environmentRoot },
},
Expand All @@ -114,13 +126,31 @@ export function getEnvTargets(
},
// runs env-bootstrap-env, install-env and stop-verdaccio
[TARGET_ENVIRONMENT_SETUP]: {
// list of inputs that all subsequent tasks depend on
inputs: [
'{projectRoot}/project.json',
{
runtime: 'node --version',
},
{
runtime: 'npm --version',
},
{
externalDependencies: ['verdaccio'],
},
// depends on underlying project being e2e tested and its own dependencies
// ! it's important that implicitDependencies are correctly configured between this project and the project being tested
// '^build-artifacts',
'^production',
],
outputs: [
'{options.environmentRoot}/node_modules',
'{options.environmentRoot}/package.json',
`{options.environmentRoot}/${VERDACCIO_STORAGE_DIR}`,
'{options.environmentRoot}/.npmrc',
'{options.environmentRoot}/package.json',
'{options.environmentRoot}/package-lock.json',
'{options.environmentRoot}/node_modules',
],
cache: false, // # @TODO enable by default after more research on cache size is done
cache: true,
executor: `${PACKAGE_NAME}:${EXECUTOR_ENVIRONMENT_SETUP}`,
options: {
environmentRoot,
Expand All @@ -129,6 +159,13 @@ export function getEnvTargets(
};
}

/**
* adjust targets to run env-setup-env
* this will add the dependsOn property to the targets that are in the targetNames array (usually e2e)
* @param projectConfig
* @param options
* @returns
*/
export function updateEnvTargetNames(
projectConfig: ProjectConfiguration,
options: Required<Pick<BuildEnvEnvironmentsOptions, 'targetNames'>>
Expand Down

0 comments on commit f129325

Please sign in to comment.