From 144f5677a3e1f935ca354474e24a7ba279236940 Mon Sep 17 00:00:00 2001 From: Miki Date: Thu, 27 Oct 2022 17:16:16 -0700 Subject: [PATCH 1/2] [@osd/cross-platform] Adds cross-platform helpers (#2681) cherry-pick from 887093d2d243045029b644680a3e8d0150318143 Signed-off-by: Miki --- CHANGELOG.md | 1 + package.json | 1 + packages/osd-config-schema/package.json | 1 + .../src/errors/schema_error.test.ts | 6 ++-- packages/osd-cross-platform/README.md | 3 ++ packages/osd-cross-platform/package.json | 15 ++++++++++ packages/osd-cross-platform/src/index.ts | 7 +++++ packages/osd-cross-platform/src/path.ts | 30 +++++++++++++++++++ packages/osd-cross-platform/src/process.ts | 22 ++++++++++++++ packages/osd-cross-platform/tsconfig.json | 11 +++++++ packages/osd-optimizer/package.json | 1 + .../src/optimizer/get_changes.test.ts | 2 +- packages/osd-plugin-generator/package.json | 1 + .../integration_tests/generate_plugin.test.ts | 3 +- packages/osd-plugin-helpers/package.json | 1 + packages/osd-plugin-helpers/src/cli.ts | 17 ++++++----- .../src/integration_tests/build.test.ts | 8 ++--- packages/osd-pm/package.json | 1 + packages/osd-pm/src/utils/projects_tree.ts | 2 +- packages/osd-utils/package.json | 1 + packages/osd-utils/src/path/index.ts | 26 +--------------- src/dev/build/lib/config.test.ts | 3 +- 22 files changed, 117 insertions(+), 46 deletions(-) create mode 100644 packages/osd-cross-platform/README.md create mode 100644 packages/osd-cross-platform/package.json create mode 100644 packages/osd-cross-platform/src/index.ts create mode 100644 packages/osd-cross-platform/src/path.ts create mode 100644 packages/osd-cross-platform/src/process.ts create mode 100644 packages/osd-cross-platform/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e3b0852b759..6d859efcc9d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### 📈 Features/Enhancements - [Windows] Facilitate building and running OSD and plugins on Windows platforms ([#2601](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2601)) +- [Windows] Add helper functions to work around the differences of platforms ([#2681](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2681)) ### 🐛 Bug Fixes diff --git a/package.json b/package.json index 9c871cb4103f..444b49629fc2 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "@osd/apm-config-loader": "1.0.0", "@osd/config": "1.0.0", "@osd/config-schema": "1.0.0", + "@osd/cross-platform": "1.0.0", "@osd/i18n": "1.0.0", "@osd/interpreter": "1.0.0", "@osd/logging": "1.0.0", diff --git a/packages/osd-config-schema/package.json b/packages/osd-config-schema/package.json index 5e727f3423c6..e6e6aed20d8a 100644 --- a/packages/osd-config-schema/package.json +++ b/packages/osd-config-schema/package.json @@ -10,6 +10,7 @@ "osd:bootstrap": "yarn build" }, "devDependencies": { + "@osd/cross-platform": "1.0.0", "typescript": "4.0.2", "tsd": "^0.16.0" }, diff --git a/packages/osd-config-schema/src/errors/schema_error.test.ts b/packages/osd-config-schema/src/errors/schema_error.test.ts index 004d186e88fe..b7b1bdefb20f 100644 --- a/packages/osd-config-schema/src/errors/schema_error.test.ts +++ b/packages/osd-config-schema/src/errors/schema_error.test.ts @@ -33,6 +33,8 @@ import { relative, sep } from 'path'; import { SchemaError } from '.'; +import { standardize, PROCESS_WORKING_DIR } from '@osd/cross-platform'; + /** * Make all paths in stacktrace relative. */ @@ -48,9 +50,7 @@ export const cleanStack = (stack: string) => } const path = parts[1]; - // Cannot use `standardize` from `@osd/utils - let relativePath = relative(process.cwd(), path); - if (process.platform === 'win32') relativePath = relativePath.replace(/\\/g, '/'); + const relativePath = standardize(relative(PROCESS_WORKING_DIR, path)); return line.replace(path, relativePath); }) diff --git a/packages/osd-cross-platform/README.md b/packages/osd-cross-platform/README.md new file mode 100644 index 000000000000..a61a2f184f02 --- /dev/null +++ b/packages/osd-cross-platform/README.md @@ -0,0 +1,3 @@ +# `@osd/cross-platform` — OpenSearch Dashboards cross-platform helpers + +This package contains the helper functions to work around the differences of platforms diff --git a/packages/osd-cross-platform/package.json b/packages/osd-cross-platform/package.json new file mode 100644 index 000000000000..c07da1fe6513 --- /dev/null +++ b/packages/osd-cross-platform/package.json @@ -0,0 +1,15 @@ +{ + "name": "@osd/cross-platform", + "main": "./target/index.js", + "version": "1.0.0", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "tsc", + "osd:bootstrap": "yarn build" + }, + "devDependencies": { + "typescript": "4.0.2", + "tsd": "^0.16.0" + } +} diff --git a/packages/osd-cross-platform/src/index.ts b/packages/osd-cross-platform/src/index.ts new file mode 100644 index 000000000000..343d7e9257a4 --- /dev/null +++ b/packages/osd-cross-platform/src/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './path'; +export * from './process'; diff --git a/packages/osd-cross-platform/src/path.ts b/packages/osd-cross-platform/src/path.ts new file mode 100644 index 000000000000..7e37443f1bca --- /dev/null +++ b/packages/osd-cross-platform/src/path.ts @@ -0,0 +1,30 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { normalize } from 'path'; + +/** + * Get a standardized reference to a path + * @param {string} path - the path to standardize + * @param {boolean} [usePosix=true] - produce a posix reference + * @param {boolean} [escapedBackslashes=true] - on Windows, double-backslash the reference + * @internal + */ +export const standardize = ( + path: string, + usePosix: boolean = true, + escapedBackslashes: boolean = true +) => { + /* Force os-dependant separators + * path.posix.normalize doesn't convert backslashes to slashes on Windows so we manually force it afterwards + */ + const normal = normalize(path); + + // Filter out in-browser executions as well as non-windows ones + if (process?.platform !== 'win32') return normal; + + if (usePosix) return normal.replace(/\\/g, '/'); + return escapedBackslashes ? normal.replace(/\\/g, '\\\\') : normal; +}; diff --git a/packages/osd-cross-platform/src/process.ts b/packages/osd-cross-platform/src/process.ts new file mode 100644 index 000000000000..fa593c943687 --- /dev/null +++ b/packages/osd-cross-platform/src/process.ts @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { execSync } from 'child_process'; + +let workingDir = process.cwd(); + +if (process.platform === 'win32') { + try { + const pathFullName = execSync('powershell "(Get-Item -LiteralPath $pwd).FullName"', { + cwd: workingDir, + encoding: 'utf8', + })?.trim?.(); + if (pathFullName?.length > 2) workingDir = pathFullName; + } catch (ex) { + // Do nothing + } +} + +export const PROCESS_WORKING_DIR = workingDir; diff --git a/packages/osd-cross-platform/tsconfig.json b/packages/osd-cross-platform/tsconfig.json new file mode 100644 index 000000000000..e9dd6313e6f7 --- /dev/null +++ b/packages/osd-cross-platform/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target", + "declaration": true, + "declarationMap": true + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/osd-optimizer/package.json b/packages/osd-optimizer/package.json index a4a008c8002a..4a22dda589cd 100644 --- a/packages/osd-optimizer/package.json +++ b/packages/osd-optimizer/package.json @@ -13,6 +13,7 @@ "@babel/cli": "^7.14.5", "@babel/core": "^7.11.6", "@osd/babel-preset": "1.0.0", + "@osd/cross-platform": "1.0.0", "@osd/dev-utils": "1.0.0", "@osd/std": "1.0.0", "@osd/ui-shared-deps": "1.0.0", diff --git a/packages/osd-optimizer/src/optimizer/get_changes.test.ts b/packages/osd-optimizer/src/optimizer/get_changes.test.ts index 4b16eeac9467..e683c83950d4 100644 --- a/packages/osd-optimizer/src/optimizer/get_changes.test.ts +++ b/packages/osd-optimizer/src/optimizer/get_changes.test.ts @@ -35,7 +35,7 @@ import path from 'path'; jest.mock('execa'); import { getChanges } from './get_changes'; -import { standardize } from '@osd/dev-utils'; +import { standardize } from '@osd/cross-platform'; const execa: jest.Mock = jest.requireMock('execa'); diff --git a/packages/osd-plugin-generator/package.json b/packages/osd-plugin-generator/package.json index 5a26c49ce670..79425727643e 100644 --- a/packages/osd-plugin-generator/package.json +++ b/packages/osd-plugin-generator/package.json @@ -9,6 +9,7 @@ "osd:watch": "node scripts/build --watch" }, "dependencies": { + "@osd/cross-platform": "1.0.0", "@osd/dev-utils": "1.0.0", "ejs": "^3.1.7", "execa": "^4.0.2", diff --git a/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts b/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts index 714b26ff4cd0..3ed08083ec4e 100644 --- a/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts +++ b/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts @@ -34,7 +34,8 @@ import Path from 'path'; import del from 'del'; import execa from 'execa'; -import { REPO_ROOT, standardize, createAbsolutePathSerializer } from '@osd/dev-utils'; +import { standardize } from '@osd/cross-platform'; +import { REPO_ROOT, createAbsolutePathSerializer } from '@osd/dev-utils'; import globby from 'globby'; // Has to be a posix reference because it is used to generate glob patterns diff --git a/packages/osd-plugin-helpers/package.json b/packages/osd-plugin-helpers/package.json index 04dec1433633..2484b66230e7 100644 --- a/packages/osd-plugin-helpers/package.json +++ b/packages/osd-plugin-helpers/package.json @@ -16,6 +16,7 @@ "osd:watch": "tsc --watch" }, "dependencies": { + "@osd/cross-platform": "1.0.0", "@osd/dev-utils": "1.0.0", "@osd/optimizer": "1.0.0", "del": "^6.1.1", diff --git a/packages/osd-plugin-helpers/src/cli.ts b/packages/osd-plugin-helpers/src/cli.ts index 6596e33bb178..0eb57187a4ad 100644 --- a/packages/osd-plugin-helpers/src/cli.ts +++ b/packages/osd-plugin-helpers/src/cli.ts @@ -32,6 +32,7 @@ import Path from 'path'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; import { RunWithCommands, createFlagError, createFailError } from '@osd/dev-utils'; import { findOpenSearchDashboardsJson } from './find_opensearch_dashboards_json'; @@ -81,10 +82,10 @@ export function runCli() { throw createFlagError('expected a single --skip-archive flag'); } - const pluginDir = await findOpenSearchDashboardsJson(process.cwd()); + const pluginDir = await findOpenSearchDashboardsJson(PROCESS_WORKING_DIR); if (!pluginDir) { throw createFailError( - `Unable to find OpenSearch Dashboards Platform plugin in [${process.cwd()}] or any of its parent directories. Has it been migrated properly? Does it have a opensearch_dashboards.json file?` + `Unable to find OpenSearch Dashboards Platform plugin in [${PROCESS_WORKING_DIR}] or any of its parent directories. Has it been migrated properly? Does it have a opensearch_dashboards.json file?` ); } @@ -150,30 +151,30 @@ export function runCli() { allowUnexpected: true, }, async run({ log, flags }) { - const pluginDir = await findOpenSearchDashboardsJson(process.cwd()); + const pluginDir = await findOpenSearchDashboardsJson(PROCESS_WORKING_DIR); if (!pluginDir) { throw createFailError( - `Unable to find OpenSearch Dashboards Platform plugin in [${process.cwd()}] or any of its parent directories. Has it been migrated properly? Does it have a opensearch_dashboards.json file?` + `Unable to find OpenSearch Dashboards Platform plugin in [${PROCESS_WORKING_DIR}] or any of its parent directories. Has it been migrated properly? Does it have a opensearch_dashboards.json file?` ); } let dashboardsPackage; try { - dashboardsPackage = await import(Path.join(process.cwd(), '../../package.json')); + dashboardsPackage = await import(Path.join(PROCESS_WORKING_DIR, '../../package.json')); } catch (ex) { throw createFailError(`Unable to parse the OpenSearch Dashboards' package.json file`); } let pluginPackage; try { - pluginPackage = await import(Path.join(process.cwd(), 'package.json')); + pluginPackage = await import(Path.join(PROCESS_WORKING_DIR, 'package.json')); } catch (ex) { throw createFailError(`Unable to parse the plugin's package.json file`); } let manifestFile; try { - manifestFile = await import(Path.join(process.cwd(), 'opensearch_dashboards.json')); + manifestFile = await import(Path.join(PROCESS_WORKING_DIR, 'opensearch_dashboards.json')); } catch (ex) { throw createFailError(`Unable to parse the plugin's opensearch_dashboards.json file`); } @@ -242,7 +243,7 @@ export function runCli() { const context: VersionContext = { log, - sourceDir: process.cwd(), + sourceDir: PROCESS_WORKING_DIR, pluginVersion: updatedPluginVersion, compatibilityVersion: updatedCompatibilityVersion, }; diff --git a/packages/osd-plugin-helpers/src/integration_tests/build.test.ts b/packages/osd-plugin-helpers/src/integration_tests/build.test.ts index c41bec25f8e4..1d70db3d18a2 100644 --- a/packages/osd-plugin-helpers/src/integration_tests/build.test.ts +++ b/packages/osd-plugin-helpers/src/integration_tests/build.test.ts @@ -34,12 +34,8 @@ import Path from 'path'; import Fs from 'fs'; import execa from 'execa'; -import { - REPO_ROOT, - standardize, - createStripAnsiSerializer, - createReplaceSerializer, -} from '@osd/dev-utils'; +import { standardize } from '@osd/cross-platform'; +import { REPO_ROOT, createStripAnsiSerializer, createReplaceSerializer } from '@osd/dev-utils'; import extract from 'extract-zip'; import del from 'del'; import globby from 'globby'; diff --git a/packages/osd-pm/package.json b/packages/osd-pm/package.json index ad967a8b067a..fe9ed7118b44 100644 --- a/packages/osd-pm/package.json +++ b/packages/osd-pm/package.json @@ -69,6 +69,7 @@ "write-pkg": "^4.0.0" }, "dependencies": { + "@osd/cross-platform": "1.0.0", "@osd/utils": "1.0.0", "tslib": "^2.0.0" } diff --git a/packages/osd-pm/src/utils/projects_tree.ts b/packages/osd-pm/src/utils/projects_tree.ts index 19c7d41ddee5..d2288ba32d09 100644 --- a/packages/osd-pm/src/utils/projects_tree.ts +++ b/packages/osd-pm/src/utils/projects_tree.ts @@ -33,7 +33,7 @@ import chalk from 'chalk'; import path from 'path'; -import { standardize } from '@osd/utils'; +import { standardize } from '@osd/cross-platform'; import { Project } from './project'; const projectKey = Symbol('__project'); diff --git a/packages/osd-utils/package.json b/packages/osd-utils/package.json index 802b03a0adb0..1d632cffbf69 100644 --- a/packages/osd-utils/package.json +++ b/packages/osd-utils/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@osd/config-schema": "1.0.0", + "@osd/cross-platform": "1.0.0", "load-json-file": "^6.2.0" }, "devDependencies": { diff --git a/packages/osd-utils/src/path/index.ts b/packages/osd-utils/src/path/index.ts index ead39b29cd93..63837ece4f0f 100644 --- a/packages/osd-utils/src/path/index.ts +++ b/packages/osd-utils/src/path/index.ts @@ -30,7 +30,7 @@ * GitHub history for details. */ -import { join, normalize } from 'path'; +import { join } from 'path'; import { accessSync, constants } from 'fs'; import { TypeOf, schema } from '@osd/config-schema'; import { REPO_ROOT } from '../repo_root'; @@ -96,27 +96,3 @@ export const config = { data: schema.string({ defaultValue: () => getDataPath() }), }), }; - -/** - * Get a standardized reference to a path - * @param {string} path - the path to standardize - * @param {boolean} [usePosix=true] - produce a posix reference - * @param {boolean} [escapedBackslashes=true] - on Windows, double-backslash the reference - * @internal - */ -export const standardize = ( - path: string, - usePosix: boolean = true, - escapedBackslashes: boolean = true -) => { - /* Force os-dependant separators - * path.posix.normalize doesn't convert backslashes to slashes on Windows so we manually force it afterwards - */ - const normal = normalize(path); - - // Filter out in-browser executions as well as non-windows ones - if (process.platform !== 'win32') return normal; - - if (usePosix) return normal.replace(/\\/g, '/'); - return escapedBackslashes ? normal.replace(/\\/g, '\\\\') : normal; -}; diff --git a/src/dev/build/lib/config.test.ts b/src/dev/build/lib/config.test.ts index 4e34699da9fc..e87ebfa9b533 100644 --- a/src/dev/build/lib/config.test.ts +++ b/src/dev/build/lib/config.test.ts @@ -27,7 +27,8 @@ import { resolve } from 'path'; -import { REPO_ROOT, standardize } from '@osd/utils'; +import { standardize } from '@osd/cross-platform'; +import { REPO_ROOT } from '@osd/utils'; import { createAbsolutePathSerializer } from '@osd/dev-utils'; import pkg from '../../../../package.json'; From 1f9c413b1838c51c9c13a364a352c305d342bf4a Mon Sep 17 00:00:00 2001 From: Miki Date: Tue, 1 Nov 2022 10:26:28 -0700 Subject: [PATCH 2/2] Adds @osd/cross-platform (#2703) * Adds helper functions, @osd/cross-platform, to work around the differences of platforms cherry-pick from 0c9ca9637d4d9577198311462d3eef93f2faf666 Signed-off-by: Miki --- CHANGELOG.md | 1 + .../src/errors/schema_error.test.ts | 4 +- packages/osd-cross-platform/README.md | 27 ++- .../src/__snapshots__/path.test.ts.snap | 13 ++ packages/osd-cross-platform/src/index.ts | 1 + packages/osd-cross-platform/src/path.test.ts | 207 ++++++++++++++++++ packages/osd-cross-platform/src/path.ts | 148 ++++++++++++- packages/osd-cross-platform/src/process.ts | 28 ++- .../src/repo_root.ts | 36 ++- .../serializers/absolute_path_serializer.ts | 19 +- .../rules/no_restricted_paths.js | 2 +- packages/osd-optimizer/src/node/cache.ts | 17 +- .../osd-optimizer/src/optimizer/cache_keys.ts | 4 +- packages/osd-plugin-generator/src/cli.ts | 4 +- .../integration_tests/generate_plugin.test.ts | 4 +- packages/osd-plugin-helpers/package.json | 2 +- .../src/integration_tests/build.test.ts | 10 +- .../src/tasks/create_archive.ts | 3 +- packages/osd-pm/src/utils/projects.test.ts | 6 +- packages/osd-utils/src/index.ts | 2 +- packages/osd-utils/src/package_json/index.ts | 2 +- packages/osd-utils/src/path/index.ts | 2 +- src/cli_plugin/install/download.test.js | 9 +- .../install/opensearch_dashboards.test.js | 9 +- src/cli_plugin/install/pack.test.js | 3 +- src/cli_plugin/list/list.test.js | 9 +- src/cli_plugin/remove/remove.test.js | 9 +- .../discovery/plugins_discovery.test.ts | 74 +++---- .../server/plugins/plugins_service.test.ts | 8 +- src/dev/build/lib/config.test.ts | 3 +- .../build/lib/integration_tests/fs.test.ts | 5 +- .../lib/integration_tests/scan_copy.test.ts | 3 +- src/dev/build/lib/scan_delete.test.ts | 12 +- src/dev/file.ts | 3 +- src/dev/i18n/integrate_locale_files.test.ts | 16 +- src/dev/i18n/utils/utils.js | 3 +- src/dev/jest/config.js | 1 + .../integration_tests/junit_reporter.test.js | 3 +- 38 files changed, 580 insertions(+), 132 deletions(-) create mode 100644 packages/osd-cross-platform/src/__snapshots__/path.test.ts.snap create mode 100644 packages/osd-cross-platform/src/path.test.ts rename packages/{osd-utils => osd-cross-platform}/src/repo_root.ts (66%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d859efcc9d5..19a95a70a18c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Windows] Facilitate building and running OSD and plugins on Windows platforms ([#2601](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2601)) - [Windows] Add helper functions to work around the differences of platforms ([#2681](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2681)) +- [Windows] Add `@osd/cross-platform` package to standardize path handling across platforms ([#2703](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2703)) ### 🐛 Bug Fixes diff --git a/packages/osd-config-schema/src/errors/schema_error.test.ts b/packages/osd-config-schema/src/errors/schema_error.test.ts index b7b1bdefb20f..0a55f3d9fd4a 100644 --- a/packages/osd-config-schema/src/errors/schema_error.test.ts +++ b/packages/osd-config-schema/src/errors/schema_error.test.ts @@ -33,7 +33,7 @@ import { relative, sep } from 'path'; import { SchemaError } from '.'; -import { standardize, PROCESS_WORKING_DIR } from '@osd/cross-platform'; +import { standardize, getRepoRoot } from '@osd/cross-platform'; /** * Make all paths in stacktrace relative. @@ -50,7 +50,7 @@ export const cleanStack = (stack: string) => } const path = parts[1]; - const relativePath = standardize(relative(PROCESS_WORKING_DIR, path)); + const relativePath = standardize(relative(getRepoRoot(path) || '.', path)); return line.replace(path, relativePath); }) diff --git a/packages/osd-cross-platform/README.md b/packages/osd-cross-platform/README.md index a61a2f184f02..0583b295c5f1 100644 --- a/packages/osd-cross-platform/README.md +++ b/packages/osd-cross-platform/README.md @@ -1,3 +1,28 @@ # `@osd/cross-platform` — OpenSearch Dashboards cross-platform helpers -This package contains the helper functions to work around the differences of platforms +This package contains the helpers to work around the differences across platforms, such as the difference in the path segment separator and the possibility of referencing a path using the short 8.3 name (SFN), a long name, and a long UNC on Windows. + +Some helpers are functions that `standardize` the reference to a path or help `getRepoRoot`, and some are constants referencing the `PROCESS_WORKING_DIR` or `REPO_ROOT`. + +### Example + +When the relative reference of `path` to the working directory is needed, using the code below would produce different results on Linux that it would on Windows and if the process was started in a Windows shell that used short paths, the results differ from a Windows shell that used long paths. +```js +import { relative } from 'path'; + +const relativePath = relative(process.cwd(), path); + +// Output on Linux: relative-path/to/a/file +// Windows: relative-path\to\a\file +// Windows SFN: RELATI~1\to\a\file +``` + +To avoid those differences, helper functions and constants can be used: +```js +import { relative } from 'path'; +import { standardize, PROCESS_WORKING_DIR } from '@osd/cross-platform'; + +const relativePath = standardize(relative(PROCESS_WORKING_DIR, path)); + +// Output: relative-path/to/a/file +``` \ No newline at end of file diff --git a/packages/osd-cross-platform/src/__snapshots__/path.test.ts.snap b/packages/osd-cross-platform/src/__snapshots__/path.test.ts.snap new file mode 100644 index 000000000000..20c44ec1c2d6 --- /dev/null +++ b/packages/osd-cross-platform/src/__snapshots__/path.test.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Cross Platform path standardize on POSIX-compatible platforms ignores additional parameters 1`] = `"/a/b/c"`; + +exports[`Cross Platform path standardize on POSIX-compatible platforms produces a path in POSIX format 1`] = `"/a/b/c"`; + +exports[`Cross Platform path standardize on Windows produces a path in POSIX format 1`] = `"C:/a/b/c"`; + +exports[`Cross Platform path standardize on Windows produces a path in native format 1`] = `"C:\\\\a\\\\b\\\\c"`; + +exports[`Cross Platform path standardize on Windows produces a path in native format even for POSIX input 1`] = `"C:\\\\a\\\\b\\\\c"`; + +exports[`Cross Platform path standardize on Windows produces a path in native format with escaped backslashes 1`] = `"C:\\\\\\\\a\\\\\\\\b\\\\\\\\c"`; diff --git a/packages/osd-cross-platform/src/index.ts b/packages/osd-cross-platform/src/index.ts index 343d7e9257a4..bc05aa9a955d 100644 --- a/packages/osd-cross-platform/src/index.ts +++ b/packages/osd-cross-platform/src/index.ts @@ -5,3 +5,4 @@ export * from './path'; export * from './process'; +export * from './repo_root'; diff --git a/packages/osd-cross-platform/src/path.test.ts b/packages/osd-cross-platform/src/path.test.ts new file mode 100644 index 000000000000..367d92634d1d --- /dev/null +++ b/packages/osd-cross-platform/src/path.test.ts @@ -0,0 +1,207 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import path from 'path'; +import fs from 'fs'; +import del from 'del'; + +import { + resolveToFullNameSync, + resolveToFullPathSync, + resolveToShortNameSync, + resolveToShortPathSync, + shortNamesSupportedSync, + realPathSync, + realShortPathSync, + standardize, +} from './path'; + +const tmpTestFolder = './__test_artifacts__'; +const longFolderName = '.long-folder-name'; +const longFileName = '.long-file-name.txt'; +const longSymlinkName = '.sym.link'; +const shortFolderName = 'LONG-F~1'; +const shortFileName = 'LONG-F~1.TXT'; +const dummyWindowsPath = 'C:\\a\\b\\c'; +const dummyWindowsPOSIXPath = 'C:/a/b/c'; +const dummyPOSIXPath = '/a/b/c'; + +const onWindows = process.platform === 'win32' ? describe : xdescribe; +const onWindowsWithShortNames = shortNamesSupportedSync() ? describe : xdescribe; + +// Save the real process.platform +const realPlatform = Object.getOwnPropertyDescriptor(process, 'platform')!; + +describe('Cross Platform', () => { + describe('path', () => { + onWindows('on Windows', () => { + onWindowsWithShortNames('when 8.3 is supported', () => { + beforeAll(() => { + // Cleanup + try { + // If leftover artifacts were found, get rid of them + del.sync(tmpTestFolder); + } catch (ex) { + // Do nothing; if `rmdir` failed, let the `mkdir` below throw the error + } + + fs.mkdirSync(tmpTestFolder); + fs.mkdirSync(path.resolve(tmpTestFolder, longFolderName)); + fs.writeFileSync(path.resolve(tmpTestFolder, longFolderName, longFileName), ''); + fs.symlinkSync( + path.resolve(tmpTestFolder, longFolderName), + path.resolve(tmpTestFolder, longSymlinkName), + 'junction' + ); + }); + + afterAll(() => { + try { + del.sync(tmpTestFolder); + } catch (ex) { + // Do nothing + } + }); + + it('can synchronously extract full name of a folder', () => { + const name = path.basename( + resolveToFullPathSync(path.resolve(tmpTestFolder, shortFolderName)) + ); + expect(name).toBe(longFolderName); + }); + + it('can synchronously extract full name of a file', () => { + const name = path.basename( + resolveToFullNameSync(path.resolve(tmpTestFolder, shortFolderName, shortFileName)) + ); + expect(name).toBe(longFileName); + }); + + it('can synchronously extract short name of a folder', () => { + const name = path.basename( + resolveToShortPathSync(path.resolve(tmpTestFolder, longFolderName)) + ); + expect(name).toBe(shortFolderName); + }); + + it('can synchronously extract short name of a file', () => { + const name = path.basename( + resolveToShortNameSync(path.resolve(tmpTestFolder, longFolderName, longFileName)) + ); + expect(name).toBe(shortFileName); + }); + + it('can synchronously extract full name of a symbolic link', () => { + const name = path.basename(realPathSync(path.resolve(tmpTestFolder, longSymlinkName))); + expect(name).toBe(longFolderName); + }); + + it('can synchronously extract short name of a symbolic link', () => { + const name = path.basename( + realShortPathSync(path.resolve(tmpTestFolder, longSymlinkName)) + ); + expect(name).toBe(shortFolderName); + }); + }); + }); + + describe('on platforms other than Windows', () => { + let mockPathNormalize: jest.SpyInstance; + let mockPathResolve: jest.SpyInstance; + let mockFSRealPathSync: jest.SpyInstance; + + beforeAll(() => { + Object.defineProperty(process, 'platform', { + ...Object.getOwnPropertyDescriptor(process, 'property'), + value: 'linux', + }); + + mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyPOSIXPath); + mockPathResolve = jest.spyOn(path, 'resolve').mockReturnValue(dummyPOSIXPath); + mockFSRealPathSync = jest + .spyOn(fs, 'realpathSync') + .mockReturnValue(dummyPOSIXPath) as jest.SpyInstance; + }); + + afterAll(() => { + // Restore the real property value after each test + Object.defineProperty(process, 'platform', realPlatform); + mockPathNormalize.mockRestore(); + mockPathResolve.mockRestore(); + mockFSRealPathSync.mockRestore(); + }); + + it('all short and full name methods return just the normalized paths', () => { + expect(shortNamesSupportedSync()).toBe(false); + expect(resolveToFullPathSync(dummyPOSIXPath)).toBe(dummyPOSIXPath); + expect(resolveToShortPathSync(dummyPOSIXPath)).toBe(dummyPOSIXPath); + }); + }); + + describe('standardize', () => { + describe('on Windows', () => { + let mockPathNormalize: jest.SpyInstance; + + beforeAll(() => { + Object.defineProperty(process, 'platform', { + ...Object.getOwnPropertyDescriptor(process, 'property'), + value: 'win32', + }); + + mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyWindowsPath); + }); + + afterAll(() => { + // Restore the real property value after each test + Object.defineProperty(process, 'platform', realPlatform); + mockPathNormalize.mockRestore(); + }); + + it('produces a path in native format', () => { + expect(standardize(dummyWindowsPath, false, false)).toMatchSnapshot(); + }); + + it('produces a path in native format even for POSIX input', () => { + expect(standardize(dummyWindowsPOSIXPath, false, false)).toMatchSnapshot(); + }); + + it('produces a path in native format with escaped backslashes', () => { + expect(standardize(dummyWindowsPath, false, true)).toMatchSnapshot(); + }); + + it('produces a path in POSIX format', () => { + expect(standardize(dummyWindowsPath)).toMatchSnapshot(); + }); + }); + + describe('on POSIX-compatible platforms', () => { + let mockPathNormalize: jest.SpyInstance; + + beforeAll(() => { + Object.defineProperty(process, 'platform', { + ...Object.getOwnPropertyDescriptor(process, 'property'), + value: 'linux', + }); + + mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyPOSIXPath); + }); + + afterAll(() => { + // Restore the real property value after each test + Object.defineProperty(process, 'platform', realPlatform); + mockPathNormalize.mockRestore(); + }); + + it('produces a path in POSIX format', () => { + expect(standardize(dummyPOSIXPath)).toMatchSnapshot(); + }); + + it('ignores additional parameters', () => { + expect(standardize(dummyPOSIXPath, false, true)).toMatchSnapshot(); + }); + }); + }); + }); +}); diff --git a/packages/osd-cross-platform/src/path.ts b/packages/osd-cross-platform/src/path.ts index 7e37443f1bca..95f8fc1daeb4 100644 --- a/packages/osd-cross-platform/src/path.ts +++ b/packages/osd-cross-platform/src/path.ts @@ -3,28 +3,160 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { normalize } from 'path'; +import { execSync } from 'child_process'; +import { basename, normalize, resolve } from 'path'; +import { + realpathSync as nativeRealpathSync, + openSync, + closeSync, + existsSync, + unlinkSync, +} from 'fs'; + +export const NAMESPACE_PREFIX = process.platform === 'win32' ? '\\\\?\\' : ''; /** * Get a standardized reference to a path * @param {string} path - the path to standardize * @param {boolean} [usePosix=true] - produce a posix reference * @param {boolean} [escapedBackslashes=true] - on Windows, double-backslash the reference - * @internal + * @param {boolean} [returnUNC=false] - produce an extended reference */ export const standardize = ( path: string, usePosix: boolean = true, - escapedBackslashes: boolean = true + escapedBackslashes: boolean = true, + returnUNC: boolean = false ) => { - /* Force os-dependant separators - * path.posix.normalize doesn't convert backslashes to slashes on Windows so we manually force it afterwards - */ + // Force os-dependant separators const normal = normalize(path); // Filter out in-browser executions as well as non-windows ones - if (process?.platform !== 'win32') return normal; + if (process.platform !== 'win32') return normal; if (usePosix) return normal.replace(/\\/g, '/'); - return escapedBackslashes ? normal.replace(/\\/g, '\\\\') : normal; + else if (escapedBackslashes) return normal.replace(/\\/g, '\\\\'); + else if (returnUNC) return '\\\\?\\' + normal; + return normal; +}; + +/** + * Windows-only function that uses PowerShell to calculate the full path + * @param {string} path + * @private + */ +const getFullPathSync = (path: string) => { + if (process.platform !== 'win32') return path; + + try { + const psOutput = execSync(`powershell "(Get-Item -LiteralPath '${path}').FullName"`, { + encoding: 'utf8', + }); + const fullName = psOutput && psOutput.trim && psOutput.trim(); + + // Make sure we got something back + if (fullName && fullName.length > 2) return fullName; + } catch (ex) { + // Do nothing + } + + return path; +}; + +/** + * Windows-only function that uses PowerShell and Com Object to calculate the 8.3 path + * @param {string} path + * @private + */ +const getShortPathSync = (path: string) => { + if (process.platform !== 'win32') return path; + + try { + const psOutput = execSync( + `powershell "$FSO = New-Object -ComObject Scripting.FileSystemObject; $O = (Get-Item -LiteralPath '${path}'); if ($O.PSIsContainer) { $FSO.GetFolder($O.FullName).ShortPath } else { $FSO.GetFile($O.FullName).ShortPath }"`, + { + encoding: 'utf8', + } + ); + const shortPath = psOutput && psOutput.trim && psOutput.trim(); + + // Make sure we got something back + if (shortPath && shortPath.length > 2) return shortPath; + } catch (ex) { + // Do nothing + } + + return path; }; + +/** + * Checks if Windows 8.3 short names are supported on the volume of the given path + * @param {string} [path='.'] - the path to examine + */ +export const shortNamesSupportedSync = (path: string = '.') => { + if (process.platform !== 'win32') return false; + + const testFileName = '.___osd-cross-platform-test.file'; + const file = resolve(path, testFileName); + + // Create a test file if it doesn't exist + if (!existsSync(file)) closeSync(openSync(file, 'w')); + + // If the returned value's basename is not the same as the requested file name, it must be a short name + const foundShortName = basename(getShortPathSync(file)) !== testFileName; + + // Cleanup + unlinkSync(file); + + return foundShortName; +}; + +/** + * @borrows shortNamesSupportedSync + */ +export const shortNameSupportedSync = shortNamesSupportedSync; + +/** + * Get the full pathname + * @param {string} path - the path to resolve + */ +export const resolveToFullPathSync = (path: string) => getFullPathSync(resolve(path)); + +/** + * @borrows resolveToFullPathSync + */ +export const resolveToFullNameSync = resolveToFullPathSync; + +/** + * Get the short pathname + * @param {string} path - the path to resolve + */ +export const resolveToShortPathSync = (path: string) => getShortPathSync(resolve(path)); + +/** + * @borrows resolveToShortPathSync + */ +export const resolveToShortNameSync = resolveToShortPathSync; + +/** + * Get the canonical pathname + * @param {string} path - the path to resolve + */ +export const realPathSync = (path: string) => getFullPathSync(nativeRealpathSync(path, 'utf8')); + +/** + * @borrows realPathSync + */ +export const realpathSync = realPathSync; + +/** + * Get the canonical pathname + * @param {string} path - the path to resolve + */ +export const realShortPathSync = (path: string) => + getShortPathSync(nativeRealpathSync(path, 'utf8')); + +/** + * @borrows realShortPathSync + */ +export const realshortpathSync = realShortPathSync; diff --git a/packages/osd-cross-platform/src/process.ts b/packages/osd-cross-platform/src/process.ts index fa593c943687..ade19cca5615 100644 --- a/packages/osd-cross-platform/src/process.ts +++ b/packages/osd-cross-platform/src/process.ts @@ -3,20 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { execSync } from 'child_process'; +import { resolveToFullPathSync, standardize } from './path'; -let workingDir = process.cwd(); - -if (process.platform === 'win32') { - try { - const pathFullName = execSync('powershell "(Get-Item -LiteralPath $pwd).FullName"', { - cwd: workingDir, - encoding: 'utf8', - })?.trim?.(); - if (pathFullName?.length > 2) workingDir = pathFullName; - } catch (ex) { - // Do nothing - } -} +/** + * The full pathname of the working directory of the process + * @constant + * @type {string} + */ +export const PROCESS_WORKING_DIR: string = resolveToFullPathSync(process.cwd()); -export const PROCESS_WORKING_DIR = workingDir; +/** + * The full pathname of the working directory of the process, in POSIX format + * @constant + * @type {string} + */ +export const PROCESS_POSIX_WORKING_DIR: string = standardize(PROCESS_WORKING_DIR); diff --git a/packages/osd-utils/src/repo_root.ts b/packages/osd-cross-platform/src/repo_root.ts similarity index 66% rename from packages/osd-utils/src/repo_root.ts rename to packages/osd-cross-platform/src/repo_root.ts index c12004442f3c..f4f914a5c633 100644 --- a/packages/osd-utils/src/repo_root.ts +++ b/packages/osd-cross-platform/src/repo_root.ts @@ -30,15 +30,15 @@ * GitHub history for details. */ -import Path from 'path'; -import Fs from 'fs'; +import { resolve, parse, dirname, isAbsolute, relative } from 'path'; import loadJsonFile from 'load-json-file'; +import { resolveToFullPathSync, resolveToShortPathSync, realPathSync } from './path'; const readOpenSearchDashboardsPkgJson = (dir: string) => { try { - const path = Path.resolve(dir, 'package.json'); - const json = loadJsonFile.sync(path); + const path = resolve(dir, 'package.json'); + const json = loadJsonFile.sync(path) as { [key: string]: any }; if ( json && typeof json === 'object' && @@ -60,8 +60,8 @@ const findOpenSearchDashboardsPackageJson = () => { // search for the opensearch-dashboards directory, since this file is moved around it might // not be where we think but should always be a relatively close parent // of this directory - const startDir = Fs.realpathSync(__dirname); - const { root: rootDir } = Path.parse(startDir); + const startDir = realPathSync(__dirname); + const { root: rootDir } = parse(startDir); let cursor = startDir; while (true) { const opensearchDashboardsPkgJson = readOpenSearchDashboardsPkgJson(cursor); @@ -75,7 +75,7 @@ const findOpenSearchDashboardsPackageJson = () => { }; } - const parent = Path.dirname(cursor); + const parent = dirname(cursor); if (parent === rootDir) { throw new Error(`unable to find opensearch-dashboards directory from ${startDir}`); } @@ -88,5 +88,25 @@ const { opensearchDashboardsPkgJson, } = findOpenSearchDashboardsPackageJson(); -export const REPO_ROOT = opensearchDashboardsDir; +export const REPO_ROOT = resolveToFullPathSync(opensearchDashboardsDir); +export const REPO_ROOT_8_3 = resolveToShortPathSync(opensearchDashboardsDir); export const UPSTREAM_BRANCH = opensearchDashboardsPkgJson.branch; + +export const getMatchingRoot = (path: string, rootPaths: string | string[]) => { + const rootPathsArray = Array.isArray(rootPaths) ? rootPaths : [rootPaths]; + + // We can only find the appropriate root if an absolute path was given + if (path && isAbsolute(path)) { + // Return the matching root if one is found or return `undefined` + return rootPathsArray.find((root) => path.startsWith(root)); + } + + return undefined; +}; + +export const getRepoRoot = (path: string) => getMatchingRoot(path, [REPO_ROOT, REPO_ROOT_8_3]); + +export const relativeToRepoRoot = (path: string) => { + const repoRoot = getRepoRoot(path); + return repoRoot ? relative(repoRoot, path) : null; +}; diff --git a/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts b/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts index 05172f5ac330..5d4b638819e8 100644 --- a/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts +++ b/packages/osd-dev-utils/src/serializers/absolute_path_serializer.ts @@ -30,14 +30,25 @@ * GitHub history for details. */ -import { REPO_ROOT } from '@osd/utils'; +// Not importing from @osd/cross-platform to allow some complicated tests to run: suite_tracker.test.ts +import { REPO_ROOT, REPO_ROOT_8_3 } from '@osd/utils'; export function createAbsolutePathSerializer( - rootPath: string = REPO_ROOT, + rootPath: string | string[] = [REPO_ROOT, REPO_ROOT_8_3], replacement = '' ) { + const rootPaths = Array.isArray(rootPath) ? rootPath : [rootPath]; + return { - test: (value: any) => typeof value === 'string' && value.startsWith(rootPath), - serialize: (value: string) => value.replace(rootPath, replacement).replace(/\\/g, '/'), + test: (value: any) => + typeof value === 'string' && rootPaths.some((path) => value.startsWith(path)), + serialize: (value: string) => + rootPaths + // Replace all instances of `rootPaths` found at the beginning of the `value` + .reduce( + (result, path) => (result.startsWith(path) ? result.replace(path, replacement) : result), + value + ) + .replace(/\\/g, '/'), }; } diff --git a/packages/osd-eslint-plugin-eslint/rules/no_restricted_paths.js b/packages/osd-eslint-plugin-eslint/rules/no_restricted_paths.js index d3ac19fa6d19..e6139d29d592 100644 --- a/packages/osd-eslint-plugin-eslint/rules/no_restricted_paths.js +++ b/packages/osd-eslint-plugin-eslint/rules/no_restricted_paths.js @@ -47,7 +47,7 @@ function traverseToTopFolder(src, pattern) { const srcIdx = src.lastIndexOf(path.sep); src = src.slice(0, srcIdx); } - return src; + return src.replace(/\\/g, '/'); } function isSameFolderOrDescendent(src, imported, pattern) { diff --git a/packages/osd-optimizer/src/node/cache.ts b/packages/osd-optimizer/src/node/cache.ts index ff81613382ea..44cf3d7740c0 100644 --- a/packages/osd-optimizer/src/node/cache.ts +++ b/packages/osd-optimizer/src/node/cache.ts @@ -34,7 +34,8 @@ import Path from 'path'; // @ts-expect-error no types available import * as LmdbStore from 'lmdb-store'; -import { REPO_ROOT, UPSTREAM_BRANCH } from '@osd/dev-utils'; +import { REPO_ROOT, REPO_ROOT_8_3, UPSTREAM_BRANCH } from '@osd/dev-utils'; +import { getMatchingRoot } from '@osd/cross-platform'; // This is to enable parallel jobs on CI. const CACHE_DIR = process.env.CACHE_DIR @@ -139,7 +140,19 @@ export class Cache { } private getKey(path: string) { - return `${this.prefix}${path}`; + const resolvedPath = Path.resolve(path); + /* Try to find the root that is the parent to `path` so we can make a nimble + * and unique key based on the relative path. If A root was not found, just + * use any of the roots; the key would just be long. + */ + const pathRoot = getMatchingRoot(resolvedPath, [REPO_ROOT, REPO_ROOT_8_3]) || REPO_ROOT; + + const normalizedPath = + Path.sep !== '/' + ? Path.relative(pathRoot, resolvedPath).split(Path.sep).join('/') + : Path.relative(pathRoot, resolvedPath); + + return `${this.prefix}${normalizedPath}`; } private async pruneOldKeys() { diff --git a/packages/osd-optimizer/src/optimizer/cache_keys.ts b/packages/osd-optimizer/src/optimizer/cache_keys.ts index 1edc7c3c9c12..4d5d62530d1b 100644 --- a/packages/osd-optimizer/src/optimizer/cache_keys.ts +++ b/packages/osd-optimizer/src/optimizer/cache_keys.ts @@ -36,7 +36,7 @@ import { promisify } from 'util'; import Chalk from 'chalk'; import execa from 'execa'; -import { REPO_ROOT } from '@osd/utils'; +import { relativeToRepoRoot, REPO_ROOT } from '@osd/cross-platform'; import stripAnsi from 'strip-ansi'; import jestDiff from 'jest-diff'; @@ -48,7 +48,7 @@ import { getChanges } from './get_changes'; import { OptimizerConfig } from './optimizer_config'; const OPTIMIZER_DIR = Path.dirname(require.resolve('../../package.json')); -const RELATIVE_DIR = Path.relative(REPO_ROOT, OPTIMIZER_DIR); +const RELATIVE_DIR = relativeToRepoRoot(OPTIMIZER_DIR)!; export function diffCacheKey(expected?: unknown, actual?: unknown) { const expectedJson = jsonStable(expected, { diff --git a/packages/osd-plugin-generator/src/cli.ts b/packages/osd-plugin-generator/src/cli.ts index 51b6ddc7ca20..e0be64922e2e 100644 --- a/packages/osd-plugin-generator/src/cli.ts +++ b/packages/osd-plugin-generator/src/cli.ts @@ -34,7 +34,7 @@ import Path from 'path'; import Fs from 'fs'; import execa from 'execa'; -import { REPO_ROOT } from '@osd/utils'; +import { PROCESS_WORKING_DIR, REPO_ROOT } from '@osd/cross-platform'; import { run, createFailError, createFlagError } from '@osd/dev-utils'; import { snakeCase } from './casing'; @@ -80,7 +80,7 @@ export function runCli() { } log.success( - `🎉\n\nYour plugin has been created in ${Path.relative(process.cwd(), outputDir)}\n` + `🎉\n\nYour plugin has been created in ${Path.relative(PROCESS_WORKING_DIR, outputDir)}\n` ); }, { diff --git a/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts b/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts index 3ed08083ec4e..3091a7567743 100644 --- a/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts +++ b/packages/osd-plugin-generator/src/integration_tests/generate_plugin.test.ts @@ -34,8 +34,8 @@ import Path from 'path'; import del from 'del'; import execa from 'execa'; -import { standardize } from '@osd/cross-platform'; -import { REPO_ROOT, createAbsolutePathSerializer } from '@osd/dev-utils'; +import { standardize, REPO_ROOT } from '@osd/cross-platform'; +import { createAbsolutePathSerializer } from '@osd/dev-utils'; import globby from 'globby'; // Has to be a posix reference because it is used to generate glob patterns diff --git a/packages/osd-plugin-helpers/package.json b/packages/osd-plugin-helpers/package.json index 2484b66230e7..7ee45578a63b 100644 --- a/packages/osd-plugin-helpers/package.json +++ b/packages/osd-plugin-helpers/package.json @@ -12,7 +12,7 @@ "plugin-helpers": "bin/plugin-helpers.js" }, "scripts": { - "osd:bootstrap": "node ../../scripts/remove.js && tsc", + "osd:bootstrap": "node ../../scripts/remove.js target && tsc", "osd:watch": "tsc --watch" }, "dependencies": { diff --git a/packages/osd-plugin-helpers/src/integration_tests/build.test.ts b/packages/osd-plugin-helpers/src/integration_tests/build.test.ts index 1d70db3d18a2..d1cae4f76a26 100644 --- a/packages/osd-plugin-helpers/src/integration_tests/build.test.ts +++ b/packages/osd-plugin-helpers/src/integration_tests/build.test.ts @@ -34,8 +34,8 @@ import Path from 'path'; import Fs from 'fs'; import execa from 'execa'; -import { standardize } from '@osd/cross-platform'; -import { REPO_ROOT, createStripAnsiSerializer, createReplaceSerializer } from '@osd/dev-utils'; +import { REPO_ROOT, standardize } from '@osd/cross-platform'; +import { createStripAnsiSerializer, createReplaceSerializer } from '@osd/dev-utils'; import extract from 'extract-zip'; import del from 'del'; import globby from 'globby'; @@ -109,7 +109,8 @@ it('builds a generated plugin into a viable archive', async () => { info copying assets from \`public/assets\` to build info copying server source into the build and converting with babel info running yarn to install dependencies - info compressing plugin into [fooTestPlugin-1.0.0.zip]" + info compressing plugin into [fooTestPlugin-1.0.0.zip] + info cleaning up compression temporary artifacts" `); await extract(PLUGIN_ARCHIVE, { dir: TMP_DIR }, () => {}); @@ -198,7 +199,8 @@ it('builds a non-semver generated plugin into a viable archive', async () => { info copying assets from \`public/assets\` to build info copying server source into the build and converting with babel info running yarn to install dependencies - info compressing plugin into [fooTestPlugin-1.0.0.x.zip]" + info compressing plugin into [fooTestPlugin-1.0.0.x.zip] + info cleaning up compression temporary artifacts" `); await extract(PLUGIN_ARCHIVE_X, { dir: TMP_DIR }, () => {}); diff --git a/packages/osd-plugin-helpers/src/tasks/create_archive.ts b/packages/osd-plugin-helpers/src/tasks/create_archive.ts index dc44cfb3f38d..f6c926a05ba1 100644 --- a/packages/osd-plugin-helpers/src/tasks/create_archive.ts +++ b/packages/osd-plugin-helpers/src/tasks/create_archive.ts @@ -64,6 +64,7 @@ export async function createArchive({ opensearchDashboardsVersion, plugin, log } vfs.dest(buildDir) ); + log.info(`cleaning up compression temporary artifacts`); // delete the files that were zipped - await del(Path.resolve(buildDir, 'opensearch-dashboards')); + await del(Path.resolve(buildDir, 'opensearch-dashboards'), { cwd: buildDir }); } diff --git a/packages/osd-pm/src/utils/projects.test.ts b/packages/osd-pm/src/utils/projects.test.ts index 769f87e20610..f063e715288b 100644 --- a/packages/osd-pm/src/utils/projects.test.ts +++ b/packages/osd-pm/src/utils/projects.test.ts @@ -45,12 +45,16 @@ import { ProjectMap, topologicallyBatchProjects, } from './projects'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; const rootPath = resolve(__dirname, '__fixtures__/opensearch-dashboards'); const rootPlugins = join(rootPath, 'plugins'); describe('#getProjects', () => { beforeAll(async () => { + // Make sure we start clean + await del(rootPlugins, { cwd: PROCESS_WORKING_DIR }); + await promisify(mkdir)(rootPlugins); await promisify(symlink)( @@ -60,7 +64,7 @@ describe('#getProjects', () => { ); }); - afterAll(async () => await del(rootPlugins)); + afterAll(async () => await del(rootPlugins, { cwd: PROCESS_WORKING_DIR })); test('find all packages in the packages directory', async () => { const projects = await getProjects(rootPath, ['packages/*']); diff --git a/packages/osd-utils/src/index.ts b/packages/osd-utils/src/index.ts index b5cfa8c478d9..3a921ea8911f 100644 --- a/packages/osd-utils/src/index.ts +++ b/packages/osd-utils/src/index.ts @@ -32,4 +32,4 @@ export * from './package_json'; export * from './path'; -export * from './repo_root'; +export { REPO_ROOT, REPO_ROOT_8_3, UPSTREAM_BRANCH } from '@osd/cross-platform'; diff --git a/packages/osd-utils/src/package_json/index.ts b/packages/osd-utils/src/package_json/index.ts index 073a80da3e10..f31bb9e137e9 100644 --- a/packages/osd-utils/src/package_json/index.ts +++ b/packages/osd-utils/src/package_json/index.ts @@ -31,7 +31,7 @@ */ import { dirname, resolve } from 'path'; -import { REPO_ROOT } from '../repo_root'; +import { REPO_ROOT } from '@osd/cross-platform'; export const opensearchDashboardsPackageJSON = { __filename: resolve(REPO_ROOT, 'package.json'), diff --git a/packages/osd-utils/src/path/index.ts b/packages/osd-utils/src/path/index.ts index 63837ece4f0f..aeed9d620602 100644 --- a/packages/osd-utils/src/path/index.ts +++ b/packages/osd-utils/src/path/index.ts @@ -33,7 +33,7 @@ import { join } from 'path'; import { accessSync, constants } from 'fs'; import { TypeOf, schema } from '@osd/config-schema'; -import { REPO_ROOT } from '../repo_root'; +import { REPO_ROOT } from '@osd/cross-platform'; const isString = (v: any): v is string => typeof v === 'string'; diff --git a/src/cli_plugin/install/download.test.js b/src/cli_plugin/install/download.test.js index b07531018774..438305bcb666 100644 --- a/src/cli_plugin/install/download.test.js +++ b/src/cli_plugin/install/download.test.js @@ -42,6 +42,7 @@ import del from 'del'; import { Logger } from '../lib/logger'; import { UnsupportedProtocolError } from '../lib/errors'; import { download, _downloadSingle, _getFilePath, _checkFilePathDeprecation } from './download'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; describe('opensearchDashboards cli', function () { describe('plugin downloader', function () { @@ -72,17 +73,17 @@ describe('opensearchDashboards cli', function () { throw new Error('expected the promise to reject'); } - beforeEach(function () { + beforeEach(async () => { sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); - del.sync(testWorkingPath); + await del(testWorkingPath, { cwd: PROCESS_WORKING_DIR }); Fs.mkdirSync(testWorkingPath, { recursive: true }); }); - afterEach(function () { + afterEach(async () => { logger.log.restore(); logger.error.restore(); - del.sync(testWorkingPath); + del(testWorkingPath, { cwd: PROCESS_WORKING_DIR }); }); describe('_downloadSingle', function () { diff --git a/src/cli_plugin/install/opensearch_dashboards.test.js b/src/cli_plugin/install/opensearch_dashboards.test.js index 4b518ff79c0e..2bf92158600c 100644 --- a/src/cli_plugin/install/opensearch_dashboards.test.js +++ b/src/cli_plugin/install/opensearch_dashboards.test.js @@ -38,6 +38,7 @@ import del from 'del'; import { existingInstall, assertVersion } from './opensearch_dashboards'; import { Logger } from '../lib/logger'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; jest.spyOn(fs, 'statSync'); @@ -64,17 +65,17 @@ describe('opensearchDashboards cli', function () { const logger = new Logger(settings); describe('assertVersion', function () { - beforeEach(function () { - del.sync(testWorkingPath); + beforeEach(async () => { + await del(testWorkingPath, { cwd: PROCESS_WORKING_DIR }); fs.mkdirSync(testWorkingPath, { recursive: true }); sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); }); - afterEach(function () { + afterEach(async () => { logger.log.restore(); logger.error.restore(); - del.sync(testWorkingPath); + await del(testWorkingPath, { cwd: PROCESS_WORKING_DIR }); }); it('should succeed with exact match', function () { diff --git a/src/cli_plugin/install/pack.test.js b/src/cli_plugin/install/pack.test.js index fd35178e0f8a..488cb0402f5e 100644 --- a/src/cli_plugin/install/pack.test.js +++ b/src/cli_plugin/install/pack.test.js @@ -40,6 +40,7 @@ import del from 'del'; import { Logger } from '../lib/logger'; import { extract, getPackData } from './pack'; import { _downloadSingle } from './download'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; describe('opensearchDashboards cli', function () { describe('pack', function () { @@ -78,7 +79,7 @@ describe('opensearchDashboards cli', function () { logger.log.restore(); logger.error.restore(); - await del(workingPathRoot); + await del(workingPathRoot, { cwd: PROCESS_WORKING_DIR }); }); function copyReplyFile(filename) { diff --git a/src/cli_plugin/list/list.test.js b/src/cli_plugin/list/list.test.js index 62bbca8be9d9..ee62ba90cc0e 100644 --- a/src/cli_plugin/list/list.test.js +++ b/src/cli_plugin/list/list.test.js @@ -36,6 +36,7 @@ import { writeFileSync, mkdirSync } from 'fs'; import del from 'del'; import { list } from './list'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; function createPlugin(name, version, pluginBaseDir) { const pluginDir = join(pluginBaseDir, name); @@ -63,14 +64,14 @@ describe('opensearchDashboards cli', function () { describe('plugin lister', function () { const pluginDir = join(__dirname, '.test.data.list'); - beforeEach(function () { + beforeEach(async () => { logger.messages.length = 0; - del.sync(pluginDir); + await del(pluginDir, { cwd: PROCESS_WORKING_DIR }); mkdirSync(pluginDir, { recursive: true }); }); - afterEach(function () { - del.sync(pluginDir); + afterEach(async () => { + await del(pluginDir, { cwd: PROCESS_WORKING_DIR }); }); it('list all of the folders in the plugin folder, ignoring dot prefixed plugins and regular files', function () { diff --git a/src/cli_plugin/remove/remove.test.js b/src/cli_plugin/remove/remove.test.js index e03e4a1da0d4..d5e30ef29367 100644 --- a/src/cli_plugin/remove/remove.test.js +++ b/src/cli_plugin/remove/remove.test.js @@ -39,6 +39,7 @@ import del from 'del'; import { Logger } from '../lib/logger'; import { remove } from './remove'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; describe('opensearchDashboards cli', function () { describe('plugin remover', function () { @@ -48,20 +49,20 @@ describe('opensearchDashboards cli', function () { const settings = { pluginDir }; - beforeEach(function () { + beforeEach(async () => { processExitStub = sinon.stub(process, 'exit'); logger = new Logger(settings); sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); - del.sync(pluginDir); + await del(pluginDir, { cwd: PROCESS_WORKING_DIR }); mkdirSync(pluginDir, { recursive: true }); }); - afterEach(function () { + afterEach(async () => { processExitStub.restore(); logger.log.restore(); logger.error.restore(); - del.sync(pluginDir); + await del(pluginDir, { cwd: PROCESS_WORKING_DIR }); }); it('throw an error if the plugin is not installed.', function () { diff --git a/src/core/server/plugins/discovery/plugins_discovery.test.ts b/src/core/server/plugins/discovery/plugins_discovery.test.ts index 9e1186744e6f..9cf958687e02 100644 --- a/src/core/server/plugins/discovery/plugins_discovery.test.ts +++ b/src/core/server/plugins/discovery/plugins_discovery.test.ts @@ -44,9 +44,7 @@ import { PluginsConfig, PluginsConfigType, config } from '../plugins_config'; import type { InstanceInfo } from '../plugin_context'; import { discover } from './plugins_discovery'; import { CoreContext } from '../../core_context'; - -const OPENSEARCH_DASHBOARDS_ROOT = process.cwd(); -const EXTENDED_PATH_PREFIX = process.platform === 'win32' ? '\\\\?\\' : ''; +import { PROCESS_WORKING_DIR, standardize } from '@osd/cross-platform'; const Plugins = { invalid: () => ({ @@ -89,13 +87,7 @@ const packageMock = { }; const manifestPath = (...pluginPath: string[]) => - resolve( - OPENSEARCH_DASHBOARDS_ROOT, - 'src', - 'plugins', - ...pluginPath, - 'opensearch_dashboards.json' - ); + resolve(PROCESS_WORKING_DIR, 'src', 'plugins', ...pluginPath, 'opensearch_dashboards.json'); describe('plugins discovery system', () => { let logger: ReturnType; @@ -159,8 +151,8 @@ describe('plugins discovery system', () => { mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_a`]: Plugins.valid('pluginA'), - [`${OPENSEARCH_DASHBOARDS_ROOT}/plugins/plugin_b`]: Plugins.valid('pluginB'), + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_a`]: Plugins.valid('pluginA'), + [`${PROCESS_WORKING_DIR}/plugins/plugin_b`]: Plugins.valid('pluginB'), }, { createCwd: false } ); @@ -181,10 +173,10 @@ describe('plugins discovery system', () => { mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_a`]: Plugins.invalid(), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_b`]: Plugins.incomplete(), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_c`]: Plugins.incompatible(), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_ad`]: Plugins.missingManifest(), + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_a`]: Plugins.invalid(), + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_b`]: Plugins.incomplete(), + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_c`]: Plugins.incompatible(), + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_ad`]: Plugins.missingManifest(), }, { createCwd: false } ); @@ -223,7 +215,7 @@ describe('plugins discovery system', () => { mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins`]: mockFs.directory({ + [`${PROCESS_WORKING_DIR}/src/plugins`]: mockFs.directory({ mode: 0, // 0000 items: { plugin_a: Plugins.valid('pluginA'), @@ -243,10 +235,15 @@ describe('plugins discovery system', () => { ) .toPromise(); - const srcPluginsPath = resolve(OPENSEARCH_DASHBOARDS_ROOT, 'src', 'plugins'); + const srcPluginsPath = resolve(PROCESS_WORKING_DIR, 'src', 'plugins'); expect(errors).toEqual( expect.arrayContaining([ - `Error: EACCES, permission denied '${EXTENDED_PATH_PREFIX}${srcPluginsPath}' (invalid-search-path, ${srcPluginsPath})`, + `Error: EACCES, permission denied '${standardize( + srcPluginsPath, + false, + false, + true + )}' (invalid-search-path, ${srcPluginsPath})`, ]) ); }); @@ -260,7 +257,7 @@ describe('plugins discovery system', () => { mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_a`]: { + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_a`]: { ...Plugins.inaccessibleManifest(), nested_plugin: Plugins.valid('nestedPlugin'), }, @@ -281,7 +278,12 @@ describe('plugins discovery system', () => { const errorPath = manifestPath('plugin_a'); expect(errors).toEqual( expect.arrayContaining([ - `Error: EACCES, permission denied '${EXTENDED_PATH_PREFIX}${errorPath}' (missing-manifest, ${errorPath})`, + `Error: EACCES, permission denied '${standardize( + errorPath, + false, + false, + true + )}' (missing-manifest, ${errorPath})`, ]) ); }); @@ -295,10 +297,10 @@ describe('plugins discovery system', () => { mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_a`]: Plugins.valid('pluginA'), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/plugin_b`]: Plugins.valid('pluginB'), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/sub2/plugin_c`]: Plugins.valid('pluginC'), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/sub2/plugin_d`]: Plugins.incomplete(), + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_a`]: Plugins.valid('pluginA'), + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/plugin_b`]: Plugins.valid('pluginB'), + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/sub2/plugin_c`]: Plugins.valid('pluginC'), + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/sub2/plugin_d`]: Plugins.incomplete(), }, { createCwd: false } ); @@ -332,7 +334,7 @@ describe('plugins discovery system', () => { mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/plugin_a`]: { + [`${PROCESS_WORKING_DIR}/src/plugins/plugin_a`]: { ...Plugins.valid('pluginA'), nested_plugin: Plugins.valid('nestedPlugin'), }, @@ -351,18 +353,14 @@ describe('plugins discovery system', () => { mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/plugin`]: Plugins.valid('plugin1'), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/sub2/plugin`]: Plugins.valid('plugin2'), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/sub2/sub3/plugin`]: Plugins.valid( - 'plugin3' - ), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/sub2/sub3/sub4/plugin`]: Plugins.valid( - 'plugin4' - ), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/sub2/sub3/sub4/sub5/plugin`]: Plugins.valid( + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/plugin`]: Plugins.valid('plugin1'), + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/sub2/plugin`]: Plugins.valid('plugin2'), + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/sub2/sub3/plugin`]: Plugins.valid('plugin3'), + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/sub2/sub3/sub4/plugin`]: Plugins.valid('plugin4'), + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/sub2/sub3/sub4/sub5/plugin`]: Plugins.valid( 'plugin5' ), - [`${OPENSEARCH_DASHBOARDS_ROOT}/src/plugins/sub1/sub2/sub3/sub4/sub5/sub6/plugin`]: Plugins.valid( + [`${PROCESS_WORKING_DIR}/src/plugins/sub1/sub2/sub3/sub4/sub5/sub6/plugin`]: Plugins.valid( 'plugin6' ), }, @@ -381,11 +379,11 @@ describe('plugins discovery system', () => { it('works with symlinks', async () => { const { plugin$ } = discover(new PluginsConfig(pluginConfig, env), coreContext, instanceInfo); - const pluginFolder = resolve(OPENSEARCH_DASHBOARDS_ROOT, '..', 'ext-plugins'); + const pluginFolder = resolve(PROCESS_WORKING_DIR, '..', 'ext-plugins'); mockFs( { - [`${OPENSEARCH_DASHBOARDS_ROOT}/plugins`]: mockFs.symlink({ + [`${PROCESS_WORKING_DIR}/plugins`]: mockFs.symlink({ path: '../ext-plugins', }), [pluginFolder]: { diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 1c6f729600f3..423ce7f90588 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -35,7 +35,8 @@ import { mockDiscover, mockPackage } from './plugins_service.test.mocks'; import { resolve, posix } from 'path'; import { BehaviorSubject, from } from 'rxjs'; import { schema } from '@osd/config-schema'; -import { createAbsolutePathSerializer, REPO_ROOT } from '@osd/dev-utils'; +import { getRepoRoot } from '@osd/cross-platform'; +import { createAbsolutePathSerializer } from '@osd/dev-utils'; import { ConfigPath, ConfigService, Env } from '../config'; import { rawConfigServiceMock, getEnvOptions } from '../config/mocks'; @@ -129,7 +130,10 @@ describe('PluginsService', () => { }; coreId = Symbol('core'); - env = Env.createDefault(REPO_ROOT, getEnvOptions()); + /* Using getRepoRoot to get the appropriate one between REPO_ROOT and REPO_ROOT_8_3; this + * is only a problem on Windows. + */ + env = Env.createDefault(getRepoRoot(resolve('.'))!, getEnvOptions()); config$ = new BehaviorSubject>({ plugins: { initialize: true } }); const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); diff --git a/src/dev/build/lib/config.test.ts b/src/dev/build/lib/config.test.ts index e87ebfa9b533..839ae71c24ef 100644 --- a/src/dev/build/lib/config.test.ts +++ b/src/dev/build/lib/config.test.ts @@ -27,8 +27,7 @@ import { resolve } from 'path'; -import { standardize } from '@osd/cross-platform'; -import { REPO_ROOT } from '@osd/utils'; +import { standardize, REPO_ROOT } from '@osd/cross-platform'; import { createAbsolutePathSerializer } from '@osd/dev-utils'; import pkg from '../../../../package.json'; diff --git a/src/dev/build/lib/integration_tests/fs.test.ts b/src/dev/build/lib/integration_tests/fs.test.ts index 17afe861ab75..4f5b06070cf3 100644 --- a/src/dev/build/lib/integration_tests/fs.test.ts +++ b/src/dev/build/lib/integration_tests/fs.test.ts @@ -31,6 +31,7 @@ import { chmodSync, statSync } from 'fs'; import del from 'del'; import { mkdirp, write, read, getChildPaths, copyAll, getFileHash, untar, gunzip } from '../fs'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; const TMP = resolve(__dirname, '../__tmp__'); const FIXTURES = resolve(__dirname, '../__fixtures__'); @@ -58,13 +59,13 @@ beforeAll(async () => { // clean and recreate TMP directory beforeEach(async () => { - await del(TMP); + await del(TMP, { cwd: PROCESS_WORKING_DIR }); await mkdirp(TMP); }); // cleanup TMP directory afterAll(async () => { - await del(TMP); + await del(TMP, { cwd: PROCESS_WORKING_DIR }); }); describe('mkdirp()', () => { diff --git a/src/dev/build/lib/integration_tests/scan_copy.test.ts b/src/dev/build/lib/integration_tests/scan_copy.test.ts index fffd13981e24..362f7511112f 100644 --- a/src/dev/build/lib/integration_tests/scan_copy.test.ts +++ b/src/dev/build/lib/integration_tests/scan_copy.test.ts @@ -32,6 +32,7 @@ import del from 'del'; import { getChildPaths } from '../fs'; import { scanCopy } from '../scan_copy'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; const IS_WINDOWS = process.platform === 'win32'; const FIXTURES = resolve(__dirname, '../__fixtures__'); @@ -47,7 +48,7 @@ beforeAll(async () => { // cleanup TMP directory afterEach(async () => { - await del(TMP); + await del(TMP, { cwd: PROCESS_WORKING_DIR }); }); it('rejects if source path is not absolute', async () => { diff --git a/src/dev/build/lib/scan_delete.test.ts b/src/dev/build/lib/scan_delete.test.ts index 0abc171aea01..1c87eaab12b3 100644 --- a/src/dev/build/lib/scan_delete.test.ts +++ b/src/dev/build/lib/scan_delete.test.ts @@ -33,12 +33,16 @@ import del from 'del'; // @ts-ignore import { mkdirp, write } from './fs'; import { scanDelete } from './scan_delete'; +import { PROCESS_WORKING_DIR, getRepoRoot } from '@osd/cross-platform'; -const TMP = resolve(__dirname, '__tests__/__tmp__'); +const rootPath = getRepoRoot(__dirname); +const currentDir = rootPath ? resolve('.', relative(rootPath, __dirname)) : __dirname; + +const TMP = resolve(currentDir, '__tests__/__tmp__'); // clean and recreate TMP directory beforeEach(async () => { - await del(TMP); + await del(TMP, { cwd: currentDir }); await mkdirp(resolve(TMP, 'foo/bar/baz')); await mkdirp(resolve(TMP, 'foo/bar/box')); await mkdirp(resolve(TMP, 'a/b/c/d/e')); @@ -47,13 +51,13 @@ beforeEach(async () => { // cleanup TMP directory afterAll(async () => { - await del(TMP); + await del(TMP, { cwd: currentDir }); }); it('requires absolute paths', async () => { await expect( scanDelete({ - directory: relative(process.cwd(), TMP), + directory: relative(PROCESS_WORKING_DIR, TMP), regularExpressions: [], }) ).rejects.toMatchInlineSnapshot( diff --git a/src/dev/file.ts b/src/dev/file.ts index 8536ade18f97..d8da4eda359a 100644 --- a/src/dev/file.ts +++ b/src/dev/file.ts @@ -31,6 +31,7 @@ */ import { dirname, extname, join, relative, resolve, sep, basename } from 'path'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; export class File { private path: string; @@ -39,7 +40,7 @@ export class File { constructor(path: string) { this.path = resolve(path); - this.relativePath = relative(process.cwd(), this.path); + this.relativePath = relative(PROCESS_WORKING_DIR, this.path); this.ext = extname(this.path); } diff --git a/src/dev/i18n/integrate_locale_files.test.ts b/src/dev/i18n/integrate_locale_files.test.ts index dd5d97c162bc..27a6a95e0adf 100644 --- a/src/dev/i18n/integrate_locale_files.test.ts +++ b/src/dev/i18n/integrate_locale_files.test.ts @@ -32,11 +32,12 @@ import { mockMakeDirAsync, mockWriteFileAsync } from './integrate_locale_files.test.mocks'; -import path from 'path'; +import { resolve } from 'path'; import { integrateLocaleFiles, verifyMessages } from './integrate_locale_files'; -import { normalizePath } from './utils'; +import { relativeToRepoRoot, standardize } from '@osd/cross-platform'; -const localePath = path.resolve(__dirname, '__fixtures__', 'integrate_locale_files', 'fr.json'); +const currentDir = relativeToRepoRoot(__dirname); +const localePath = resolve(currentDir, '__fixtures__', 'integrate_locale_files', 'fr.json'); const mockDefaultMessagesMap = new Map([ ['plugin-1.message-id-1', { message: 'Message text 1' }], @@ -182,9 +183,12 @@ Map { const [[path1, json1], [path2, json2]] = mockWriteFileAsync.mock.calls; const [[dirPath1], [dirPath2]] = mockMakeDirAsync.mock.calls; - expect([normalizePath(path1), json1]).toMatchSnapshot(); - expect([normalizePath(path2), json2]).toMatchSnapshot(); - expect([normalizePath(dirPath1), normalizePath(dirPath2)]).toMatchSnapshot(); + expect([standardize(relativeToRepoRoot(path1)), json1]).toMatchSnapshot(); + expect([standardize(relativeToRepoRoot(path2)), json2]).toMatchSnapshot(); + expect([ + standardize(relativeToRepoRoot(dirPath1)), + standardize(relativeToRepoRoot(dirPath2)), + ]).toMatchSnapshot(); }); }); }); diff --git a/src/dev/i18n/utils/utils.js b/src/dev/i18n/utils/utils.js index 743a6142b79a..fd405d3fb529 100644 --- a/src/dev/i18n/utils/utils.js +++ b/src/dev/i18n/utils/utils.js @@ -50,6 +50,7 @@ import chalk from 'chalk'; import parser from 'intl-messageformat-parser'; import { createFailError } from '@osd/dev-utils'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; const ESCAPE_LINE_BREAK_REGEX = /(?/src/plugins/maps_legacy', '/src/plugins/region_map', + '/src/cli_plugin/list/.test.data.list', ], testEnvironment: 'jest-environment-jsdom-thirteen', testMatch: ['**/*.test.{js,mjs,ts,tsx}'], diff --git a/src/dev/jest/integration_tests/junit_reporter.test.js b/src/dev/jest/integration_tests/junit_reporter.test.js index 25abcfb2c3b6..6d163efdb63c 100644 --- a/src/dev/jest/integration_tests/junit_reporter.test.js +++ b/src/dev/jest/integration_tests/junit_reporter.test.js @@ -38,6 +38,7 @@ import del from 'del'; import execa from 'execa'; import xml2js from 'xml2js'; import { getUniqueJunitReportPath } from '@osd/test'; +import { PROCESS_WORKING_DIR } from '@osd/cross-platform'; const MINUTE = 1000 * 60; const ROOT_DIR = resolve(__dirname, '../../../../'); @@ -46,7 +47,7 @@ const TARGET_DIR = resolve(FIXTURE_DIR, 'target'); const XML_PATH = getUniqueJunitReportPath(FIXTURE_DIR, 'Jest Tests'); afterAll(async () => { - await del(TARGET_DIR); + await del(TARGET_DIR, { cwd: PROCESS_WORKING_DIR }); }); const parseXml = promisify(xml2js.parseString);