diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c0b3e9e16f75..79cd564f63ba 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -294,6 +294,9 @@ Here is a list of the npm packages in this repository:
| Folder Name | Package Name | Purpose |
| :----------------------------------------------------- | :--------------------------------- | :--------------------------------------------------------------------------- |
+ | [eslint-plugin-dev](./npm/eslint-plugin-dev) | `@cypress/eslint-plugin-dev` | Eslint plugin for internal development. |
+ | [react](./npm/react) | `@cypress/react` | Cypress component testing for React. |
+ | [vue](./npm/vue) | `@cypress/vue` | Cypress component testing for Vue. |
| [webpack-preprocessor](./npm/webpack-preprocessor) | `@cypress/webpack-preprocessor` | Cypress preprocessor for bundling JavaScript via webpack. |
We try to tag all issues with a `pkg/` or `npm/` tag describing the appropriate package the work is required in. For public packages, we use their qualified package name: For example, issues relating to the webpack preprocessor are tagged under [`npm: @cypress/webpack-preprocessor`](https://github.com/cypress-io/cypress/labels/npm%3A%20%40cypress%2Fwebpack-preprocessor) label and issues related to the `driver` package are tagged with the [`pkg/driver`](https://github.com/cypress-io/cypress/labels/pkg%2Fdriver) label.
@@ -356,6 +359,8 @@ $ yarn workspace @packages/server add my-new-dep1
$ yarn workspace @packages/server add --dev my-new-dep1
```
+Alternatively, you can directly add the dependency to the corresponding `package.json`.
+
#### Tasks
> Scripts are intended to be **run** from the **root of the repo**. **Do not install dependencies or run scripts from within a sub-directory.**
@@ -439,7 +444,7 @@ DEBUG=cypress:launcher
### Coding Style
We use [eslint](https://eslint.org/) to lint all JavaScript code and follow rules specified in
-[@cypress/eslint-plugin-dev](https://github.com/cypress-io/eslint-plugin-cypress) plugin.
+[@cypress/eslint-plugin-dev](./npm/eslint-plugin-cypress) plugin.
When you edit files, you can quickly fix all changed files before you commit using
@@ -464,14 +469,18 @@ This is to ensure that links do not go dead in older versions of Cypress when th
### Tests
-For most packages there are typically unit and some integration tests.
+For most packages there are typically unit and integration tests.
Our true e2e tests are in [`packages/server`](packages/server), which test the full stack all together.
+Additionally, we test the code by running it against various other example projects in CI. See CI badges and links at the top of this document.
+
Please refer to each packages' `README.md` which documents how to run tests. It is not feasible to try to run all of the tests together. We run our entire test fleet across over a dozen containers in CI.
If you're curious how we manage all of these tests in CI check out our [`circle.yml`](circle.yml) file found in the root `cypress` directory.
+Each of the independent packages (in the [`/npm`](./npm) folder) have a `ciJobs` field in their `package.json`. This field corresponds to the CI jobs for that package and is used when determining what tests must pass before the package can be released.
+
#### Docker
Sometimes tests pass locally, but fail in CI. Our CI environment is dockerized. In order to run the image used in CI locally:
@@ -559,6 +568,52 @@ All updates to `master` are automatically merged into `develop`, so `develop` al
+### Independent Packages CI Workflow
+
+Independent packages are automatically released when code is merged into `master`. In order to make these automatic releases work smoothly, independent packages have a couple of special configuration options in their `package.json`.
+
+##### `ciJobs`
+
+List of Circle CI jobs that directly test the current package. These tests must pass before the package can be released.
+
+In addition, these tests will run when a PR is created that changes this package. All tests will run on `develop` and `master`, regardless of what packages were changed.
+
+Note: CI jobs should be unique to a package. Any jobs that are not listed within a `ciJobs` field are considered to be part of the binary and will only run when the binary is changed.
+
+This option takes an array of CI job names.
+
+Example
+```json
+{
+ "ciJobs": [
+ "npm-react",
+ "npm-react-axe",
+ "npm-react-next"
+ ]
+}
+```
+
+##### `ciDependents`
+
+List of local independent (npm) packages that are dependent on the current package. The tests specified in these packages' `ciJobs` must pass before the current package will be released.
+
+When the current package is changed in a PR, it will consider these packages to be changed as well and run CI accordingly.
+
+This option takes an array of package names.
+
+Example
+```json
+{
+ "ciDependents": [
+ "@cypress/react",
+ "@cypress/vue",
+ "@cypress/webpack-preprocessor"
+ ]
+}
+```
+
+You can read more about our CI design decisions in [#8730](https://github.com/cypress-io/cypress/pull/8730#issue-496593325)
+
### Pull Requests
- When opening a PR for a specific issue already open, please name the branch you are working on using the convention `issue-[issue number]`. For example, if your PR fixes Issue #803, name your branch `issue-803`. If the PR is a larger issue, you can add more context like `issue-803-new-scrollable-area` If there is not an associated open issue, **create an issue using our [Issue Template](./.github/ISSUE_TEMPLATE.md)**.
@@ -568,12 +623,6 @@ All updates to `master` are automatically merged into `develop`, so `develop` al
- Please check the "Allow edits from maintainers" checkbox when submitting your PR. This will make it easier for the maintainers to make minor adjustments, to help with tests or any other changes we may need.
![Allow edits from maintainers checkbox](https://user-images.githubusercontent.com/1271181/31393427-b3105d44-ada9-11e7-80f2-0dac51e3919e.png)
-### Testing
-
-This repository is exhaustively tested by [CircleCI](https://circleci.com/gh/cypress-io/cypress). Additionally we test the code by running it against various other example projects. See CI badges and links at the top of this document.
-
-To run local tests, consult the `README.md` of each package.
-
### Dependencies
We use [RenovateBot](https://renovatebot.com/) to automatically upgrade our dependencies. The bot uses the settings in [renovate.json](renovate.json) to maintain our [Update Dependencies](https://github.com/cypress-io/cypress/issues/3777) issue and open PRs. You can manually select a package to open a PR from our [Update Dependencies](https://github.com/cypress-io/cypress/issues/3777) issue.
@@ -651,7 +700,9 @@ Below are some guidelines Cypress uses when reviewing dependency updates.
## Deployment
-We will try to review and merge pull requests quickly. After merging we will try releasing a new version. If you want to know our build process or build your own Cypress binary, read [DEPLOY.md](./DEPLOY.md)
+We will try to review and merge pull requests quickly. If you want to know our build process or build your own Cypress binary, read [DEPLOY.md](./DEPLOY.md).
+
+Independent packages are deployed immediately upon being merged into master. You can read more [above](#independent-packages-ci-workflow).
## Known problems
diff --git a/DEPLOY.md b/DEPLOY.md
index 3d3150092922..d4c4d86d6cbc 100644
--- a/DEPLOY.md
+++ b/DEPLOY.md
@@ -1,6 +1,8 @@
# Deployment
-These deployment procedures mainly concern the Cypress binary and `cypress` npm module. Independent `@cypress/` packages that live inside the [`npm`](./npm) directory are automatically published to npm (with [`semantic-release`](https://semantic-release.gitbook.io/semantic-release/)) upon being merged into master.
+These deployment procedures mainly concern the Cypress binary and `cypress` npm module.
+
+Independent `@cypress/` packages that live inside the [`npm`](./npm) directory are automatically published to npm (with [`semantic-release`](https://semantic-release.gitbook.io/semantic-release/)) upon being merged into master. You can read more about this in [CONTRIBUTING.md](./CONTRIBUTING.md#committing-code)
Anyone can build the binary and npm package, but you can only deploy the Cypress application and publish the npm module `cypress` if you are a member of the `cypress` npm organization.
diff --git a/circle.yml b/circle.yml
index 2d49c0c14a6d..8347b98956ff 100644
--- a/circle.yml
+++ b/circle.yml
@@ -120,6 +120,7 @@ commands:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
environment:
CYPRESS_KONFIG_ENV: production
@@ -161,6 +162,7 @@ commands:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: |
cmd=$([[ <> == 'true' ]] && echo 'yarn percy exec --') || true
@@ -184,6 +186,7 @@ commands:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: yarn workspace @packages/server test ./test/e2e/$(( $CIRCLE_NODE_INDEX ))_*spec* --browser <>
- verify-mocha-results
@@ -281,6 +284,7 @@ commands:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# make sure the binary and NPM package files are present
- run: ls -l
- run: ls -l cypress.zip cypress.tgz
@@ -381,6 +385,13 @@ commands:
name: "Waiting on Circle CI jobs: <>"
command: node ./scripts/wait-on-circle-jobs.js --job-names="<>"
+ check-halt:
+ description: Halt CI if the package that this job corresponds to is unchanged
+ steps:
+ - run:
+ name: Check if job should run
+ command: yarn check-halt || circleci-agent step halt
+
jobs:
## code checkout and yarn installs
build:
@@ -459,6 +470,7 @@ jobs:
root: ~/
paths:
- cypress
+ - .ssh
lint:
<<: *defaults
@@ -478,6 +490,15 @@ jobs:
command: node cli/bin/cypress info --dev
- store-npm-logs
+ list-changed-packages:
+ <<: *defaults
+ steps:
+ - attach_workspace:
+ at: ~/
+ - run:
+ name: List changed packages
+ command: node scripts/changed-packages.js
+
# a special job that keeps polling Circle and when all
# individual jobs are finished, it closes the Percy build
percy-finalize:
@@ -515,6 +536,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run: mkdir -p cli/visual-snapshots
- run:
command: node cli/bin/cypress info --dev | yarn --silent term-to-html | node scripts/sanitize --type cli-info > cli/visual-snapshots/cypress-info.html
@@ -539,6 +561,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# make sure mocha runs
- run: yarn test-mocha
# test binary build code
@@ -584,6 +607,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- install-required-node
- run:
name: Mocha tests
@@ -603,6 +627,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run: yarn test-unit --scope @packages/server
- verify-mocha-results:
expectedResultCount: 1
@@ -616,6 +641,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run: yarn test-integration --scope @packages/server
- verify-mocha-results:
expectedResultCount: 1
@@ -628,6 +654,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: yarn workspace @packages/server test-performance
- verify-mocha-results:
@@ -708,6 +735,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: yarn build-prod
working_directory: packages/desktop-gui
@@ -733,6 +761,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: yarn build-prod
working_directory: packages/desktop-gui
@@ -767,6 +796,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
# builds JS and CSS, and we need the app CSS
# to correctly apply component styles
@@ -800,6 +830,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: yarn build-for-tests
working_directory: packages/reporter
@@ -821,6 +852,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: yarn build-for-tests
working_directory: packages/ui-components
@@ -842,6 +874,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
command: node index.js
working_directory: packages/launcher
@@ -851,6 +884,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
name: Build
command: yarn workspace @cypress/webpack-preprocessor build
@@ -884,23 +918,12 @@ jobs:
working_directory: npm/webpack-preprocessor/examples/react-app
- store-npm-logs
- npm-webpack-preprocessor-release:
- <<: *defaults
- steps:
- - attach_workspace:
- at: ~/
- - run:
- name: Build
- command: yarn workspace @cypress/webpack-preprocessor build
- - run:
- name: Release
- command: yarn semantic-release @cypress/webpack-preprocessor
-
npm-vue:
<<: *defaults
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
name: Build
command: yarn workspace @cypress/vue build
@@ -909,20 +932,12 @@ jobs:
command: yarn workspace @cypress/vue test
- store-npm-logs
- npm-vue-release:
- <<: *defaults
- steps:
- - attach_workspace:
- at: ~/
- - run:
- name: Release
- command: yarn semantic-release @cypress/vue
-
npm-react:
<<: *defaults
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
name: Build
command: yarn workspace @cypress/react build
@@ -951,6 +966,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- restore_cache:
name: Restore Cache
keys:
@@ -969,32 +985,24 @@ jobs:
command: yarn test
working_directory: npm/react/<>
- npm-react-release:
- <<: *defaults
- steps:
- - attach_workspace:
- at: ~/
- - run:
- name: Release
- command: yarn semantic-release @cypress/react
-
npm-eslint-plugin-dev:
<<: *defaults
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run:
name: Run tests
command: yarn workspace @cypress/eslint-plugin-dev test
- npm-eslint-plugin-dev-release:
+ npm-release:
<<: *defaults
steps:
- attach_workspace:
at: ~/
- run:
- name: Release
- command: yarn semantic-release @cypress/eslint-plugin-dev
+ name: Release packages
+ command: yarn npm-release
build-binary:
<<: *defaults
@@ -1029,6 +1037,7 @@ jobs:
- attach_workspace:
at: ~/
+ - check-halt
- run: $(yarn bin)/print-arch
- install-required-node
- run:
@@ -1061,6 +1070,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run: ls -l
- run:
name: upload unique binary
@@ -1078,6 +1088,7 @@ jobs:
test-kitchensink:
<<: *defaults
steps:
+ - check-halt
- clone-repo-and-checkout-release-branch:
repo: cypress-example-kitchensink
- install-required-node
@@ -1104,6 +1115,7 @@ jobs:
"test-kitchensink-against-staging":
<<: *defaults
steps:
+ - check-halt
- clone-repo-and-checkout-release-branch:
repo: cypress-example-kitchensink
- run:
@@ -1128,6 +1140,7 @@ jobs:
"test-against-staging":
<<: *defaults
steps:
+ - check-halt
- clone-repo-and-checkout-release-branch:
repo: cypress-test-tiny
- run:
@@ -1144,6 +1157,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- install-required-node
- run:
name: Check next dev version
@@ -1190,6 +1204,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run: ls -l
# NPM package file should have filename cypress-.tgz
- run:
@@ -1212,6 +1227,7 @@ jobs:
# needs uploaded NPM and test binary
- attach_workspace:
at: ~/
+ - check-halt
- run: ls -la
# make sure JSON files with uploaded urls are present
- run: ls -la binary-url.json npm-package-url.json
@@ -1249,6 +1265,7 @@ jobs:
# needs uploaded NPM and test binary
- attach_workspace:
at: ~/
+ - check-halt
- run: ls -la
- post-install-comment
@@ -1257,6 +1274,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# make sure we have cypress.zip received
- run: ls -l
- run: ls -l cypress.zip cypress.tgz
@@ -1292,6 +1310,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# make sure we have cypress.zip received
- run: ls -l
- run: ls -l cypress.zip cypress.tgz
@@ -1333,6 +1352,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# make sure we have cypress.zip received
- run: ls -l
- run: ls -l cypress.zip cypress.tgz
@@ -1376,6 +1396,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# make sure we have cypress.zip received
- run: ls -l
- run: ls -l cypress.zip cypress.tgz
@@ -1419,6 +1440,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# make sure we have cypress.zip received
- run: ls -l
- run: ls -l cypress.zip cypress.tgz
@@ -1451,6 +1473,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
- run: ls -l
# make sure we have the binary and NPM package
- run: ls -l cypress.zip cypress.tgz
@@ -1586,6 +1609,7 @@ jobs:
steps:
- attach_workspace:
at: ~/
+ - check-halt
# the user should be "node"
- run: whoami
- run: pwd
@@ -1636,6 +1660,9 @@ linux-workflow: &linux-workflow
name: Linux lint
requires:
- build
+ - list-changed-packages:
+ requires:
+ - build
- percy-finalize:
context: test-runner:poll-circle-workflow
required_env_var: PERCY_TOKEN # skips job if not defined (external PR)
@@ -1712,69 +1739,50 @@ linux-workflow: &linux-workflow
- npm-webpack-preprocessor:
requires:
- build
- - npm-webpack-preprocessor-release:
- filters:
- branches:
- only:
- - master
- requires:
- - build
- - npm-webpack-preprocessor
-
- npm-vue:
requires:
- build
- - npm-vue-release:
- filters:
- branches:
- only:
- - master
- requires:
- - build
- - npm-vue
-
- npm-react:
requires:
- build
-
# Run tests for end-to-end react component tests examples
- npm-react-e2e-example:
- name: React + Axe component testing
+ name: npm-react-axe
path: examples/a11y
requires:
- npm-react
- npm-react-e2e-example:
- name: React + Next component testing
+ name: npm-react-next
path: examples/nextjs
requires:
- npm-react
- npm-react-e2e-example:
- name: React + CRA component testing
+ name: npm-react-cra
path: examples/react-scripts
requires:
- npm-react
- npm-react-e2e-example:
- name: React + CRA folder component testing
+ name: npm-react-cra-folder
path: examples/react-scripts-folder
requires:
- npm-react
- npm-react-e2e-example:
- name: React + Rollup component testing
+ name: npm-react-rollup
path: examples/rollup
requires:
- npm-react
- npm-react-e2e-example:
- name: React + Sass + TS component testing
+ name: npm-react-sass-ts
path: examples/sass-and-ts
requires:
- npm-react
- npm-react-e2e-example:
- name: React + Snapshots component testing
+ name: npm-react-snapshots
path: examples/snapshots
requires:
- npm-react
- npm-react-e2e-example:
- name: React + Tailwind component testing
+ name: npm-react-tailwind
path: examples/tailwind
requires:
- npm-react
@@ -1785,7 +1793,7 @@ linux-workflow: &linux-workflow
# requires:
# - npm-react
- npm-react-e2e-example:
- name: React + Percy component testing
+ name: npm-react-percy
path: examples/visual-testing-with-percy
requires:
- npm-react
@@ -1796,36 +1804,23 @@ linux-workflow: &linux-workflow
# requires:
# - npm-react
- npm-react-e2e-example:
- name: React + Webpack file component testing
+ name: npm-react-webpack-file
path: examples/webpack-file
requires:
- npm-react
- npm-react-e2e-example:
- name: React + Webpack options component testing
+ name: npm-react-webpack-options
path: examples/webpack-options
requires:
- npm-react
- - npm-react-release:
- filters:
- branches:
- only:
- - master
- requires:
- - build
- - npm-vue
-
- npm-eslint-plugin-dev:
requires:
- build
- - npm-eslint-plugin-dev-release:
- filters:
- branches:
- only:
- - master
+
+ - npm-release:
requires:
- build
- - npm-eslint-plugin-dev
# various testing scenarios, like building full binary
# and testing it on a real project
diff --git a/npm/eslint-plugin-dev/package.json b/npm/eslint-plugin-dev/package.json
index 2b1a8dd29b2a..9e91d78b4638 100644
--- a/npm/eslint-plugin-dev/package.json
+++ b/npm/eslint-plugin-dev/package.json
@@ -53,5 +53,8 @@
"cypress",
"eslint",
"eslintplugin"
+ ],
+ "ciJobs": [
+ "npm-eslint-plugin-dev"
]
}
diff --git a/npm/react/package.json b/npm/react/package.json
index 5bbab242b2b1..e987ac38ae59 100644
--- a/npm/react/package.json
+++ b/npm/react/package.json
@@ -159,6 +159,20 @@
"optional": true
}
},
+ "ciJobs": [
+ "npm-react",
+ "npm-react-axe",
+ "npm-react-next",
+ "npm-react-cra",
+ "npm-react-cra-folder",
+ "npm-react-rollup",
+ "npm-react-sass-ts",
+ "npm-react-snapshots",
+ "npm-react-tailwind",
+ "npm-react-percy",
+ "npm-react-webpack-file",
+ "npm-react-webpack-options"
+ ],
"standard": {
"globals": [
"Cypress",
diff --git a/npm/vue/package.json b/npm/vue/package.json
index 9cea65ab81a2..0adbc08fcfca 100644
--- a/npm/vue/package.json
+++ b/npm/vue/package.json
@@ -73,5 +73,8 @@
],
"publishConfig": {
"registry": "http://registry.npmjs.org/"
- }
+ },
+ "ciJobs": [
+ "npm-vue"
+ ]
}
diff --git a/npm/webpack-preprocessor/package.json b/npm/webpack-preprocessor/package.json
index 9f91c731f5e9..c1486e9946a8 100644
--- a/npm/webpack-preprocessor/package.json
+++ b/npm/webpack-preprocessor/package.json
@@ -88,5 +88,12 @@
"cypress-plugin",
"cypress-preprocessor",
"webpack"
+ ],
+ "ciDependents": [
+ "@cypress/react",
+ "@cypress/vue"
+ ],
+ "ciJobs": [
+ "npm-webpack-preprocessor"
]
}
diff --git a/package.json b/package.json
index 825e904bd110..b2fa31cd307b 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"build": "lerna run build --stream",
"build-prod": "lerna run build-prod --stream",
"bump": "node ./scripts/binary.js bump",
+ "check-halt": "node scripts/check-halt.js",
"check-next-dev-version": "node scripts/check-next-dev-version.js",
"check-node-version": "node scripts/check-node-version.js",
"check-terminal": "node scripts/check-terminal.js",
@@ -41,6 +42,7 @@
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json .",
"lint-changed": "lint-changed",
"move-binaries": "node ./scripts/binary.js move-binaries",
+ "npm-release": "node scripts/npm-release.js",
"semantic-release": "node ./scripts/semantic-release.js",
"set-next-ci-version": "node ./scripts/binary.js setNextVersion",
"prestart": "yarn ensure-deps",
@@ -180,6 +182,7 @@
"ramda": "0.24.1",
"semantic-release": "17.1.1",
"semantic-release-monorepo": "7.0.3",
+ "semver": "7.3.2",
"shelljs": "0.8.3",
"shx": "0.3.2",
"sinon": "7.3.2",
diff --git a/patches/semantic-release-monorepo+7.0.3.patch b/patches/semantic-release-monorepo+7.0.3.patch
deleted file mode 100644
index 343896a48b66..000000000000
--- a/patches/semantic-release-monorepo+7.0.3.patch
+++ /dev/null
@@ -1,142 +0,0 @@
-diff --git a/node_modules/semantic-release-monorepo/src/lerna-utils.js b/node_modules/semantic-release-monorepo/src/lerna-utils.js
-new file mode 100644
-index 0000000..1b620f8
---- /dev/null
-+++ b/node_modules/semantic-release-monorepo/src/lerna-utils.js
-@@ -0,0 +1,24 @@
-+const execa = require('execa');
-+
-+const lerna = async (args, options = {}) => {
-+ const { stdout } = await execa('lerna', args, options);
-+ return stdout;
-+};
-+
-+/**
-+ * @async
-+ * @return {Promise