diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dead22e..bf89efe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,4 +70,4 @@ jobs: - name: Install dependencies run: npm i - name: E2E test affected projects - run: npx nx affected -t e2e --exclude="tag:type:example" + run: npx nx affected -t nxv-e2e --exclude="tag:type:example" diff --git a/code-pushup.config.ts b/code-pushup.config.ts index 1038a48..4c985c1 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -1,8 +1,9 @@ -import { CoreConfig } from '@code-pushup/models'; +import {CoreConfig} from '@code-pushup/models'; import nxPerformancePlugin, { nxPerformanceCategoryRefs, type OnlyAudit, } from './tooling/measures/nx-performance/nx-performance.plugin'; +import {TaskTimeAuditOption} from './tooling/measures/nx-performance'; const onlyAudits: OnlyAudit[] = [ 'graph-time-project', @@ -11,22 +12,18 @@ const onlyAudits: OnlyAudit[] = [ 'task-time', ]; const taskGraphTasks = ['cli-e2e:nxv-env-install']; -const taskTimeTasks = [ - 'cli-e2e:e2e', - 'cli-e2e:nxv-env-setup', - 'cli:unit-test', - 'cli:build', - 'core-e2e:e2e', - 'utils-e2e:e2e', - 'models-e2e:e2e', - 'cli-e2e-original:original-e2e', +const taskTimeTasks: TaskTimeAuditOption[] = [ + {task: 'models-e2e:nxv-env-teardown'}, + {task: 'models-e2e:nxv-env-bootstrap'}, + {task: 'models-e2e:nxv-env-setup'}, + {task: 'models-e2e:e2e'}, + {task: 'models-e2e:nxv-e2e'}, + {task: 'nx-verdaccio-e2e:nxv-e2e'}, + {task: 'cli-e2e-original:original-e2e'}, ]; const cacheSizeTasks = [ 'models-e2e:nxv-env-setup', - 'utils-e2e:nxv-env-setup', - 'core-e2e:nxv-env-setup', - 'cli-e2e:nxv-env-setup', - 'playground-e2e:nxv-env-setup', + 'nx-verdaccio-e2e:nxv-env-setup', ]; export default { plugins: [ diff --git a/e2e/nx-verdaccio-e2e/test/__snapshots__/plugin-create-nodes.e2e.test.ts.snap b/e2e/nx-verdaccio-e2e/test/__snapshots__/plugin-create-nodes.e2e.test.ts.snap index 8799aa0..bfc9cfb 100644 --- a/e2e/nx-verdaccio-e2e/test/__snapshots__/plugin-create-nodes.e2e.test.ts.snap +++ b/e2e/nx-verdaccio-e2e/test/__snapshots__/plugin-create-nodes.e2e.test.ts.snap @@ -14,6 +14,15 @@ exports[`nx-verdaccio plugin create-nodes-v2 > should add environment targets to "options": {}, "parallelism": true, }, + "nxv-e2e": { + "configurations": {}, + "dependsOn": [ + "e2e", + ], + "executor": "@push-based/nx-verdaccio:env-teardown", + "options": {}, + "parallelism": true, + }, "nxv-env-bootstrap": { "configurations": {}, "executor": "@push-based/nx-verdaccio:env-bootstrap", @@ -52,6 +61,12 @@ exports[`nx-verdaccio plugin create-nodes-v2 > should add environment targets to ], "parallelism": true, }, + "nxv-env-teardown": { + "configurations": {}, + "executor": "@push-based/nx-verdaccio:env-teardown", + "options": {}, + "parallelism": true, + }, "nxv-verdaccio-start": { "configurations": {}, "executor": "@nx/js:verdaccio", diff --git a/e2e/nx-verdaccio-e2e/test/plugin-create-nodes.e2e.test.ts b/e2e/nx-verdaccio-e2e/test/plugin-create-nodes.e2e.test.ts index df219ed..388ec8f 100644 --- a/e2e/nx-verdaccio-e2e/test/plugin-create-nodes.e2e.test.ts +++ b/e2e/nx-verdaccio-e2e/test/plugin-create-nodes.e2e.test.ts @@ -18,6 +18,10 @@ import { TARGET_ENVIRONMENT_VERDACCIO_STOP, } from '@push-based/nx-verdaccio'; import { teardownTestFolder } from '@push-based/test-utils'; +import { + TARGET_ENVIRONMENT_E2E, + TARGET_ENVIRONMENT_TEARDOWN, +} from '../../../projects/nx-verdaccio/src/plugin/targets/environment.targets'; describe('nx-verdaccio plugin create-nodes-v2', () => { let tree: Tree; @@ -239,6 +243,13 @@ describe('nx-verdaccio plugin create-nodes-v2', () => { filePath: 'tmp/environments/verdaccio-registry.json', }, }), + [TARGET_ENVIRONMENT_E2E]: expect.objectContaining({ + executor: '@push-based/nx-verdaccio:env-teardown', + dependsOn: ['e2e'], + }), + [TARGET_ENVIRONMENT_TEARDOWN]: expect.objectContaining({ + executor: '@push-based/nx-verdaccio:env-teardown', + }), }) ); diff --git a/examples/e2e/cli-static-e2e/project.json b/examples/e2e/cli-static-e2e/project.json index b09f738..2ce212a 100644 --- a/examples/e2e/cli-static-e2e/project.json +++ b/examples/e2e/cli-static-e2e/project.json @@ -12,7 +12,6 @@ "inputs": ["default", "^production"], "outputs": ["{options.reportsDirectory}"], "options": { - "environmentRoot": "static-environments/user-lists", "reportsDirectory": "../../../coverage/projects/cli-static-e2e" } } diff --git a/nx.json b/nx.json index 8503016..75f9e01 100644 --- a/nx.json +++ b/nx.json @@ -73,9 +73,6 @@ } }, "plugins": [ - { - "plugin": "./examples/e2e/cli-e2e-original/tooling/original.plugin.ts" - }, { "plugin": "@push-based/nx-verdaccio", "options": { @@ -88,6 +85,9 @@ }, "exclude": ["./examples/e2e/cli-e2e-original"] }, + { + "plugin": "./examples/e2e/cli-e2e-original/tooling/original.plugin.ts" + }, { "plugin": "@nx/eslint/plugin", "options": { diff --git a/package-lock.json b/package-lock.json index 07ce014..250a6e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "memfs": "^4.11.1", + "simple-git": "^3.27.0", "tslib": "^2.3.0", "yargs": "^17.7.2" }, @@ -3219,7 +3220,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "dev": true, "dependencies": { "debug": "^4.1.1" } @@ -3227,8 +3227,7 @@ "node_modules/@kwsites/promise-deferred": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", - "dev": true + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, "node_modules/@mole-inc/bin-wrapper": { "version": "8.0.1", @@ -7917,7 +7916,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -12120,8 +12118,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/multi-progress-bars": { "version": "5.0.3", @@ -14063,7 +14060,6 @@ "version": "3.27.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.27.0.tgz", "integrity": "sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==", - "dev": true, "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", diff --git a/package.json b/package.json index c329ddd..9f1103a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "private": true, "dependencies": { "memfs": "^4.11.1", + "simple-git": "^3.27.0", "tslib": "^2.3.0", "yargs": "^17.7.2" }, @@ -68,5 +69,9 @@ "verdaccio", "performance" ], - "author": "Michael Hladky" + "author": "Michael Hladky", + "directories": { + "doc": "docs", + "example": "examples" + } } diff --git a/project.json b/project.json index 89c3216..9d2d98e 100644 --- a/project.json +++ b/project.json @@ -4,6 +4,12 @@ "includedScripts": [], "// targets": "to see all targets run: nx show project workspace-source --web", "targets": { + "show-project": { + "executor": "nx:run-commands", + "options": { + "command": "tsx --tsconfig=./tooling/bin/tsconfig.bin.json ./tooling/bin/nx-show-project.ts" + } + }, "code-pushup": { "executor": "nx:run-commands", "options": { diff --git a/projects/nx-verdaccio/executors.json b/projects/nx-verdaccio/executors.json index e21f0bd..5f45df7 100644 --- a/projects/nx-verdaccio/executors.json +++ b/projects/nx-verdaccio/executors.json @@ -10,6 +10,11 @@ "schema": "./src/executors/env-setup/schema.json", "description": "Generate and install test environments in your workspace. Cached and ready for use." }, + "env-teardown": { + "implementation": "./src/executors/env-teardown/executor", + "schema": "./src/executors/env-teardown/schema.json", + "description": "Teardown test environments in your workspace." + }, "pkg-publish": { "implementation": "./src/executors/pkg-publish/executor", "schema": "./src/executors/pkg-publish/schema.json", diff --git a/projects/nx-verdaccio/src/executors/env-setup/executor.ts b/projects/nx-verdaccio/src/executors/env-setup/executor.ts index 9b2a1aa..509311a 100644 --- a/projects/nx-verdaccio/src/executors/env-setup/executor.ts +++ b/projects/nx-verdaccio/src/executors/env-setup/executor.ts @@ -12,6 +12,7 @@ import { TARGET_ENVIRONMENT_VERDACCIO_STOP, } from '../../plugin/targets/environment.targets'; import { runSingleExecutor } from '../../internal/run-executor'; +import { rm } from 'node:fs/promises'; export type ExecutorOutput = { success: boolean; @@ -82,6 +83,19 @@ export default async function runSetupEnvironmentExecutor( }, context ); + // delete storage, npmrc + await rm(join(environmentRoot, 'storage'), { + recursive: true, + force: true, + retryDelay: 100, + maxRetries: 2, + }); + await rm(join(environmentRoot, '.npmrc'), { + recursive: true, + force: true, + retryDelay: 100, + maxRetries: 2, + }); } else { const { url } = readJsonFile( join(environmentRoot, VERDACCIO_REGISTRY_JSON) diff --git a/projects/nx-verdaccio/src/executors/env-teardown/README.md b/projects/nx-verdaccio/src/executors/env-teardown/README.md new file mode 100644 index 0000000..32f7579 --- /dev/null +++ b/projects/nx-verdaccio/src/executors/env-teardown/README.md @@ -0,0 +1,53 @@ +# Teardown Environment Executor + +This executor helps to cleanup a [environment](../../../../../README.md#-environment-folders-to-isolate-files-during-e2e-tests) of a given folder. +If a server is running for the given environment, it will be stopped. +If this folder is checked into github all it's changes will be reverted, if it is not checked into github, the folder will be deleted. + +**Environment folder** + +#### @push-based/nx-verdaccio:env-teardown + +## Usage + +// project.json + +```json +{ + "name": "my-project", + "targets": { + "env-teardown": { + "executor": "@push-based/nx-verdaccio:env-teardown" + } + } +} +``` + +By default, the Nx executor will derive the options from the executor options. + +```jsonc +{ + "name": "my-project", + "targets": { + "env-teardown": { + "executor": "@code-pushup/nx-verdaccio:env-teardown", + "options": { + "envRoot": "/tmp/test-npm-workspace" + "verbose": true, + } + } + } +} +``` + +Show what will be executed without actually executing it: + +`nx run my-project:env-teardown --print-config` + +## Options + +| Name | type | description | +| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| **envRoot** | `string` (REQUIRED) | The folder in which the package should get published. This folder is the environment folder and contains a configured `.npmrc` file. | +| **verbose** | `boolean` | Show more verbose logs | +| **printConfig** | `boolean` | Print config without executing | diff --git a/projects/nx-verdaccio/src/executors/env-teardown/constants.ts b/projects/nx-verdaccio/src/executors/env-teardown/constants.ts new file mode 100644 index 0000000..91647e3 --- /dev/null +++ b/projects/nx-verdaccio/src/executors/env-teardown/constants.ts @@ -0,0 +1 @@ +export const EXECUTOR_ENVIRONMENT_TEARDOWN = 'env-teardown'; diff --git a/projects/nx-verdaccio/src/executors/env-teardown/executor.ts b/projects/nx-verdaccio/src/executors/env-teardown/executor.ts new file mode 100644 index 0000000..c0ee650 --- /dev/null +++ b/projects/nx-verdaccio/src/executors/env-teardown/executor.ts @@ -0,0 +1,42 @@ +import { type ExecutorContext, logger } from '@nx/devkit'; +import type { TeardownExecutorOptions } from './schema'; +import { teardownEnvironment } from './teardown-env'; +import { PACKAGE_NAME } from '../../plugin/constants'; +import { EXECUTOR_ENVIRONMENT_TEARDOWN } from './constants'; +import { ExecutorOutput } from '../internal/executor-output'; + +export async function teardownExecutor( + options: TeardownExecutorOptions, + context: ExecutorContext +): Promise { + const { environmentRoot, verbose } = options; + + if (verbose) { + logger.info( + `Execute ${PACKAGE_NAME}:${EXECUTOR_ENVIRONMENT_TEARDOWN} with options: ${JSON.stringify( + options, + null, + 2 + )}` + ); + } + try { + await teardownEnvironment(context, { + environmentRoot, + verbose, + }); + } catch (error) { + logger.error(error); + return { + success: false, + command: error?.message ?? (error as Error).toString(), + }; + } + + return { + success: true, + command: 'Teared down environment successfully.', + }; +} + +export default teardownExecutor; diff --git a/projects/nx-verdaccio/src/executors/env-teardown/git.ts b/projects/nx-verdaccio/src/executors/env-teardown/git.ts new file mode 100644 index 0000000..957b723 --- /dev/null +++ b/projects/nx-verdaccio/src/executors/env-teardown/git.ts @@ -0,0 +1,20 @@ +import {simpleGit} from 'simple-git'; + +export async function isFolderInGit(folderPath: string): Promise { + try { + const git = simpleGit(folderPath); + // Check if the folder is a git repository + const isRepo = (await git.checkIgnore(folderPath)).length === 0; + return isRepo; + } catch (error) { + if ( + (error as Error).message.includes( + 'Cannot use simple-git on a directory that does not exist' + ) + ) { + return true; + } + console.log(`${error}`); + return false; + } +} diff --git a/projects/nx-verdaccio/src/executors/env-teardown/schema.json b/projects/nx-verdaccio/src/executors/env-teardown/schema.json new file mode 100644 index 0000000..581dd21 --- /dev/null +++ b/projects/nx-verdaccio/src/executors/env-teardown/schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "TeardownExecutorOptions", + "title": "Environment teardown executor options", + "type": "object", + "properties": { + "environmentRoot": { + "type": "string", + "description": "The root directory of the environment", + "aliases": ["envRoot", "e"] + }, + "verbose": { + "type": "boolean", + "description": "Print additional logs" + } + }, + "additionalProperties": true, + "required": [] +} diff --git a/projects/nx-verdaccio/src/executors/env-teardown/schema.ts b/projects/nx-verdaccio/src/executors/env-teardown/schema.ts new file mode 100644 index 0000000..e918251 --- /dev/null +++ b/projects/nx-verdaccio/src/executors/env-teardown/schema.ts @@ -0,0 +1,7 @@ +import { Environment } from '../env-bootstrap/npm'; + +export type TeardownExecutorOptions = Partial< + Environment & { + verbose: boolean; + } +>; diff --git a/projects/nx-verdaccio/src/executors/env-teardown/teardown-env.ts b/projects/nx-verdaccio/src/executors/env-teardown/teardown-env.ts new file mode 100644 index 0000000..f35395f --- /dev/null +++ b/projects/nx-verdaccio/src/executors/env-teardown/teardown-env.ts @@ -0,0 +1,67 @@ +import { Environment } from '../env-bootstrap/npm'; +import { simpleGit, type SimpleGit } from 'simple-git'; +import { isFolderInGit } from './git'; +import { ExecutorContext, logger } from '@nx/devkit'; +import { join } from 'node:path'; +import { VERDACCIO_REGISTRY_JSON } from '../env-bootstrap/constants'; +import { fileExists } from '../../internal/file-system'; +import { rm } from 'node:fs/promises'; +import runKillProcessExecutor from '../kill-process/executor'; +import { DEFAULT_ENVIRONMENTS_OUTPUT_DIR } from '../../plugin/constants'; +import { ExpandedPluginConfiguration } from 'nx/src/config/nx-json'; +import type { NxVerdaccioCreateNodeOptions } from '../../plugin/schema'; + +export const gitClient: SimpleGit = simpleGit(process.cwd()); +export type TeardownEnvironmentOptions = Environment & { verbose?: boolean }; + +export async function teardownEnvironment( + context: ExecutorContext, + options: TeardownEnvironmentOptions, + git: SimpleGit = gitClient +): Promise { + const { environmentRoot: optEnvironmentRoot } = options; + const plugin = context.nxJsonConfiguration.plugins.find((pCfg) => { + return ( + typeof pCfg === 'object' && pCfg?.plugin === '@push-based/nx-verdaccio' + ); + }) as ExpandedPluginConfiguration; + const environmentsDir = + plugin.options.environments?.environmentsDir ?? + DEFAULT_ENVIRONMENTS_OUTPUT_DIR; + const environmentRoot = + optEnvironmentRoot ?? join(environmentsDir, context.projectName); + // kill verdaccio process if running + const registryPath = join(environmentRoot, VERDACCIO_REGISTRY_JSON); + const registryJsonExists = await fileExists(registryPath); + if (registryJsonExists) { + await runKillProcessExecutor({ ...options, filePath: registryPath }); + } else { + logger.info(`No verdaccio-registry.json file found in ${environmentRoot}`); + } + + if (environmentRoot === '.') { + logger.info(`Skip teardown environment in root folder`); + return; + } + + const environmentRootInRepo = await isFolderInGit(environmentRoot); + if (environmentRootInRepo) { + // await git.checkout([environmentRoot]); + // await git.clean('f', [environmentRoot]); + logger.info(`Cleaned git history in ${environmentRoot}`); + } else { + try { + await rm(environmentRoot, { + recursive: true, + force: true, + retryDelay: 100, + maxRetries: 2, + }); + logger.info(`deleted folder ${environmentRoot}.`); + } catch (error) { + throw new Error( + `Error cleaning history of folder ${environmentRoot}. ${error.message}` + ); + } + } +} diff --git a/projects/nx-verdaccio/src/executors/internal/executor-output.ts b/projects/nx-verdaccio/src/executors/internal/executor-output.ts new file mode 100644 index 0000000..fd6dca3 --- /dev/null +++ b/projects/nx-verdaccio/src/executors/internal/executor-output.ts @@ -0,0 +1,5 @@ +export type ExecutorOutput = { + success: boolean; + command?: string; + error?: Error; +}; diff --git a/projects/nx-verdaccio/src/internal/file-system.ts b/projects/nx-verdaccio/src/internal/file-system.ts index 63f3175..a563321 100644 --- a/projects/nx-verdaccio/src/internal/file-system.ts +++ b/projects/nx-verdaccio/src/internal/file-system.ts @@ -1,4 +1,4 @@ -import { mkdir } from 'node:fs/promises'; +import { mkdir, stat } from 'node:fs/promises'; export async function ensureDirectoryExists(baseDir: string) { try { @@ -11,3 +11,12 @@ export async function ensureDirectoryExists(baseDir: string) { } } } + +export async function fileExists(path: string): Promise { + try { + const stats = await stat(path); + return stats.isFile(); + } catch { + return false; + } +} diff --git a/projects/nx-verdaccio/src/plugin/normalize-create-nodes-options.ts b/projects/nx-verdaccio/src/plugin/normalize-create-nodes-options.ts index 9b13cb9..f8d512a 100644 --- a/projects/nx-verdaccio/src/plugin/normalize-create-nodes-options.ts +++ b/projects/nx-verdaccio/src/plugin/normalize-create-nodes-options.ts @@ -1,6 +1,6 @@ import type { NxVerdaccioEnvironmentsOptions, - BuildEnvPluginCreateNodeOptions, + NxVerdaccioCreateNodeOptions, NxVerdaccioPackagesOptions, } from './schema'; import { @@ -20,7 +20,7 @@ export type NormalizedCreateNodeOptions = { }; export function normalizeCreateNodesOptions( - options: BuildEnvPluginCreateNodeOptions + options: NxVerdaccioCreateNodeOptions ): NormalizedCreateNodeOptions { const { environments = {}, packages = {} } = options ?? {}; const { targetNames = [] } = environments; diff --git a/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts b/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts index a7c703b..913eda7 100644 --- a/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts +++ b/projects/nx-verdaccio/src/plugin/nx-verdaccio.plugin.ts @@ -9,7 +9,7 @@ import { type TargetConfiguration, } from '@nx/devkit'; import { dirname, join } from 'node:path'; -import type { BuildEnvPluginCreateNodeOptions } from './schema'; +import type { NxVerdaccioCreateNodeOptions } from './schema'; import { normalizeCreateNodesOptions, type NormalizedCreateNodeOptions, @@ -27,7 +27,7 @@ import { createTargets } from './targets/create-targets'; const PROJECT_JSON_FILE_GLOB = '**/project.json'; -export const createNodesV2: CreateNodesV2 = [ +export const createNodesV2: CreateNodesV2 = [ PROJECT_JSON_FILE_GLOB, async (configFiles, options, context) => { const normalizedOptions = normalizeCreateNodesOptions(options); diff --git a/projects/nx-verdaccio/src/plugin/schema.ts b/projects/nx-verdaccio/src/plugin/schema.ts index 8a3b7a3..86c56ba 100644 --- a/projects/nx-verdaccio/src/plugin/schema.ts +++ b/projects/nx-verdaccio/src/plugin/schema.ts @@ -8,7 +8,7 @@ export type NxVerdaccioPackagesOptions = { targetNames?: string[]; filterByTags?: string[]; }; -export type BuildEnvPluginCreateNodeOptions = { +export type NxVerdaccioCreateNodeOptions = { environments?: NxVerdaccioEnvironmentsOptions; packages?: NxVerdaccioPackagesOptions; }; diff --git a/projects/nx-verdaccio/src/plugin/targets/create-targets.ts b/projects/nx-verdaccio/src/plugin/targets/create-targets.ts index c43590f..9938bab 100644 --- a/projects/nx-verdaccio/src/plugin/targets/create-targets.ts +++ b/projects/nx-verdaccio/src/plugin/targets/create-targets.ts @@ -1,4 +1,4 @@ -import type { BuildEnvPluginCreateNodeOptions } from '../schema'; +import type { NxVerdaccioCreateNodeOptions } from '../schema'; import type { CreateNodesResult, ProjectConfiguration } from '@nx/devkit'; import { logger } from '@nx/devkit'; import { normalizeCreateNodesOptions } from '../normalize-create-nodes-options'; @@ -12,7 +12,7 @@ import { getPkgTargets, isPkgProject } from './package.targets'; export function createTargets( projectConfiguration: ProjectConfiguration, - options: BuildEnvPluginCreateNodeOptions + options: NxVerdaccioCreateNodeOptions ): CreateNodesResult['projects'][string]['targets'] { const { environments, packages } = normalizeCreateNodesOptions(options); diff --git a/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts b/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts index 6c6d542..a8af103 100644 --- a/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts +++ b/projects/nx-verdaccio/src/plugin/targets/environment.targets.ts @@ -13,10 +13,13 @@ 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 { iterateEntries } from '../../internal/transform'; +import { EXECUTOR_ENVIRONMENT_TEARDOWN } from '../../executors/env-teardown/constants'; export const TARGET_ENVIRONMENT_BOOTSTRAP = 'nxv-env-bootstrap'; export const TARGET_ENVIRONMENT_INSTALL = 'nxv-env-install'; export const TARGET_ENVIRONMENT_SETUP = 'nxv-env-setup'; +export const TARGET_ENVIRONMENT_TEARDOWN = 'nxv-env-teardown'; +export const TARGET_ENVIRONMENT_E2E = 'nxv-e2e'; export const TARGET_ENVIRONMENT_VERDACCIO_START = 'nxv-verdaccio-start'; export const TARGET_ENVIRONMENT_VERDACCIO_STOP = 'nxv-verdaccio-stop'; @@ -93,7 +96,7 @@ export function getEnvTargets( options: NormalizedCreateNodeOptions['environments'] ): Record { const { name: envProject } = projectConfig; - const { environmentsDir } = options; + const { environmentsDir, targetNames } = options; const environmentRoot = join(environmentsDir, envProject); return { [TARGET_ENVIRONMENT_BOOTSTRAP]: { @@ -125,6 +128,13 @@ export function getEnvTargets( environmentRoot, }, }, + [TARGET_ENVIRONMENT_TEARDOWN]: { + executor: `${PACKAGE_NAME}:${EXECUTOR_ENVIRONMENT_TEARDOWN}`, + }, + [TARGET_ENVIRONMENT_E2E]: { + dependsOn: targetNames, + executor: `${PACKAGE_NAME}:${EXECUTOR_ENVIRONMENT_TEARDOWN}`, + }, }; } diff --git a/static-environments/user-lists/package.json b/static-environments/user-lists/package.json index 38d18e4..df42d59 100644 --- a/static-environments/user-lists/package.json +++ b/static-environments/user-lists/package.json @@ -2,6 +2,10 @@ "name": "user-lists", "private": true, "scripts": {}, - "dependencies": {}, - "devDependencies": {} + "version": "1.0.0", + "main": "index.js", + "keywords": [], + "author": "", + "license": "ISC", + "description": "" } diff --git a/tooling/bin/nx-show-project.ts b/tooling/bin/nx-show-project.ts new file mode 100644 index 0000000..edf8506 --- /dev/null +++ b/tooling/bin/nx-show-project.ts @@ -0,0 +1,63 @@ +import { execFileSync, execSync } from 'node:child_process'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { objectToCliArgs } from '../../../../packages/utils/src'; +import type { NpmCheckOptions, NpmCheckResult } from '../types'; + +const argv = yargs(hideBin(process.argv)) + .options({ + pkgRange: { type: 'string', demandOption: true }, + registry: { type: 'string' }, + }) + .coerce('pkgRange', (rawVersion) => { + if (rawVersion != null && rawVersion !== '') { + return rawVersion; + } else { + return undefined; + } + }) + .coerce('registry', (rawRegistry) => { + if (rawRegistry != null && rawRegistry !== '') { + return rawRegistry; + } else { + return undefined; + } + }).argv; + +const { pkgRange, registry = 'https://registry.npmjs.org/' } = + argv as NpmCheckOptions; + +try { + const command = 'npm'; + const args = objectToCliArgs({ + _: ['view', pkgRange], + registry, + }); + + const viewResult = execFileSync( + command, + [ + ...args, + // Hide process output via "2>/dev/null". Otherwise, it will print the error message to the terminal. + '2>/dev/null', + ], + { + shell: true, + } + ).toString(); + + const existingPackage = viewResult + .split('\n') + .filter(Boolean) + .at(0) + ?.split(' ') + .at(0); + console.log(`${existingPackage}#FOUND` satisfies NpmCheckResult); // process output to parse + process.exit(0); +} catch (error) { + // @TODO we use '2>/dev/null' to hide errors from process output, but also can't check error message. Find better solution. + // if (error.message.includes(`npm ERR! 404 '${pkgRange}' is not in this registry`)) { + console.log(`${pkgRange}#NOT_FOUND` satisfies NpmCheckResult); // process output to parse + process.exit(0); + // } +} diff --git a/tooling/bin/tsconfig.bin.json b/tooling/bin/tsconfig.bin.json new file mode 100644 index 0000000..4e4cd0e --- /dev/null +++ b/tooling/bin/tsconfig.bin.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"], + "resolveJsonModule": true + }, + "include": ["src/**/*.ts"], + "exclude": [ + "vite.config.ts", + "src/**/__snapshots__/*.ts", + "src/**/*.test.ts", + "src/**/*.unit-test.ts", + "src/**/*.integration-test.ts", + "src/**/*.spec.ts", + "src/**/*.mock.ts", + "test/**/*.ts", + "mock/**/*.ts" + ] +} diff --git a/tooling/bin/tsconfig.json b/tooling/bin/tsconfig.json new file mode 100644 index 0000000..99bc14d --- /dev/null +++ b/tooling/bin/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "target": "ES2018", + "verbatimModuleSyntax": false + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/tooling/measures/nx-performance/audit/project-graph.audit.ts b/tooling/measures/nx-performance/audit/graph-project-time.audit.ts similarity index 97% rename from tooling/measures/nx-performance/audit/project-graph.audit.ts rename to tooling/measures/nx-performance/audit/graph-project-time.audit.ts index d513c8b..dd84591 100644 --- a/tooling/measures/nx-performance/audit/project-graph.audit.ts +++ b/tooling/measures/nx-performance/audit/graph-project-time.audit.ts @@ -3,7 +3,6 @@ import { execFile } from 'node:child_process'; import { promisify } from 'node:util'; export const DEFAULT_MAX_PROJECT_GRAPH_TIME = 300; - export const PROJECT_GRAPH_PERFORMANCE_AUDIT_SLUG = 'graph-time-project'; export const PROJECT_GRAPH_PERFORMANCE_AUDIT = { slug: PROJECT_GRAPH_PERFORMANCE_AUDIT_SLUG, @@ -15,7 +14,7 @@ export type ProjectGraphAuditOptions = { maxProjectGraphTime?: number; }; -export async function projectGraphAudit( +export async function graphProjectTimeAudit( options?: ProjectGraphAuditOptions ): Promise { const { maxProjectGraphTime = DEFAULT_MAX_PROJECT_GRAPH_TIME } = diff --git a/tooling/measures/nx-performance/audit/task-graph.audit.ts b/tooling/measures/nx-performance/audit/graph-task-time.audit.ts similarity index 89% rename from tooling/measures/nx-performance/audit/task-graph.audit.ts rename to tooling/measures/nx-performance/audit/graph-task-time.audit.ts index 9242500..7a5ec77 100644 --- a/tooling/measures/nx-performance/audit/task-graph.audit.ts +++ b/tooling/measures/nx-performance/audit/graph-task-time.audit.ts @@ -10,11 +10,13 @@ export function getTaskGraphTimeAuditSlug(task: string): string { } export const getTaskGraphTimeAudits = (tasks: string[]): Audit[] => { - return tasks.map((task) => ({ - slug: getTaskGraphTimeAuditSlug(task), // Unique slug for each task - title: '[Graph Time] task graph', - description: 'An audit to check performance of the Nx task graph', - })); + return tasks.map((task) => { + return { + slug: getTaskGraphTimeAuditSlug(task), // Unique slug for each task + title: '[Graph Time] task graph', + description: 'An audit to check performance of the Nx task graph', + }; + }); }; export type TaskGraphAuditOptions = { diff --git a/tooling/measures/nx-performance/audit/cache-size.audit.ts b/tooling/measures/nx-performance/audit/task-cache.audit.ts similarity index 100% rename from tooling/measures/nx-performance/audit/cache-size.audit.ts rename to tooling/measures/nx-performance/audit/task-cache.audit.ts diff --git a/tooling/measures/nx-performance/audit/project-task.audit.ts b/tooling/measures/nx-performance/audit/task-time.audit.ts similarity index 80% rename from tooling/measures/nx-performance/audit/project-task.audit.ts rename to tooling/measures/nx-performance/audit/task-time.audit.ts index 2160bf5..ad32f1c 100644 --- a/tooling/measures/nx-performance/audit/project-task.audit.ts +++ b/tooling/measures/nx-performance/audit/task-time.audit.ts @@ -1,9 +1,5 @@ import { AuditOutput, Audit, Table, Issue } from '@code-pushup/models'; -import { - executeProcess, - slugify, - formatDuration, -} from '@code-pushup/utils'; +import { executeProcess, slugify, formatDuration } from '@code-pushup/utils'; import { logger, readJsonFile } from '@nx/devkit'; import { DEFAULT_PLUGIN_OUTPUT } from '../constant'; import { join } from 'node:path'; @@ -16,27 +12,34 @@ export function getTaskTimeAuditSlug(task: string): string { return `nx-${slugify(task)}-${TASK_TIME_AUDIT_POSTFIX}`; } -export const getTaskTimeAudits = (tasks: string[]): Audit[] => { - return tasks.map((task) => ({ - slug: getTaskTimeAuditSlug(task), // Unique slug for each task - title: `[Task Time] ${task}`, - description: 'An audit to check performance of the Nx task.', - })); +export type TaskTimeAuditOption = { + task: string; + cleanup?: () => void | Promise; +}; + +export const getTaskTimeAudits = (tasks: TaskTimeAuditOption[]): Audit[] => { + return tasks.map(({ task }) => { + return { + slug: getTaskTimeAuditSlug(task), // Unique slug for each task + title: `[Task Time] ${task}`, + description: 'An audit to check performance of the Nx task.', + }; + }); }; -export type ProjectTaskAuditOptions = { - taskTimeTasks: string[]; +export type TaskTimeAuditOptions = { + taskTimeTasks: TaskTimeAuditOption[]; maxTaskTime?: number; }; export async function taskTimeAudits( - options?: ProjectTaskAuditOptions + options?: TaskTimeAuditOptions ): Promise { const { taskTimeTasks = [], maxTaskTime = DEFAULT_MAX_PROJECT_TARGET_TIME } = options ?? {}; // Get the timings for each task - const timings = await projectTaskCacheSizeData(taskTimeTasks); + const timings = await taskTimeData(taskTimeTasks); // Return an array of audits, one per task return timings.map(({ task, taskTime, data, issues }) => ({ @@ -72,12 +75,12 @@ export type TaskTimeResult = { issues?: Issue[]; }; -export async function projectTaskCacheSizeData( +export async function taskTimeData( tasks: T[] ): Promise { const results: TaskTimeResult[] = []; - for (const task of tasks) { + for (const { task } of tasks) { const dist = join(DEFAULT_PLUGIN_OUTPUT, 'task-time'); await executeProcess({ command: `NX_DAEMON=false NX_PROFILE=${dist}/${slugify( diff --git a/tooling/measures/nx-performance/index.ts b/tooling/measures/nx-performance/index.ts index 4f2cbea..b9eda64 100644 --- a/tooling/measures/nx-performance/index.ts +++ b/tooling/measures/nx-performance/index.ts @@ -1,3 +1,5 @@ +export { type TaskTimeAuditOption } from './audit/task-time.audit'; +export { type TaskGraphAuditOptions } from './audit/task-graph.audit'; export { type OnlyAudit, nxPerformanceAudits, diff --git a/tooling/measures/nx-performance/nx-performance.plugin.ts b/tooling/measures/nx-performance/nx-performance.plugin.ts index 100ac4f..df5885d 100644 --- a/tooling/measures/nx-performance/nx-performance.plugin.ts +++ b/tooling/measures/nx-performance/nx-performance.plugin.ts @@ -8,27 +8,27 @@ import { PLUGIN_SLUG } from './constant'; import { PROJECT_GRAPH_PERFORMANCE_AUDIT, PROJECT_GRAPH_PERFORMANCE_AUDIT_SLUG, - projectGraphAudit, + graphProjectTimeAudit, ProjectGraphAuditOptions, -} from './audit/project-graph.audit'; +} from './audit/graph-project-time.audit'; import { getTaskTimeAudits, - ProjectTaskAuditOptions, + TaskTimeAuditOptions, TASK_TIME_AUDIT_POSTFIX, taskTimeAudits, -} from './audit/project-task.audit'; +} from './audit/task-time.audit'; import { CACHE_SIZE_AUDIT_POSTFIX, CacheSizeAuditOptions, cacheSizeAudits, getCacheSizeAudits, -} from './audit/cache-size.audit'; +} from './audit/task-cache.audit'; import { getTaskGraphTimeAudits, TASK_GRAPH_TIME_AUDIT_POSTFIX, TaskGraphAuditOptions, taskGraphAudits, -} from './audit/task-graph.audit'; +} from './audit/graph-task-time.audit'; export const nxPerformanceAudits = ({ taskTimeTasks, @@ -63,10 +63,12 @@ export type OnlyAudit = | typeof TASK_GRAPH_TIME_AUDIT_POSTFIX; export type NxPerfPluginConfig = { onlyAudits?: OnlyAudit[]; -} & ProjectGraphAuditOptions & - ProjectTaskAuditOptions & - CacheSizeAuditOptions & - TaskGraphAuditOptions; +} & Partial< + ProjectGraphAuditOptions & + TaskTimeAuditOptions & + CacheSizeAuditOptions & + TaskGraphAuditOptions +>; export function nxPerformancePlugin( options?: NxPerfPluginConfig @@ -137,7 +139,7 @@ export async function runnerFunction( const onlyAuditsSet = new Set(onlyAudits); return [ ...(onlyAuditsSet.has(PROJECT_GRAPH_PERFORMANCE_AUDIT_SLUG) - ? [await projectGraphAudit({ maxProjectGraphTime })] + ? [await graphProjectTimeAudit({ maxProjectGraphTime })] : []), ...(onlyAuditsSet.has(CACHE_SIZE_AUDIT_POSTFIX) ? await cacheSizeAudits({ maxCacheSize, cacheSizeTasks })