diff --git a/packages/nx/migrations.json b/packages/nx/migrations.json index 49cb44c679ead..1ea3b538560e5 100644 --- a/packages/nx/migrations.json +++ b/packages/nx/migrations.json @@ -65,6 +65,12 @@ "version": "17.0.0-beta.1", "description": "Updates the default cache directory to .nx/cache", "implementation": "./src/migrations/update-17-0-0/move-cache-directory" + }, + "17.0.0-use-minimal-config-for-tasks-runner-options": { + "cli": "nx", + "version": "17.0.0-beta.2", + "description": "Use minimal config for tasksRunnerOptions", + "implementation": "./src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options" } } } diff --git a/packages/nx/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.spec.ts b/packages/nx/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.spec.ts new file mode 100644 index 0000000000000..c6e8555f1a187 --- /dev/null +++ b/packages/nx/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.spec.ts @@ -0,0 +1,183 @@ +import { NxJsonConfiguration } from '../../config/nx-json'; +import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace'; +import { readJson, writeJson } from '../../generators/utils/json'; +import { Tree } from '../../generators/tree'; +import migrate from './use-minimal-config-for-tasks-runner-options'; + +describe('use-minimal-config-for-tasks-runner-options migration', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it('should update nx.json with minimal config', async () => { + writeJson(tree, 'nx.json', { + tasksRunnerOptions: { + default: { + runner: 'nx/tasks-runners/default', + options: { + cacheableOperations: ['build', 'test'], + }, + }, + }, + }); + + await migrate(tree); + + const nxJson = readJson(tree, 'nx.json'); + expect(nxJson.tasksRunnerOptions).toEqual(undefined); + expect(nxJson.targetDefaults).toMatchInlineSnapshot(` + { + "build": { + "cache": true, + }, + "test": { + "cache": true, + }, + } + `); + }); + + it('should not update nx.json if there are multiple tasks runners', async () => { + writeJson(tree, 'nx.json', { + tasksRunnerOptions: { + default: { + runner: 'nx/tasks-runners/default', + options: {}, + }, + custom: { + runner: 'custom', + options: {}, + }, + }, + }); + + await migrate(tree); + + const nxJson = readJson(tree, 'nx.json'); + expect(nxJson.tasksRunnerOptions).toEqual({ + default: { + runner: 'nx/tasks-runners/default', + options: {}, + }, + custom: { + runner: 'custom', + options: {}, + }, + }); + }); + + it('should move nxCloudAccessToken and nxCloudUrl for nx-cloud', async () => { + writeJson(tree, 'nx.json', { + tasksRunnerOptions: { + default: { + runner: 'nx-cloud', + options: { + accessToken: 'abc123', + url: 'https://nx.app', + encryptionKey: 'secret', + }, + }, + }, + }); + + await migrate(tree); + + const nxJson = readJson(tree, 'nx.json'); + expect(nxJson.nxCloudAccessToken).toEqual('abc123'); + expect(nxJson.nxCloudUrl).toEqual('https://nx.app'); + expect(nxJson.nxCloudEncryptionKey).toEqual('secret'); + expect(nxJson.tasksRunnerOptions).not.toBeDefined(); + }); + + it('should move nxCloudAccessToken and nxCloudUrl for @nrwl/nx-cloud', async () => { + writeJson(tree, 'nx.json', { + tasksRunnerOptions: { + default: { + runner: '@nrwl/nx-cloud', + options: { + accessToken: 'abc123', + url: 'https://nx.app', + maskedProperties: 'secret', + }, + }, + }, + }); + + await migrate(tree); + + const nxJson = readJson(tree, 'nx.json'); + expect(nxJson.nxCloudAccessToken).toEqual('abc123'); + expect(nxJson.nxCloudUrl).toEqual('https://nx.app'); + expect(nxJson.tasksRunnerOptions.default.options).toMatchInlineSnapshot(` + { + "maskedProperties": "secret", + } + `); + expect(nxJson.tasksRunnerOptions.default.runner).not.toBeDefined(); + }); + + it('should not update accessToken if runner is not nx-cloud', async () => { + writeJson(tree, 'nx.json', { + tasksRunnerOptions: { + default: { + runner: 'custom', + options: { + cacheDirectory: '.nx/cache', + useDaemonProcess: false, + accessToken: 'xxxx-xxx-xxxx', + }, + }, + }, + }); + await migrate(tree); + expect(readJson(tree, 'nx.json')) + .toMatchInlineSnapshot(` + { + "cacheDirectory": ".nx/cache", + "tasksRunnerOptions": { + "default": { + "options": { + "accessToken": "xxxx-xxx-xxxx", + }, + "runner": "custom", + }, + }, + "useDaemonProcess": false, + } + `); + }); + + it('should work if nx.json does not exist', async () => { + tree.delete('nx.json'); + await migrate(tree); + expect(tree.exists('nx.json')).toEqual(false); + }); + + it('should not throw is cacheableOperations is an unexpected type', async () => { + writeJson(tree, 'nx.json', { + tasksRunnerOptions: { + default: { + runner: 'nx/tasks-runners/default', + options: { + cacheableOperations: 'invalid', + }, + }, + }, + }); + + await migrate(tree); + + const nxJson = readJson(tree, 'nx.json'); + expect(nxJson.tasksRunnerOptions).toMatchInlineSnapshot(` + { + "default": { + "options": { + "cacheableOperations": "invalid", + }, + }, + } + `); + }); +}); diff --git a/packages/nx/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.ts b/packages/nx/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.ts new file mode 100644 index 0000000000000..421c7c796faa8 --- /dev/null +++ b/packages/nx/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.ts @@ -0,0 +1,72 @@ +import { updateJson } from '../../generators/utils/json'; +import { Tree } from '../../generators/tree'; +import { NxJsonConfiguration } from '../../config/nx-json'; + +export default async function migrate(tree: Tree) { + if (!tree.exists('nx.json')) { + return; + } + updateJson(tree, 'nx.json', (nxJson) => { + // Already migrated + if (!nxJson.tasksRunnerOptions?.default) { + return nxJson; + } + + const { runner, options } = nxJson.tasksRunnerOptions.default; + + // This property shouldn't ever be part of tasks runner options. + if (options.useDaemonProcess !== undefined) { + nxJson.useDaemonProcess = options.useDaemonProcess; + delete options.useDaemonProcess; + } + + // Remaining keys may be specific to a given runner, so leave them alone if there are multiple runners. + if (Object.keys(nxJson.tasksRunnerOptions ?? {}).length > 1) { + return nxJson; + } + + // These options can only be moved for nx-cloud. + if (runner === 'nx-cloud' || runner === '@nrwl/nx-cloud') { + nxJson.nxCloudAccessToken = options.accessToken; + delete options.accessToken; + + if (options.url) { + nxJson.nxCloudUrl = options.url; + delete options.url; + } + if (options.encryptionKey) { + nxJson.nxCloudEncryptionKey = options.encryptionKey; + delete options.encryptionKey; + } + } + + // These options should be safe to move for all tasks runners: + if (options.parallel !== undefined) { + nxJson.parallel = options.parallel; + delete options.parallel; + } + if (options.cacheDirectory !== undefined) { + nxJson.cacheDirectory = options.cacheDirectory; + delete options.cacheDirectory; + } + if (Array.isArray(options.cacheableOperations)) { + nxJson.targetDefaults ??= {}; + for (const target of options.cacheableOperations) { + nxJson.targetDefaults[target] ??= {}; + nxJson.targetDefaults[target].cache ??= true; + } + delete options.cacheableOperations; + } + if ( + ['nx-cloud', '@nrwl/nx-cloud', 'nx/tasks-runners/default'].includes( + runner + ) + ) { + delete nxJson.tasksRunnerOptions.default.runner; + if (Object.values(options).length === 0) { + delete nxJson.tasksRunnerOptions; + } + } + return nxJson; + }); +}