Skip to content

Commit

Permalink
feat(core): atomizer with nx:atomizer executor -- WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxKless committed Jul 15, 2024
1 parent bd76db6 commit c994fba
Show file tree
Hide file tree
Showing 16 changed files with 192 additions and 294 deletions.
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -8617,6 +8617,14 @@
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "atomizer",
"path": "/nx-api/nx/executors/atomizer",
"name": "atomizer",
"children": [],
"isExternal": false,
"disableCollapsible": false
}
],
"isExternal": false,
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/nx-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,15 @@
"originalFilePath": "/packages/nx/src/executors/run-script/schema.json",
"path": "/nx-api/nx/executors/run-script",
"type": "executor"
},
"/nx-api/nx/executors/atomizer": {
"description": "An executor that is used as the root task for atomized tasks. Ensures it's being run in distribution and otherwise does nothing.",
"file": "generated/packages/nx/executors/atomizer.json",
"hidden": false,
"name": "atomizer",
"originalFilePath": "/packages/nx/src/executors/atomizer/schema.json",
"path": "/nx-api/nx/executors/atomizer",
"type": "executor"
}
},
"generators": {
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -1957,6 +1957,15 @@
"originalFilePath": "/packages/nx/src/executors/run-script/schema.json",
"path": "nx/executors/run-script",
"type": "executor"
},
{
"description": "An executor that is used as the root task for atomized tasks. Ensures it's being run in distribution and otherwise does nothing.",
"file": "generated/packages/nx/executors/atomizer.json",
"hidden": false,
"name": "atomizer",
"originalFilePath": "/packages/nx/src/executors/atomizer/schema.json",
"path": "nx/executors/atomizer",
"type": "executor"
}
],
"generators": [
Expand Down
20 changes: 20 additions & 0 deletions docs/generated/packages/nx/executors/atomizer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "atomizer",
"implementation": "/packages/nx/src/executors/atomizer/atomizer.impl.ts",
"schema": {
"version": 2,
"title": "Atomizer",
"description": "An executor that is used as the root task for atomized tasks. Ensures it's being run in distribution and otherwise does nothing.",
"type": "object",
"cli": "nx",
"outputCapture": "pipe",
"properties": {},
"additionalProperties": true,
"presets": []
},
"description": "An executor that is used as the root task for atomized tasks. Ensures it's being run in distribution and otherwise does nothing.",
"aliases": [],
"hidden": false,
"path": "/packages/nx/src/executors/atomizer/schema.json",
"type": "executor"
}
1 change: 1 addition & 0 deletions docs/shared/reference/sitemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@
- [noop](/nx-api/nx/executors/noop)
- [run-commands](/nx-api/nx/executors/run-commands)
- [run-script](/nx-api/nx/executors/run-script)
- [atomizer](/nx-api/nx/executors/atomizer)
- [generators](/nx-api/nx/generators)
- [connect-to-nx-cloud](/nx-api/nx/generators/connect-to-nx-cloud)
- [playwright](/nx-api/playwright)
Expand Down
2 changes: 1 addition & 1 deletion packages/cypress/src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ async function buildCypressTargets(
}

targets[options.ciTargetName] = {
executor: 'nx:noop',
executor: 'nx:atomizer',
cache: true,
inputs,
outputs,
Expand Down
2 changes: 1 addition & 1 deletion packages/jest/src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ async function buildJestTargets(
const dependsOn: string[] = [];

targets[options.ciTargetName] = {
executor: 'nx:noop',
executor: 'nx:atomizer',
cache: true,
inputs,
outputs,
Expand Down
7 changes: 6 additions & 1 deletion packages/nx/executors.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"implementation": "./src/executors/run-script/run-script.impl",
"schema": "./src/executors/run-script/schema.json",
"description": "Run an NPM script using Nx."
},
"atomizer": {
"implementation": "./src/executors/atomizer/atomizer.impl",
"schema": "./src/executors/atomizer/schema.json",
"description": "An executor that is used as the root task for atomized tasks. Ensures it's being run in distribution and otherwise does nothing."
}
}
}
}
74 changes: 74 additions & 0 deletions packages/nx/src/executors/atomizer/atomizer.impl.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import '../../internal-testing-utils/mock-fs';

import { vol } from 'memfs';
import { ExecutorContext } from '../../config/misc-interfaces';
import atomize from './atomizer.impl';
import * as fs from 'fs';
import { join } from 'path';

const context = {
root: '/root',
projectName: 'proj',
targetName: 'e2e-ci',
} as ExecutorContext;

describe('nx:atomizer', () => {
let mockProcessExit: jest.SpyInstance;
let env: NodeJS.ProcessEnv;

beforeEach(() => {
env = process.env;
process.env = {};

jest.mock('fs');
});

afterEach(() => {
process.env = env;
vol.reset();
});

it('should fail if atomized task is present but no DTE', async () => {
const result = await atomize({}, context);
expect(result).toEqual(expect.objectContaining({ success: false }));
});

it('should do nothing if atomized task is present in Nx Agents', async () => {
process.env['NX_AGENT_NAME'] = '123';
const result = await atomize({}, context);
expect(result).toEqual(expect.objectContaining({ success: true }));
});

it('should do nothing if atomized task is present in DTE', async () => {
process.env['NX_CLOUD_DISTRIBUTED_EXECUTION_ID'] = '123';
const result = await atomize({}, context);
expect(result).toEqual(expect.objectContaining({ success: true }));
});

it('should do nothing if atomized task is present and dte marker file exists', async () => {
vol.fromJSON(
{
'node_modules/.cache/nx/NX_CLOUD_DISTRIBUTED_EXECUTION': 'true',
},
context.root
);

const result = await atomize({}, context);
expect(result).toEqual(expect.objectContaining({ success: true }));
});

it('should do nothing if atomized task is present and dte marker file exists in NX_CACHE_DIRECTORY', async () => {
const cacheDirectory = join('node_modules', 'my-cache-dir');
process.env['NX_CACHE_DIRECTORY'] = cacheDirectory;

vol.fromJSON(
{
'node_modules/my-cache-dir/NX_CLOUD_DISTRIBUTED_EXECUTION': 'true',
},
context.root
);

const result = await atomize({}, context);
expect(result).toEqual(expect.objectContaining({ success: true }));
});
});
45 changes: 45 additions & 0 deletions packages/nx/src/executors/atomizer/atomizer.impl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { join } from 'path';
import { ExecutorContext } from '../../config/misc-interfaces';
import { existsSync, readFileSync } from 'fs';
import { output } from '../../utils/output';
import { serializeTarget } from '../../utils/serialize-target';

export default async function (_: any, context: ExecutorContext) {
if (isInDTE(context.root)) {
return { success: true };
} else {
output.error({
title: `The ${serializeTarget(
context.projectName,
context.targetName,
context.configurationName
)} task uses the atomizer and should only be run with Nx Cloud distribution.`,
bodyLines: [
'Learn more at https://nx.dev/ci/features/split-e2e-tasks#use-atomizer-only-with-nx-cloud-distribution',
],
});
return { success: false };
}
}

function isInDTE(workspaceRoot: string): boolean {
if (
process.env['NX_CLOUD_DISTRIBUTED_EXECUTION_ID'] ||
process.env['NX_AGENT_NAME']
) {
return true;
}

// checks for DTE marker file - needed so we can check for DTE on the main nx job
const nxCacheDirectory = process.env.NX_CACHE_DIRECTORY
? [process.env['NX_CACHE_DIRECTORY']]
: ['node_modules', '.cache', 'nx'];
const dir = join(workspaceRoot, ...nxCacheDirectory);
const dteMarker = join(dir, 'NX_CLOUD_DISTRIBUTED_EXECUTION');

if (existsSync(dteMarker) && readFileSync(dteMarker, 'utf-8') === 'true') {
return true;
}

return false;
}
10 changes: 10 additions & 0 deletions packages/nx/src/executors/atomizer/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 2,
"title": "Atomizer",
"description": "An executor that is used as the root task for atomized tasks. Ensures it's being run in distribution and otherwise does nothing.",
"type": "object",
"cli": "nx",
"outputCapture": "pipe",
"properties": {},
"additionalProperties": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -904,8 +904,12 @@ export function isCompatibleTarget(
) {
const oneHasNoExecutor = !a.executor || !b.executor;
const bothHaveSameExecutor = a.executor === b.executor;
const areNoopAndAtomizer =
(a.executor === 'nx:noop' && b.executor === 'nx:atomizer') ||
(a.executor === 'nx:atomizer' && b.executor === 'nx:noop');

if (oneHasNoExecutor) return true;
if (areNoopAndAtomizer) return true;
if (!bothHaveSameExecutor) return false;

const isRunCommands = a.executor === 'nx:run-commands';
Expand Down
12 changes: 3 additions & 9 deletions packages/nx/src/tasks-runner/run-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ import { StoreRunInformationLifeCycle } from './life-cycles/store-run-informatio
import { TaskHistoryLifeCycle } from './life-cycles/task-history-life-cycle';
import { TaskProfilingLifeCycle } from './life-cycles/task-profiling-life-cycle';
import { TaskTimingsLifeCycle } from './life-cycles/task-timings-life-cycle';
import {
findCycle,
makeAcyclic,
validateAtomizedTasks,
} from './task-graph-utils';
import { findCycle, makeAcyclic } from './task-graph-utils';
import { TasksRunner, TaskStatus } from './tasks-runner';
import { shouldStreamOutput } from './utils';

Expand Down Expand Up @@ -95,7 +91,7 @@ async function getTerminalOutputLifeCycle(
}
}

function createTaskGraphAndRunValidations(
function createTaskGraphAndValidateCycles(
projectGraph: ProjectGraph,
extraTargetDependencies: TargetDependencies,
projectNames: string[],
Expand Down Expand Up @@ -133,8 +129,6 @@ function createTaskGraphAndRunValidations(
}
}

validateAtomizedTasks(taskGraph, projectGraph);

return taskGraph;
}

Expand All @@ -153,7 +147,7 @@ export async function runCommand(
async () => {
const projectNames = projectsToRun.map((t) => t.name);

const taskGraph = createTaskGraphAndRunValidations(
const taskGraph = createTaskGraphAndValidateCycles(
projectGraph,
extraTargetDependencies ?? {},
projectNames,
Expand Down
Loading

0 comments on commit c994fba

Please sign in to comment.